Initial import of Cobalt 2.8885 2016-07-27
diff --git a/src/base/allocator/README b/src/base/allocator/README
new file mode 100644
index 0000000..ec8a707
--- /dev/null
+++ b/src/base/allocator/README
@@ -0,0 +1,59 @@
+Notes about the Chrome memory allocator.
+
+Background
+----------
+We use this library as a generic way to fork into any of several allocators.
+Currently we can, at runtime, switch between:
+ the default windows allocator
+ the windows low-fragmentation-heap
+ tcmalloc
+ jemalloc (the heap used most notably within Mozilla Firefox)
+
+The mechanism for hooking LIBCMT in windows is rather tricky. The core
+problem is that by default, the windows library does not declare malloc and
+free as weak symbols. Because of this, they cannot be overriden. To work
+around this, we start with the LIBCMT.LIB, and manually remove all allocator
+related functions from it using the visual studio library tool. Once removed,
+we can now link against the library and provide custom versions of the
+allocator related functionality.
+
+
+Source code
+-----------
+This directory contains just the allocator (i.e. shim) layer that switches
+between the different underlying memory allocation implementations.
+
+The tcmalloc and jemalloc libraries originate outside of Chromium
+and exist in ../../third_party/tcmalloc and ../../third_party/jemalloc
+(currently, the actual locations are defined in the allocator.gyp file).
+The third party sources use a vendor-branch SCM pattern to track
+Chromium-specific changes independently from upstream changes.
+
+The general intent is to push local changes upstream so that over
+time we no longer need any forked files.
+
+
+Adding a new allocator
+----------------------
+Adding a new allocator requires definition of the following five functions:
+
+ extern "C" {
+ bool init();
+ void* malloc(size_t s);
+ void* realloc(void* p, size_t s);
+ void free(void* s);
+ size_t msize(void* p);
+ }
+
+All other allocation related functions (new/delete/calloc/etc) have been
+implemented generically to work across all allocators.
+
+
+Usage
+-----
+You can use the different allocators by setting the environment variable
+CHROME_ALLOCATOR to:
+ "tcmalloc" - TC Malloc (default)
+ "jemalloc" - JE Malloc
+ "winheap" - Windows default heap
+ "winlfh" - Windows Low-Fragmentation heap
diff --git a/src/base/allocator/allocator.gyp b/src/base/allocator/allocator.gyp
new file mode 100644
index 0000000..f5bda73
--- /dev/null
+++ b/src/base/allocator/allocator.gyp
@@ -0,0 +1,671 @@
+# Copyright (c) 2012 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': {
+ 'jemalloc_dir': '../../third_party/jemalloc/chromium',
+ 'tcmalloc_dir': '../../third_party/tcmalloc/chromium',
+ 'use_vtable_verify%': 0,
+ },
+ 'targets': [
+ # Only executables and not libraries should depend on the
+ # allocator target; only the application (the final executable)
+ # knows what allocator makes sense.
+ {
+ 'target_name': 'allocator',
+ 'type': 'static_library',
+ # Make sure the allocation library is optimized to
+ # the hilt in official builds.
+ 'variables': {
+ 'optimize': 'max',
+ },
+ 'include_dirs': [
+ '.',
+ '<(tcmalloc_dir)/src/base',
+ '<(tcmalloc_dir)/src',
+ '../..',
+ ],
+ 'direct_dependent_settings': {
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'IgnoreDefaultLibraryNames': ['libcmtd.lib', 'libcmt.lib'],
+ 'AdditionalDependencies': [
+ '<(SHARED_INTERMEDIATE_DIR)/allocator/libcmt.lib'
+ ],
+ },
+ },
+ },
+ },
+ 'conditions': [
+ ['OS=="win"', {
+ 'defines': [
+ 'PERFTOOLS_DLL_DECL=',
+ ],
+ }],
+ ],
+ },
+ 'sources': [
+ # Generated for our configuration from tcmalloc's build
+ # and checked in.
+ '<(tcmalloc_dir)/src/config.h',
+ '<(tcmalloc_dir)/src/config_linux.h',
+ '<(tcmalloc_dir)/src/config_win.h',
+
+ # all tcmalloc native and forked files
+ '<(tcmalloc_dir)/src/addressmap-inl.h',
+ '<(tcmalloc_dir)/src/base/abort.cc',
+ '<(tcmalloc_dir)/src/base/abort.h',
+ '<(tcmalloc_dir)/src/base/arm_instruction_set_select.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-linuxppc.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-arm-generic.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-arm-v6plus.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-macosx.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-windows.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-x86.cc',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-x86.h',
+ '<(tcmalloc_dir)/src/base/atomicops.h',
+ '<(tcmalloc_dir)/src/base/basictypes.h',
+ '<(tcmalloc_dir)/src/base/commandlineflags.h',
+ '<(tcmalloc_dir)/src/base/cycleclock.h',
+ # We don't list dynamic_annotations.c since its copy is already
+ # present in the dynamic_annotations target.
+ '<(tcmalloc_dir)/src/base/dynamic_annotations.h',
+ '<(tcmalloc_dir)/src/base/elf_mem_image.cc',
+ '<(tcmalloc_dir)/src/base/elf_mem_image.h',
+ '<(tcmalloc_dir)/src/base/elfcore.h',
+ '<(tcmalloc_dir)/src/base/googleinit.h',
+ '<(tcmalloc_dir)/src/base/linux_syscall_support.h',
+ '<(tcmalloc_dir)/src/base/linuxthreads.cc',
+ '<(tcmalloc_dir)/src/base/linuxthreads.h',
+ '<(tcmalloc_dir)/src/base/logging.cc',
+ '<(tcmalloc_dir)/src/base/logging.h',
+ '<(tcmalloc_dir)/src/base/low_level_alloc.cc',
+ '<(tcmalloc_dir)/src/base/low_level_alloc.h',
+ '<(tcmalloc_dir)/src/base/simple_mutex.h',
+ '<(tcmalloc_dir)/src/base/spinlock.cc',
+ '<(tcmalloc_dir)/src/base/spinlock.h',
+ '<(tcmalloc_dir)/src/base/spinlock_internal.cc',
+ '<(tcmalloc_dir)/src/base/spinlock_internal.h',
+ '<(tcmalloc_dir)/src/base/spinlock_linux-inl.h',
+ '<(tcmalloc_dir)/src/base/spinlock_posix-inl.h',
+ '<(tcmalloc_dir)/src/base/spinlock_win32-inl.h',
+ '<(tcmalloc_dir)/src/base/stl_allocator.h',
+ '<(tcmalloc_dir)/src/base/synchronization_profiling.h',
+ '<(tcmalloc_dir)/src/base/sysinfo.cc',
+ '<(tcmalloc_dir)/src/base/sysinfo.h',
+ '<(tcmalloc_dir)/src/base/thread_annotations.h',
+ '<(tcmalloc_dir)/src/base/thread_lister.c',
+ '<(tcmalloc_dir)/src/base/thread_lister.h',
+ '<(tcmalloc_dir)/src/base/vdso_support.cc',
+ '<(tcmalloc_dir)/src/base/vdso_support.h',
+ '<(tcmalloc_dir)/src/central_freelist.cc',
+ '<(tcmalloc_dir)/src/central_freelist.h',
+ '<(tcmalloc_dir)/src/common.cc',
+ '<(tcmalloc_dir)/src/common.h',
+ '<(tcmalloc_dir)/src/debugallocation.cc',
+ '<(tcmalloc_dir)/src/deep-heap-profile.cc',
+ '<(tcmalloc_dir)/src/deep-heap-profile.h',
+ '<(tcmalloc_dir)/src/free_list.cc',
+ '<(tcmalloc_dir)/src/free_list.h',
+ '<(tcmalloc_dir)/src/getpc.h',
+ '<(tcmalloc_dir)/src/gperftools/heap-checker.h',
+ '<(tcmalloc_dir)/src/gperftools/heap-profiler.h',
+ '<(tcmalloc_dir)/src/gperftools/malloc_extension.h',
+ '<(tcmalloc_dir)/src/gperftools/malloc_extension_c.h',
+ '<(tcmalloc_dir)/src/gperftools/malloc_hook.h',
+ '<(tcmalloc_dir)/src/gperftools/malloc_hook_c.h',
+ '<(tcmalloc_dir)/src/gperftools/profiler.h',
+ '<(tcmalloc_dir)/src/gperftools/stacktrace.h',
+ '<(tcmalloc_dir)/src/gperftools/tcmalloc.h',
+ '<(tcmalloc_dir)/src/heap-checker-bcad.cc',
+ '<(tcmalloc_dir)/src/heap-checker.cc',
+ '<(tcmalloc_dir)/src/heap-profile-table.cc',
+ '<(tcmalloc_dir)/src/heap-profile-table.h',
+ '<(tcmalloc_dir)/src/heap-profiler.cc',
+ '<(tcmalloc_dir)/src/internal_logging.cc',
+ '<(tcmalloc_dir)/src/internal_logging.h',
+ '<(tcmalloc_dir)/src/libc_override.h',
+ '<(tcmalloc_dir)/src/libc_override_gcc_and_weak.h',
+ '<(tcmalloc_dir)/src/libc_override_glibc.h',
+ '<(tcmalloc_dir)/src/libc_override_osx.h',
+ '<(tcmalloc_dir)/src/libc_override_redefine.h',
+ '<(tcmalloc_dir)/src/linked_list.h',
+ '<(tcmalloc_dir)/src/malloc_extension.cc',
+ '<(tcmalloc_dir)/src/malloc_hook-inl.h',
+ '<(tcmalloc_dir)/src/malloc_hook.cc',
+ '<(tcmalloc_dir)/src/malloc_hook_mmap_freebsd.h',
+ '<(tcmalloc_dir)/src/malloc_hook_mmap_linux.h',
+ '<(tcmalloc_dir)/src/maybe_threads.cc',
+ '<(tcmalloc_dir)/src/maybe_threads.h',
+ '<(tcmalloc_dir)/src/memfs_malloc.cc',
+ '<(tcmalloc_dir)/src/memory_region_map.cc',
+ '<(tcmalloc_dir)/src/memory_region_map.h',
+ '<(tcmalloc_dir)/src/packed-cache-inl.h',
+ '<(tcmalloc_dir)/src/page_heap.cc',
+ '<(tcmalloc_dir)/src/page_heap.h',
+ '<(tcmalloc_dir)/src/page_heap_allocator.h',
+ '<(tcmalloc_dir)/src/pagemap.h',
+ '<(tcmalloc_dir)/src/profile-handler.cc',
+ '<(tcmalloc_dir)/src/profile-handler.h',
+ '<(tcmalloc_dir)/src/profiledata.cc',
+ '<(tcmalloc_dir)/src/profiledata.h',
+ '<(tcmalloc_dir)/src/profiler.cc',
+ '<(tcmalloc_dir)/src/raw_printer.cc',
+ '<(tcmalloc_dir)/src/raw_printer.h',
+ '<(tcmalloc_dir)/src/sampler.cc',
+ '<(tcmalloc_dir)/src/sampler.h',
+ '<(tcmalloc_dir)/src/span.cc',
+ '<(tcmalloc_dir)/src/span.h',
+ '<(tcmalloc_dir)/src/stack_trace_table.cc',
+ '<(tcmalloc_dir)/src/stack_trace_table.h',
+ '<(tcmalloc_dir)/src/stacktrace.cc',
+ '<(tcmalloc_dir)/src/stacktrace_arm-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_config.h',
+ '<(tcmalloc_dir)/src/stacktrace_generic-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_libunwind-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_powerpc-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_win32-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_with_context.cc',
+ '<(tcmalloc_dir)/src/stacktrace_x86-inl.h',
+ '<(tcmalloc_dir)/src/static_vars.cc',
+ '<(tcmalloc_dir)/src/static_vars.h',
+ '<(tcmalloc_dir)/src/symbolize.cc',
+ '<(tcmalloc_dir)/src/symbolize.h',
+ '<(tcmalloc_dir)/src/system-alloc.cc',
+ '<(tcmalloc_dir)/src/system-alloc.h',
+ '<(tcmalloc_dir)/src/tcmalloc.cc',
+ '<(tcmalloc_dir)/src/tcmalloc_guard.h',
+ '<(tcmalloc_dir)/src/thread_cache.cc',
+ '<(tcmalloc_dir)/src/thread_cache.h',
+ '<(tcmalloc_dir)/src/windows/config.h',
+ '<(tcmalloc_dir)/src/windows/get_mangled_names.cc',
+ '<(tcmalloc_dir)/src/windows/gperftools/tcmalloc.h',
+ '<(tcmalloc_dir)/src/windows/ia32_modrm_map.cc',
+ '<(tcmalloc_dir)/src/windows/ia32_opcode_map.cc',
+ '<(tcmalloc_dir)/src/windows/mingw.h',
+ '<(tcmalloc_dir)/src/windows/mini_disassembler.cc',
+ '<(tcmalloc_dir)/src/windows/mini_disassembler.h',
+ '<(tcmalloc_dir)/src/windows/mini_disassembler_types.h',
+ '<(tcmalloc_dir)/src/windows/override_functions.cc',
+ '<(tcmalloc_dir)/src/windows/patch_functions.cc',
+ '<(tcmalloc_dir)/src/windows/port.cc',
+ '<(tcmalloc_dir)/src/windows/port.h',
+ '<(tcmalloc_dir)/src/windows/preamble_patcher.cc',
+ '<(tcmalloc_dir)/src/windows/preamble_patcher.h',
+ '<(tcmalloc_dir)/src/windows/preamble_patcher_with_stub.cc',
+
+ # jemalloc files
+ '<(jemalloc_dir)/jemalloc.c',
+ '<(jemalloc_dir)/jemalloc.h',
+ '<(jemalloc_dir)/ql.h',
+ '<(jemalloc_dir)/qr.h',
+ '<(jemalloc_dir)/rb.h',
+
+ 'allocator_shim.cc',
+ 'allocator_shim.h',
+ 'debugallocation_shim.cc',
+ 'generic_allocators.cc',
+ 'win_allocator.cc',
+ ],
+ # sources! means that these are not compiled directly.
+ 'sources!': [
+ # Included by allocator_shim.cc for maximal inlining.
+ 'generic_allocators.cc',
+ 'win_allocator.cc',
+
+ # Included by debugallocation_shim.cc.
+ '<(tcmalloc_dir)/src/debugallocation.cc',
+ '<(tcmalloc_dir)/src/tcmalloc.cc',
+
+ # We simply don't use these, but list them above so that IDE
+ # users can view the full available source for reference, etc.
+ '<(tcmalloc_dir)/src/addressmap-inl.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-linuxppc.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-macosx.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-x86-msvc.h',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-x86.cc',
+ '<(tcmalloc_dir)/src/base/atomicops-internals-x86.h',
+ '<(tcmalloc_dir)/src/base/atomicops.h',
+ '<(tcmalloc_dir)/src/base/basictypes.h',
+ '<(tcmalloc_dir)/src/base/commandlineflags.h',
+ '<(tcmalloc_dir)/src/base/cycleclock.h',
+ '<(tcmalloc_dir)/src/base/elf_mem_image.h',
+ '<(tcmalloc_dir)/src/base/elfcore.h',
+ '<(tcmalloc_dir)/src/base/googleinit.h',
+ '<(tcmalloc_dir)/src/base/linux_syscall_support.h',
+ '<(tcmalloc_dir)/src/base/simple_mutex.h',
+ '<(tcmalloc_dir)/src/base/spinlock_linux-inl.h',
+ '<(tcmalloc_dir)/src/base/spinlock_posix-inl.h',
+ '<(tcmalloc_dir)/src/base/spinlock_win32-inl.h',
+ '<(tcmalloc_dir)/src/base/stl_allocator.h',
+ '<(tcmalloc_dir)/src/base/thread_annotations.h',
+ '<(tcmalloc_dir)/src/getpc.h',
+ '<(tcmalloc_dir)/src/gperftools/heap-checker.h',
+ '<(tcmalloc_dir)/src/gperftools/heap-profiler.h',
+ '<(tcmalloc_dir)/src/gperftools/malloc_extension.h',
+ '<(tcmalloc_dir)/src/gperftools/malloc_extension_c.h',
+ '<(tcmalloc_dir)/src/gperftools/malloc_hook.h',
+ '<(tcmalloc_dir)/src/gperftools/malloc_hook_c.h',
+ '<(tcmalloc_dir)/src/gperftools/profiler.h',
+ '<(tcmalloc_dir)/src/gperftools/stacktrace.h',
+ '<(tcmalloc_dir)/src/gperftools/tcmalloc.h',
+ '<(tcmalloc_dir)/src/libc_override.h',
+ '<(tcmalloc_dir)/src/libc_override_gcc_and_weak.h',
+ '<(tcmalloc_dir)/src/libc_override_glibc.h',
+ '<(tcmalloc_dir)/src/libc_override_osx.h',
+ '<(tcmalloc_dir)/src/libc_override_redefine.h',
+ '<(tcmalloc_dir)/src/malloc_hook_mmap_freebsd.h',
+ '<(tcmalloc_dir)/src/malloc_hook_mmap_linux.h',
+ '<(tcmalloc_dir)/src/memfs_malloc.cc',
+ '<(tcmalloc_dir)/src/packed-cache-inl.h',
+ '<(tcmalloc_dir)/src/page_heap_allocator.h',
+ '<(tcmalloc_dir)/src/pagemap.h',
+ '<(tcmalloc_dir)/src/stacktrace_arm-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_config.h',
+ '<(tcmalloc_dir)/src/stacktrace_generic-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_libunwind-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_powerpc-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_win32-inl.h',
+ '<(tcmalloc_dir)/src/stacktrace_with_context.cc',
+ '<(tcmalloc_dir)/src/stacktrace_x86-inl.h',
+ '<(tcmalloc_dir)/src/tcmalloc_guard.h',
+ '<(tcmalloc_dir)/src/windows/config.h',
+ '<(tcmalloc_dir)/src/windows/gperftools/tcmalloc.h',
+ '<(tcmalloc_dir)/src/windows/get_mangled_names.cc',
+ '<(tcmalloc_dir)/src/windows/ia32_modrm_map.cc',
+ '<(tcmalloc_dir)/src/windows/ia32_opcode_map.cc',
+ '<(tcmalloc_dir)/src/windows/mingw.h',
+ '<(tcmalloc_dir)/src/windows/mini_disassembler.cc',
+ '<(tcmalloc_dir)/src/windows/mini_disassembler.h',
+ '<(tcmalloc_dir)/src/windows/mini_disassembler_types.h',
+ '<(tcmalloc_dir)/src/windows/override_functions.cc',
+ '<(tcmalloc_dir)/src/windows/patch_functions.cc',
+ '<(tcmalloc_dir)/src/windows/preamble_patcher.cc',
+ '<(tcmalloc_dir)/src/windows/preamble_patcher.h',
+ '<(tcmalloc_dir)/src/windows/preamble_patcher_with_stub.cc',
+ ],
+ 'dependencies': [
+ '../third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ ],
+ 'msvs_settings': {
+ # TODO(sgk): merge this with build/common.gypi settings
+ 'VCLibrarianTool': {
+ 'AdditionalOptions': ['/ignore:4006,4221'],
+ 'AdditionalLibraryDirectories':
+ ['<(DEPTH)/third_party/platformsdk_win7/files/Lib'],
+ },
+ 'VCLinkerTool': {
+ 'AdditionalOptions': ['/ignore:4006'],
+ },
+ },
+ 'configurations': {
+ 'Debug_Base': {
+ 'msvs_settings': {
+ 'VCCLCompilerTool': {
+ 'RuntimeLibrary': '0',
+ },
+ },
+ 'variables': {
+ # Provide a way to force disable debugallocation in Debug builds,
+ # e.g. for profiling (it's more rare to profile Debug builds,
+ # but people sometimes need to do that).
+ 'disable_debugallocation%': 1,
+ },
+ 'conditions': [
+ ['disable_debugallocation==0', {
+ 'defines': [
+ # Use debugallocation for Debug builds to catch problems early
+ # and cleanly, http://crbug.com/30715 .
+ 'TCMALLOC_FOR_DEBUGALLOCATION',
+ ],
+ }],
+ ],
+ },
+ },
+ 'conditions': [
+ ['OS=="linux" and clang_type_profiler==1', {
+ 'dependencies': [
+ 'type_profiler_tcmalloc',
+ ],
+ # It is undoing dependencies and cflags_cc for type_profiler which
+ # build/common.gypi injects into all targets.
+ 'dependencies!': [
+ 'type_profiler',
+ ],
+ 'cflags_cc!': [
+ '-fintercept-allocation-functions',
+ ],
+ }],
+ ['OS=="win"', {
+ 'defines': [
+ 'PERFTOOLS_DLL_DECL=',
+ ],
+ 'defines!': [
+ # tcmalloc source files unconditionally define this, remove it from
+ # the list of defines that common.gypi defines globally.
+ 'NOMINMAX',
+ ],
+ 'dependencies': [
+ 'libcmt',
+ ],
+ 'include_dirs': [
+ '<(jemalloc_dir)',
+ '<(tcmalloc_dir)/src/windows',
+ ],
+ 'sources!': [
+ '<(tcmalloc_dir)/src/base/elf_mem_image.cc',
+ '<(tcmalloc_dir)/src/base/elf_mem_image.h',
+ '<(tcmalloc_dir)/src/base/linuxthreads.cc',
+ '<(tcmalloc_dir)/src/base/linuxthreads.h',
+ '<(tcmalloc_dir)/src/base/vdso_support.cc',
+ '<(tcmalloc_dir)/src/base/vdso_support.h',
+ '<(tcmalloc_dir)/src/maybe_threads.cc',
+ '<(tcmalloc_dir)/src/maybe_threads.h',
+ '<(tcmalloc_dir)/src/symbolize.h',
+ '<(tcmalloc_dir)/src/system-alloc.cc',
+ '<(tcmalloc_dir)/src/system-alloc.h',
+
+ # included by allocator_shim.cc
+ 'debugallocation_shim.cc',
+
+ # heap-profiler/checker/cpuprofiler
+ '<(tcmalloc_dir)/src/base/thread_lister.c',
+ '<(tcmalloc_dir)/src/base/thread_lister.h',
+ '<(tcmalloc_dir)/src/deep-heap-profile.cc',
+ '<(tcmalloc_dir)/src/deep-heap-profile.h',
+ '<(tcmalloc_dir)/src/heap-checker-bcad.cc',
+ '<(tcmalloc_dir)/src/heap-checker.cc',
+ '<(tcmalloc_dir)/src/heap-profiler.cc',
+ '<(tcmalloc_dir)/src/heap-profile-table.cc',
+ '<(tcmalloc_dir)/src/heap-profile-table.h',
+ '<(tcmalloc_dir)/src/memory_region_map.cc',
+ '<(tcmalloc_dir)/src/memory_region_map.h',
+ '<(tcmalloc_dir)/src/profiledata.cc',
+ '<(tcmalloc_dir)/src/profiledata.h',
+ '<(tcmalloc_dir)/src/profile-handler.cc',
+ '<(tcmalloc_dir)/src/profile-handler.h',
+ '<(tcmalloc_dir)/src/profiler.cc',
+ ],
+ }],
+ ['OS=="linux" or OS=="freebsd" or OS=="solaris"', {
+ 'sources!': [
+ '<(tcmalloc_dir)/src/system-alloc.h',
+ '<(tcmalloc_dir)/src/windows/port.cc',
+ '<(tcmalloc_dir)/src/windows/port.h',
+
+ # TODO(willchan): Support allocator shim later on.
+ 'allocator_shim.cc',
+
+ # TODO(willchan): support jemalloc on other platforms
+ # jemalloc files
+ '<(jemalloc_dir)/jemalloc.c',
+ '<(jemalloc_dir)/jemalloc.h',
+ '<(jemalloc_dir)/ql.h',
+ '<(jemalloc_dir)/qr.h',
+ '<(jemalloc_dir)/rb.h',
+
+ ],
+ # We enable all warnings by default, but upstream disables a few.
+ # Keep "-Wno-*" flags in sync with upstream by comparing against:
+ # http://code.google.com/p/google-perftools/source/browse/trunk/Makefile.am
+ 'cflags': [
+ '-Wno-sign-compare',
+ '-Wno-unused-result',
+ ],
+ 'cflags!': [
+ '-fvisibility=hidden',
+ ],
+ 'link_settings': {
+ 'ldflags': [
+ # Don't let linker rip this symbol out, otherwise the heap&cpu
+ # profilers will not initialize properly on startup.
+ '-Wl,-uIsHeapProfilerRunning,-uProfilerStart',
+ # Do the same for heap leak checker.
+ '-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi',
+ '-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl',
+ '-Wl,-u_ZN15HeapLeakChecker12IgnoreObjectEPKv,-u_ZN15HeapLeakChecker14UnIgnoreObjectEPKv',
+ ]},
+ }],
+ [ 'use_vtable_verify==1', {
+ 'cflags': [
+ '-fvtable-verify=preinit',
+ ],
+ }],
+ [ 'linux_keep_shadow_stacks==1', {
+ 'sources': [
+ '<(tcmalloc_dir)/src/linux_shadow_stacks.cc',
+ '<(tcmalloc_dir)/src/linux_shadow_stacks.h',
+ '<(tcmalloc_dir)/src/stacktrace_shadow-inl.h',
+ ],
+ 'cflags': [
+ '-finstrument-functions',
+ ],
+ 'defines': [
+ 'KEEP_SHADOW_STACKS',
+ ],
+ }],
+ [ 'linux_use_heapchecker==0', {
+ # Do not compile and link the heapchecker source.
+ 'sources!': [
+ '<(tcmalloc_dir)/src/heap-checker-bcad.cc',
+ '<(tcmalloc_dir)/src/heap-checker.cc',
+ ],
+ # Disable the heap checker in tcmalloc.
+ 'defines': [
+ 'NO_HEAP_CHECK',
+ ],
+ }],
+ [ 'clang==1', {
+ 'cflags': [
+ '-Wno-non-literal-null-conversion',
+ ],
+ }],
+ ['order_profiling != 0', {
+ 'target_conditions' : [
+ ['_toolset=="target"', {
+ 'cflags!': [ '-finstrument-functions' ],
+ }],
+ ],
+ }],
+ ],
+ },
+ {
+ # This library is linked in to src/base.gypi:base and allocator_unittests
+ # It can't depend on either and nothing else should depend on it - all
+ # other code should use the interfaced provided by base.
+ 'target_name': 'allocator_extension_thunks',
+ 'type': 'static_library',
+ 'sources': [
+ 'allocator_extension_thunks.cc',
+ 'allocator_extension_thunks.h',
+ ],
+ 'toolsets': ['host', 'target'],
+ 'include_dirs': [
+ '../../'
+ ],
+ 'conditions': [
+ ['OS=="linux" and clang_type_profiler==1', {
+ # It is undoing dependencies and cflags_cc for type_profiler which
+ # build/common.gypi injects into all targets.
+ 'dependencies!': [
+ 'type_profiler',
+ ],
+ 'cflags_cc!': [
+ '-fintercept-allocation-functions',
+ ],
+ }],
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS=="win"', {
+ 'targets': [
+ {
+ 'target_name': 'libcmt',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'libcmt',
+ 'inputs': [
+ 'prep_libc.py',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/allocator/libcmt.lib',
+ ],
+ 'action': [
+ 'python',
+ 'prep_libc.py',
+ '$(VCInstallDir)lib',
+ '<(SHARED_INTERMEDIATE_DIR)/allocator',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'allocator_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ 'allocator',
+ 'allocator_extension_thunks',
+ '../../testing/gtest.gyp:gtest',
+ ],
+ 'include_dirs': [
+ '.',
+ '<(tcmalloc_dir)/src/base',
+ '<(tcmalloc_dir)/src',
+ '../..',
+ ],
+ 'sources': [
+ 'allocator_unittests.cc',
+ '../profiler/alternate_timer.cc',
+ '../profiler/alternate_timer.h',
+ ],
+ },
+ {
+ 'target_name': 'allocator_extension_thunks_win64',
+ 'type': 'static_library',
+ 'sources': [
+ 'allocator_extension_thunks.cc',
+ 'allocator_extension_thunks.h',
+ ],
+ 'toolsets': ['host', 'target'],
+ 'include_dirs': [
+ '../../'
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ },
+ {
+ 'target_name': 'tcmalloc_unittest',
+ 'type': 'executable',
+ 'sources': [
+ 'tcmalloc_unittest.cc',
+ ],
+ 'include_dirs': [
+ '../..',
+ # For constants of TCMalloc.
+ '<(tcmalloc_dir)/src',
+ ],
+ 'dependencies': [
+ '../../testing/gtest.gyp:gtest',
+ '../base.gyp:base',
+ 'allocator',
+ ],
+ },
+ ],
+ }],
+ ['OS=="linux" and clang_type_profiler==1', {
+ # Some targets in this section undo dependencies and cflags_cc for
+ # type_profiler which build/common.gypi injects into all targets.
+ 'targets': [
+ {
+ 'target_name': 'type_profiler',
+ 'type': 'static_library',
+ 'dependencies!': [
+ 'type_profiler',
+ ],
+ 'cflags_cc!': [
+ '-fintercept-allocation-functions',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'type_profiler.cc',
+ 'type_profiler.h',
+ 'type_profiler_control.h',
+ ],
+ 'toolsets': ['host', 'target'],
+ },
+ {
+ 'target_name': 'type_profiler_tcmalloc',
+ 'type': 'static_library',
+ 'dependencies!': [
+ 'type_profiler',
+ ],
+ 'cflags_cc!': [
+ '-fintercept-allocation-functions',
+ ],
+ 'include_dirs': [
+ '<(tcmalloc_dir)/src',
+ '../..',
+ ],
+ 'sources': [
+ 'type_profiler_tcmalloc.cc',
+ 'type_profiler_tcmalloc.h',
+ '<(tcmalloc_dir)/src/gperftools/type_profiler_map.h',
+ '<(tcmalloc_dir)/src/type_profiler_map.cc',
+ ],
+ },
+ {
+ 'target_name': 'type_profiler_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../../testing/gtest.gyp:gtest',
+ '../base.gyp:base',
+ 'allocator',
+ 'type_profiler_tcmalloc',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'type_profiler_control.cc',
+ 'type_profiler_control.h',
+ 'type_profiler_unittests.cc',
+ ],
+ },
+ {
+ 'target_name': 'type_profiler_map_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../../testing/gtest.gyp:gtest',
+ '../base.gyp:base',
+ 'allocator',
+ ],
+ 'dependencies!': [
+ 'type_profiler',
+ ],
+ 'cflags_cc!': [
+ '-fintercept-allocation-functions',
+ ],
+ 'include_dirs': [
+ '<(tcmalloc_dir)/src',
+ '../..',
+ ],
+ 'sources': [
+ 'type_profiler_map_unittests.cc',
+ '<(tcmalloc_dir)/src/gperftools/type_profiler_map.h',
+ '<(tcmalloc_dir)/src/type_profiler_map.cc',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/src/base/allocator/allocator_extension.cc b/src/base/allocator/allocator_extension.cc
new file mode 100644
index 0000000..83e460a
--- /dev/null
+++ b/src/base/allocator/allocator_extension.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 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/allocator/allocator_extension.h"
+
+#include "base/logging.h"
+
+namespace base {
+namespace allocator {
+
+bool GetAllocatorWasteSize(size_t* size) {
+ thunks::GetAllocatorWasteSizeFunction get_allocator_waste_size_function =
+ thunks::GetGetAllocatorWasteSizeFunction();
+ return get_allocator_waste_size_function != NULL &&
+ get_allocator_waste_size_function(size);
+}
+
+void GetStats(char* buffer, int buffer_length) {
+ DCHECK_GT(buffer_length, 0);
+ thunks::GetStatsFunction get_stats_function = thunks::GetGetStatsFunction();
+ if (get_stats_function)
+ get_stats_function(buffer, buffer_length);
+ else
+ buffer[0] = '\0';
+}
+
+void ReleaseFreeMemory() {
+ thunks::ReleaseFreeMemoryFunction release_free_memory_function =
+ thunks::GetReleaseFreeMemoryFunction();
+ if (release_free_memory_function)
+ release_free_memory_function();
+}
+
+void SetGetAllocatorWasteSizeFunction(
+ thunks::GetAllocatorWasteSizeFunction get_allocator_waste_size_function) {
+ DCHECK_EQ(thunks::GetGetAllocatorWasteSizeFunction(),
+ reinterpret_cast<thunks::GetAllocatorWasteSizeFunction>(NULL));
+ thunks::SetGetAllocatorWasteSizeFunction(get_allocator_waste_size_function);
+}
+
+void SetGetStatsFunction(thunks::GetStatsFunction get_stats_function) {
+ DCHECK_EQ(thunks::GetGetStatsFunction(),
+ reinterpret_cast<thunks::GetStatsFunction>(NULL));
+ thunks::SetGetStatsFunction(get_stats_function);
+}
+
+void SetReleaseFreeMemoryFunction(
+ thunks::ReleaseFreeMemoryFunction release_free_memory_function) {
+ DCHECK_EQ(thunks::GetReleaseFreeMemoryFunction(),
+ reinterpret_cast<thunks::ReleaseFreeMemoryFunction>(NULL));
+ thunks::SetReleaseFreeMemoryFunction(release_free_memory_function);
+}
+
+} // namespace allocator
+} // namespace base
diff --git a/src/base/allocator/allocator_extension.h b/src/base/allocator/allocator_extension.h
new file mode 100644
index 0000000..de3119f
--- /dev/null
+++ b/src/base/allocator/allocator_extension.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2012 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_ALLOCATOR_ALLOCATOR_EXTENSION_H
+#define BASE_ALLOCATOR_ALLOCATOR_EXTENSION_H
+
+#include <stddef.h> // for size_t
+
+#include "base/allocator/allocator_extension_thunks.h"
+#include "base/base_export.h"
+#include "build/build_config.h"
+
+namespace base {
+namespace allocator {
+
+// Request the allocator to report value of its waste memory size.
+// Waste size corresponds to memory that has been allocated from the OS but
+// not passed up to the application. It e.g. includes memory retained by free
+// lists, internal data, chunks padding, etc.
+//
+// |size| pointer to the returned value, must be not NULL.
+// Returns true if the value has been returned, false otherwise.
+BASE_EXPORT bool GetAllocatorWasteSize(size_t* size);
+
+// Request that the allocator print a human-readable description of the current
+// state of the allocator into a null-terminated string in the memory segment
+// buffer[0,buffer_length-1].
+//
+// |buffer| must point to a valid piece of memory
+// |buffer_length| must be > 0.
+BASE_EXPORT void GetStats(char* buffer, int buffer_length);
+
+// Request that the allocator release any free memory it knows about to the
+// system.
+BASE_EXPORT void ReleaseFreeMemory();
+
+
+// These settings allow specifying a callback used to implement the allocator
+// extension functions. These are optional, but if set they must only be set
+// once. These will typically called in an allocator-specific initialization
+// routine.
+//
+// No threading promises are made. The caller is responsible for making sure
+// these pointers are set before any other threads attempt to call the above
+// functions.
+BASE_EXPORT void SetGetAllocatorWasteSizeFunction(
+ thunks::GetAllocatorWasteSizeFunction get_allocator_waste_size_function);
+
+BASE_EXPORT void SetGetStatsFunction(
+ thunks::GetStatsFunction get_stats_function);
+
+BASE_EXPORT void SetReleaseFreeMemoryFunction(
+ thunks::ReleaseFreeMemoryFunction release_free_memory_function);
+
+} // namespace allocator
+} // namespace base
+
+#endif
diff --git a/src/base/allocator/allocator_extension_thunks.cc b/src/base/allocator/allocator_extension_thunks.cc
new file mode 100644
index 0000000..e4024fb
--- /dev/null
+++ b/src/base/allocator/allocator_extension_thunks.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2012 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/allocator/allocator_extension_thunks.h"
+
+#include <cstddef> // for NULL
+
+namespace base {
+namespace allocator {
+namespace thunks {
+
+// This slightly odd translation unit exists because of the peculularity of how
+// allocator_unittests work on windows. That target has to perform
+// tcmalloc-specific initialization on windows, but it cannot depend on base
+// otherwise. This target sits in the middle - base and allocator_unittests
+// can depend on it. This file can't depend on anything else in base, including
+// logging.
+
+static GetAllocatorWasteSizeFunction g_get_allocator_waste_size_function = NULL;
+static GetStatsFunction g_get_stats_function = NULL;
+static ReleaseFreeMemoryFunction g_release_free_memory_function = NULL;
+
+void SetGetAllocatorWasteSizeFunction(
+ GetAllocatorWasteSizeFunction get_allocator_waste_size_function) {
+ g_get_allocator_waste_size_function = get_allocator_waste_size_function;
+}
+
+GetAllocatorWasteSizeFunction GetGetAllocatorWasteSizeFunction() {
+ return g_get_allocator_waste_size_function;
+}
+
+void SetGetStatsFunction(GetStatsFunction get_stats_function) {
+ g_get_stats_function = get_stats_function;
+}
+
+GetStatsFunction GetGetStatsFunction() {
+ return g_get_stats_function;
+}
+
+void SetReleaseFreeMemoryFunction(
+ ReleaseFreeMemoryFunction release_free_memory_function) {
+ g_release_free_memory_function = release_free_memory_function;
+}
+
+ReleaseFreeMemoryFunction GetReleaseFreeMemoryFunction() {
+ return g_release_free_memory_function;
+}
+
+} // namespace thunks
+} // namespace allocator
+} // namespace base
diff --git a/src/base/allocator/allocator_extension_thunks.h b/src/base/allocator/allocator_extension_thunks.h
new file mode 100644
index 0000000..1e97a84
--- /dev/null
+++ b/src/base/allocator/allocator_extension_thunks.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 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_ALLOCATOR_ALLOCATOR_THUNKS_EXTENSION_H
+#define BASE_ALLOCATOR_ALLOCATOR_THUNKS_EXTENSION_H
+
+#include <stddef.h> // for size_t
+
+namespace base {
+namespace allocator {
+namespace thunks {
+
+// WARNING: You probably don't want to use this file unless you are routing a
+// new allocator extension from a specific allocator implementation to base.
+// See allocator_extension.h to see the interface that base exports.
+
+typedef bool (*GetAllocatorWasteSizeFunction)(size_t* size);
+void SetGetAllocatorWasteSizeFunction(
+ GetAllocatorWasteSizeFunction get_allocator_waste_size_function);
+GetAllocatorWasteSizeFunction GetGetAllocatorWasteSizeFunction();
+
+typedef void (*GetStatsFunction)(char* buffer, int buffer_length);
+void SetGetStatsFunction(GetStatsFunction get_stats_function);
+GetStatsFunction GetGetStatsFunction();
+
+typedef void (*ReleaseFreeMemoryFunction)();
+void SetReleaseFreeMemoryFunction(
+ ReleaseFreeMemoryFunction release_free_memory_function);
+ReleaseFreeMemoryFunction GetReleaseFreeMemoryFunction();
+
+} // namespace thunks
+} // namespace allocator
+} // namespace base
+
+#endif
diff --git a/src/base/allocator/allocator_shim.cc b/src/base/allocator/allocator_shim.cc
new file mode 100644
index 0000000..6e41327
--- /dev/null
+++ b/src/base/allocator/allocator_shim.cc
@@ -0,0 +1,431 @@
+// Copyright (c) 2012 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/allocator/allocator_shim.h"
+
+#include <config.h>
+#include "base/allocator/allocator_extension_thunks.h"
+#include "base/profiler/alternate_timer.h"
+#include "base/sysinfo.h"
+#include "jemalloc.h"
+
+// When defined, different heap allocators can be used via an environment
+// variable set before running the program. This may reduce the amount
+// of inlining that we get with malloc/free/etc. Disabling makes it
+// so that only tcmalloc can be used.
+#define ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+
+// TODO(mbelshe): Ensure that all calls to tcmalloc have the proper call depth
+// from the "user code" so that debugging tools (HeapChecker) can work.
+
+// __THROW is defined in glibc systems. It means, counter-intuitively,
+// "This function will never throw an exception." It's an optional
+// optimization tool, but we may need to use it to match glibc prototypes.
+#ifndef __THROW // I guess we're not on a glibc system
+# define __THROW // __THROW is just an optimization, so ok to make it ""
+#endif
+
+// new_mode behaves similarly to MSVC's _set_new_mode.
+// If flag is 0 (default), calls to malloc will behave normally.
+// If flag is 1, calls to malloc will behave like calls to new,
+// and the std_new_handler will be invoked on failure.
+// Can be set by calling _set_new_mode().
+static int new_mode = 0;
+
+typedef enum {
+ TCMALLOC, // TCMalloc is the default allocator.
+ JEMALLOC, // JEMalloc.
+ WINHEAP, // Windows Heap (standard Windows allocator).
+ WINLFH, // Windows LFH Heap.
+} Allocator;
+
+// This is the default allocator. This value can be changed at startup by
+// specifying environment variables shown below it.
+// See SetupSubprocessAllocator() to specify a default secondary (subprocess)
+// allocator.
+// TODO(jar): Switch to using TCMALLOC for the renderer as well.
+static Allocator allocator = TCMALLOC;
+
+// The names of the environment variables that can optionally control the
+// selection of the allocator. The primary may be used to control overall
+// allocator selection, and the secondary can be used to specify an allocator
+// to use in sub-processes.
+static const char primary_name[] = "CHROME_ALLOCATOR";
+static const char secondary_name[] = "CHROME_ALLOCATOR_2";
+
+// We include tcmalloc and the win_allocator to get as much inlining as
+// possible.
+#include "debugallocation_shim.cc"
+#include "win_allocator.cc"
+
+// Forward declarations from jemalloc.
+extern "C" {
+void* je_malloc(size_t s);
+void* je_realloc(void* p, size_t s);
+void je_free(void* s);
+size_t je_msize(void* p);
+bool je_malloc_init_hard();
+void* je_memalign(size_t a, size_t s);
+}
+
+extern "C" {
+
+// Call the new handler, if one has been set.
+// Returns true on successfully calling the handler, false otherwise.
+inline bool call_new_handler(bool nothrow) {
+ // Get the current new handler. NB: this function is not
+ // thread-safe. We make a feeble stab at making it so here, but
+ // this lock only protects against tcmalloc interfering with
+ // itself, not with other libraries calling set_new_handler.
+ std::new_handler nh;
+ {
+ SpinLockHolder h(&set_new_handler_lock);
+ nh = std::set_new_handler(0);
+ (void) std::set_new_handler(nh);
+ }
+#if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
+ if (!nh)
+ return false;
+ // Since exceptions are disabled, we don't really know if new_handler
+ // failed. Assume it will abort if it fails.
+ (*nh)();
+ return false; // break out of the retry loop.
+#else
+ // If no new_handler is established, the allocation failed.
+ if (!nh) {
+ if (nothrow)
+ return 0;
+ throw std::bad_alloc();
+ }
+ // Otherwise, try the new_handler. If it returns, retry the
+ // allocation. If it throws std::bad_alloc, fail the allocation.
+ // if it throws something else, don't interfere.
+ try {
+ (*nh)();
+ } catch (const std::bad_alloc&) {
+ if (!nothrow)
+ throw;
+ return true;
+ }
+#endif // (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
+}
+
+void* malloc(size_t size) __THROW {
+ void* ptr;
+ for (;;) {
+#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+ switch (allocator) {
+ case JEMALLOC:
+ ptr = je_malloc(size);
+ break;
+ case WINHEAP:
+ case WINLFH:
+ ptr = win_heap_malloc(size);
+ break;
+ case TCMALLOC:
+ default:
+ ptr = do_malloc(size);
+ break;
+ }
+#else
+ // TCMalloc case.
+ ptr = do_malloc(size);
+#endif
+ if (ptr)
+ return ptr;
+
+ if (!new_mode || !call_new_handler(true))
+ break;
+ }
+ return ptr;
+}
+
+void free(void* p) __THROW {
+#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+ switch (allocator) {
+ case JEMALLOC:
+ je_free(p);
+ return;
+ case WINHEAP:
+ case WINLFH:
+ win_heap_free(p);
+ return;
+ }
+#endif
+ // TCMalloc case.
+ do_free(p);
+}
+
+void* realloc(void* ptr, size_t size) __THROW {
+ // Webkit is brittle for allocators that return NULL for malloc(0). The
+ // realloc(0, 0) code path does not guarantee a non-NULL return, so be sure
+ // to call malloc for this case.
+ if (!ptr)
+ return malloc(size);
+
+ void* new_ptr;
+ for (;;) {
+#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+ switch (allocator) {
+ case JEMALLOC:
+ new_ptr = je_realloc(ptr, size);
+ break;
+ case WINHEAP:
+ case WINLFH:
+ new_ptr = win_heap_realloc(ptr, size);
+ break;
+ case TCMALLOC:
+ default:
+ new_ptr = do_realloc(ptr, size);
+ break;
+ }
+#else
+ // TCMalloc case.
+ new_ptr = do_realloc(ptr, size);
+#endif
+
+ // Subtle warning: NULL return does not alwas indicate out-of-memory. If
+ // the requested new size is zero, realloc should free the ptr and return
+ // NULL.
+ if (new_ptr || !size)
+ return new_ptr;
+ if (!new_mode || !call_new_handler(true))
+ break;
+ }
+ return new_ptr;
+}
+
+// TODO(mbelshe): Implement this for other allocators.
+void malloc_stats(void) __THROW {
+#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+ switch (allocator) {
+ case JEMALLOC:
+ // No stats.
+ return;
+ case WINHEAP:
+ case WINLFH:
+ // No stats.
+ return;
+ }
+#endif
+ tc_malloc_stats();
+}
+
+#ifdef WIN32
+
+extern "C" size_t _msize(void* p) {
+#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+ switch (allocator) {
+ case JEMALLOC:
+ return je_msize(p);
+ case WINHEAP:
+ case WINLFH:
+ return win_heap_msize(p);
+ }
+#endif
+ return MallocExtension::instance()->GetAllocatedSize(p);
+}
+
+// This is included to resolve references from libcmt.
+extern "C" intptr_t _get_heap_handle() {
+ return 0;
+}
+
+static bool get_allocator_waste_size_thunk(size_t* size) {
+#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+ switch (allocator) {
+ case JEMALLOC:
+ case WINHEAP:
+ case WINLFH:
+ // TODO(alexeif): Implement for allocators other than tcmalloc.
+ return false;
+ }
+#endif
+ size_t heap_size, allocated_bytes, unmapped_bytes;
+ MallocExtension* ext = MallocExtension::instance();
+ if (ext->GetNumericProperty("generic.heap_size", &heap_size) &&
+ ext->GetNumericProperty("generic.current_allocated_bytes",
+ &allocated_bytes) &&
+ ext->GetNumericProperty("tcmalloc.pageheap_unmapped_bytes",
+ &unmapped_bytes)) {
+ *size = heap_size - allocated_bytes - unmapped_bytes;
+ return true;
+ }
+ return false;
+}
+
+static void get_stats_thunk(char* buffer, int buffer_length) {
+ MallocExtension::instance()->GetStats(buffer, buffer_length);
+}
+
+static void release_free_memory_thunk() {
+ MallocExtension::instance()->ReleaseFreeMemory();
+}
+
+// The CRT heap initialization stub.
+extern "C" int _heap_init() {
+#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+ const char* environment_value = GetenvBeforeMain(primary_name);
+ if (environment_value) {
+ if (!stricmp(environment_value, "jemalloc"))
+ allocator = JEMALLOC;
+ else if (!stricmp(environment_value, "winheap"))
+ allocator = WINHEAP;
+ else if (!stricmp(environment_value, "winlfh"))
+ allocator = WINLFH;
+ else if (!stricmp(environment_value, "tcmalloc"))
+ allocator = TCMALLOC;
+ }
+
+ switch (allocator) {
+ case JEMALLOC:
+ return je_malloc_init_hard() ? 0 : 1;
+ case WINHEAP:
+ return win_heap_init(false) ? 1 : 0;
+ case WINLFH:
+ return win_heap_init(true) ? 1 : 0;
+ case TCMALLOC:
+ default:
+ // fall through
+ break;
+ }
+#endif
+ // Initializing tcmalloc.
+ // We intentionally leak this object. It lasts for the process
+ // lifetime. Trying to teardown at _heap_term() is so late that
+ // you can't do anything useful anyway.
+ new TCMallocGuard();
+
+ // Provide optional hook for monitoring allocation quantities on a per-thread
+ // basis. Only set the hook if the environment indicates this needs to be
+ // enabled.
+ const char* profiling =
+ GetenvBeforeMain(tracked_objects::kAlternateProfilerTime);
+ if (profiling && *profiling == '1') {
+ tracked_objects::SetAlternateTimeSource(
+ tcmalloc::ThreadCache::GetBytesAllocatedOnCurrentThread,
+ tracked_objects::TIME_SOURCE_TYPE_TCMALLOC);
+ }
+
+ base::allocator::thunks::SetGetAllocatorWasteSizeFunction(
+ get_allocator_waste_size_thunk);
+ base::allocator::thunks::SetGetStatsFunction(get_stats_thunk);
+ base::allocator::thunks::SetReleaseFreeMemoryFunction(
+ release_free_memory_thunk);
+
+ return 1;
+}
+
+// The CRT heap cleanup stub.
+extern "C" void _heap_term() {}
+
+// We set this to 1 because part of the CRT uses a check of _crtheap != 0
+// to test whether the CRT has been initialized. Once we've ripped out
+// the allocators from libcmt, we need to provide this definition so that
+// the rest of the CRT is still usable.
+extern "C" void* _crtheap = reinterpret_cast<void*>(1);
+
+// Provide support for aligned memory through Windows only _aligned_malloc().
+void* _aligned_malloc(size_t size, size_t alignment) {
+ // _aligned_malloc guarantees parameter validation, so do so here. These
+ // checks are somewhat stricter than _aligned_malloc() since we're effectively
+ // using memalign() under the hood.
+ DCHECK_GT(size, 0U);
+ DCHECK_EQ(alignment & (alignment - 1), 0U);
+ DCHECK_EQ(alignment % sizeof(void*), 0U);
+
+ void* ptr;
+ for (;;) {
+#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+ switch (allocator) {
+ case JEMALLOC:
+ ptr = je_memalign(alignment, size);
+ break;
+ case WINHEAP:
+ case WINLFH:
+ ptr = win_heap_memalign(alignment, size);
+ break;
+ case TCMALLOC:
+ default:
+ ptr = tc_memalign(alignment, size);
+ break;
+ }
+#else
+ // TCMalloc case.
+ ptr = tc_memalign(alignment, size);
+#endif
+ if (ptr) {
+ // Sanity check alignment.
+ DCHECK_EQ(reinterpret_cast<uintptr_t>(ptr) & (alignment - 1), 0U);
+ return ptr;
+ }
+
+ if (!new_mode || !call_new_handler(true))
+ break;
+ }
+ return ptr;
+}
+
+void _aligned_free(void* p) {
+ // Both JEMalloc and TCMalloc return pointers from memalign() that are safe to
+ // use with free(). Pointers allocated with win_heap_memalign() MUST be freed
+ // via win_heap_memalign_free() since the aligned pointer is not the real one.
+#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+ switch (allocator) {
+ case JEMALLOC:
+ je_free(p);
+ return;
+ case WINHEAP:
+ case WINLFH:
+ win_heap_memalign_free(p);
+ return;
+ }
+#endif
+ // TCMalloc case.
+ do_free(p);
+}
+
+#endif // WIN32
+
+#include "generic_allocators.cc"
+
+} // extern C
+
+namespace base {
+namespace allocator {
+
+void SetupSubprocessAllocator() {
+#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+ size_t primary_length = 0;
+ getenv_s(&primary_length, NULL, 0, primary_name);
+
+ size_t secondary_length = 0;
+ char buffer[20];
+ getenv_s(&secondary_length, buffer, sizeof(buffer), secondary_name);
+ DCHECK_GT(sizeof(buffer), secondary_length);
+ buffer[sizeof(buffer) - 1] = '\0';
+
+ if (secondary_length || !primary_length) {
+ const char* secondary_value = secondary_length ? buffer : "TCMALLOC";
+ // Force renderer (or other subprocesses) to use secondary_value.
+ int ret_val = _putenv_s(primary_name, secondary_value);
+ DCHECK_EQ(0, ret_val);
+ }
+#endif // ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+}
+
+void* TCMallocDoMallocForTest(size_t size) {
+ return do_malloc(size);
+}
+
+void TCMallocDoFreeForTest(void* ptr) {
+ do_free(ptr);
+}
+
+size_t ExcludeSpaceForMarkForTest(size_t size) {
+ return ExcludeSpaceForMark(size);
+}
+
+} // namespace allocator.
+} // namespace base.
diff --git a/src/base/allocator/allocator_shim.h b/src/base/allocator/allocator_shim.h
new file mode 100644
index 0000000..e10fa8d
--- /dev/null
+++ b/src/base/allocator/allocator_shim.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2012 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_ALLOCATOR_ALLOCATOR_SHIM_H_
+#define BASE_ALLOCATOR_ALLOCATOR_SHIM_H_
+
+namespace base {
+namespace allocator {
+
+// Resets the environment variable CHROME_ALLOCATOR to specify the choice to
+// be used by subprocesses. Priority is given to the current value of
+// CHROME_ALLOCATOR_2 (if specified), then CHROME_ALLOCATOR (if specified), and
+// then a default value (typically set to TCMALLOC).
+void SetupSubprocessAllocator();
+
+// Expose some of tcmalloc functions for test.
+void* TCMallocDoMallocForTest(size_t size);
+void TCMallocDoFreeForTest(void* ptr);
+size_t ExcludeSpaceForMarkForTest(size_t size);
+
+} // namespace allocator.
+} // namespace base.
+
+#endif // BASE_ALLOCATOR_ALLOCATOR_SHIM_H_
diff --git a/src/base/allocator/allocator_unittests.cc b/src/base/allocator/allocator_unittests.cc
new file mode 100644
index 0000000..3c455ba
--- /dev/null
+++ b/src/base/allocator/allocator_unittests.cc
@@ -0,0 +1,555 @@
+// Copyright (c) 2012 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 <stdio.h>
+#include <stdlib.h>
+#include <algorithm> // for min()
+#include "base/atomicops.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Number of bits in a size_t.
+static const int kSizeBits = 8 * sizeof(size_t);
+// The maximum size of a size_t.
+static const size_t kMaxSize = ~static_cast<size_t>(0);
+// Maximum positive size of a size_t if it were signed.
+static const size_t kMaxSignedSize = ((size_t(1) << (kSizeBits-1)) - 1);
+
+#if !defined(__LB_SHELL__) && !defined(OS_STARBOARD)
+// An allocation size which is not too big to be reasonable.
+static const size_t kNotTooBig = 100000;
+// An allocation size which is just too big.
+static const size_t kTooBig = ~static_cast<size_t>(0);
+#endif
+
+namespace {
+
+using std::min;
+
+// Fill a buffer of the specified size with a predetermined pattern
+static void Fill(unsigned char* buffer, int n) {
+ for (int i = 0; i < n; i++) {
+ buffer[i] = (i & 0xff);
+ }
+}
+
+// Check that the specified buffer has the predetermined pattern
+// generated by Fill()
+static bool Valid(unsigned char* buffer, int n) {
+ for (int i = 0; i < n; i++) {
+ if (buffer[i] != (i & 0xff)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// Check that a buffer is completely zeroed.
+static bool IsZeroed(unsigned char* buffer, int n) {
+ for (int i = 0; i < n; i++) {
+ if (buffer[i] != 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// Check alignment
+static void CheckAlignment(void* p, int align) {
+ EXPECT_EQ(0, reinterpret_cast<uintptr_t>(p) & (align-1));
+}
+
+// Return the next interesting size/delta to check. Returns -1 if no more.
+static int NextSize(int size) {
+ if (size < 100)
+ return size+1;
+
+ if (size < 100000) {
+ // Find next power of two
+ int power = 1;
+ while (power < size)
+ power <<= 1;
+
+ // Yield (power-1, power, power+1)
+ if (size < power-1)
+ return power-1;
+
+ if (size == power-1)
+ return power;
+
+ assert(size == power);
+ return power+1;
+ } else {
+ return -1;
+ }
+}
+
+#if defined(__LB_SHELL__) || defined(OS_STARBOARD)
+#if defined(GG_ULONGLONG)
+// GG_ULONGLONG is already defined in base/port.h
+#undef GG_ULONGLONG
+#endif
+
+// namespace resolution for typedefs
+using base::subtle::AtomicWord;
+using base::subtle::Atomic32;
+#endif
+
+#define GG_ULONGLONG(x) static_cast<uint64>(x)
+
+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 to 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 increment at the half-width boundary of the atomic type.
+ // It is primarily for testing at the 32-bit boundary for 64-bit atomic type.
+ AtomicType test_val = GG_ULONGLONG(1) << (NUM_BITS(AtomicType) / 2);
+ AtomicType value = test_val - 1;
+ 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(test_val - 1, value);
+}
+
+// This is a simple sanity check that values are correct. Not testing
+// atomicity
+template <class AtomicType>
+static void TestStore() {
+ const AtomicType kVal1 = static_cast<AtomicType>(0xa5a5a5a5a5a5a5a5LL);
+ 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 = static_cast<AtomicType>(0xa5a5a5a5a5a5a5a5LL);
+ 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));
+}
+
+template <class AtomicType>
+static void TestAtomicOps() {
+ TestCompareAndSwap<AtomicType>();
+ TestAtomicExchange<AtomicType>();
+ TestAtomicIncrementBounds<AtomicType>();
+ TestStore<AtomicType>();
+ TestLoad<AtomicType>();
+}
+
+static void TestCalloc(size_t n, size_t s, bool ok) {
+ char* p = reinterpret_cast<char*>(calloc(n, s));
+ if (!ok) {
+ EXPECT_EQ(NULL, p) << "calloc(n, s) should not succeed";
+ } else {
+ // TODO(__LB_SHELL__): This behavior is not defined by c++ standard.
+ // However it is possible that chromium expects it. We need to make sure
+ // these functions work the same way as expected by chromium when we override
+ // memory functions.
+ EXPECT_NE(reinterpret_cast<void*>(NULL), p) <<
+ "calloc(n, s) should succeed";
+ for (int i = 0; i < n*s; i++) {
+ EXPECT_EQ('\0', p[i]);
+ }
+ free(p);
+ }
+}
+
+#if !defined(__LB_SHELL__) && !defined(OS_STARBOARD)
+// Exceptions are disabled on lbshell and Starboard.
+
+// A global test counter for number of times the NewHandler is called.
+static int news_handled = 0;
+static void TestNewHandler() {
+ ++news_handled;
+ throw std::bad_alloc();
+}
+
+// Because we compile without exceptions, we expect these will not throw.
+static void TestOneNewWithoutExceptions(void* (*func)(size_t),
+ bool should_throw) {
+ // success test
+ try {
+ void* ptr = (*func)(kNotTooBig);
+ EXPECT_NE(reinterpret_cast<void*>(NULL), ptr) <<
+ "allocation should not have failed.";
+ } catch(...) {
+ EXPECT_EQ(0, 1) << "allocation threw unexpected exception.";
+ }
+
+ // failure test
+ try {
+ void* rv = (*func)(kTooBig);
+ EXPECT_EQ(NULL, rv);
+ EXPECT_FALSE(should_throw) << "allocation should have thrown.";
+ } catch(...) {
+ EXPECT_TRUE(should_throw) << "allocation threw unexpected exception.";
+ }
+}
+
+static void TestNothrowNew(void* (*func)(size_t)) {
+ news_handled = 0;
+
+ // test without new_handler:
+ std::new_handler saved_handler = std::set_new_handler(0);
+ TestOneNewWithoutExceptions(func, false);
+
+ // test with new_handler:
+ std::set_new_handler(TestNewHandler);
+ TestOneNewWithoutExceptions(func, true);
+ EXPECT_EQ(news_handled, 1) << "nothrow new_handler was not called.";
+ std::set_new_handler(saved_handler);
+}
+#endif
+} // namespace
+
+//-----------------------------------------------------------------------------
+
+TEST(Atomics, AtomicIncrementWord) {
+ TestAtomicIncrement<AtomicWord>();
+}
+
+TEST(Atomics, AtomicIncrement32) {
+ TestAtomicIncrement<Atomic32>();
+}
+
+TEST(Atomics, AtomicOpsWord) {
+ TestAtomicIncrement<AtomicWord>();
+}
+
+TEST(Atomics, AtomicOps32) {
+ TestAtomicIncrement<Atomic32>();
+}
+
+TEST(Allocators, Malloc) {
+ // Try allocating data with a bunch of alignments and sizes
+ for (int size = 1; size < 1048576; size *= 2) {
+ unsigned char* ptr = reinterpret_cast<unsigned char*>(malloc(size));
+ CheckAlignment(ptr, 2); // Should be 2 byte aligned
+ Fill(ptr, size);
+ EXPECT_TRUE(Valid(ptr, size));
+ free(ptr);
+ }
+}
+
+TEST(Allocators, Calloc) {
+ TestCalloc(0, 0, true);
+ TestCalloc(0, 1, true);
+ TestCalloc(1, 1, true);
+ TestCalloc(1<<10, 0, true);
+ TestCalloc(1<<20, 0, true);
+ TestCalloc(0, 1<<10, true);
+ TestCalloc(0, 1<<20, true);
+ TestCalloc(1<<20, 2, true);
+ TestCalloc(2, 1<<20, true);
+ TestCalloc(1000, 1000, true);
+
+ TestCalloc(kMaxSize, 2, false);
+ TestCalloc(2, kMaxSize, false);
+ TestCalloc(kMaxSize, kMaxSize, false);
+
+ TestCalloc(kMaxSignedSize, 3, false);
+ TestCalloc(3, kMaxSignedSize, false);
+ TestCalloc(kMaxSignedSize, kMaxSignedSize, false);
+}
+
+#if !defined(__LB_SHELL__) && !defined(OS_STARBOARD)
+// Exceptions are disabled on lbshell.
+TEST(Allocators, New) {
+ TestNothrowNew(&::operator new);
+ TestNothrowNew(&::operator new[]);
+}
+#endif
+
+#if !defined(__LB_SHELL__) && !defined(OS_STARBOARD)
+// The test assumes the memory space to be very empty
+// and doesn't work on lb_shell platforms.
+
+// This makes sure that reallocing a small number of bytes in either
+// direction doesn't cause us to allocate new memory.
+TEST(Allocators, Realloc1) {
+ int start_sizes[] = { 100, 1000, 10000, 100000 };
+ int deltas[] = { 1, -2, 4, -8, 16, -32, 64, -128 };
+
+ for (int s = 0; s < sizeof(start_sizes)/sizeof(*start_sizes); ++s) {
+ void* p = malloc(start_sizes[s]);
+ ASSERT_TRUE(p);
+ // The larger the start-size, the larger the non-reallocing delta.
+ for (int d = 0; d < s*2; ++d) {
+ void* new_p = realloc(p, start_sizes[s] + deltas[d]);
+ ASSERT_EQ(p, new_p); // realloc should not allocate new memory
+ }
+ // Test again, but this time reallocing smaller first.
+ for (int d = 0; d < s*2; ++d) {
+ void* new_p = realloc(p, start_sizes[s] - deltas[d]);
+ ASSERT_EQ(p, new_p); // realloc should not allocate new memory
+ }
+ free(p);
+ }
+}
+#endif
+
+TEST(Allocators, Realloc2) {
+ for (int src_size = 0; src_size >= 0; src_size = NextSize(src_size)) {
+ for (int dst_size = 0; dst_size >= 0; dst_size = NextSize(dst_size)) {
+ unsigned char* src = reinterpret_cast<unsigned char*>(malloc(src_size));
+ Fill(src, src_size);
+ unsigned char* dst =
+ reinterpret_cast<unsigned char*>(realloc(src, dst_size));
+ EXPECT_TRUE(Valid(dst, min(src_size, dst_size)));
+ Fill(dst, dst_size);
+ EXPECT_TRUE(Valid(dst, dst_size));
+ if (dst != NULL) free(dst);
+ }
+ }
+
+ // Now make sure realloc works correctly even when we overflow the
+ // packed cache, so some entries are evicted from the cache.
+ // The cache has 2^12 entries, keyed by page number.
+ const int kNumEntries = 1 << 14;
+ int** p = reinterpret_cast<int**>(malloc(sizeof(*p) * kNumEntries));
+ int sum = 0;
+ for (int i = 0; i < kNumEntries; i++) {
+ // no page size is likely to be bigger than 8192?
+ p[i] = reinterpret_cast<int*>(malloc(8192));
+ p[i][1000] = i; // use memory deep in the heart of p
+ }
+ for (int i = 0; i < kNumEntries; i++) {
+ p[i] = reinterpret_cast<int*>(realloc(p[i], 9000));
+ }
+ for (int i = 0; i < kNumEntries; i++) {
+ sum += p[i][1000];
+ free(p[i]);
+ }
+ EXPECT_EQ(kNumEntries/2 * (kNumEntries - 1), sum); // assume kNE is even
+ free(p);
+}
+
+// TODO(__LB_SHELL__): This behavior is not defined by c++ standard.
+// However it is possible that chromium expects it. We need to make sure
+// these functions work the same way as expected by chromium when we override
+// memory functions.
+TEST(Allocators, ReallocZero) {
+ // Test that realloc to zero does not return NULL.
+ for (int size = 0; size >= 0; size = NextSize(size)) {
+ char* ptr = reinterpret_cast<char*>(malloc(size));
+ EXPECT_NE(static_cast<char*>(NULL), ptr);
+ ptr = reinterpret_cast<char*>(realloc(ptr, 0));
+ EXPECT_NE(static_cast<char*>(NULL), ptr);
+ if (ptr)
+ free(ptr);
+ }
+}
+
+#ifdef WIN32
+// Test recalloc
+TEST(Allocators, Recalloc) {
+ for (int src_size = 0; src_size >= 0; src_size = NextSize(src_size)) {
+ for (int dst_size = 0; dst_size >= 0; dst_size = NextSize(dst_size)) {
+ unsigned char* src =
+ reinterpret_cast<unsigned char*>(_recalloc(NULL, 1, src_size));
+ EXPECT_TRUE(IsZeroed(src, src_size));
+ Fill(src, src_size);
+ unsigned char* dst =
+ reinterpret_cast<unsigned char*>(_recalloc(src, 1, dst_size));
+ EXPECT_TRUE(Valid(dst, min(src_size, dst_size)));
+ Fill(dst, dst_size);
+ EXPECT_TRUE(Valid(dst, dst_size));
+ if (dst != NULL)
+ free(dst);
+ }
+ }
+}
+
+// Test windows specific _aligned_malloc() and _aligned_free() methods.
+TEST(Allocators, AlignedMalloc) {
+ // Try allocating data with a bunch of alignments and sizes
+ static const int kTestAlignments[] = {8, 16, 256, 4096, 8192, 16384};
+ for (int size = 1; size > 0; size = NextSize(size)) {
+ for (int i = 0; i < ARRAYSIZE(kTestAlignments); ++i) {
+ unsigned char* ptr = static_cast<unsigned char*>(
+ _aligned_malloc(size, kTestAlignments[i]));
+ CheckAlignment(ptr, kTestAlignments[i]);
+ Fill(ptr, size);
+ EXPECT_TRUE(Valid(ptr, size));
+
+ // Make a second allocation of the same size and alignment to prevent
+ // allocators from passing this test by accident. Per jar, tcmalloc
+ // provides allocations for new (never before seen) sizes out of a thread
+ // local heap of a given "size class." Each time the test requests a new
+ // size, it will usually get the first element of a span, which is a
+ // 4K aligned allocation.
+ unsigned char* ptr2 = static_cast<unsigned char*>(
+ _aligned_malloc(size, kTestAlignments[i]));
+ CheckAlignment(ptr2, kTestAlignments[i]);
+ Fill(ptr2, size);
+ EXPECT_TRUE(Valid(ptr2, size));
+
+ // Should never happen, but sanity check just in case.
+ ASSERT_NE(ptr, ptr2);
+ _aligned_free(ptr);
+ _aligned_free(ptr2);
+ }
+ }
+}
+
+#endif
+
+#if !defined(__LB_SHELL__) && !defined(OS_STARBOARD)
+// main defined in run_all_unittests
+int main(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+#endif
diff --git a/src/base/allocator/debugallocation_shim.cc b/src/base/allocator/debugallocation_shim.cc
new file mode 100644
index 0000000..d1cf52a
--- /dev/null
+++ b/src/base/allocator/debugallocation_shim.cc
@@ -0,0 +1,9 @@
+// Copyright (c) 2012 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.
+
+#if defined(TCMALLOC_FOR_DEBUGALLOCATION)
+#include "third_party/tcmalloc/chromium/src/debugallocation.cc"
+#else
+#include "third_party/tcmalloc/chromium/src/tcmalloc.cc"
+#endif
diff --git a/src/base/allocator/generic_allocators.cc b/src/base/allocator/generic_allocators.cc
new file mode 100644
index 0000000..d4cf19e
--- /dev/null
+++ b/src/base/allocator/generic_allocators.cc
@@ -0,0 +1,168 @@
+// 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.
+
+// When possible, we implement allocator functions on top of the basic
+// low-level functions malloc() and free(). This way, including a new
+// allocator is as simple as providing just a small interface.
+//
+// As such, this file should not contain any allocator-specific code.
+
+// Implement a C++ style allocation, which always calls the new_handler
+// on failure.
+inline void* generic_cpp_alloc(size_t size, bool nothrow) {
+ void* ptr;
+ for (;;) {
+ ptr = malloc(size);
+ if (ptr)
+ return ptr;
+ if (!call_new_handler(nothrow))
+ break;
+ }
+ return ptr;
+}
+
+extern "C++" {
+
+void* __cdecl operator new(size_t size) {
+ return generic_cpp_alloc(size, false);
+}
+
+void operator delete(void* p) __THROW {
+ free(p);
+}
+
+void* operator new[](size_t size) {
+ return generic_cpp_alloc(size, false);
+}
+
+void operator delete[](void* p) __THROW {
+ free(p);
+}
+
+void* operator new(size_t size, const std::nothrow_t& nt) __THROW {
+ return generic_cpp_alloc(size, true);
+}
+
+void* operator new[](size_t size, const std::nothrow_t& nt) __THROW {
+ return generic_cpp_alloc(size, true);
+}
+
+// This function behaves similarly to MSVC's _set_new_mode.
+// If flag is 0 (default), calls to malloc will behave normally.
+// If flag is 1, calls to malloc will behave like calls to new,
+// and the std_new_handler will be invoked on failure.
+// Returns the previous mode.
+int _set_new_mode(int flag) __THROW {
+ int old_mode = new_mode;
+ new_mode = flag;
+ return old_mode;
+}
+
+} // extern "C++"
+
+extern "C" {
+
+void* calloc(size_t n, size_t elem_size) __THROW {
+ // Overflow check
+ const size_t size = n * elem_size;
+ if (elem_size != 0 && size / elem_size != n) return NULL;
+
+ void* result = malloc(size);
+ if (result != NULL) {
+ memset(result, 0, size);
+ }
+ return result;
+}
+
+void cfree(void* p) __THROW {
+ free(p);
+}
+
+#ifdef WIN32
+
+void* _recalloc(void* p, size_t n, size_t elem_size) {
+ if (!p)
+ return calloc(n, elem_size);
+
+ // This API is a bit odd.
+ // Note: recalloc only guarantees zeroed memory when p is NULL.
+ // Generally, calls to malloc() have padding. So a request
+ // to malloc N bytes actually malloc's N+x bytes. Later, if
+ // that buffer is passed to recalloc, we don't know what N
+ // was anymore. We only know what N+x is. As such, there is
+ // no way to know what to zero out.
+ const size_t size = n * elem_size;
+ if (elem_size != 0 && size / elem_size != n) return NULL;
+ return realloc(p, size);
+}
+
+void* _calloc_impl(size_t n, size_t size) {
+ return calloc(n, size);
+}
+
+#ifndef NDEBUG
+#undef malloc
+#undef free
+#undef calloc
+
+static int error_handler(int reportType) {
+ switch (reportType) {
+ case 0: // _CRT_WARN
+ __debugbreak();
+ return 0;
+
+ case 1: // _CRT_ERROR
+ __debugbreak();
+ return 0;
+
+ case 2: // _CRT_ASSERT
+ __debugbreak();
+ return 0;
+ }
+ char* p = NULL;
+ *p = '\0';
+ return 0;
+}
+
+int _CrtDbgReport(int reportType,
+ const char*,
+ int, const char*,
+ const char*,
+ ...) {
+ return error_handler(reportType);
+}
+
+int _CrtDbgReportW(int reportType,
+ const wchar_t*,
+ int, const wchar_t*,
+ const wchar_t*,
+ ...) {
+ return error_handler(reportType);
+}
+
+int _CrtSetReportMode(int, int) {
+ return 0;
+}
+
+void* _malloc_dbg(size_t size, int , const char*, int) {
+ return malloc(size);
+}
+
+void* _realloc_dbg(void* ptr, size_t size, int, const char*, int) {
+ return realloc(ptr, size);
+}
+
+void _free_dbg(void* ptr, int) {
+ free(ptr);
+}
+
+void* _calloc_dbg(size_t n, size_t size, int, const char*, int) {
+ return calloc(n, size);
+}
+#endif // NDEBUG
+
+#endif // WIN32
+
+} // extern C
+
diff --git a/src/base/allocator/prep_libc.py b/src/base/allocator/prep_libc.py
new file mode 100755
index 0000000..be7030f
--- /dev/null
+++ b/src/base/allocator/prep_libc.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2012 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 script takes libcmt.lib for VS2005/08/10 and removes the allocation
+# related functions from it.
+#
+# Usage: prep_libc.py <VCInstallDir> <OutputDir>
+#
+# VCInstallDir is the path where VC is installed, something like:
+# C:\Program Files\Microsoft Visual Studio 8\VC\
+#
+# OutputDir is the directory where the modified libcmt file should be stored.
+
+import os
+import shutil
+import subprocess
+import sys
+
+def run(command, filter=None):
+ """Run |command|, removing any lines that match |filter|. The filter is
+ to remove the echoing of input filename that 'lib' does."""
+ popen = subprocess.Popen(
+ command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ out, _ = popen.communicate()
+ for line in out.splitlines():
+ if filter and line.strip() != filter:
+ print line
+ return popen.returncode
+
+def main():
+ vs_install_dir = sys.argv[1]
+ outdir = sys.argv[2]
+ output_lib = os.path.join(outdir, 'libcmt.lib')
+ shutil.copyfile(os.path.join(vs_install_dir, 'libcmt.lib'), output_lib)
+ shutil.copyfile(os.path.join(vs_install_dir, 'libcmt.pdb'),
+ os.path.join(outdir, 'libcmt.pdb'))
+ vspaths = [
+ 'build\\intel\\mt_obj\\',
+ 'f:\\dd\\vctools\\crt_bld\\SELF_X86\\crt\\src\\build\\INTEL\\mt_obj\\',
+ 'F:\\dd\\vctools\\crt_bld\\SELF_X86\\crt\\src\\build\\INTEL\\mt_obj\\nativec\\\\',
+ 'F:\\dd\\vctools\\crt_bld\\SELF_X86\\crt\\src\\build\\INTEL\\mt_obj\\nativecpp\\\\',
+ ]
+ objfiles = ['malloc', 'free', 'realloc', 'new', 'delete', 'new2', 'delete2',
+ 'align', 'msize', 'heapinit', 'expand', 'heapchk', 'heapwalk',
+ 'heapmin', 'sbheap', 'calloc', 'recalloc', 'calloc_impl',
+ 'new_mode', 'newopnt']
+ for obj in objfiles:
+ for vspath in vspaths:
+ cmd = ('lib /nologo /ignore:4006,4014,4221 /remove:%s%s.obj %s' %
+ (vspath, obj, output_lib))
+ run(cmd, obj + '.obj')
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/src/base/allocator/tcmalloc_unittest.cc b/src/base/allocator/tcmalloc_unittest.cc
new file mode 100644
index 0000000..053a9d5
--- /dev/null
+++ b/src/base/allocator/tcmalloc_unittest.cc
@@ -0,0 +1,81 @@
+// Copyright (c) 2012 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 <stdio.h>
+#include "base/allocator/allocator_shim.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+// TCMalloc header files
+#include "common.h" // For TCMalloc constants like page size, etc.
+
+using base::allocator::TCMallocDoMallocForTest;
+using base::allocator::TCMallocDoFreeForTest;
+using base::allocator::ExcludeSpaceForMarkForTest;
+
+TEST(TCMallocFreeCheck, BadPointerInFirstPageOfTheLargeObject) {
+ char* p = reinterpret_cast<char*>(
+ TCMallocDoMallocForTest(ExcludeSpaceForMarkForTest(kMaxSize + 1)));
+ for (int offset = 1; offset < kPageSize ; offset <<= 1) {
+ ASSERT_DEATH(TCMallocDoFreeForTest(p + offset),
+ "Pointer is not pointing to the start of a span");
+ }
+}
+
+TEST(TCMallocFreeCheck, BadPageAlignedPointerInsideLargeObject) {
+ char* p = reinterpret_cast<char*>(
+ TCMallocDoMallocForTest(ExcludeSpaceForMarkForTest(kMaxSize + 1)));
+
+ for (int offset = kPageSize; offset < kMaxSize; offset += kPageSize) {
+ // Only the first and last page of a span are in heap map. So for others
+ // tcmalloc will give a general error of invalid pointer.
+ ASSERT_DEATH(TCMallocDoFreeForTest(p + offset),
+ "Attempt to free invalid pointer");
+ }
+ ASSERT_DEATH(TCMallocDoFreeForTest(p + kMaxSize),
+ "Pointer is not pointing to the start of a span");
+}
+
+TEST(TCMallocFreeCheck, DoubleFreeLargeObject) {
+ char* p = reinterpret_cast<char*>(
+ TCMallocDoMallocForTest(ExcludeSpaceForMarkForTest(kMaxSize + 1)));
+ ASSERT_DEATH(TCMallocDoFreeForTest(p); TCMallocDoFreeForTest(p),
+ "Object was not in-use");
+}
+
+
+#ifdef NDEBUG
+TEST(TCMallocFreeCheck, DoubleFreeSmallObject) {
+ for (size_t size = 1;
+ size <= ExcludeSpaceForMarkForTest(kMaxSize);
+ size <<= 1) {
+ char* p = reinterpret_cast<char*>(TCMallocDoMallocForTest(size));
+ ASSERT_DEATH(TCMallocDoFreeForTest(p); TCMallocDoFreeForTest(p),
+ "Circular loop in list detected");
+ }
+}
+#else
+TEST(TCMallocFreeCheck, DoubleFreeSmallObject) {
+ size_t size = 1;
+
+ // When the object is small, tcmalloc validation can not distinguish normal
+ // memory corruption or double free, because there's not enough space in
+ // freed objects to keep the mark.
+ for (; size <= ExcludeSpaceForMarkForTest(kMinClassSize); size <<= 1) {
+ char* p = reinterpret_cast<char*>(TCMallocDoMallocForTest(size));
+ ASSERT_DEATH(TCMallocDoFreeForTest(p); TCMallocDoFreeForTest(p),
+ "Memory corrupted");
+ }
+
+ for (; size <= ExcludeSpaceForMarkForTest(kMaxSize); size <<= 1) {
+ char* p = reinterpret_cast<char*>(TCMallocDoMallocForTest(size));
+ ASSERT_DEATH(TCMallocDoFreeForTest(p); TCMallocDoFreeForTest(p),
+ "Attempt to double free");
+ }
+}
+#endif
+
+int main(int argc, char **argv) {
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/src/base/allocator/type_profiler.cc b/src/base/allocator/type_profiler.cc
new file mode 100644
index 0000000..635fbcf
--- /dev/null
+++ b/src/base/allocator/type_profiler.cc
@@ -0,0 +1,63 @@
+// Copyright 2012 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.
+
+#if defined(TYPE_PROFILING)
+
+#include "base/allocator/type_profiler.h"
+
+#include <assert.h>
+
+namespace {
+
+void* NopIntercept(void* ptr, size_t size, const std::type_info& type) {
+ return ptr;
+}
+
+base::type_profiler::InterceptFunction* g_new_intercept = NopIntercept;
+base::type_profiler::InterceptFunction* g_delete_intercept = NopIntercept;
+
+}
+
+void* __op_new_intercept__(void* ptr,
+ size_t size,
+ const std::type_info& type) {
+ return g_new_intercept(ptr, size, type);
+}
+
+void* __op_delete_intercept__(void* ptr,
+ size_t size,
+ const std::type_info& type) {
+ return g_delete_intercept(ptr, size, type);
+}
+
+namespace base {
+namespace type_profiler {
+
+// static
+void InterceptFunctions::SetFunctions(InterceptFunction* new_intercept,
+ InterceptFunction* delete_intercept) {
+ // Don't use DCHECK, as this file is injected into targets
+ // that do not and should not depend on base/base.gyp:base
+ assert(g_new_intercept == NopIntercept);
+ assert(g_delete_intercept == NopIntercept);
+
+ g_new_intercept = new_intercept;
+ g_delete_intercept = delete_intercept;
+}
+
+// static
+void InterceptFunctions::ResetFunctions() {
+ g_new_intercept = NopIntercept;
+ g_delete_intercept = NopIntercept;
+}
+
+// static
+bool InterceptFunctions::IsAvailable() {
+ return g_new_intercept != NopIntercept || g_delete_intercept != NopIntercept;
+}
+
+} // namespace type_profiler
+} // namespace base
+
+#endif // defined(TYPE_PROFILING)
diff --git a/src/base/allocator/type_profiler.h b/src/base/allocator/type_profiler.h
new file mode 100644
index 0000000..86b5711
--- /dev/null
+++ b/src/base/allocator/type_profiler.h
@@ -0,0 +1,40 @@
+// Copyright 2012 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_ALLOCATOR_TYPE_PROFILER_H_
+#define BASE_ALLOCATOR_TYPE_PROFILER_H_
+
+#if defined(TYPE_PROFILING)
+
+#include <stddef.h> // for size_t
+#include <typeinfo> // for std::typeinfo
+
+namespace base {
+namespace type_profiler {
+
+typedef void* InterceptFunction(void*, size_t, const std::type_info&);
+
+class InterceptFunctions {
+ public:
+ // It must be called only once in a process while it is in single-thread.
+ // For now, ContentMainRunnerImpl::Initialize is the only supposed caller
+ // of this function except for single-threaded unit tests.
+ static void SetFunctions(InterceptFunction* new_intercept,
+ InterceptFunction* delete_intercept);
+
+ private:
+ friend class TypeProfilerTest;
+
+ // These functions are not thread safe.
+ // They must be used only from single-threaded unit tests.
+ static void ResetFunctions();
+ static bool IsAvailable();
+};
+
+} // namespace type_profiler
+} // namespace base
+
+#endif // defined(TYPE_PROFILING)
+
+#endif // BASE_ALLOCATOR_TYPE_PROFILER_H_
diff --git a/src/base/allocator/type_profiler_control.cc b/src/base/allocator/type_profiler_control.cc
new file mode 100644
index 0000000..6be7984
--- /dev/null
+++ b/src/base/allocator/type_profiler_control.cc
@@ -0,0 +1,38 @@
+// Copyright 2012 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/allocator/type_profiler_control.h"
+
+namespace base {
+namespace type_profiler {
+
+namespace {
+
+#if defined(TYPE_PROFILING)
+const bool kTypeProfilingEnabled = true;
+#else
+const bool kTypeProfilingEnabled = false;
+#endif
+
+bool g_enable_intercept = kTypeProfilingEnabled;
+
+} // namespace
+
+// static
+void Controller::Stop() {
+ g_enable_intercept = false;
+}
+
+// static
+bool Controller::IsProfiling() {
+ return kTypeProfilingEnabled && g_enable_intercept;
+}
+
+// static
+void Controller::Restart() {
+ g_enable_intercept = kTypeProfilingEnabled;
+}
+
+} // namespace type_profiler
+} // namespace base
diff --git a/src/base/allocator/type_profiler_control.h b/src/base/allocator/type_profiler_control.h
new file mode 100644
index 0000000..17cf5b6
--- /dev/null
+++ b/src/base/allocator/type_profiler_control.h
@@ -0,0 +1,31 @@
+// Copyright 2012 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_ALLOCATOR_TYPE_PROFILER_CONTROL_H_
+#define BASE_ALLOCATOR_TYPE_PROFILER_CONTROL_H_
+
+#include "base/gtest_prod_util.h"
+
+namespace base {
+namespace type_profiler {
+
+class Controller {
+ public:
+ static void Stop();
+ static bool IsProfiling();
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(TypeProfilerTest,
+ TestProfileNewWithoutProfiledDelete);
+
+ // It must be used only from allowed unit tests. The following is only
+ // allowed for use in unit tests. Profiling should never be restarted in
+ // regular use.
+ static void Restart();
+};
+
+} // namespace type_profiler
+} // namespace base
+
+#endif // BASE_ALLOCATOR_TYPE_PROFILER_CONTROL_H_
diff --git a/src/base/allocator/type_profiler_map_unittests.cc b/src/base/allocator/type_profiler_map_unittests.cc
new file mode 100644
index 0000000..ff74e11
--- /dev/null
+++ b/src/base/allocator/type_profiler_map_unittests.cc
@@ -0,0 +1,99 @@
+// Copyright 2012 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 unittest set for type_profiler_map in third_party/tcmalloc. It is
+// independent from other tests and executed manually like allocator_unittests
+// since type_profiler_map is a singleton (like TCMalloc's heap-profiler), and
+// it requires RTTI and different compiling/linking options from others.
+
+#if defined(TYPE_PROFILING)
+
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/tcmalloc/chromium/src/gperftools/type_profiler_map.h"
+
+namespace base {
+namespace type_profiler {
+
+static const void* const g_const_null = static_cast<const void*>(NULL);
+
+TEST(TypeProfilerMapTest, NormalOperation) {
+ // Allocate an object just to get a valid address.
+ // This 'new' is not profiled by type_profiler.
+ scoped_ptr<int> dummy(new int(48));
+ const std::type_info* type;
+
+ type = LookupType(dummy.get());
+ EXPECT_EQ(g_const_null, type);
+
+ InsertType(dummy.get(), 12, typeid(int));
+ type = LookupType(dummy.get());
+ ASSERT_NE(g_const_null, type);
+ EXPECT_STREQ(typeid(int).name(), type->name());
+
+ EraseType(dummy.get());
+ type = LookupType(dummy.get());
+ EXPECT_EQ(g_const_null, type);
+}
+
+TEST(TypeProfilerMapTest, EraseWithoutInsert) {
+ scoped_ptr<int> dummy(new int(48));
+ const std::type_info* type;
+
+ for (int i = 0; i < 10; ++i) {
+ EraseType(dummy.get());
+ type = LookupType(dummy.get());
+ EXPECT_EQ(g_const_null, type);
+ }
+}
+
+TEST(TypeProfilerMapTest, InsertThenMultipleErase) {
+ scoped_ptr<int> dummy(new int(48));
+ const std::type_info* type;
+
+ InsertType(dummy.get(), 12, typeid(int));
+ type = LookupType(dummy.get());
+ ASSERT_NE(g_const_null, type);
+ EXPECT_STREQ(typeid(int).name(), type->name());
+
+ for (int i = 0; i < 10; ++i) {
+ EraseType(dummy.get());
+ type = LookupType(dummy.get());
+ EXPECT_EQ(g_const_null, type);
+ }
+}
+
+TEST(TypeProfilerMapTest, MultipleInsertWithoutErase) {
+ scoped_ptr<int> dummy(new int(48));
+ const std::type_info* type;
+
+ InsertType(dummy.get(), 12, typeid(int));
+ type = LookupType(dummy.get());
+ ASSERT_NE(g_const_null, type);
+ EXPECT_STREQ(typeid(int).name(), type->name());
+
+ InsertType(dummy.get(), 5, typeid(char));
+ type = LookupType(dummy.get());
+ ASSERT_NE(g_const_null, type);
+ EXPECT_STREQ(typeid(char).name(), type->name());
+
+ InsertType(dummy.get(), 129, typeid(long));
+ type = LookupType(dummy.get());
+ ASSERT_NE(g_const_null, type);
+ EXPECT_STREQ(typeid(long).name(), type->name());
+
+ EraseType(dummy.get());
+ type = LookupType(dummy.get());
+ EXPECT_EQ(g_const_null, type);
+}
+
+} // namespace type_profiler
+} // namespace base
+
+int main(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+#endif // defined(TYPE_PROFILING)
diff --git a/src/base/allocator/type_profiler_tcmalloc.cc b/src/base/allocator/type_profiler_tcmalloc.cc
new file mode 100644
index 0000000..e5e10e0
--- /dev/null
+++ b/src/base/allocator/type_profiler_tcmalloc.cc
@@ -0,0 +1,37 @@
+// Copyright 2012 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.
+
+#if defined(TYPE_PROFILING)
+
+#include "base/allocator/type_profiler_tcmalloc.h"
+
+#include "base/allocator/type_profiler_control.h"
+#include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h"
+#include "third_party/tcmalloc/chromium/src/gperftools/type_profiler_map.h"
+
+namespace base {
+namespace type_profiler {
+
+void* NewInterceptForTCMalloc(void* ptr,
+ size_t size,
+ const std::type_info& type) {
+ if (Controller::IsProfiling())
+ InsertType(ptr, size, type);
+
+ return ptr;
+}
+
+void* DeleteInterceptForTCMalloc(void* ptr,
+ size_t size,
+ const std::type_info& type) {
+ if (Controller::IsProfiling())
+ EraseType(ptr);
+
+ return ptr;
+}
+
+} // namespace type_profiler
+} // namespace base
+
+#endif // defined(TYPE_PROFILING)
diff --git a/src/base/allocator/type_profiler_tcmalloc.h b/src/base/allocator/type_profiler_tcmalloc.h
new file mode 100644
index 0000000..ac55995
--- /dev/null
+++ b/src/base/allocator/type_profiler_tcmalloc.h
@@ -0,0 +1,29 @@
+// Copyright 2012 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_ALLOCATOR_TYPE_PROFILER_TCMALLOC_H_
+#define BASE_ALLOCATOR_TYPE_PROFILER_TCMALLOC_H_
+
+#if defined(TYPE_PROFILING)
+
+#include <cstddef> // for size_t
+#include <typeinfo> // for std::type_info
+
+namespace base {
+namespace type_profiler {
+
+void* NewInterceptForTCMalloc(void* ptr,
+ size_t size,
+ const std::type_info& type);
+
+void* DeleteInterceptForTCMalloc(void* ptr,
+ size_t size,
+ const std::type_info& type);
+
+} // namespace type_profiler
+} // namespace base
+
+#endif // defined(TYPE_PROFILING)
+
+#endif // BASE_ALLOCATOR_TYPE_PROFILER_TCMALLOC_H_
diff --git a/src/base/allocator/type_profiler_unittests.cc b/src/base/allocator/type_profiler_unittests.cc
new file mode 100644
index 0000000..16af86d
--- /dev/null
+++ b/src/base/allocator/type_profiler_unittests.cc
@@ -0,0 +1,189 @@
+// Copyright 2012 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 unittest set for type_profiler. It is independent from other
+// tests and executed manually like allocator_unittests since type_profiler_map
+// used in type_profiler is a singleton (like TCMalloc's heap-profiler), and
+// it requires RTTI and different compiling/linking options from others
+//
+// It tests that the profiler doesn't fail in suspicous cases. For example,
+// 'new' is not profiled, but 'delete' for the created object is profiled.
+
+#if defined(TYPE_PROFILING)
+
+#include "base/allocator/type_profiler.h"
+#include "base/allocator/type_profiler_control.h"
+#include "base/allocator/type_profiler_tcmalloc.h"
+#include "base/basictypes.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/tcmalloc/chromium/src/gperftools/type_profiler_map.h"
+
+namespace base {
+namespace type_profiler {
+
+class TypeProfilerTest : public testing::Test {
+ public:
+ TypeProfilerTest() {}
+
+ void SetInterceptFunctions() {
+ InterceptFunctions::SetFunctions(NewInterceptForTCMalloc,
+ DeleteInterceptForTCMalloc);
+ }
+
+ void ResetInterceptFunctions() {
+ InterceptFunctions::ResetFunctions();
+ }
+
+ void SetUp() {
+ SetInterceptFunctions();
+ }
+
+ void TearDown() {
+ ResetInterceptFunctions();
+ }
+
+ protected:
+ static const size_t kDummyArraySize;
+ static const void* const kConstNull;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TypeProfilerTest);
+};
+
+const size_t TypeProfilerTest::kDummyArraySize = 10;
+const void* const TypeProfilerTest::kConstNull = static_cast<const void*>(NULL);
+
+TEST_F(TypeProfilerTest, TestNormalProfiling) {
+ int* dummy = new int(48);
+ const std::type_info* type;
+
+ type = LookupType(dummy);
+ ASSERT_NE(kConstNull, type);
+ EXPECT_STREQ(typeid(int).name(), type->name());
+ delete dummy;
+
+ type = LookupType(dummy);
+ EXPECT_EQ(kConstNull, type);
+}
+
+TEST_F(TypeProfilerTest, TestNormalArrayProfiling) {
+ int* dummy = new int[kDummyArraySize];
+ const std::type_info* type;
+
+ type = LookupType(dummy);
+ ASSERT_NE(kConstNull, type);
+ // For an array, the profiler remembers its base type.
+ EXPECT_STREQ(typeid(int).name(), type->name());
+ delete[] dummy;
+
+ type = LookupType(dummy);
+ EXPECT_EQ(kConstNull, type);
+}
+
+TEST_F(TypeProfilerTest, TestRepeatedNewAndDelete) {
+ int *dummy[kDummyArraySize];
+ const std::type_info* type;
+ for (int i = 0; i < kDummyArraySize; ++i)
+ dummy[i] = new int(i);
+
+ for (int i = 0; i < kDummyArraySize; ++i) {
+ type = LookupType(dummy[i]);
+ ASSERT_NE(kConstNull, type);
+ EXPECT_STREQ(typeid(int).name(), type->name());
+ }
+
+ for (int i = 0; i < kDummyArraySize; ++i) {
+ delete dummy[i];
+ type = LookupType(dummy[i]);
+ ASSERT_EQ(kConstNull, type);
+ }
+}
+
+TEST_F(TypeProfilerTest, TestMultipleNewWithDroppingDelete) {
+ static const size_t large_size = 256 * 1024;
+
+ char* dummy_char = new char[large_size / sizeof(*dummy_char)];
+ const std::type_info* type;
+
+ type = LookupType(dummy_char);
+ ASSERT_NE(kConstNull, type);
+ EXPECT_STREQ(typeid(char).name(), type->name());
+
+ // Call "::operator delete" directly to drop __op_delete_intercept__.
+ ::operator delete[](dummy_char);
+
+ type = LookupType(dummy_char);
+ ASSERT_NE(kConstNull, type);
+ EXPECT_STREQ(typeid(char).name(), type->name());
+
+ // Allocates a little different size.
+ int* dummy_int = new int[large_size / sizeof(*dummy_int) - 1];
+
+ // We expect that tcmalloc returns the same address for these large (over 32k)
+ // allocation calls. It usually happens, but maybe probablistic.
+ ASSERT_EQ(static_cast<void*>(dummy_char), static_cast<void*>(dummy_int)) <<
+ "two new (malloc) calls didn't return the same address; retry it.";
+
+ type = LookupType(dummy_int);
+ ASSERT_NE(kConstNull, type);
+ EXPECT_STREQ(typeid(int).name(), type->name());
+
+ delete[] dummy_int;
+
+ type = LookupType(dummy_int);
+ EXPECT_EQ(kConstNull, type);
+}
+
+TEST_F(TypeProfilerTest, TestProfileDeleteWithoutProfiledNew) {
+ // 'dummy' should be new'ed in this test before intercept functions are set.
+ ResetInterceptFunctions();
+
+ int* dummy = new int(48);
+ const std::type_info* type;
+
+ // Set intercept functions again after 'dummy' is new'ed.
+ SetInterceptFunctions();
+
+ delete dummy;
+
+ type = LookupType(dummy);
+ EXPECT_EQ(kConstNull, type);
+
+ ResetInterceptFunctions();
+}
+
+TEST_F(TypeProfilerTest, TestProfileNewWithoutProfiledDelete) {
+ int* dummy = new int(48);
+ const std::type_info* type;
+
+ EXPECT_TRUE(Controller::IsProfiling());
+
+ // Stop profiling before deleting 'dummy'.
+ Controller::Stop();
+ EXPECT_FALSE(Controller::IsProfiling());
+
+ delete dummy;
+
+ // NOTE: We accept that a profile entry remains when a profiled object is
+ // deleted after Controller::Stop().
+ type = LookupType(dummy);
+ ASSERT_NE(kConstNull, type);
+ EXPECT_STREQ(typeid(int).name(), type->name());
+
+ Controller::Restart();
+ EXPECT_TRUE(Controller::IsProfiling());
+
+ // Remove manually since 'dummy' is not removed from type_profiler_map.
+ EraseType(dummy);
+}
+
+} // namespace type_profiler
+} // namespace base
+
+int main(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+#endif // defined(TYPE_PROFILING)
diff --git a/src/base/allocator/unittest_utils.cc b/src/base/allocator/unittest_utils.cc
new file mode 100644
index 0000000..130ba15
--- /dev/null
+++ b/src/base/allocator/unittest_utils.cc
@@ -0,0 +1,18 @@
+// 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.
+
+// The unittests need a this in order to link up without pulling in tons
+// of other libraries
+
+#include <config.h>
+
+inline int snprintf(char* buffer, size_t count, const char* format, ...) {
+ int result;
+ va_list args;
+ va_start(args, format);
+ result = _vsnprintf(buffer, count, format, args);
+ va_end(args);
+ return result;
+}
+
diff --git a/src/base/allocator/win_allocator.cc b/src/base/allocator/win_allocator.cc
new file mode 100644
index 0000000..899b867
--- /dev/null
+++ b/src/base/allocator/win_allocator.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 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 simple allocator based on the windows heap.
+
+extern "C" {
+
+HANDLE win_heap;
+
+bool win_heap_init(bool use_lfh) {
+ win_heap = HeapCreate(0, 0, 0);
+ if (win_heap == NULL)
+ return false;
+
+ if (use_lfh) {
+ ULONG enable_lfh = 2;
+ HeapSetInformation(win_heap, HeapCompatibilityInformation,
+ &enable_lfh, sizeof(enable_lfh));
+ // NOTE: Setting LFH may fail. Vista already has it enabled.
+ // And under the debugger, it won't use LFH. So we
+ // ignore any errors.
+ }
+
+ return true;
+}
+
+void* win_heap_malloc(size_t size) {
+ return HeapAlloc(win_heap, 0, size);
+}
+
+void win_heap_free(void* size) {
+ HeapFree(win_heap, 0, size);
+}
+
+void* win_heap_realloc(void* ptr, size_t size) {
+ if (!ptr)
+ return win_heap_malloc(size);
+ if (!size) {
+ win_heap_free(ptr);
+ return NULL;
+ }
+ return HeapReAlloc(win_heap, 0, ptr, size);
+}
+
+size_t win_heap_msize(void* ptr) {
+ return HeapSize(win_heap, 0, ptr);
+}
+
+void* win_heap_memalign(size_t alignment, size_t size) {
+ // Reserve enough space to ensure we can align and set aligned_ptr[-1] to the
+ // original allocation for use with win_heap_memalign_free() later.
+ size_t allocation_size = size + (alignment - 1) + sizeof(void*);
+
+ // Check for overflow. Alignment and size are checked in allocator_shim.
+ DCHECK_LT(size, allocation_size);
+ DCHECK_LT(alignment, allocation_size);
+
+ void* ptr = win_heap_malloc(allocation_size);
+ char* aligned_ptr = static_cast<char*>(ptr) + sizeof(void*);
+ aligned_ptr +=
+ alignment - reinterpret_cast<uintptr_t>(aligned_ptr) & (alignment - 1);
+
+ reinterpret_cast<void**>(aligned_ptr)[-1] = ptr;
+ return aligned_ptr;
+}
+
+void win_heap_memalign_free(void* ptr) {
+ if (ptr)
+ win_heap_free(static_cast<void**>(ptr)[-1]);
+}
+
+} // extern "C"
diff --git a/src/base/android/base_jni_registrar.cc b/src/base/android/base_jni_registrar.cc
new file mode 100644
index 0000000..2395d98
--- /dev/null
+++ b/src/base/android/base_jni_registrar.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 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/android/base_jni_registrar.h"
+
+#include "base/basictypes.h"
+#include "base/android/build_info.h"
+#include "base/android/cpu_features.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_registrar.h"
+#include "base/android/locale_utils.h"
+#include "base/android/path_service_android.h"
+#include "base/android/path_utils.h"
+#include "base/message_pump_android.h"
+#include "base/system_monitor/system_monitor_android.h"
+
+namespace base {
+namespace android {
+
+static RegistrationMethod kBaseRegisteredMethods[] = {
+ { "BuildInfo", base::android::BuildInfo::RegisterBindings },
+ { "CpuFeatures", base::android::RegisterCpuFeatures },
+ { "LocaleUtils", base::android::RegisterLocaleUtils },
+ { "PathService", base::android::RegisterPathService },
+ { "PathUtils", base::android::RegisterPathUtils },
+ { "SystemMessageHandler", base::MessagePumpForUI::RegisterBindings },
+ { "SystemMonitor", base::RegisterSystemMonitor },
+};
+
+bool RegisterJni(JNIEnv* env) {
+ return RegisterNativeMethods(env, kBaseRegisteredMethods,
+ arraysize(kBaseRegisteredMethods));
+}
+
+} // namespace android
+} // namespace base
diff --git a/src/base/android/base_jni_registrar.h b/src/base/android/base_jni_registrar.h
new file mode 100644
index 0000000..fdaf5f2
--- /dev/null
+++ b/src/base/android/base_jni_registrar.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 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_ANDROID_BASE_JNI_REGISTRAR_H_
+#define BASE_ANDROID_BASE_JNI_REGISTRAR_H_
+
+#include <jni.h>
+
+#include "base/base_export.h"
+
+namespace base {
+namespace android {
+
+// Register all JNI bindings necessary for base.
+BASE_EXPORT bool RegisterJni(JNIEnv* env);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_BASE_JNI_REGISTRAR_H_
diff --git a/src/base/android/build_info.cc b/src/base/android/build_info.cc
new file mode 100644
index 0000000..cdde6a9
--- /dev/null
+++ b/src/base/android/build_info.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2012 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/android/build_info.h"
+
+#include <string>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "jni/BuildInfo_jni.h"
+
+namespace {
+
+// The caller takes ownership of the returned const char*.
+const char* StrDupJString(const base::android::JavaRef<jstring>& java_string) {
+ std::string str = ConvertJavaStringToUTF8(java_string);
+ return strdup(str.c_str());
+}
+
+} // namespace
+
+namespace base {
+namespace android {
+
+struct BuildInfoSingletonTraits {
+ static BuildInfo* New() {
+ return new BuildInfo(AttachCurrentThread());
+ }
+
+ static void Delete(BuildInfo* x) {
+ // We're leaking this type, see kRegisterAtExit.
+ NOTREACHED();
+ }
+
+ static const bool kRegisterAtExit = false;
+ static const bool kAllowedToAccessOnNonjoinableThread = true;
+};
+
+BuildInfo::BuildInfo(JNIEnv* env)
+ : device_(StrDupJString(Java_BuildInfo_getDevice(env))),
+ model_(StrDupJString(Java_BuildInfo_getDeviceModel(env))),
+ brand_(StrDupJString(Java_BuildInfo_getBrand(env))),
+ android_build_id_(StrDupJString(Java_BuildInfo_getAndroidBuildId(env))),
+ android_build_fp_(StrDupJString(
+ Java_BuildInfo_getAndroidBuildFingerprint(env))),
+ package_version_code_(StrDupJString(Java_BuildInfo_getPackageVersionCode(
+ env, GetApplicationContext()))),
+ package_version_name_(StrDupJString(Java_BuildInfo_getPackageVersionName(
+ env, GetApplicationContext()))),
+ package_label_(StrDupJString(Java_BuildInfo_getPackageLabel(
+ env, GetApplicationContext()))),
+ package_name_(StrDupJString(Java_BuildInfo_getPackageName(
+ env, GetApplicationContext()))),
+ sdk_int_(Java_BuildInfo_getSdkInt(env)),
+ java_exception_info_(NULL) {
+}
+
+// static
+BuildInfo* BuildInfo::GetInstance() {
+ return Singleton<BuildInfo, BuildInfoSingletonTraits >::get();
+}
+
+void BuildInfo::set_java_exception_info(const std::string& info) {
+ DCHECK(!java_exception_info_) << "info should be set only once.";
+ java_exception_info_ = strndup(info.c_str(), 1024);
+}
+
+// static
+bool BuildInfo::RegisterBindings(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace base
diff --git a/src/base/android/build_info.h b/src/base/android/build_info.h
new file mode 100644
index 0000000..6273d3e
--- /dev/null
+++ b/src/base/android/build_info.h
@@ -0,0 +1,114 @@
+// Copyright (c) 2012 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_ANDROID_BUILD_INFO_H_
+#define BASE_ANDROID_BUILD_INFO_H_
+
+#include <jni.h>
+
+#include <string>
+
+#include "base/memory/singleton.h"
+
+namespace base {
+namespace android {
+
+// BuildInfo is a singleton class that stores android build and device
+// information. It will be called from Android specific code and gets used
+// primarily in crash reporting.
+
+// It is also used to store the last java exception seen during JNI.
+// TODO(nileshagrawal): Find a better place to store this info.
+class BuildInfo {
+ public:
+
+ ~BuildInfo() {}
+
+ // Static factory method for getting the singleton BuildInfo instance.
+ // Note that ownership is not conferred on the caller and the BuildInfo in
+ // question isn't actually freed until shutdown. This is ok because there
+ // should only be one instance of BuildInfo ever created.
+ static BuildInfo* GetInstance();
+
+ // Const char* is used instead of std::strings because these values must be
+ // available even if the process is in a crash state. Sadly
+ // std::string.c_str() doesn't guarantee that memory won't be allocated when
+ // it is called.
+ const char* device() const {
+ return device_;
+ }
+
+ const char* model() const {
+ return model_;
+ }
+
+ const char* brand() const {
+ return brand_;
+ }
+
+ const char* android_build_id() const {
+ return android_build_id_;
+ }
+
+ const char* android_build_fp() const {
+ return android_build_fp_;
+ }
+
+ const char* package_version_code() const {
+ return package_version_code_;
+ }
+
+ const char* package_version_name() const {
+ return package_version_name_;
+ }
+
+ const char* package_label() const {
+ return package_label_;
+ }
+
+ const char* package_name() const {
+ return package_name_;
+ }
+
+ int sdk_int() const {
+ return sdk_int_;
+ }
+
+ const char* java_exception_info() const {
+ return java_exception_info_;
+ }
+
+ void set_java_exception_info(const std::string& info);
+
+ static bool RegisterBindings(JNIEnv* env);
+
+ private:
+ friend struct BuildInfoSingletonTraits;
+
+ explicit BuildInfo(JNIEnv* env);
+
+ // Const char* is used instead of std::strings because these values must be
+ // available even if the process is in a crash state. Sadly
+ // std::string.c_str() doesn't guarantee that memory won't be allocated when
+ // it is called.
+ const char* const device_;
+ const char* const model_;
+ const char* const brand_;
+ const char* const android_build_id_;
+ const char* const android_build_fp_;
+ const char* const package_version_code_;
+ const char* const package_version_name_;
+ const char* const package_label_;
+ const char* const package_name_;
+ const int sdk_int_;
+ // This is set via set_java_exception_info, not at constructor time.
+ const char* java_exception_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(BuildInfo);
+};
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_BUILD_INFO_H_
diff --git a/src/base/android/cpu_features.cc b/src/base/android/cpu_features.cc
new file mode 100644
index 0000000..6a18695
--- /dev/null
+++ b/src/base/android/cpu_features.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 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 <cpu-features.h>
+
+#include "base/android/jni_android.h"
+#include "jni/CpuFeatures_jni.h"
+
+namespace base {
+namespace android {
+
+jint GetCoreCount(JNIEnv*, jclass) {
+ return android_getCpuCount();
+}
+
+jlong GetCpuFeatures(JNIEnv*, jclass) {
+ return android_getCpuFeatures();
+}
+
+bool RegisterCpuFeatures(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace base
diff --git a/src/base/android/cpu_features.h b/src/base/android/cpu_features.h
new file mode 100644
index 0000000..0a27822
--- /dev/null
+++ b/src/base/android/cpu_features.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2012 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_ANDROID_CPU_FEATURES_H_
+#define BASE_ANDROID_CPU_FEATURES_H_
+
+#include <jni.h>
+
+namespace base {
+namespace android {
+
+bool RegisterCpuFeatures(JNIEnv* env);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_CPU_FEATURES_H_
diff --git a/src/base/android/java/src/org/chromium/base/AccessedByNative.java b/src/base/android/java/src/org/chromium/base/AccessedByNative.java
new file mode 100644
index 0000000..8248cc6
--- /dev/null
+++ b/src/base/android/java/src/org/chromium/base/AccessedByNative.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @AccessedByNative is used to ensure proguard will keep this field, since it's
+ * only accessed by native.
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AccessedByNative {
+ public String value() default "";
+}
diff --git a/src/base/android/java/src/org/chromium/base/ActivityStatus.java b/src/base/android/java/src/org/chromium/base/ActivityStatus.java
new file mode 100644
index 0000000..e728870
--- /dev/null
+++ b/src/base/android/java/src/org/chromium/base/ActivityStatus.java
@@ -0,0 +1,99 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base;
+
+import android.app.Activity;
+import android.os.Looper;
+
+import java.util.ArrayList;
+
+/**
+ * Provides information about the parent activity's status.
+ */
+public class ActivityStatus {
+
+ // Constants matching activity states reported to StateListener.onStateChange
+ public static final int CREATED = 1;
+ public static final int STARTED = 2;
+ public static final int RESUMED = 3;
+ public static final int PAUSED = 4;
+ public static final int STOPPED = 5;
+ public static final int DESTROYED = 6;
+
+ // Current main activity, or null if none.
+ private static Activity sActivity;
+
+ // Current main activity's state. This can be set even if sActivity is null, to simplify unit
+ // testing.
+ private static int sActivityState;
+
+ private static final ArrayList<StateListener> sStateListeners = new ArrayList<StateListener>();
+
+ // Use this interface to listen to all state changes.
+ public interface StateListener {
+ /**
+ * Called when the activity's state changes.
+ * @param newState New activity state.
+ */
+ public void onActivityStateChange(int newState);
+ }
+
+ private ActivityStatus() {}
+
+ /**
+ * Must be called by the main activity when it changes state.
+ * @param activity Current activity.
+ * @param newState New state value.
+ */
+ public static void onStateChange(Activity activity, int newState) {
+ if (newState == CREATED) {
+ sActivity = activity;
+ }
+ sActivityState = newState;
+ for (StateListener listener : sStateListeners) {
+ listener.onActivityStateChange(newState);
+ }
+ if (newState == DESTROYED) {
+ sActivity = null;
+ }
+ }
+
+ /**
+ * Indicates that the parent activity is currently paused.
+ */
+ public static boolean isPaused() {
+ return sActivityState == PAUSED;
+ }
+
+ /**
+ * Returns the current main application activity.
+ */
+ public static Activity getActivity() {
+ return sActivity;
+ }
+
+ /**
+ * Returns the current main application activity's state.
+ */
+ public static int getState() {
+ return sActivityState;
+ }
+
+ /**
+ * Registers the given listener to receive activity state changes.
+ * @param listener Listener to receive state changes.
+ */
+ public static void registerStateListener(StateListener listener) {
+ sStateListeners.add(listener);
+ }
+
+ /**
+ * Unregisters the given listener from receiving activity state changes.
+ * @param listener Listener that doesn't want to receive state changes.
+ */
+ public static void unregisterStateListener(StateListener listener) {
+ sStateListeners.remove(listener);
+ }
+}
diff --git a/src/base/android/java/src/org/chromium/base/BuildInfo.java b/src/base/android/java/src/org/chromium/base/BuildInfo.java
new file mode 100644
index 0000000..2314051
--- /dev/null
+++ b/src/base/android/java/src/org/chromium/base/BuildInfo.java
@@ -0,0 +1,114 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Build;
+import android.util.Log;
+
+import org.chromium.base.CalledByNative;
+
+/**
+ * BuildInfo is a utility class providing easy access to {@link PackageInfo}
+ * information. This is primarly of use for accessesing package information
+ * from native code.
+ */
+public class BuildInfo {
+ private static final String TAG = "BuildInfo";
+ private static final int MAX_FINGERPRINT_LENGTH = 128;
+
+ /**
+ * BuildInfo is a static utility class and therefore shouldn't be
+ * instantiated.
+ */
+ private BuildInfo() {
+ }
+
+ @CalledByNative
+ public static String getDevice() {
+ return Build.DEVICE;
+ }
+
+ @CalledByNative
+ public static String getBrand() {
+ return Build.BRAND;
+ }
+
+ @CalledByNative
+ public static String getAndroidBuildId() {
+ return Build.ID;
+ }
+
+ /**
+ * @return The build fingerprint for the current Android install. The value is truncated to a
+ * 128 characters as this is used for crash and UMA reporting, which should avoid huge
+ * strings.
+ */
+ @CalledByNative
+ public static String getAndroidBuildFingerprint() {
+ return Build.FINGERPRINT.substring(
+ 0, Math.min(Build.FINGERPRINT.length(), MAX_FINGERPRINT_LENGTH));
+ }
+
+ @CalledByNative
+ public static String getDeviceModel() {
+ return Build.MODEL;
+ }
+
+ @CalledByNative
+ public static String getPackageVersionCode(Context context) {
+ String msg = "versionCode not available.";
+ try {
+ PackageManager pm = context.getPackageManager();
+ PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0);
+ msg = "" + pi.versionCode;
+ } catch (NameNotFoundException e) {
+ Log.d(TAG, msg);
+ }
+ return msg;
+
+ }
+
+ @CalledByNative
+ public static String getPackageVersionName(Context context) {
+ String msg = "versionName not available";
+ try {
+ PackageManager pm = context.getPackageManager();
+ PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0);
+ msg = pi.versionName;
+ } catch (NameNotFoundException e) {
+ Log.d(TAG, msg);
+ }
+ return msg;
+ }
+
+ @CalledByNative
+ public static String getPackageLabel(Context context) {
+ try {
+ PackageManager packageManager = context.getPackageManager();
+ ApplicationInfo appInfo = packageManager.getApplicationInfo(context.getPackageName(),
+ PackageManager.GET_META_DATA);
+ CharSequence label = packageManager.getApplicationLabel(appInfo);
+ return label != null ? label.toString() : "";
+ } catch (NameNotFoundException e) {
+ return "";
+ }
+ }
+
+ @CalledByNative
+ public static String getPackageName(Context context) {
+ String packageName = context != null ? context.getPackageName() : null;
+ return packageName != null ? packageName : "";
+ }
+
+ @CalledByNative
+ public static int getSdkInt() {
+ return Build.VERSION.SDK_INT;
+ }
+}
diff --git a/src/base/android/java/src/org/chromium/base/CalledByNative.java b/src/base/android/java/src/org/chromium/base/CalledByNative.java
new file mode 100644
index 0000000..8d3dcad
--- /dev/null
+++ b/src/base/android/java/src/org/chromium/base/CalledByNative.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @CalledByNative is used by the JNI generator to create the necessary JNI
+ * bindings and expose this method to native code.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface CalledByNative {
+ /*
+ * If present, tells which inner class the method belongs to.
+ */
+ public String value() default "";
+}
diff --git a/src/base/android/java/src/org/chromium/base/CalledByNativeUnchecked.java b/src/base/android/java/src/org/chromium/base/CalledByNativeUnchecked.java
new file mode 100644
index 0000000..cc264a2
--- /dev/null
+++ b/src/base/android/java/src/org/chromium/base/CalledByNativeUnchecked.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @CalledByNativeUnchecked is used to generate JNI bindings that do not check for exceptions.
+ * It only makes sense to use this annotation on methods that declare a throws... spec.
+ * However, note that the exception received native side maybe an 'unchecked' (RuntimeExpception)
+ * such as NullPointerException, so the native code should differentiate these cases.
+ * Usage of this should be very rare; where possible handle exceptions in the Java side and use a
+ * return value to indicate success / failure.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface CalledByNativeUnchecked {
+ /*
+ * If present, tells which inner class the method belongs to.
+ */
+ public String value() default "";
+}
diff --git a/src/base/android/java/src/org/chromium/base/ChromiumActivity.java b/src/base/android/java/src/org/chromium/base/ChromiumActivity.java
new file mode 100644
index 0000000..65f5ce9
--- /dev/null
+++ b/src/base/android/java/src/org/chromium/base/ChromiumActivity.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+// All Chromium main activities should extend this class. This allows various sub-systems to
+// properly react to activity state changes.
+public class ChromiumActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstance) {
+ super.onCreate(savedInstance);
+ ActivityStatus.onStateChange(this, ActivityStatus.CREATED);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ ActivityStatus.onStateChange(this, ActivityStatus.STARTED);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ ActivityStatus.onStateChange(this, ActivityStatus.RESUMED);
+ }
+
+ @Override
+ protected void onPause() {
+ ActivityStatus.onStateChange(this, ActivityStatus.PAUSED);
+ super.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ ActivityStatus.onStateChange(this, ActivityStatus.STOPPED);
+ super.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ ActivityStatus.onStateChange(this, ActivityStatus.DESTROYED);
+ super.onDestroy();
+ }
+}
diff --git a/src/base/android/java/src/org/chromium/base/CpuFeatures.java b/src/base/android/java/src/org/chromium/base/CpuFeatures.java
new file mode 100644
index 0000000..f298fb1
--- /dev/null
+++ b/src/base/android/java/src/org/chromium/base/CpuFeatures.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base;
+
+// The only purpose of this class is to allow sending CPU properties
+// from the browser process to sandboxed renderer processes. This is
+// needed because sandboxed processes cannot, on ARM, query the kernel
+// about the CPU's properties by parsing /proc, so this operation must
+// be performed in the browser process, and the result passed to
+// renderer ones.
+//
+// For more context, see http://crbug.com/164154
+//
+// Technically, this is a wrapper around the native NDK cpufeatures
+// library. The exact CPU features bits are never used in Java so
+// there is no point in duplicating their definitions here.
+//
+@JNINamespace("base::android")
+public abstract class CpuFeatures {
+ /**
+ * Return the number of CPU Cores on the device.
+ */
+ public static int getCount() {
+ return nativeGetCoreCount();
+ }
+
+ /**
+ * Return the CPU feature mask.
+ * This is a 64-bit integer that corresponds to the CPU's features.
+ * The value comes directly from android_getCpuFeatures().
+ */
+ public static long getMask() {
+ return nativeGetCpuFeatures();
+ }
+
+ private static native int nativeGetCoreCount();
+ private static native long nativeGetCpuFeatures();
+}
diff --git a/src/base/android/java/src/org/chromium/base/JNINamespace.java b/src/base/android/java/src/org/chromium/base/JNINamespace.java
new file mode 100644
index 0000000..cfffc91
--- /dev/null
+++ b/src/base/android/java/src/org/chromium/base/JNINamespace.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @JNINamespace is used by the JNI generator to create the necessary JNI
+ * bindings and expose this method to native code using the specified namespace.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface JNINamespace {
+ public String value();
+}
diff --git a/src/base/android/java/src/org/chromium/base/LocaleUtils.java b/src/base/android/java/src/org/chromium/base/LocaleUtils.java
new file mode 100644
index 0000000..6bca1f7
--- /dev/null
+++ b/src/base/android/java/src/org/chromium/base/LocaleUtils.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base;
+
+import java.util.Locale;
+
+/**
+ * This class provides the locale related methods for the native library.
+ */
+class LocaleUtils {
+
+ private LocaleUtils() { /* cannot be instantiated */ }
+
+ /**
+ * @return the default locale.
+ */
+ @CalledByNative
+ public static String getDefaultLocale() {
+ Locale locale = Locale.getDefault();
+ String language = locale.getLanguage();
+ String country = locale.getCountry();
+ return country.isEmpty() ? language : language + "-" + country;
+ }
+}
diff --git a/src/base/android/java/src/org/chromium/base/NativeClassQualifiedName.java b/src/base/android/java/src/org/chromium/base/NativeClassQualifiedName.java
new file mode 100644
index 0000000..309169b
--- /dev/null
+++ b/src/base/android/java/src/org/chromium/base/NativeClassQualifiedName.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @NativeClassQualifiedName is used by the JNI generator to create the necessary JNI
+ * bindings to call into the specified native class name.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NativeClassQualifiedName {
+ /*
+ * Tells which native class the method is going to be bound to.
+ * The first parameter of the annotated method must be an int nativePtr pointing to
+ * an instance of this class.
+ */
+ public String value();
+}
diff --git a/src/base/android/java/src/org/chromium/base/PathService.java b/src/base/android/java/src/org/chromium/base/PathService.java
new file mode 100644
index 0000000..dfda736
--- /dev/null
+++ b/src/base/android/java/src/org/chromium/base/PathService.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base;
+
+/**
+ * This class provides java side access to the native PathService.
+ */
+@JNINamespace("base::android")
+public abstract class PathService {
+
+ // Must match the value of DIR_MODULE in base/base_paths.h!
+ public static final int DIR_MODULE = 3;
+
+ // Prevent instantiation.
+ private PathService() {}
+
+ public static void override(int what, String path) {
+ nativeOverride(what, path);
+ }
+
+ private static native void nativeOverride(int what, String path);
+}
diff --git a/src/base/android/java/src/org/chromium/base/PathUtils.java b/src/base/android/java/src/org/chromium/base/PathUtils.java
new file mode 100644
index 0000000..c3503a4
--- /dev/null
+++ b/src/base/android/java/src/org/chromium/base/PathUtils.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.Environment;
+
+/**
+ * This class provides the path related methods for the native library.
+ */
+public abstract class PathUtils {
+
+ private static String sDataDirectorySuffix;
+
+ // Prevent instantiation.
+ private PathUtils() {}
+
+ /**
+ * Sets the suffix that should be used for the directory where private data is to be stored
+ * by the application.
+ * @param suffix The private data directory suffix.
+ * @see Context#getDir(String, int)
+ */
+ public static void setPrivateDataDirectorySuffix(String suffix) {
+ sDataDirectorySuffix = suffix;
+ }
+
+ /**
+ * @return the private directory that is used to store application data.
+ */
+ @CalledByNative
+ public static String getDataDirectory(Context appContext) {
+ if (sDataDirectorySuffix == null) {
+ throw new IllegalStateException(
+ "setDataDirectorySuffix must be called before getDataDirectory");
+ }
+ return appContext.getDir(sDataDirectorySuffix, Context.MODE_PRIVATE).getPath();
+ }
+
+ /**
+ * @return the cache directory.
+ */
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private static String getCacheDirectory(Context appContext) {
+ return appContext.getCacheDir().getPath();
+ }
+
+ /**
+ * @return the public downloads directory.
+ */
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private static String getDownloadsDirectory(Context appContext) {
+ return Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS).getPath();
+ }
+
+ /**
+ * @return the path to native libraries.
+ */
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private static String getNativeLibraryDirectory(Context appContext) {
+ ApplicationInfo ai = appContext.getApplicationInfo();
+ if ((ai.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0 ||
+ (ai.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ return ai.nativeLibraryDir;
+ }
+
+ return "/system/lib/";
+ }
+
+ /**
+ * @return the external storage directory.
+ */
+ @SuppressWarnings("unused")
+ @CalledByNative
+ public static String getExternalStorageDirectory() {
+ return Environment.getExternalStorageDirectory().getAbsolutePath();
+ }
+}
diff --git a/src/base/android/java/src/org/chromium/base/PowerStatusReceiver.java b/src/base/android/java/src/org/chromium/base/PowerStatusReceiver.java
new file mode 100644
index 0000000..89594b8
--- /dev/null
+++ b/src/base/android/java/src/org/chromium/base/PowerStatusReceiver.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+
+/**
+ * A BroadcastReceiver that listens to changes in power status and notifies
+ * SystemMonitor.
+ * It's instantiated by the framework via the application intent-filter
+ * declared in its manifest.
+ */
+public class PowerStatusReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ SystemMonitor.onBatteryChargingChanged(intent);
+ }
+}
diff --git a/src/base/android/java/src/org/chromium/base/SystemMessageHandler.java b/src/base/android/java/src/org/chromium/base/SystemMessageHandler.java
new file mode 100644
index 0000000..f7bb19f
--- /dev/null
+++ b/src/base/android/java/src/org/chromium/base/SystemMessageHandler.java
@@ -0,0 +1,93 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base;
+
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+class SystemMessageHandler extends Handler {
+
+ private static final int TIMER_MESSAGE = 1;
+ private static final int DELAYED_TIMER_MESSAGE = 2;
+
+ // Native class pointer set by the constructor of the SharedClient native class.
+ private int mMessagePumpDelegateNative = 0;
+
+ // Used to ensure we have at most one TIMER_MESSAGE pending at once.
+ private AtomicBoolean mTimerFired = new AtomicBoolean(true);
+
+ // Used to insert TIMER_MESSAGE on the front of the system message queue during startup only.
+ // This is a wee hack, to give a priority boost to native tasks during startup as they tend to
+ // be on the critical path. (After startup, handling the UI with minimum latency is more
+ // important).
+ private boolean mStartupComplete = false;
+ private final long mStartupCompleteTime = System.currentTimeMillis() + 2000;
+ private final boolean startupComplete() {
+ if (!mStartupComplete && System.currentTimeMillis() > mStartupCompleteTime) {
+ mStartupComplete = true;
+ }
+ return mStartupComplete;
+ }
+
+ private SystemMessageHandler(int messagePumpDelegateNative) {
+ mMessagePumpDelegateNative = messagePumpDelegateNative;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == TIMER_MESSAGE) {
+ mTimerFired.set(true);
+ }
+ while (nativeDoRunLoopOnce(mMessagePumpDelegateNative)) {
+ if (startupComplete()) {
+ setTimer();
+ break;
+ }
+ }
+ }
+
+ @CalledByNative
+ private void setTimer() {
+ if (!mTimerFired.getAndSet(false)) {
+ // mTimerFired was already false.
+ return;
+ }
+ if (startupComplete()) {
+ sendEmptyMessage(TIMER_MESSAGE);
+ } else {
+ sendMessageAtFrontOfQueue(obtainMessage(TIMER_MESSAGE));
+ }
+ }
+
+ // If millis <=0, it'll send a TIMER_MESSAGE instead of
+ // a DELAYED_TIMER_MESSAGE.
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private void setDelayedTimer(long millis) {
+ if (millis <= 0) {
+ setTimer();
+ } else {
+ removeMessages(DELAYED_TIMER_MESSAGE);
+ sendEmptyMessageDelayed(DELAYED_TIMER_MESSAGE, millis);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private void removeTimer() {
+ removeMessages(TIMER_MESSAGE);
+ removeMessages(DELAYED_TIMER_MESSAGE);
+ }
+
+ @CalledByNative
+ private static SystemMessageHandler create(int messagePumpDelegateNative) {
+ return new SystemMessageHandler(messagePumpDelegateNative);
+ }
+
+ private native boolean nativeDoRunLoopOnce(int messagePumpDelegateNative);
+}
diff --git a/src/base/android/java/src/org/chromium/base/SystemMonitor.java b/src/base/android/java/src/org/chromium/base/SystemMonitor.java
new file mode 100644
index 0000000..30f61a6
--- /dev/null
+++ b/src/base/android/java/src/org/chromium/base/SystemMonitor.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.Handler;
+import android.os.Looper;
+
+
+/**
+ * Integrates native SystemMonitor with the java side.
+ */
+@JNINamespace("base::android")
+public class SystemMonitor implements ActivityStatus.StateListener {
+ private static final long SUSPEND_DELAY_MS = 1 * 60 * 1000; // 1 minute.
+ private static SystemMonitor sInstance;
+
+ private boolean mIsBatteryPower;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+ // Asynchronous task used to fire the "paused" event to the native side 1 minute after the main
+ // activity transitioned to the "paused" state. This event is not sent immediately because it
+ // would be too aggressive. An Android activity can be in the "paused" state quite often. This
+ // can happen when a dialog window shows up for instance.
+ private static final Runnable sSuspendTask = new Runnable() {
+ @Override
+ public void run() {
+ nativeOnMainActivitySuspended();
+ }
+ };
+
+ public static void createForTests(Context context) {
+ // Applications will create this once the JNI side has been fully wired up both sides. For
+ // tests, we just need native -> java, that is, we don't need to notify java -> native on
+ // creation.
+ sInstance = new SystemMonitor();
+ }
+
+ public static void create(Context context) {
+ if (sInstance == null) {
+ sInstance = new SystemMonitor();
+ ActivityStatus.registerStateListener(sInstance);
+ IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ Intent batteryStatusIntent = context.registerReceiver(null, ifilter);
+ onBatteryChargingChanged(batteryStatusIntent);
+ }
+ }
+
+ private SystemMonitor() {
+ }
+
+ public static void onBatteryChargingChanged(Intent intent) {
+ if (sInstance == null) {
+ // We may be called by the framework intent-filter before being fully initialized. This
+ // is not a problem, since our constructor will check for the state later on.
+ return;
+ }
+ int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
+ // If we're not plugged, assume we're running on battery power.
+ sInstance.mIsBatteryPower = chargePlug != BatteryManager.BATTERY_PLUGGED_USB &&
+ chargePlug != BatteryManager.BATTERY_PLUGGED_AC;
+ nativeOnBatteryChargingChanged();
+ }
+
+ @Override
+ public void onActivityStateChange(int newState) {
+ if (newState == ActivityStatus.RESUMED) {
+ // Remove the callback from the message loop in case it hasn't been executed yet.
+ mHandler.removeCallbacks(sSuspendTask);
+ nativeOnMainActivityResumed();
+ } else if (newState == ActivityStatus.PAUSED) {
+ mHandler.postDelayed(sSuspendTask, SUSPEND_DELAY_MS);
+ }
+ }
+
+ @CalledByNative
+ private static boolean isBatteryPower() {
+ return sInstance.mIsBatteryPower;
+ }
+
+ private static native void nativeOnBatteryChargingChanged();
+ private static native void nativeOnMainActivitySuspended();
+ private static native void nativeOnMainActivityResumed();
+}
diff --git a/src/base/android/java/src/org/chromium/base/ThreadUtils.java b/src/base/android/java/src/org/chromium/base/ThreadUtils.java
new file mode 100644
index 0000000..70232c4
--- /dev/null
+++ b/src/base/android/java/src/org/chromium/base/ThreadUtils.java
@@ -0,0 +1,152 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
+/**
+ * Helper methods to deal with threading related tasks.
+ */
+public class ThreadUtils {
+
+ /**
+ * Run the supplied Runnable on the main thread. The method will block until
+ * the Runnable completes.
+ *
+ * @param r The Runnable to run.
+ */
+ public static void runOnUiThreadBlocking(final Runnable r) {
+ if (runningOnUiThread()) {
+ r.run();
+ } else {
+ FutureTask<Void> task = new FutureTask<Void>(r, null);
+ postOnUiThread(task);
+ try {
+ task.get();
+ } catch (Exception e) {
+ throw new RuntimeException("Exception occured while waiting for runnable", e);
+ }
+ }
+ }
+
+ /**
+ * Run the supplied Callable on the main thread, wrapping any exceptions in
+ * a RuntimeException. The method will block until the Callable completes.
+ *
+ * @param c The Callable to run
+ * @return The result of the callable
+ */
+ public static <T> T runOnUiThreadBlockingNoException(Callable<T> c) {
+ try {
+ return runOnUiThreadBlocking(c);
+ } catch (ExecutionException e) {
+ throw new RuntimeException("Error occured waiting for callable", e);
+ }
+ }
+
+ /**
+ * Run the supplied Callable on the main thread, The method will block until
+ * the Callable completes.
+ *
+ * @param c The Callable to run
+ * @return The result of the callable
+ * @throws ExecutionException c's exception
+ */
+ public static <T> T runOnUiThreadBlocking(Callable<T> c) throws ExecutionException {
+ FutureTask<T> task = new FutureTask<T>(c);
+ runOnUiThread(task);
+ try {
+ return task.get();
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted waiting for callable", e);
+ }
+ }
+
+ /**
+ * Run the supplied FutureTask on the main thread. The method will block
+ * only if the current thread is the main thread.
+ *
+ * @param task The FutureTask to run
+ * @return The queried task (to aid inline construction)
+ */
+ public static <T> FutureTask<T> runOnUiThread(FutureTask<T> task) {
+ if (runningOnUiThread()) {
+ task.run();
+ } else {
+ postOnUiThread(task);
+ }
+ return task;
+ }
+
+ /**
+ * Run the supplied Callable on the main thread. The method will block
+ * only if the current thread is the main thread.
+ *
+ * @param c The Callable to run
+ * @return A FutureTask wrapping the callable to retrieve results
+ */
+ public static <T> FutureTask<T> runOnUiThread(Callable<T> c) {
+ return runOnUiThread(new FutureTask<T>(c));
+ }
+
+ /**
+ * Run the supplied Runnable on the main thread. The method will block
+ * only if the current thread is the main thread.
+ *
+ * @param r The Runnable to run
+ */
+ public static void runOnUiThread(Runnable r) {
+ if (runningOnUiThread()) {
+ r.run();
+ } else {
+ LazyHolder.sUiThreadHandler.post(r);
+ }
+ }
+
+ /**
+ * Post the supplied FutureTask to run on the main thread. The method will
+ * not block, even if called on the UI thread.
+ *
+ * @param task The FutureTask to run
+ * @return The queried task (to aid inline construction)
+ */
+ public static <T> FutureTask<T> postOnUiThread(FutureTask<T> task) {
+ LazyHolder.sUiThreadHandler.post(task);
+ return task;
+ }
+
+ /**
+ * Post the supplied Runnable to run on the main thread. The method will
+ * not block, even if called on the UI thread.
+ *
+ * @param task The Runnable to run
+ */
+ public static void postOnUiThread(Runnable r) {
+ LazyHolder.sUiThreadHandler.post(r);
+ }
+
+ /**
+ * Asserts that the current thread is running on the main thread.
+ */
+ public static void assertOnUiThread() {
+ assert runningOnUiThread();
+ }
+
+ /**
+ * @return true iff the current thread is the main (UI) thread.
+ */
+ public static boolean runningOnUiThread() {
+ return Looper.getMainLooper() == Looper.myLooper();
+ }
+
+ private static class LazyHolder {
+ private static Handler sUiThreadHandler = new Handler(Looper.getMainLooper());
+ }
+}
diff --git a/src/base/android/java/src/org/chromium/base/WeakContext.java b/src/base/android/java/src/org/chromium/base/WeakContext.java
new file mode 100644
index 0000000..d660cc9
--- /dev/null
+++ b/src/base/android/java/src/org/chromium/base/WeakContext.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base;
+
+import android.content.Context;
+
+import java.lang.ref.WeakReference;
+import java.util.concurrent.Callable;
+
+// Holds a WeakReference to Context to allow it to be GC'd.
+// Also provides utility functions to getSystemService from the UI or any
+// other thread (may return null, if the Context has been nullified).
+public class WeakContext {
+ private static WeakReference<Context> sWeakContext;
+
+ public static void initializeWeakContext(final Context context) {
+ sWeakContext = new WeakReference<Context>(context);
+ }
+
+ public static Context getContext() {
+ return sWeakContext.get();
+ }
+
+ // Returns a system service. May be called from any thread.
+ // If necessary, it will send a message to the main thread to acquire the
+ // service, and block waiting for it to complete.
+ // May return null if context is no longer available.
+ public static Object getSystemService(final String name) {
+ final Context context = sWeakContext.get();
+ if (context == null) {
+ return null;
+ }
+ if (ThreadUtils.runningOnUiThread()) {
+ return context.getSystemService(name);
+ }
+ return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Object>() {
+ @Override
+ public Object call() {
+ return context.getSystemService(name);
+ }
+ });
+ }
+}
diff --git a/src/base/android/jni_android.cc b/src/base/android/jni_android.cc
new file mode 100644
index 0000000..fdc2170
--- /dev/null
+++ b/src/base/android/jni_android.cc
@@ -0,0 +1,327 @@
+// Copyright (c) 2012 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/android/jni_android.h"
+
+#include <map>
+
+#include "base/android/build_info.h"
+#include "base/android/jni_string.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/platform_thread.h"
+
+namespace {
+using base::android::GetClass;
+using base::android::MethodID;
+using base::android::ScopedJavaLocalRef;
+
+struct MethodIdentifier {
+ const char* class_name;
+ const char* method;
+ const char* jni_signature;
+
+ bool operator<(const MethodIdentifier& other) const {
+ int r = strcmp(class_name, other.class_name);
+ if (r < 0) {
+ return true;
+ } else if (r > 0) {
+ return false;
+ }
+
+ r = strcmp(method, other.method);
+ if (r < 0) {
+ return true;
+ } else if (r > 0) {
+ return false;
+ }
+
+ return strcmp(jni_signature, other.jni_signature) < 0;
+ }
+};
+
+typedef std::map<MethodIdentifier, jmethodID> MethodIDMap;
+
+const base::subtle::AtomicWord kUnlocked = 0;
+const base::subtle::AtomicWord kLocked = 1;
+base::subtle::AtomicWord g_method_id_map_lock = kUnlocked;
+JavaVM* g_jvm = NULL;
+// Leak the global app context, as it is used from a non-joinable worker thread
+// that may still be running at shutdown. There is no harm in doing this.
+base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky
+ g_application_context = LAZY_INSTANCE_INITIALIZER;
+base::LazyInstance<MethodIDMap> g_method_id_map = LAZY_INSTANCE_INITIALIZER;
+
+std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) {
+ ScopedJavaLocalRef<jclass> throwable_clazz =
+ GetClass(env, "java/lang/Throwable");
+ jmethodID throwable_printstacktrace =
+ MethodID::Get<MethodID::TYPE_INSTANCE>(
+ env, throwable_clazz.obj(), "printStackTrace",
+ "(Ljava/io/PrintStream;)V");
+
+ // Create an instance of ByteArrayOutputStream.
+ ScopedJavaLocalRef<jclass> bytearray_output_stream_clazz =
+ GetClass(env, "java/io/ByteArrayOutputStream");
+ jmethodID bytearray_output_stream_constructor =
+ MethodID::Get<MethodID::TYPE_INSTANCE>(
+ env, bytearray_output_stream_clazz.obj(), "<init>", "()V");
+ jmethodID bytearray_output_stream_tostring =
+ MethodID::Get<MethodID::TYPE_INSTANCE>(
+ env, bytearray_output_stream_clazz.obj(), "toString",
+ "()Ljava/lang/String;");
+ ScopedJavaLocalRef<jobject> bytearray_output_stream(env,
+ env->NewObject(bytearray_output_stream_clazz.obj(),
+ bytearray_output_stream_constructor));
+
+ // Create an instance of PrintStream.
+ ScopedJavaLocalRef<jclass> printstream_clazz =
+ GetClass(env, "java/io/PrintStream");
+ jmethodID printstream_constructor =
+ MethodID::Get<MethodID::TYPE_INSTANCE>(
+ env, printstream_clazz.obj(), "<init>",
+ "(Ljava/io/OutputStream;)V");
+ ScopedJavaLocalRef<jobject> printstream(env,
+ env->NewObject(printstream_clazz.obj(), printstream_constructor,
+ bytearray_output_stream.obj()));
+
+ // Call Throwable.printStackTrace(PrintStream)
+ env->CallVoidMethod(java_throwable, throwable_printstacktrace,
+ printstream.obj());
+
+ // Call ByteArrayOutputStream.toString()
+ ScopedJavaLocalRef<jstring> exception_string(
+ env, static_cast<jstring>(
+ env->CallObjectMethod(bytearray_output_stream.obj(),
+ bytearray_output_stream_tostring)));
+
+ return ConvertJavaStringToUTF8(exception_string);
+}
+
+} // namespace
+
+namespace base {
+namespace android {
+
+JNIEnv* AttachCurrentThread() {
+ DCHECK(g_jvm);
+ JNIEnv* env = NULL;
+ jint ret = g_jvm->AttachCurrentThread(&env, NULL);
+ DCHECK_EQ(JNI_OK, ret);
+ return env;
+}
+
+void DetachFromVM() {
+ // Ignore the return value, if the thread is not attached, DetachCurrentThread
+ // will fail. But it is ok as the native thread may never be attached.
+ if (g_jvm)
+ g_jvm->DetachCurrentThread();
+}
+
+void InitVM(JavaVM* vm) {
+ DCHECK(!g_jvm);
+ g_jvm = vm;
+}
+
+void InitApplicationContext(const JavaRef<jobject>& context) {
+ DCHECK(g_application_context.Get().is_null());
+ g_application_context.Get().Reset(context);
+}
+
+const jobject GetApplicationContext() {
+ DCHECK(!g_application_context.Get().is_null());
+ return g_application_context.Get().obj();
+}
+
+ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) {
+ return ScopedJavaLocalRef<jclass>(env, GetUnscopedClass(env, class_name));
+}
+
+jclass GetUnscopedClass(JNIEnv* env, const char* class_name) {
+ jclass clazz = env->FindClass(class_name);
+ CHECK(!ClearException(env) && clazz) << "Failed to find class " << class_name;
+ return clazz;
+}
+
+bool HasClass(JNIEnv* env, const char* class_name) {
+ ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name));
+ if (!clazz.obj()) {
+ ClearException(env);
+ return false;
+ }
+ bool error = ClearException(env);
+ DCHECK(!error);
+ return true;
+}
+
+template<MethodID::Type type>
+jmethodID MethodID::Get(JNIEnv* env,
+ jclass clazz,
+ const char* method_name,
+ const char* jni_signature) {
+ jmethodID id = type == TYPE_STATIC ?
+ env->GetStaticMethodID(clazz, method_name, jni_signature) :
+ env->GetMethodID(clazz, method_name, jni_signature);
+ CHECK(base::android::ClearException(env) || id) <<
+ "Failed to find " <<
+ (type == TYPE_STATIC ? "static " : "") <<
+ "method " << method_name << " " << jni_signature;
+ return id;
+}
+
+// If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call
+// into ::Get() above. If there's a race, it's ok since the values are the same
+// (and the duplicated effort will happen only once).
+template<MethodID::Type type>
+jmethodID MethodID::LazyGet(JNIEnv* env,
+ jclass clazz,
+ const char* method_name,
+ const char* jni_signature,
+ base::subtle::AtomicWord* atomic_method_id) {
+ COMPILE_ASSERT(sizeof(subtle::AtomicWord) >= sizeof(jmethodID),
+ AtomicWord_SmallerThan_jMethodID);
+ subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_method_id);
+ if (value)
+ return reinterpret_cast<jmethodID>(value);
+ jmethodID id = MethodID::Get<type>(env, clazz, method_name, jni_signature);
+ base::subtle::Release_Store(
+ atomic_method_id, reinterpret_cast<subtle::AtomicWord>(id));
+ return id;
+}
+
+// Various template instantiations.
+template jmethodID MethodID::Get<MethodID::TYPE_STATIC>(
+ JNIEnv* env, jclass clazz, const char* method_name,
+ const char* jni_signature);
+
+template jmethodID MethodID::Get<MethodID::TYPE_INSTANCE>(
+ JNIEnv* env, jclass clazz, const char* method_name,
+ const char* jni_signature);
+
+template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>(
+ JNIEnv* env, jclass clazz, const char* method_name,
+ const char* jni_signature, base::subtle::AtomicWord* atomic_method_id);
+
+template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>(
+ JNIEnv* env, jclass clazz, const char* method_name,
+ const char* jni_signature, base::subtle::AtomicWord* atomic_method_id);
+
+jfieldID GetFieldID(JNIEnv* env,
+ const JavaRef<jclass>& clazz,
+ const char* field_name,
+ const char* jni_signature) {
+ jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature);
+ CHECK(!ClearException(env) && field_id) << "Failed to find field " <<
+ field_name << " " << jni_signature;
+ return field_id;
+}
+
+bool HasField(JNIEnv* env,
+ const JavaRef<jclass>& clazz,
+ const char* field_name,
+ const char* jni_signature) {
+ jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature);
+ if (!field_id) {
+ ClearException(env);
+ return false;
+ }
+ bool error = ClearException(env);
+ DCHECK(!error);
+ return true;
+}
+
+jfieldID GetStaticFieldID(JNIEnv* env,
+ const JavaRef<jclass>& clazz,
+ const char* field_name,
+ const char* jni_signature) {
+ jfieldID field_id =
+ env->GetStaticFieldID(clazz.obj(), field_name, jni_signature);
+ CHECK(!ClearException(env) && field_id) << "Failed to find static field " <<
+ field_name << " " << jni_signature;
+ return field_id;
+}
+
+jmethodID GetMethodIDFromClassName(JNIEnv* env,
+ const char* class_name,
+ const char* method,
+ const char* jni_signature) {
+ MethodIdentifier key;
+ key.class_name = class_name;
+ key.method = method;
+ key.jni_signature = jni_signature;
+
+ MethodIDMap* map = g_method_id_map.Pointer();
+ bool found = false;
+
+ while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock,
+ kUnlocked,
+ kLocked) != kUnlocked) {
+ base::PlatformThread::YieldCurrentThread();
+ }
+ MethodIDMap::const_iterator iter = map->find(key);
+ if (iter != map->end()) {
+ found = true;
+ }
+ base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked);
+
+ // Addition to the map does not invalidate this iterator.
+ if (found) {
+ return iter->second;
+ }
+
+ ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name));
+ jmethodID id = MethodID::Get<MethodID::TYPE_INSTANCE>(
+ env, clazz.obj(), method, jni_signature);
+
+ while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock,
+ kUnlocked,
+ kLocked) != kUnlocked) {
+ base::PlatformThread::YieldCurrentThread();
+ }
+ // Another thread may have populated the map already.
+ std::pair<MethodIDMap::const_iterator, bool> result =
+ map->insert(std::make_pair(key, id));
+ DCHECK_EQ(id, result.first->second);
+ base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked);
+
+ return id;
+}
+
+bool HasException(JNIEnv* env) {
+ return env->ExceptionCheck() != JNI_FALSE;
+}
+
+bool ClearException(JNIEnv* env) {
+ if (!HasException(env))
+ return false;
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return true;
+}
+
+void CheckException(JNIEnv* env) {
+ if (!HasException(env)) return;
+
+ // Exception has been found, might as well tell breakpad about it.
+ jthrowable java_throwable = env->ExceptionOccurred();
+ if (!java_throwable) {
+ // Do nothing but return false.
+ CHECK(false);
+ }
+
+ // Clear the pending exception, since a local reference is now held.
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+
+ // Set the exception_string in BuildInfo so that breakpad can read it.
+ // RVO should avoid any extra copies of the exception string.
+ base::android::BuildInfo::GetInstance()->set_java_exception_info(
+ GetJavaExceptionInfo(env, java_throwable));
+
+ // Now, feel good about it and die.
+ CHECK(false);
+}
+
+} // namespace android
+} // namespace base
diff --git a/src/base/android/jni_android.h b/src/base/android/jni_android.h
new file mode 100644
index 0000000..16b85af
--- /dev/null
+++ b/src/base/android/jni_android.h
@@ -0,0 +1,137 @@
+// Copyright (c) 2012 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_ANDROID_JNI_ANDROID_H_
+#define BASE_ANDROID_JNI_ANDROID_H_
+
+#include <jni.h>
+#include <sys/types.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/atomicops.h"
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+
+namespace base {
+namespace android {
+
+// Used to mark symbols to be exported in a shared library's symbol table.
+#define JNI_EXPORT __attribute__ ((visibility("default")))
+
+// Contains the registration method information for initializing JNI bindings.
+struct RegistrationMethod {
+ const char* name;
+ bool (*func)(JNIEnv* env);
+};
+
+// Attach the current thread to the VM (if necessary) and return the JNIEnv*.
+BASE_EXPORT JNIEnv* AttachCurrentThread();
+
+// Detach the current thread from VM if it is attached.
+BASE_EXPORT void DetachFromVM();
+
+// Initializes the global JVM. It is not necessarily called before
+// InitApplicationContext().
+BASE_EXPORT void InitVM(JavaVM* vm);
+
+// Initializes the global application context object. The |context| can be any
+// valid reference to the application context. Internally holds a global ref to
+// the context. InitVM and InitApplicationContext maybe called in either order.
+BASE_EXPORT void InitApplicationContext(const JavaRef<jobject>& context);
+
+// Gets a global ref to the application context set with
+// InitApplicationContext(). Ownership is retained by the function - the caller
+// must NOT release it.
+const BASE_EXPORT jobject GetApplicationContext();
+
+// Finds the class named |class_name| and returns it.
+// Use this method instead of invoking directly the JNI FindClass method (to
+// prevent leaking local references).
+// This method triggers a fatal assertion if the class could not be found.
+// Use HasClass if you need to check whether the class exists.
+BASE_EXPORT ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env,
+ const char* class_name);
+
+// Similar to the above, but the caller is responsible to manage the jclass
+// lifetime.
+BASE_EXPORT jclass GetUnscopedClass(JNIEnv* env,
+ const char* class_name) WARN_UNUSED_RESULT;
+
+// Returns true iff the class |class_name| could be found.
+BASE_EXPORT bool HasClass(JNIEnv* env, const char* class_name);
+
+// This class is a wrapper for JNIEnv Get(Static)MethodID.
+class BASE_EXPORT MethodID {
+ public:
+ enum Type {
+ TYPE_STATIC,
+ TYPE_INSTANCE,
+ };
+
+ // Returns the method ID for the method with the specified name and signature.
+ // This method triggers a fatal assertion if the method could not be found.
+ template<Type type>
+ static jmethodID Get(JNIEnv* env,
+ jclass clazz,
+ const char* method_name,
+ const char* jni_signature);
+
+ // The caller is responsible to zero-initialize |atomic_method_id|.
+ // It's fine to simultaneously call this on multiple threads referencing the
+ // same |atomic_method_id|.
+ template<Type type>
+ static jmethodID LazyGet(JNIEnv* env,
+ jclass clazz,
+ const char* method_name,
+ const char* jni_signature,
+ base::subtle::AtomicWord* atomic_method_id);
+};
+
+// Gets the method ID from the class name. Clears the pending Java exception
+// and returns NULL if the method is not found. Caches results. Note that
+// MethodID::Get() above avoids a class lookup, but does not cache results.
+// Strings passed to this function are held in the cache and MUST remain valid
+// beyond the duration of all future calls to this function, across all
+// threads. In practice, this means that the function should only be used with
+// string constants.
+BASE_EXPORT jmethodID GetMethodIDFromClassName(JNIEnv* env,
+ const char* class_name,
+ const char* method,
+ const char* jni_signature);
+
+// Gets the field ID for a class field.
+// This method triggers a fatal assertion if the field could not be found.
+BASE_EXPORT jfieldID GetFieldID(JNIEnv* env,
+ const JavaRef<jclass>& clazz,
+ const char* field_name,
+ const char* jni_signature);
+
+// Returns true if |clazz| as a field with the given name and signature.
+// TODO(jcivelli): Determine whether we explicitly have to pass the environment.
+BASE_EXPORT bool HasField(JNIEnv* env,
+ const JavaRef<jclass>& clazz,
+ const char* field_name,
+ const char* jni_signature);
+
+// Gets the field ID for a static class field.
+// This method triggers a fatal assertion if the field could not be found.
+BASE_EXPORT jfieldID GetStaticFieldID(JNIEnv* env,
+ const JavaRef<jclass>& clazz,
+ const char* field_name,
+ const char* jni_signature);
+
+// Returns true if an exception is pending in the provided JNIEnv*.
+BASE_EXPORT bool HasException(JNIEnv* env);
+
+// If an exception is pending in the provided JNIEnv*, this function clears it
+// and returns true.
+BASE_EXPORT bool ClearException(JNIEnv* env);
+
+// This function will call CHECK() macro if there's any pending exception.
+BASE_EXPORT void CheckException(JNIEnv* env);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_JNI_ANDROID_H_
diff --git a/src/base/android/jni_android_unittest.cc b/src/base/android/jni_android_unittest.cc
new file mode 100644
index 0000000..920b395
--- /dev/null
+++ b/src/base/android/jni_android_unittest.cc
@@ -0,0 +1,141 @@
+// Copyright (c) 2012 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/android/jni_android.h"
+
+#include "base/at_exit.h"
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace android {
+
+namespace {
+
+const char kJavaLangObject[] = "java/lang/Object";
+const char kGetClass[] = "getClass";
+const char kToString[] = "toString";
+const char kReturningJavaLangClass[] = "()Ljava/lang/Class;";
+const char kReturningJavaLangString[] = "()Ljava/lang/String;";
+
+const char* g_last_method;
+const char* g_last_jni_signature;
+jmethodID g_last_method_id;
+
+const JNINativeInterface* g_previous_functions;
+
+jmethodID GetMethodIDWrapper(JNIEnv* env, jclass clazz, const char* method,
+ const char* jni_signature) {
+ g_last_method = method;
+ g_last_jni_signature = jni_signature;
+ g_last_method_id = g_previous_functions->GetMethodID(env, clazz, method,
+ jni_signature);
+ return g_last_method_id;
+}
+
+} // namespace
+
+class JNIAndroidTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ JNIEnv* env = AttachCurrentThread();
+ g_previous_functions = env->functions;
+ hooked_functions = *g_previous_functions;
+ env->functions = &hooked_functions;
+ hooked_functions.GetMethodID = &GetMethodIDWrapper;
+ }
+
+ virtual void TearDown() {
+ JNIEnv* env = AttachCurrentThread();
+ env->functions = g_previous_functions;
+ Reset();
+ }
+
+ void Reset() {
+ g_last_method = 0;
+ g_last_jni_signature = 0;
+ g_last_method_id = NULL;
+ }
+ // Needed to cleanup the cached method map in the implementation between
+ // runs (e.g. if using --gtest_repeat)
+ base::ShadowingAtExitManager exit_manager;
+ // From JellyBean release, the instance of this struct provided in JNIEnv is
+ // read-only, so we deep copy it to allow individual functions to be hooked.
+ JNINativeInterface hooked_functions;
+};
+
+TEST_F(JNIAndroidTest, GetMethodIDFromClassNameCaching) {
+ JNIEnv* env = AttachCurrentThread();
+
+ Reset();
+ jmethodID id1 = GetMethodIDFromClassName(env, kJavaLangObject, kGetClass,
+ kReturningJavaLangClass);
+ EXPECT_STREQ(kGetClass, g_last_method);
+ EXPECT_STREQ(kReturningJavaLangClass, g_last_jni_signature);
+ EXPECT_EQ(g_last_method_id, id1);
+
+ Reset();
+ jmethodID id2 = GetMethodIDFromClassName(env, kJavaLangObject, kGetClass,
+ kReturningJavaLangClass);
+ EXPECT_STREQ(0, g_last_method);
+ EXPECT_STREQ(0, g_last_jni_signature);
+ EXPECT_EQ(NULL, g_last_method_id);
+ EXPECT_EQ(id1, id2);
+
+ Reset();
+ jmethodID id3 = GetMethodIDFromClassName(env, kJavaLangObject, kToString,
+ kReturningJavaLangString);
+ EXPECT_STREQ(kToString, g_last_method);
+ EXPECT_STREQ(kReturningJavaLangString, g_last_jni_signature);
+ EXPECT_EQ(g_last_method_id, id3);
+}
+
+namespace {
+
+base::subtle::AtomicWord g_atomic_id = 0;
+int LazyMethodIDCall(JNIEnv* env, jclass clazz, int p) {
+ jmethodID id = base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_STATIC>(
+ env, clazz,
+ "abs",
+ "(I)I",
+ &g_atomic_id);
+
+ return env->CallStaticIntMethod(clazz, id, p);
+}
+
+int MethodIDCall(JNIEnv* env, jclass clazz, jmethodID id, int p) {
+ return env->CallStaticIntMethod(clazz, id, p);
+}
+
+} // namespace
+
+TEST(JNIAndroidMicrobenchmark, MethodId) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jclass> clazz(GetClass(env, "java/lang/Math"));
+ base::Time start_lazy = base::Time::Now();
+ int o = 0;
+ for (int i = 0; i < 1024; ++i)
+ o += LazyMethodIDCall(env, clazz.obj(), i);
+ base::Time end_lazy = base::Time::Now();
+
+ jmethodID id = reinterpret_cast<jmethodID>(g_atomic_id);
+ base::Time start = base::Time::Now();
+ for (int i = 0; i < 1024; ++i)
+ o += MethodIDCall(env, clazz.obj(), id, i);
+ base::Time end = base::Time::Now();
+
+ // On a Galaxy Nexus, results were in the range of:
+ // JNI LazyMethodIDCall (us) 1984
+ // JNI MethodIDCall (us) 1861
+ LOG(ERROR) << "JNI LazyMethodIDCall (us) " <<
+ base::TimeDelta(end_lazy - start_lazy).InMicroseconds();
+ LOG(ERROR) << "JNI MethodIDCall (us) " <<
+ base::TimeDelta(end - start).InMicroseconds();
+ LOG(ERROR) << "JNI " << o;
+}
+
+
+} // namespace android
+} // namespace base
diff --git a/src/base/android/jni_array.cc b/src/base/android/jni_array.cc
new file mode 100644
index 0000000..fe2aadb
--- /dev/null
+++ b/src/base/android/jni_array.cc
@@ -0,0 +1,153 @@
+// Copyright (c) 2012 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/android/jni_array.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/logging.h"
+
+namespace base {
+namespace android {
+
+ScopedJavaLocalRef<jbyteArray> ToJavaByteArray(
+ JNIEnv* env, const uint8* bytes, size_t len) {
+ jbyteArray byte_array = env->NewByteArray(len);
+ CheckException(env);
+ DCHECK(byte_array);
+
+ jbyte* elements = env->GetByteArrayElements(byte_array, NULL);
+ memcpy(elements, bytes, len);
+ env->ReleaseByteArrayElements(byte_array, elements, 0);
+ CheckException(env);
+
+ return ScopedJavaLocalRef<jbyteArray>(env, byte_array);
+}
+
+ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfByteArray(
+ JNIEnv* env, const std::vector<std::string>& v) {
+ ScopedJavaLocalRef<jclass> byte_array_clazz = GetClass(env, "[B");
+ jobjectArray joa = env->NewObjectArray(v.size(),
+ byte_array_clazz.obj(), NULL);
+ CheckException(env);
+
+ for (size_t i = 0; i < v.size(); ++i) {
+ ScopedJavaLocalRef<jbyteArray> byte_array = ToJavaByteArray(env,
+ reinterpret_cast<const uint8*>(v[i].data()), v[i].length());
+ env->SetObjectArrayElement(joa, i, byte_array.obj());
+ }
+ return ScopedJavaLocalRef<jobjectArray>(env, joa);
+}
+
+ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfStrings(
+ JNIEnv* env, const std::vector<std::string>& v) {
+ ScopedJavaLocalRef<jclass> string_clazz = GetClass(env, "java/lang/String");
+ jobjectArray joa = env->NewObjectArray(v.size(), string_clazz.obj(), NULL);
+ CheckException(env);
+
+ for (size_t i = 0; i < v.size(); ++i) {
+ ScopedJavaLocalRef<jstring> item = ConvertUTF8ToJavaString(env, v[i]);
+ env->SetObjectArrayElement(joa, i, item.obj());
+ }
+ return ScopedJavaLocalRef<jobjectArray>(env, joa);
+}
+
+ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfStrings(
+ JNIEnv* env, const std::vector<string16>& v) {
+ ScopedJavaLocalRef<jclass> string_clazz = GetClass(env, "java/lang/String");
+ jobjectArray joa = env->NewObjectArray(v.size(), string_clazz.obj(), NULL);
+ CheckException(env);
+
+ for (size_t i = 0; i < v.size(); ++i) {
+ ScopedJavaLocalRef<jstring> item = ConvertUTF16ToJavaString(env, v[i]);
+ env->SetObjectArrayElement(joa, i, item.obj());
+ }
+ return ScopedJavaLocalRef<jobjectArray>(env, joa);
+}
+
+void AppendJavaStringArrayToStringVector(JNIEnv* env,
+ jobjectArray array,
+ std::vector<string16>* out) {
+ DCHECK(out);
+ if (!array)
+ return;
+ jsize len = env->GetArrayLength(array);
+ size_t back = out->size();
+ out->resize(back + len);
+ for (jsize i = 0; i < len; ++i) {
+ ScopedJavaLocalRef<jstring> str(env,
+ static_cast<jstring>(env->GetObjectArrayElement(array, i)));
+ ConvertJavaStringToUTF16(env, str.obj(), &((*out)[back + i]));
+ }
+}
+
+void AppendJavaStringArrayToStringVector(JNIEnv* env,
+ jobjectArray array,
+ std::vector<std::string>* out) {
+ DCHECK(out);
+ if (!array)
+ return;
+ jsize len = env->GetArrayLength(array);
+ size_t back = out->size();
+ out->resize(back + len);
+ for (jsize i = 0; i < len; ++i) {
+ ScopedJavaLocalRef<jstring> str(env,
+ static_cast<jstring>(env->GetObjectArrayElement(array, i)));
+ ConvertJavaStringToUTF8(env, str.obj(), &((*out)[back + i]));
+ }
+}
+
+void AppendJavaByteArrayToByteVector(JNIEnv* env,
+ jbyteArray byte_array,
+ std::vector<uint8>* out) {
+ DCHECK(out);
+ if (!byte_array)
+ return;
+ jsize len = env->GetArrayLength(byte_array);
+ jbyte* bytes = env->GetByteArrayElements(byte_array, NULL);
+ out->insert(out->end(), bytes, bytes + len);
+ env->ReleaseByteArrayElements(byte_array, bytes, JNI_ABORT);
+}
+
+void JavaByteArrayToByteVector(JNIEnv* env,
+ jbyteArray byte_array,
+ std::vector<uint8>* out) {
+ DCHECK(out);
+ out->clear();
+ AppendJavaByteArrayToByteVector(env, byte_array, out);
+}
+
+void JavaIntArrayToIntVector(JNIEnv* env,
+ jintArray array,
+ std::vector<int>* out) {
+ DCHECK(out);
+ out->clear();
+ jsize len = env->GetArrayLength(array);
+ jint* ints = env->GetIntArrayElements(array, NULL);
+ for (jsize i = 0; i < len; ++i) {
+ out->push_back(static_cast<int>(ints[i]));
+ }
+ env->ReleaseIntArrayElements(array, ints, JNI_ABORT);
+}
+
+void JavaArrayOfByteArrayToStringVector(
+ JNIEnv* env,
+ jobjectArray array,
+ std::vector<std::string>* out) {
+ DCHECK(out);
+ out->clear();
+ jsize len = env->GetArrayLength(array);
+ out->resize(len);
+ for (jsize i = 0; i < len; ++i) {
+ jbyteArray bytes_array = static_cast<jbyteArray>(
+ env->GetObjectArrayElement(array, i));
+ jsize bytes_len = env->GetArrayLength(bytes_array);
+ jbyte* bytes = env->GetByteArrayElements(bytes_array, NULL);
+ (*out)[i].assign(reinterpret_cast<const char*>(bytes), bytes_len);
+ env->ReleaseByteArrayElements(bytes_array, bytes, JNI_ABORT);
+ }
+}
+
+} // namespace android
+} // namespace base
diff --git a/src/base/android/jni_array.h b/src/base/android/jni_array.h
new file mode 100644
index 0000000..fbc131e
--- /dev/null
+++ b/src/base/android/jni_array.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 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_ANDROID_JNI_ARRAY_H_
+#define BASE_ANDROID_JNI_ARRAY_H_
+
+#include <jni.h>
+#include <string>
+#include <vector>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/string16.h"
+
+namespace base {
+namespace android {
+
+// Returns a new Java byte array converted from the given bytes array.
+BASE_EXPORT ScopedJavaLocalRef<jbyteArray> ToJavaByteArray(
+ JNIEnv* env, const uint8* bytes, size_t len);
+
+// Returns a array of Java byte array converted from |v|.
+BASE_EXPORT ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfByteArray(
+ JNIEnv* env, const std::vector<std::string>& v);
+
+BASE_EXPORT ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfStrings(
+ JNIEnv* env, const std::vector<std::string>& v);
+
+BASE_EXPORT ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfStrings(
+ JNIEnv* env, const std::vector<string16>& v);
+
+// Converts a Java string array to a native array.
+BASE_EXPORT void AppendJavaStringArrayToStringVector(
+ JNIEnv* env,
+ jobjectArray array,
+ std::vector<string16>* out);
+
+BASE_EXPORT void AppendJavaStringArrayToStringVector(
+ JNIEnv* env,
+ jobjectArray array,
+ std::vector<std::string>* out);
+
+// Appends the Java bytes in |bytes_array| onto the end of |out|.
+BASE_EXPORT void AppendJavaByteArrayToByteVector(
+ JNIEnv* env,
+ jbyteArray byte_array,
+ std::vector<uint8>* out);
+
+// Replaces the content of |out| with the Java bytes in |bytes_array|.
+BASE_EXPORT void JavaByteArrayToByteVector(
+ JNIEnv* env,
+ jbyteArray byte_array,
+ std::vector<uint8>* out);
+
+// Replaces the content of |out| with the Java ints in |int_array|.
+BASE_EXPORT void JavaIntArrayToIntVector(
+ JNIEnv* env,
+ jintArray int_array,
+ std::vector<int>* out);
+
+// Assuming |array| is an byte[][] (array of byte arrays), replaces the
+// content of |out| with the corresponding vector of strings. No UTF-8
+// conversion is performed.
+void JavaArrayOfByteArrayToStringVector(
+ JNIEnv* env,
+ jobjectArray array,
+ std::vector<std::string>* out);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_JNI_ARRAY_H_
diff --git a/src/base/android/jni_array_unittest.cc b/src/base/android/jni_array_unittest.cc
new file mode 100644
index 0000000..a4e3025
--- /dev/null
+++ b/src/base/android/jni_array_unittest.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 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/android/jni_array.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace android {
+
+TEST(JniArray, BasicConversions) {
+ const uint8 kBytes[] = { 0, 1, 2, 3 };
+ const size_t kLen = arraysize(kBytes);
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jbyteArray> bytes = ToJavaByteArray(env, kBytes, kLen);
+ ASSERT_TRUE(bytes.obj());
+
+ std::vector<uint8> vec(5);
+ JavaByteArrayToByteVector(env, bytes.obj(), &vec);
+ EXPECT_EQ(4U, vec.size());
+ EXPECT_EQ(std::vector<uint8>(kBytes, kBytes + kLen), vec);
+
+ AppendJavaByteArrayToByteVector(env, bytes.obj(), &vec);
+ EXPECT_EQ(8U, vec.size());
+}
+
+TEST(JniArray, JavaArrayOfByteArrayToStringVector) {
+ const int kMaxItems = 50;
+ JNIEnv* env = AttachCurrentThread();
+
+ // Create a byte[][] object.
+ ScopedJavaLocalRef<jclass> byte_array_clazz(env, env->FindClass("[B"));
+ ASSERT_TRUE(byte_array_clazz.obj());
+
+ ScopedJavaLocalRef<jobjectArray> array(
+ env, env->NewObjectArray(kMaxItems, byte_array_clazz.obj(), NULL));
+ ASSERT_TRUE(array.obj());
+
+ // Create kMaxItems byte buffers.
+ char text[16];
+ for (int i = 0; i < kMaxItems; ++i) {
+ snprintf(text, sizeof text, "%d", i);
+ ScopedJavaLocalRef<jbyteArray> byte_array = ToJavaByteArray(
+ env, reinterpret_cast<uint8*>(text),
+ static_cast<size_t>(strlen(text)));
+ ASSERT_TRUE(byte_array.obj());
+
+ env->SetObjectArrayElement(array.obj(), i, byte_array.obj());
+ ASSERT_FALSE(HasException(env));
+ }
+
+ // Convert to std::vector<std::string>, check the content.
+ std::vector<std::string> vec;
+ JavaArrayOfByteArrayToStringVector(env, array.obj(), &vec);
+
+ EXPECT_EQ(static_cast<size_t>(kMaxItems), vec.size());
+ for (int i = 0; i < kMaxItems; ++i) {
+ snprintf(text, sizeof text, "%d", i);
+ EXPECT_STREQ(text, vec[i].c_str());
+ }
+}
+
+} // namespace android
+} // namespace base
diff --git a/src/base/android/jni_generator/SampleForTests.java b/src/base/android/jni_generator/SampleForTests.java
new file mode 100644
index 0000000..341f0ea
--- /dev/null
+++ b/src/base/android/jni_generator/SampleForTests.java
@@ -0,0 +1,171 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.example.jni_generator;
+
+import android.graphics.Rect;
+
+// This class serves as a reference test for the bindings generator, and as example documentation
+// for how to use the jni generator.
+// The C++ counter-part is sample_for_tests.cc.
+// jni_generator.gyp has a jni_generator_tests target that will:
+// * Generate a header file for the JNI bindings based on this file.
+// * Compile sample_for_tests.cc using the generated header file.
+// * link a native executable to prove the generated header + cc file are self-contained.
+// All comments are informational only, and are ignored by the jni generator.
+//
+// This JNINamespace annotation indicates that all native methods should be
+// generated inside this namespace, including the native class that this
+// object binds to.
+@JNINamespace("base::android")
+class SampleForTests {
+ // Classes can store their C++ pointer counter part as an int that is normally initialized by
+ // calling out a nativeInit() function.
+ int nativePtr;
+
+ // You can define methods and attributes on the java class just like any other.
+ // Methods without the @CalledByNative annotation won't be exposed to JNI.
+ public SampleForTests() {
+ }
+
+ public void startExample() {
+ // Calls native code and holds a pointer to the C++ class.
+ nativePtr = nativeInit("myParam");
+ }
+
+ public void doStuff() {
+ // This will call CPPClass::Method() using nativePtr as a pointer to the object. This must be
+ // done to:
+ // * avoid leaks.
+ // * using finalizers are not allowed to destroy the cpp class.
+ nativeMethod(nativePtr);
+ }
+
+ public void finishExample() {
+ // We're done, so let's destroy nativePtr object.
+ nativeDestroy(nativePtr);
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // The following methods demonstrate exporting Java methods for invocation from C++ code.
+ // Java functions are mapping into C global functions by prefixing the method name with
+ // "Java_<Class>_"
+ // This is triggered by the @CalledByNative annotation; the methods may be named as you wish.
+
+ // Exported to C++ as:
+ // Java_Example_javaMethod(JNIEnv* env, jobject obj, jint foo, jint bar)
+ // Typically the C++ code would have obtained the jobject via the Init() call described above.
+ @CalledByNative
+ public int javaMethod(int foo,
+ int bar) {
+ return 0;
+ }
+
+ // Exported to C++ as Java_Example_staticJavaMethod(JNIEnv* env)
+ // Note no jobject argument, as it is static.
+ @CalledByNative
+ public static boolean staticJavaMethod() {
+ return true;
+ }
+
+ // No prefix, so this method is package private. It will still be exported.
+ @CalledByNative
+ void packagePrivateJavaMethod() {}
+
+ // Note the "Unchecked" suffix. By default, @CalledByNative will always generate bindings that
+ // call CheckException(). With "@CalledByNativeUnchecked", the client C++ code is responsible to
+ // call ClearException() and act as appropriate.
+ // See more details at the "@CalledByNativeUnchecked" annotation.
+ @CalledByNativeUnchecked
+ void methodThatThrowsException() throws Exception {}
+
+ // The generator is not confused by inline comments:
+ // @CalledByNative void thisShouldNotAppearInTheOutput();
+ // @CalledByNativeUnchecked public static void neitherShouldThis(int foo);
+
+ /**
+ * The generator is not confused by block comments:
+ * @CalledByNative void thisShouldNotAppearInTheOutputEither();
+ * @CalledByNativeUnchecked public static void andDefinitelyNotThis(int foo);
+ */
+
+ // String constants that look like comments don't confuse the generator:
+ private String arrgh = "*/*";
+
+ //------------------------------------------------------------------------------------------------
+ // Java fields which are accessed from C++ code only must be annotated with @AccessedByNative to
+ // prevent them being eliminated when unreferenced code is stripped.
+ @AccessedByNative
+ private int javaField;
+
+ //------------------------------------------------------------------------------------------------
+ // The following methods demonstrate declaring methods to call into C++ from Java.
+ // The generator detects the "native" and "static" keywords, the type and name of the first
+ // parameter, and the "native" prefix to the function name to determine the C++ function
+ // signatures. Besides these constraints the methods can be freely named.
+
+ // This declares a C++ function which the application code must implement:
+ // static jint Init(JNIEnv* env, jobject obj);
+ // The jobject parameter refers back to this java side object instance.
+ // The implementation must return the pointer to the C++ object cast to jint.
+ // The caller of this method should store it, and supply it as a the nativeCPPClass param to
+ // subsequent native method calls (see the methods below that take an "int native..." as first
+ // param).
+ private native int nativeInit();
+
+ // This defines a function binding to the associated C++ class member function. The name is
+ // derived from |nativeDestroy| and |nativeCPPClass| to arrive at CPPClass::Destroy() (i.e. native
+ // prefixes stripped).
+ // The |nativeCPPClass| is automatically cast to type CPPClass* in order to obtain the object on
+ // which to invoke the member function.
+ private native void nativeDestroy(int nativeCPPClass);
+
+ // This declares a C++ function which the application code must implement:
+ // static jdouble GetDoubleFunction(JNIEnv* env, jobject obj);
+ // The jobject parameter refers back to this java side object instance.
+ private native double nativeGetDoubleFunction();
+
+ // Similar to nativeGetDoubleFunction(), but here the C++ side will receive a jclass rather than
+ // jobject param, as the function is declared static.
+ private static native float nativeGetFloatFunction();
+
+ // This function takes a non-POD datatype. We have a list mapping them to their full classpath in
+ // jni_generator.py JavaParamToJni. If you require a new datatype, make sure you add to that
+ // function.
+ private native void nativeSetNonPODDatatype(Rect rect);
+
+ // This declares a C++ function which the application code must implement:
+ // static ScopedJavaLocalRef<jobject> GetNonPODDatatype(JNIEnv* env, jobject obj);
+ // The jobject parameter refers back to this java side object instance.
+ // Note that it returns a ScopedJavaLocalRef<jobject> so that you don' have to worry about
+ // deleting the JNI local reference. This is similar with Strings and arrays.
+ private native Object nativeGetNonPODDatatype();
+
+ // Similar to nativeDestroy above, this will cast nativeCPPClass into pointer of CPPClass type and
+ // call its Method member function.
+ private native int nativeMethod(int nativeCPPClass);
+
+ // Similar to nativeMethod above, but here the C++ fully qualified class name is taken from the
+ // annotation rather than parameter name, which can thus be chosen freely.
+ @NativeClassQualifiedName("CPPClass::InnerClass")
+ private native double nativeMethodOtherP0(int nativePtr);
+
+ // An inner class has some special attributes for annotation.
+ class InnerClass {
+ @CalledByNative("InnerClass")
+ public float JavaInnerMethod() {
+ }
+
+ @CalledByNative("InnerClass")
+ public static void javaInnerFunction() {
+ }
+
+ @NativeCall("InnerClass")
+ private static native int nativeInnerFunction();
+
+ @NativeCall("InnerClass")
+ private static native String nativeInnerMethod(int nativeCPPClass);
+
+ }
+}
diff --git a/src/base/android/jni_generator/golden_sample_for_tests_jni.h b/src/base/android/jni_generator/golden_sample_for_tests_jni.h
new file mode 100644
index 0000000..08f25a7
--- /dev/null
+++ b/src/base/android/jni_generator/golden_sample_for_tests_jni.h
@@ -0,0 +1,293 @@
+// Copyright (c) 2012 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 autogenerated by
+// base/android/jni_generator/jni_generator_tests.py
+// For
+// org/chromium/example/jni_generator/SampleForTests
+
+#ifndef org_chromium_example_jni_generator_SampleForTests_JNI
+#define org_chromium_example_jni_generator_SampleForTests_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+using base::android::ScopedJavaLocalRef;
+
+// Step 1: forward declarations.
+namespace {
+const char kInnerClassClassPath[] =
+ "org/chromium/example/jni_generator/SampleForTests$InnerClass";
+const char kSampleForTestsClassPath[] =
+ "org/chromium/example/jni_generator/SampleForTests";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_InnerClass_clazz = NULL;
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_SampleForTests_clazz = NULL;
+} // namespace
+
+namespace base {
+namespace android {
+
+static jint Init(JNIEnv* env, jobject obj);
+
+static jdouble GetDoubleFunction(JNIEnv* env, jobject obj);
+
+static jfloat GetFloatFunction(JNIEnv* env, jclass clazz);
+
+static void SetNonPODDatatype(JNIEnv* env, jobject obj,
+ jobject rect);
+
+static jobject GetNonPODDatatype(JNIEnv* env, jobject obj);
+
+static jint InnerFunction(JNIEnv* env, jclass clazz);
+
+// Step 2: method stubs.
+static void Destroy(JNIEnv* env, jobject obj,
+ jint nativeCPPClass) {
+ DCHECK(nativeCPPClass) << "Destroy";
+ CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
+ return native->Destroy(env, obj);
+}
+
+static jint Method(JNIEnv* env, jobject obj,
+ jint nativeCPPClass) {
+ DCHECK(nativeCPPClass) << "Method";
+ CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
+ return native->Method(env, obj);
+}
+
+static jdouble MethodOtherP0(JNIEnv* env, jobject obj,
+ jint nativePtr) {
+ DCHECK(nativePtr) << "MethodOtherP0";
+ CPPClass::InnerClass* native =
+ reinterpret_cast<CPPClass::InnerClass*>(nativePtr);
+ return native->MethodOtherP0(env, obj);
+}
+
+static jstring InnerMethod(JNIEnv* env, jobject obj,
+ jint nativeCPPClass) {
+ DCHECK(nativeCPPClass) << "InnerMethod";
+ CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
+ return native->InnerMethod(env, obj).Release();
+}
+
+static base::subtle::AtomicWord g_SampleForTests_javaMethod = 0;
+static jint Java_SampleForTests_javaMethod(JNIEnv* env, jobject obj, jint foo,
+ jint bar) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_SampleForTests_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_SampleForTests_clazz,
+ "javaMethod",
+
+"("
+"I"
+"I"
+")"
+"I",
+ &g_SampleForTests_javaMethod);
+
+ jint ret =
+ env->CallIntMethod(obj,
+ method_id, foo, bar);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_SampleForTests_staticJavaMethod = 0;
+static jboolean Java_SampleForTests_staticJavaMethod(JNIEnv* env) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_SampleForTests_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_STATIC>(
+ env, g_SampleForTests_clazz,
+ "staticJavaMethod",
+
+"("
+")"
+"Z",
+ &g_SampleForTests_staticJavaMethod);
+
+ jboolean ret =
+ env->CallStaticBooleanMethod(g_SampleForTests_clazz,
+ method_id);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_SampleForTests_packagePrivateJavaMethod = 0;
+static void Java_SampleForTests_packagePrivateJavaMethod(JNIEnv* env, jobject
+ obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_SampleForTests_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_SampleForTests_clazz,
+ "packagePrivateJavaMethod",
+
+"("
+")"
+"V",
+ &g_SampleForTests_packagePrivateJavaMethod);
+
+ env->CallVoidMethod(obj,
+ method_id);
+ base::android::CheckException(env);
+
+}
+
+static base::subtle::AtomicWord g_SampleForTests_methodThatThrowsException = 0;
+static void Java_SampleForTests_methodThatThrowsException(JNIEnv* env, jobject
+ obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_SampleForTests_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_SampleForTests_clazz,
+ "methodThatThrowsException",
+
+"("
+")"
+"V",
+ &g_SampleForTests_methodThatThrowsException);
+
+ env->CallVoidMethod(obj,
+ method_id);
+
+}
+
+static base::subtle::AtomicWord g_InnerClass_JavaInnerMethod = 0;
+static jfloat Java_InnerClass_JavaInnerMethod(JNIEnv* env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InnerClass_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InnerClass_clazz,
+ "JavaInnerMethod",
+
+"("
+")"
+"F",
+ &g_InnerClass_JavaInnerMethod);
+
+ jfloat ret =
+ env->CallFloatMethod(obj,
+ method_id);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_InnerClass_javaInnerFunction = 0;
+static void Java_InnerClass_javaInnerFunction(JNIEnv* env) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InnerClass_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_STATIC>(
+ env, g_InnerClass_clazz,
+ "javaInnerFunction",
+
+"("
+")"
+"V",
+ &g_InnerClass_javaInnerFunction);
+
+ env->CallStaticVoidMethod(g_InnerClass_clazz,
+ method_id);
+ base::android::CheckException(env);
+
+}
+
+// Step 3: RegisterNatives.
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+
+ g_InnerClass_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetUnscopedClass(env, kInnerClassClassPath)));
+ g_SampleForTests_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetUnscopedClass(env, kSampleForTestsClassPath)));
+ static const JNINativeMethod kMethodsInnerClass[] = {
+ { "nativeInnerFunction",
+"("
+")"
+"I", reinterpret_cast<void*>(InnerFunction) },
+ { "nativeInnerMethod",
+"("
+"I"
+")"
+"Ljava/lang/String;", reinterpret_cast<void*>(InnerMethod) },
+ };
+ const int kMethodsInnerClassSize = arraysize(kMethodsInnerClass);
+
+ if (env->RegisterNatives(g_InnerClass_clazz,
+ kMethodsInnerClass,
+ kMethodsInnerClassSize) < 0) {
+ LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
+ return false;
+ }
+
+ static const JNINativeMethod kMethodsSampleForTests[] = {
+ { "nativeInit",
+"("
+")"
+"I", reinterpret_cast<void*>(Init) },
+ { "nativeDestroy",
+"("
+"I"
+")"
+"V", reinterpret_cast<void*>(Destroy) },
+ { "nativeGetDoubleFunction",
+"("
+")"
+"D", reinterpret_cast<void*>(GetDoubleFunction) },
+ { "nativeGetFloatFunction",
+"("
+")"
+"F", reinterpret_cast<void*>(GetFloatFunction) },
+ { "nativeSetNonPODDatatype",
+"("
+"Landroid/graphics/Rect;"
+")"
+"V", reinterpret_cast<void*>(SetNonPODDatatype) },
+ { "nativeGetNonPODDatatype",
+"("
+")"
+"Ljava/lang/Object;", reinterpret_cast<void*>(GetNonPODDatatype) },
+ { "nativeMethod",
+"("
+"I"
+")"
+"I", reinterpret_cast<void*>(Method) },
+ { "nativeMethodOtherP0",
+"("
+"I"
+")"
+"D", reinterpret_cast<void*>(MethodOtherP0) },
+ };
+ const int kMethodsSampleForTestsSize = arraysize(kMethodsSampleForTests);
+
+ if (env->RegisterNatives(g_SampleForTests_clazz,
+ kMethodsSampleForTests,
+ kMethodsSampleForTestsSize) < 0) {
+ LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
+ return false;
+ }
+
+ return true;
+}
+} // namespace android
+} // namespace base
+
+#endif // org_chromium_example_jni_generator_SampleForTests_JNI
diff --git a/src/base/android/jni_generator/jni_generator.gyp b/src/base/android/jni_generator/jni_generator.gyp
new file mode 100644
index 0000000..dc32d22
--- /dev/null
+++ b/src/base/android/jni_generator/jni_generator.gyp
@@ -0,0 +1,55 @@
+# Copyright (c) 2012 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'jni_generator_py_tests',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'run_jni_generator_py_tests',
+ 'inputs': [
+ 'jni_generator.py',
+ 'jni_generator_tests.py',
+ 'SampleForTests.java',
+ 'golden_sample_for_tests_jni.h',
+ ],
+ 'outputs': [
+ '',
+ ],
+ 'action': [
+ 'python', 'jni_generator_tests.py',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'jni_sample_header',
+ 'type': 'none',
+ 'sources': [
+ 'SampleForTests.java',
+ ],
+ 'variables': {
+ 'jni_gen_dir': 'base',
+ },
+ 'includes': [ '../../../build/jni_generator.gypi' ],
+ },
+ {
+ 'target_name': 'jni_generator_tests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../../base.gyp:test_support_base',
+ 'jni_generator_py_tests',
+ 'jni_sample_header',
+ ],
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)/base',
+ ],
+ 'sources': [
+ 'sample_for_tests.cc',
+ ],
+ },
+ ],
+}
diff --git a/src/base/android/jni_generator/jni_generator.py b/src/base/android/jni_generator/jni_generator.py
new file mode 100755
index 0000000..18312c6
--- /dev/null
+++ b/src/base/android/jni_generator/jni_generator.py
@@ -0,0 +1,1005 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 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.
+
+"""Extracts native methods from a Java file and generates the JNI bindings.
+If you change this, please run and update the tests."""
+
+import collections
+import optparse
+import os
+import re
+import string
+from string import Template
+import subprocess
+import sys
+import textwrap
+import zipfile
+
+
+class ParseError(Exception):
+ """Exception thrown when we can't parse the input file."""
+
+ def __init__(self, description, *context_lines):
+ Exception.__init__(self)
+ self.description = description
+ self.context_lines = context_lines
+
+ def __str__(self):
+ context = '\n'.join(self.context_lines)
+ return '***\nERROR: %s\n\n%s\n***' % (self.description, context)
+
+
+class Param(object):
+ """Describes a param for a method, either java or native."""
+
+ def __init__(self, **kwargs):
+ self.datatype = kwargs['datatype']
+ self.name = kwargs['name']
+
+
+class NativeMethod(object):
+ """Describes a C/C++ method that is called by Java code"""
+
+ def __init__(self, **kwargs):
+ self.static = kwargs['static']
+ self.java_class_name = kwargs['java_class_name']
+ self.return_type = kwargs['return_type']
+ self.name = kwargs['name']
+ self.params = kwargs['params']
+ if self.params:
+ assert type(self.params) is list
+ assert type(self.params[0]) is Param
+ if (self.params and
+ self.params[0].datatype == 'int' and
+ self.params[0].name.startswith('native')):
+ self.type = 'method'
+ self.p0_type = self.params[0].name[len('native'):]
+ if kwargs.get('native_class_name'):
+ self.p0_type = kwargs['native_class_name']
+ else:
+ self.type = 'function'
+ self.method_id_var_name = kwargs.get('method_id_var_name', None)
+
+
+class CalledByNative(object):
+ """Describes a java method exported to c/c++"""
+
+ def __init__(self, **kwargs):
+ self.system_class = kwargs['system_class']
+ self.unchecked = kwargs['unchecked']
+ self.static = kwargs['static']
+ self.java_class_name = kwargs['java_class_name']
+ self.return_type = kwargs['return_type']
+ self.name = kwargs['name']
+ self.params = kwargs['params']
+ self.method_id_var_name = kwargs.get('method_id_var_name', None)
+ self.is_constructor = kwargs.get('is_constructor', False)
+ self.env_call = GetEnvCall(self.is_constructor, self.static,
+ self.return_type)
+ self.static_cast = GetStaticCastForReturnType(self.return_type)
+
+
+def JavaDataTypeToC(java_type):
+ """Returns a C datatype for the given java type."""
+ java_pod_type_map = {
+ 'int': 'jint',
+ 'byte': 'jbyte',
+ 'boolean': 'jboolean',
+ 'long': 'jlong',
+ 'double': 'jdouble',
+ 'float': 'jfloat',
+ }
+ java_type_map = {
+ 'void': 'void',
+ 'String': 'jstring',
+ 'java/lang/String': 'jstring',
+ 'Class': 'jclass',
+ 'java/lang/Class': 'jclass',
+ }
+
+ if java_type in java_pod_type_map:
+ return java_pod_type_map[java_type]
+ elif java_type in java_type_map:
+ return java_type_map[java_type]
+ elif java_type.endswith('[]'):
+ if java_type[:-2] in java_pod_type_map:
+ return java_pod_type_map[java_type[:-2]] + 'Array'
+ return 'jobjectArray'
+ else:
+ return 'jobject'
+
+
+class JniParams(object):
+ _imports = []
+ _fully_qualified_class = ''
+ _package = ''
+ _inner_classes = []
+
+ @staticmethod
+ def SetFullyQualifiedClass(fully_qualified_class):
+ JniParams._fully_qualified_class = 'L' + fully_qualified_class
+ JniParams._package = '/'.join(fully_qualified_class.split('/')[:-1])
+
+ @staticmethod
+ def ExtractImportsAndInnerClasses(contents):
+ contents = contents.replace('\n', '')
+ re_import = re.compile(r'import.*?(?P<class>\S*?);')
+ for match in re.finditer(re_import, contents):
+ JniParams._imports += ['L' + match.group('class').replace('.', '/')]
+
+ re_inner = re.compile(r'(class|interface)\s+?(?P<name>\w+?)\W')
+ for match in re.finditer(re_inner, contents):
+ inner = match.group('name')
+ if not JniParams._fully_qualified_class.endswith(inner):
+ JniParams._inner_classes += [JniParams._fully_qualified_class + '$' +
+ inner]
+
+ @staticmethod
+ def JavaToJni(param):
+ """Converts a java param into a JNI signature type."""
+ pod_param_map = {
+ 'int': 'I',
+ 'boolean': 'Z',
+ 'long': 'J',
+ 'double': 'D',
+ 'float': 'F',
+ 'byte': 'B',
+ 'void': 'V',
+ }
+ object_param_list = [
+ 'Ljava/lang/Boolean',
+ 'Ljava/lang/Integer',
+ 'Ljava/lang/Long',
+ 'Ljava/lang/Object',
+ 'Ljava/lang/String',
+ 'Ljava/lang/Class',
+ ]
+ if param == 'byte[][]':
+ return '[[B'
+ prefix = ''
+ # Array?
+ if param[-2:] == '[]':
+ prefix = '['
+ param = param[:-2]
+ # Generic?
+ if '<' in param:
+ param = param[:param.index('<')]
+ if param in pod_param_map:
+ return prefix + pod_param_map[param]
+ if '/' in param:
+ # Coming from javap, use the fully qualified param directly.
+ return 'L' + param + ';'
+ for qualified_name in (object_param_list +
+ [JniParams._fully_qualified_class] +
+ JniParams._inner_classes):
+ if (qualified_name.endswith('/' + param) or
+ qualified_name.endswith('$' + param.replace('.', '$')) or
+ qualified_name == 'L' + param):
+ return prefix + qualified_name + ';'
+
+ # Is it from an import? (e.g. referecing Class from import pkg.Class;
+ # note that referencing an inner class Inner from import pkg.Class.Inner
+ # is not supported).
+ for qualified_name in JniParams._imports:
+ if qualified_name.endswith('/' + param):
+ # Ensure it's not an inner class.
+ components = qualified_name.split('/')
+ if len(components) > 2 and components[-2][0].isupper():
+ raise SyntaxError('Inner class (%s) can not be imported '
+ 'and used by JNI (%s). Please import the outer '
+ 'class and use Outer.Inner instead.' %
+ (qualified_name, param))
+ return prefix + qualified_name + ';'
+
+ # Is it an inner class from an outer class import? (e.g. referencing
+ # Class.Inner from import pkg.Class).
+ if '.' in param:
+ components = param.split('.')
+ outer = '/'.join(components[:-1])
+ inner = components[-1]
+ for qualified_name in JniParams._imports:
+ if qualified_name.endswith('/' + outer):
+ return prefix + qualified_name + '$' + inner
+
+ # Type not found, falling back to same package as this class.
+ return prefix + 'L' + JniParams._package + '/' + param + ';'
+
+ @staticmethod
+ def Signature(params, returns, wrap):
+ """Returns the JNI signature for the given datatypes."""
+ items = ['(']
+ items += [JniParams.JavaToJni(param.datatype) for param in params]
+ items += [')']
+ items += [JniParams.JavaToJni(returns)]
+ if wrap:
+ return '\n' + '\n'.join(['"' + item + '"' for item in items])
+ else:
+ return '"' + ''.join(items) + '"'
+
+ @staticmethod
+ def Parse(params):
+ """Parses the params into a list of Param objects."""
+ if not params:
+ return []
+ ret = []
+ for p in [p.strip() for p in params.split(',')]:
+ items = p.split(' ')
+ if 'final' in items:
+ items.remove('final')
+ param = Param(
+ datatype=items[0],
+ name=(items[1] if len(items) > 1 else 'p%s' % len(ret)),
+ )
+ ret += [param]
+ return ret
+
+
+def ExtractJNINamespace(contents):
+ re_jni_namespace = re.compile('.*?@JNINamespace\("(.*?)"\)')
+ m = re.findall(re_jni_namespace, contents)
+ if not m:
+ return ''
+ return m[0]
+
+
+def ExtractFullyQualifiedJavaClassName(java_file_name, contents):
+ re_package = re.compile('.*?package (.*?);')
+ matches = re.findall(re_package, contents)
+ if not matches:
+ raise SyntaxError('Unable to find "package" line in %s' % java_file_name)
+ return (matches[0].replace('.', '/') + '/' +
+ os.path.splitext(os.path.basename(java_file_name))[0])
+
+
+def ExtractNatives(contents):
+ """Returns a list of dict containing information about a native method."""
+ contents = contents.replace('\n', '')
+ natives = []
+ re_native = re.compile(r'(@NativeClassQualifiedName'
+ '\(\"(?P<native_class_name>.*?)\"\))?\s*'
+ '(@NativeCall(\(\"(?P<java_class_name>.*?)\"\)))?\s*'
+ '(?P<qualifiers>\w+\s\w+|\w+|\s+)\s*?native '
+ '(?P<return>\S*?) '
+ '(?P<name>\w+?)\((?P<params>.*?)\);')
+ for match in re.finditer(re_native, contents):
+ native = NativeMethod(
+ static='static' in match.group('qualifiers'),
+ java_class_name=match.group('java_class_name'),
+ native_class_name=match.group('native_class_name'),
+ return_type=match.group('return'),
+ name=match.group('name').replace('native', ''),
+ params=JniParams.Parse(match.group('params')))
+ natives += [native]
+ return natives
+
+
+def GetStaticCastForReturnType(return_type):
+ if return_type in ['String', 'java/lang/String']:
+ return 'jstring'
+ elif return_type.endswith('[]'):
+ return 'jobjectArray'
+ return None
+
+
+def GetEnvCall(is_constructor, is_static, return_type):
+ """Maps the types availabe via env->Call__Method."""
+ if is_constructor:
+ return 'NewObject'
+ env_call_map = {'boolean': 'Boolean',
+ 'byte': 'Byte',
+ 'char': 'Char',
+ 'short': 'Short',
+ 'int': 'Int',
+ 'long': 'Long',
+ 'float': 'Float',
+ 'void': 'Void',
+ 'double': 'Double',
+ 'Object': 'Object',
+ }
+ call = env_call_map.get(return_type, 'Object')
+ if is_static:
+ call = 'Static' + call
+ return 'Call' + call + 'Method'
+
+
+def GetMangledParam(datatype):
+ """Returns a mangled identifier for the datatype."""
+ if len(datatype) <= 2:
+ return datatype.replace('[', 'A')
+ ret = ''
+ for i in range(1, len(datatype)):
+ c = datatype[i]
+ if c == '[':
+ ret += 'A'
+ elif c.isupper() or datatype[i - 1] in ['/', 'L']:
+ ret += c.upper()
+ return ret
+
+
+def GetMangledMethodName(name, params, return_type):
+ """Returns a mangled method name for the given signature.
+
+ The returned name can be used as a C identifier and will be unique for all
+ valid overloads of the same method.
+
+ Args:
+ name: string.
+ params: list of Param.
+ return_type: string.
+
+ Returns:
+ A mangled name.
+ """
+ mangled_items = []
+ for datatype in [return_type] + [x.datatype for x in params]:
+ mangled_items += [GetMangledParam(JniParams.JavaToJni(datatype))]
+ mangled_name = name + '_'.join(mangled_items)
+ assert re.match(r'[0-9a-zA-Z_]+', mangled_name)
+ return mangled_name
+
+
+def MangleCalledByNatives(called_by_natives):
+ """Mangles all the overloads from the call_by_natives list."""
+ method_counts = collections.defaultdict(
+ lambda: collections.defaultdict(lambda: 0))
+ for called_by_native in called_by_natives:
+ java_class_name = called_by_native.java_class_name
+ name = called_by_native.name
+ method_counts[java_class_name][name] += 1
+ for called_by_native in called_by_natives:
+ java_class_name = called_by_native.java_class_name
+ method_name = called_by_native.name
+ method_id_var_name = method_name
+ if method_counts[java_class_name][method_name] > 1:
+ method_id_var_name = GetMangledMethodName(method_name,
+ called_by_native.params,
+ called_by_native.return_type)
+ called_by_native.method_id_var_name = method_id_var_name
+ return called_by_natives
+
+
+# Regex to match the JNI return types that should be included in a
+# ScopedJavaLocalRef.
+RE_SCOPED_JNI_RETURN_TYPES = re.compile('jobject|jclass|jstring|.*Array')
+
+# Regex to match a string like "@CalledByNative public void foo(int bar)".
+RE_CALLED_BY_NATIVE = re.compile(
+ '@CalledByNative(?P<Unchecked>(Unchecked)*?)(?:\("(?P<annotation>.*)"\))?'
+ '\s+(?P<prefix>[\w ]*?)'
+ '\s*(?P<return_type>\w+)'
+ '\s+(?P<name>\w+)'
+ '\s*\((?P<params>[^\)]*)\)')
+
+
+def ExtractCalledByNatives(contents):
+ """Parses all methods annotated with @CalledByNative.
+
+ Args:
+ contents: the contents of the java file.
+
+ Returns:
+ A list of dict with information about the annotated methods.
+ TODO(bulach): return a CalledByNative object.
+
+ Raises:
+ ParseError: if unable to parse.
+ """
+ called_by_natives = []
+ for match in re.finditer(RE_CALLED_BY_NATIVE, contents):
+ called_by_natives += [CalledByNative(
+ system_class=False,
+ unchecked='Unchecked' in match.group('Unchecked'),
+ static='static' in match.group('prefix'),
+ java_class_name=match.group('annotation') or '',
+ return_type=match.group('return_type'),
+ name=match.group('name'),
+ params=JniParams.Parse(match.group('params')))]
+ # Check for any @CalledByNative occurrences that weren't matched.
+ unmatched_lines = re.sub(RE_CALLED_BY_NATIVE, '', contents).split('\n')
+ for line1, line2 in zip(unmatched_lines, unmatched_lines[1:]):
+ if '@CalledByNative' in line1:
+ raise ParseError('could not parse @CalledByNative method signature',
+ line1, line2)
+ return MangleCalledByNatives(called_by_natives)
+
+
+class JNIFromJavaP(object):
+ """Uses 'javap' to parse a .class file and generate the JNI header file."""
+
+ def __init__(self, contents, namespace):
+ self.contents = contents
+ self.namespace = namespace
+ self.fully_qualified_class = re.match('.*?class (?P<class_name>.*?) ',
+ contents[1]).group('class_name')
+ self.fully_qualified_class = self.fully_qualified_class.replace('.', '/')
+ JniParams.SetFullyQualifiedClass(self.fully_qualified_class)
+ self.java_class_name = self.fully_qualified_class.split('/')[-1]
+ if not self.namespace:
+ self.namespace = 'JNI_' + self.java_class_name
+ re_method = re.compile('(?P<prefix>.*?)(?P<return_type>\S+?) (?P<name>\w+?)'
+ '\((?P<params>.*?)\)')
+ self.called_by_natives = []
+ for content in contents[2:]:
+ match = re.match(re_method, content)
+ if not match:
+ continue
+ self.called_by_natives += [CalledByNative(
+ system_class=True,
+ unchecked=False,
+ static='static' in match.group('prefix'),
+ java_class_name='',
+ return_type=match.group('return_type').replace('.', '/'),
+ name=match.group('name'),
+ params=JniParams.Parse(match.group('params').replace('.', '/')))]
+ re_constructor = re.compile('.*? public ' +
+ self.fully_qualified_class.replace('/', '.') +
+ '\((?P<params>.*?)\)')
+ for content in contents[2:]:
+ match = re.match(re_constructor, content)
+ if not match:
+ continue
+ self.called_by_natives += [CalledByNative(
+ system_class=True,
+ unchecked=False,
+ static=False,
+ java_class_name='',
+ return_type=self.fully_qualified_class,
+ name='Constructor',
+ params=JniParams.Parse(match.group('params').replace('.', '/')),
+ is_constructor=True)]
+ self.called_by_natives = MangleCalledByNatives(self.called_by_natives)
+ self.inl_header_file_generator = InlHeaderFileGenerator(
+ self.namespace, self.fully_qualified_class, [], self.called_by_natives)
+
+ def GetContent(self):
+ return self.inl_header_file_generator.GetContent()
+
+ @staticmethod
+ def CreateFromClass(class_file, namespace):
+ class_name = os.path.splitext(os.path.basename(class_file))[0]
+ p = subprocess.Popen(args=['javap', class_name],
+ cwd=os.path.dirname(class_file),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout, _ = p.communicate()
+ jni_from_javap = JNIFromJavaP(stdout.split('\n'), namespace)
+ return jni_from_javap
+
+
+class JNIFromJavaSource(object):
+ """Uses the given java source file to generate the JNI header file."""
+
+ def __init__(self, contents, fully_qualified_class):
+ contents = self._RemoveComments(contents)
+ JniParams.SetFullyQualifiedClass(fully_qualified_class)
+ JniParams.ExtractImportsAndInnerClasses(contents)
+ jni_namespace = ExtractJNINamespace(contents)
+ natives = ExtractNatives(contents)
+ called_by_natives = ExtractCalledByNatives(contents)
+ if len(natives) == 0 and len(called_by_natives) == 0:
+ raise SyntaxError('Unable to find any JNI methods for %s.' %
+ fully_qualified_class)
+ inl_header_file_generator = InlHeaderFileGenerator(
+ jni_namespace, fully_qualified_class, natives, called_by_natives)
+ self.content = inl_header_file_generator.GetContent()
+
+ def _RemoveComments(self, contents):
+ # We need to support both inline and block comments, and we need to handle
+ # strings that contain '//' or '/*'. Rather than trying to do all that with
+ # regexps, we just pipe the contents through the C preprocessor. We tell cpp
+ # the file has already been preprocessed, so it just removes comments and
+ # doesn't try to parse #include, #pragma etc.
+ #
+ # TODO(husky): This is a bit hacky. It would be cleaner to use a real Java
+ # parser. Maybe we could ditch JNIFromJavaSource and just always use
+ # JNIFromJavaP; or maybe we could rewrite this script in Java and use APT.
+ # http://code.google.com/p/chromium/issues/detail?id=138941
+ p = subprocess.Popen(args=['cpp', '-fpreprocessed'],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout, _ = p.communicate(contents)
+ return stdout
+
+ def GetContent(self):
+ return self.content
+
+ @staticmethod
+ def CreateFromFile(java_file_name):
+ contents = file(java_file_name).read()
+ fully_qualified_class = ExtractFullyQualifiedJavaClassName(java_file_name,
+ contents)
+ return JNIFromJavaSource(contents, fully_qualified_class)
+
+
+class InlHeaderFileGenerator(object):
+ """Generates an inline header file for JNI integration."""
+
+ def __init__(self, namespace, fully_qualified_class, natives,
+ called_by_natives):
+ self.namespace = namespace
+ self.fully_qualified_class = fully_qualified_class
+ self.class_name = self.fully_qualified_class.split('/')[-1]
+ self.natives = natives
+ self.called_by_natives = called_by_natives
+ self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI'
+
+ def GetContent(self):
+ """Returns the content of the JNI binding file."""
+ template = Template("""\
+// Copyright (c) 2012 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 autogenerated by
+// ${SCRIPT_NAME}
+// For
+// ${FULLY_QUALIFIED_CLASS}
+
+#ifndef ${HEADER_GUARD}
+#define ${HEADER_GUARD}
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+using base::android::ScopedJavaLocalRef;
+
+// Step 1: forward declarations.
+namespace {
+$CLASS_PATH_DEFINITIONS
+} // namespace
+
+$OPEN_NAMESPACE
+$FORWARD_DECLARATIONS
+
+// Step 2: method stubs.
+$METHOD_STUBS
+
+// Step 3: RegisterNatives.
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+$REGISTER_NATIVES_IMPL
+ return true;
+}
+$CLOSE_NAMESPACE
+#endif // ${HEADER_GUARD}
+""")
+ script_components = os.path.abspath(sys.argv[0]).split(os.path.sep)
+ base_index = script_components.index('base')
+ script_name = os.sep.join(script_components[base_index:])
+ values = {
+ 'SCRIPT_NAME': script_name,
+ 'FULLY_QUALIFIED_CLASS': self.fully_qualified_class,
+ 'CLASS_PATH_DEFINITIONS': self.GetClassPathDefinitionsString(),
+ 'FORWARD_DECLARATIONS': self.GetForwardDeclarationsString(),
+ 'METHOD_STUBS': self.GetMethodStubsString(),
+ 'OPEN_NAMESPACE': self.GetOpenNamespaceString(),
+ 'REGISTER_NATIVES_IMPL': self.GetRegisterNativesImplString(),
+ 'CLOSE_NAMESPACE': self.GetCloseNamespaceString(),
+ 'HEADER_GUARD': self.header_guard,
+ }
+ return WrapOutput(template.substitute(values))
+
+ def GetClassPathDefinitionsString(self):
+ ret = []
+ ret += [self.GetClassPathDefinitions()]
+ return '\n'.join(ret)
+
+ def GetForwardDeclarationsString(self):
+ ret = []
+ for native in self.natives:
+ if native.type != 'method':
+ ret += [self.GetForwardDeclaration(native)]
+ return '\n'.join(ret)
+
+ def GetMethodStubsString(self):
+ ret = []
+ for native in self.natives:
+ if native.type == 'method':
+ ret += [self.GetNativeMethodStub(native)]
+ for called_by_native in self.called_by_natives:
+ ret += [self.GetCalledByNativeMethodStub(called_by_native)]
+ return '\n'.join(ret)
+
+ def GetKMethodsString(self, clazz):
+ ret = []
+ for native in self.natives:
+ if (native.java_class_name == clazz or
+ (not native.java_class_name and clazz == self.class_name)):
+ ret += [self.GetKMethodArrayEntry(native)]
+ return '\n'.join(ret)
+
+ def GetRegisterNativesImplString(self):
+ """Returns the implementation for RegisterNatives."""
+ template = Template("""\
+ static const JNINativeMethod kMethods${JAVA_CLASS}[] = {
+${KMETHODS}
+ };
+ const int kMethods${JAVA_CLASS}Size = arraysize(kMethods${JAVA_CLASS});
+
+ if (env->RegisterNatives(g_${JAVA_CLASS}_clazz,
+ kMethods${JAVA_CLASS},
+ kMethods${JAVA_CLASS}Size) < 0) {
+ LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
+ return false;
+ }
+""")
+ ret = [self.GetFindClasses()]
+ all_classes = self.GetUniqueClasses(self.natives)
+ all_classes[self.class_name] = self.fully_qualified_class
+ for clazz in all_classes:
+ kmethods = self.GetKMethodsString(clazz)
+ if kmethods:
+ values = {'JAVA_CLASS': clazz,
+ 'KMETHODS': kmethods}
+ ret += [template.substitute(values)]
+ if not ret: return ''
+ return '\n' + '\n'.join(ret)
+
+ def GetOpenNamespaceString(self):
+ if self.namespace:
+ all_namespaces = ['namespace %s {' % ns
+ for ns in self.namespace.split('::')]
+ return '\n'.join(all_namespaces)
+ return ''
+
+ def GetCloseNamespaceString(self):
+ if self.namespace:
+ all_namespaces = ['} // namespace %s' % ns
+ for ns in self.namespace.split('::')]
+ all_namespaces.reverse()
+ return '\n'.join(all_namespaces) + '\n'
+ return ''
+
+ def GetJNIFirstParam(self, native):
+ ret = []
+ if native.type == 'method':
+ ret = ['jobject obj']
+ elif native.type == 'function':
+ if native.static:
+ ret = ['jclass clazz']
+ else:
+ ret = ['jobject obj']
+ return ret
+
+ def GetParamsInDeclaration(self, native):
+ """Returns the params for the stub declaration.
+
+ Args:
+ native: the native dictionary describing the method.
+
+ Returns:
+ A string containing the params.
+ """
+ return ',\n '.join(self.GetJNIFirstParam(native) +
+ [JavaDataTypeToC(param.datatype) + ' ' +
+ param.name
+ for param in native.params])
+
+ def GetCalledByNativeParamsInDeclaration(self, called_by_native):
+ return ',\n '.join([JavaDataTypeToC(param.datatype) + ' ' +
+ param.name
+ for param in called_by_native.params])
+
+ def GetForwardDeclaration(self, native):
+ template = Template("""
+static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS});
+""")
+ values = {'RETURN': JavaDataTypeToC(native.return_type),
+ 'NAME': native.name,
+ 'PARAMS': self.GetParamsInDeclaration(native)}
+ return template.substitute(values)
+
+ def GetNativeMethodStub(self, native):
+ """Returns stubs for native methods."""
+ template = Template("""\
+static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) {
+ DCHECK(${PARAM0_NAME}) << "${NAME}";
+ ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME});
+ return native->${NAME}(env, obj${PARAMS_IN_CALL})${POST_CALL};
+}
+""")
+ params_for_call = ', '.join(p.name for p in native.params[1:])
+ if params_for_call:
+ params_for_call = ', ' + params_for_call
+
+ return_type = JavaDataTypeToC(native.return_type)
+ if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
+ scoped_return_type = 'ScopedJavaLocalRef<' + return_type + '>'
+ post_call = '.Release()'
+ else:
+ scoped_return_type = return_type
+ post_call = ''
+ values = {
+ 'RETURN': return_type,
+ 'SCOPED_RETURN': scoped_return_type,
+ 'NAME': native.name,
+ 'PARAMS_IN_DECLARATION': self.GetParamsInDeclaration(native),
+ 'PARAM0_NAME': native.params[0].name,
+ 'P0_TYPE': native.p0_type,
+ 'PARAMS_IN_CALL': params_for_call,
+ 'POST_CALL': post_call
+ }
+ return template.substitute(values)
+
+ def GetCalledByNativeMethodStub(self, called_by_native):
+ """Returns a string."""
+ function_signature_template = Template("""\
+static ${RETURN_TYPE} Java_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}(\
+JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION})""")
+ function_header_template = Template("""\
+${FUNCTION_SIGNATURE} {""")
+ function_header_with_unused_template = Template("""\
+${FUNCTION_SIGNATURE} __attribute__ ((unused));
+${FUNCTION_SIGNATURE} {""")
+ template = Template("""
+static base::subtle::AtomicWord g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = 0;
+${FUNCTION_HEADER}
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_${JAVA_CLASS}_clazz);
+ jmethodID method_id =
+ ${GET_METHOD_ID_IMPL}
+ ${RETURN_DECLARATION}
+ ${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL},
+ method_id${PARAMS_IN_CALL})${POST_CALL};
+ ${CHECK_EXCEPTION}
+ ${RETURN_CLAUSE}
+}""")
+ if called_by_native.static or called_by_native.is_constructor:
+ first_param_in_declaration = ''
+ first_param_in_call = ('g_%s_clazz' %
+ (called_by_native.java_class_name or
+ self.class_name))
+ else:
+ first_param_in_declaration = ', jobject obj'
+ first_param_in_call = 'obj'
+ params_in_declaration = self.GetCalledByNativeParamsInDeclaration(
+ called_by_native)
+ if params_in_declaration:
+ params_in_declaration = ', ' + params_in_declaration
+ params_for_call = ', '.join(param.name
+ for param in called_by_native.params)
+ if params_for_call:
+ params_for_call = ', ' + params_for_call
+ pre_call = ''
+ post_call = ''
+ if called_by_native.static_cast:
+ pre_call = 'static_cast<%s>(' % called_by_native.static_cast
+ post_call = ')'
+ check_exception = ''
+ if not called_by_native.unchecked:
+ check_exception = 'base::android::CheckException(env);'
+ return_type = JavaDataTypeToC(called_by_native.return_type)
+ return_declaration = ''
+ return_clause = ''
+ if return_type != 'void':
+ pre_call = ' ' + pre_call
+ return_declaration = return_type + ' ret ='
+ if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
+ return_type = 'ScopedJavaLocalRef<' + return_type + '>'
+ return_clause = 'return ' + return_type + '(env, ret);'
+ else:
+ return_clause = 'return ret;'
+ values = {
+ 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
+ 'METHOD': called_by_native.name,
+ 'RETURN_TYPE': return_type,
+ 'RETURN_DECLARATION': return_declaration,
+ 'RETURN_CLAUSE': return_clause,
+ 'FIRST_PARAM_IN_DECLARATION': first_param_in_declaration,
+ 'PARAMS_IN_DECLARATION': params_in_declaration,
+ 'STATIC': 'Static' if called_by_native.static else '',
+ 'PRE_CALL': pre_call,
+ 'POST_CALL': post_call,
+ 'ENV_CALL': called_by_native.env_call,
+ 'FIRST_PARAM_IN_CALL': first_param_in_call,
+ 'PARAMS_IN_CALL': params_for_call,
+ 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
+ 'CHECK_EXCEPTION': check_exception,
+ 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native)
+ }
+ values['FUNCTION_SIGNATURE'] = (
+ function_signature_template.substitute(values))
+ if called_by_native.system_class:
+ values['FUNCTION_HEADER'] = (
+ function_header_with_unused_template.substitute(values))
+ else:
+ values['FUNCTION_HEADER'] = function_header_template.substitute(values)
+ return template.substitute(values)
+
+ def GetKMethodArrayEntry(self, native):
+ template = Template("""\
+ { "native${NAME}", ${JNI_SIGNATURE}, reinterpret_cast<void*>(${NAME}) },""")
+ values = {'NAME': native.name,
+ 'JNI_SIGNATURE': JniParams.Signature(native.params,
+ native.return_type,
+ True)}
+ return template.substitute(values)
+
+ def GetUniqueClasses(self, origin):
+ ret = {self.class_name: self.fully_qualified_class}
+ for entry in origin:
+ class_name = self.class_name
+ jni_class_path = self.fully_qualified_class
+ if entry.java_class_name:
+ class_name = entry.java_class_name
+ jni_class_path = self.fully_qualified_class + '$' + class_name
+ ret[class_name] = jni_class_path
+ return ret
+
+ def GetClassPathDefinitions(self):
+ """Returns the ClassPath constants."""
+ ret = []
+ template = Template("""\
+const char k${JAVA_CLASS}ClassPath[] = "${JNI_CLASS_PATH}";""")
+ native_classes = self.GetUniqueClasses(self.natives)
+ called_by_native_classes = self.GetUniqueClasses(self.called_by_natives)
+ all_classes = native_classes
+ all_classes.update(called_by_native_classes)
+ for clazz in all_classes:
+ values = {
+ 'JAVA_CLASS': clazz,
+ 'JNI_CLASS_PATH': all_classes[clazz],
+ }
+ ret += [template.substitute(values)]
+ ret += ''
+ for clazz in called_by_native_classes:
+ template = Template("""\
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_${JAVA_CLASS}_clazz = NULL;""")
+ values = {
+ 'JAVA_CLASS': clazz,
+ }
+ ret += [template.substitute(values)]
+ return '\n'.join(ret)
+
+ def GetFindClasses(self):
+ """Returns the imlementation of FindClass for all known classes."""
+ template = Template("""\
+ g_${JAVA_CLASS}_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetUnscopedClass(env, k${JAVA_CLASS}ClassPath)));""")
+ ret = []
+ for clazz in self.GetUniqueClasses(self.called_by_natives):
+ values = {'JAVA_CLASS': clazz}
+ ret += [template.substitute(values)]
+ return '\n'.join(ret)
+
+ def GetMethodIDImpl(self, called_by_native):
+ """Returns the implementation of GetMethodID."""
+ template = Template("""\
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_${STATIC}>(
+ env, g_${JAVA_CLASS}_clazz,
+ "${JNI_NAME}",
+ ${JNI_SIGNATURE},
+ &g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME});
+""")
+ jni_name = called_by_native.name
+ jni_return_type = called_by_native.return_type
+ if called_by_native.is_constructor:
+ jni_name = '<init>'
+ jni_return_type = 'void'
+ values = {
+ 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
+ 'JNI_NAME': jni_name,
+ 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
+ 'STATIC': 'STATIC' if called_by_native.static else 'INSTANCE',
+ 'JNI_SIGNATURE': JniParams.Signature(called_by_native.params,
+ jni_return_type,
+ True)
+ }
+ return template.substitute(values)
+
+
+def WrapOutput(output):
+ ret = []
+ for line in output.splitlines():
+ # Do not wrap lines under 80 characters or preprocessor directives.
+ if len(line) < 80 or line.lstrip()[:1] == '#':
+ stripped = line.rstrip()
+ if len(ret) == 0 or len(ret[-1]) or len(stripped):
+ ret.append(stripped)
+ else:
+ first_line_indent = ' ' * (len(line) - len(line.lstrip()))
+ subsequent_indent = first_line_indent + ' ' * 4
+ if line.startswith('//'):
+ subsequent_indent = '//' + subsequent_indent
+ wrapper = textwrap.TextWrapper(width=80,
+ subsequent_indent=subsequent_indent,
+ break_long_words=False)
+ ret += [wrapped.rstrip() for wrapped in wrapper.wrap(line)]
+ ret += ['']
+ return '\n'.join(ret)
+
+
+def ExtractJarInputFile(jar_file, input_file, out_dir):
+ """Extracts input file from jar and returns the filename.
+
+ The input file is extracted to the same directory that the generated jni
+ headers will be placed in. This is passed as an argument to script.
+
+ Args:
+ jar_file: the jar file containing the input files to extract.
+ input_files: the list of files to extract from the jar file.
+ out_dir: the name of the directories to extract to.
+
+ Returns:
+ the name of extracted input file.
+ """
+ jar_file = zipfile.ZipFile(jar_file)
+
+ out_dir = os.path.join(out_dir, os.path.dirname(input_file))
+ if not os.path.exists(out_dir):
+ os.makedirs(out_dir)
+ extracted_file_name = os.path.join(out_dir, os.path.basename(input_file))
+ with open(extracted_file_name, 'w') as outfile:
+ outfile.write(jar_file.read(input_file))
+
+ return extracted_file_name
+
+
+def GenerateJNIHeader(input_file, output_file, namespace):
+ try:
+ if os.path.splitext(input_file)[1] == '.class':
+ jni_from_javap = JNIFromJavaP.CreateFromClass(input_file, namespace)
+ content = jni_from_javap.GetContent()
+ else:
+ jni_from_java_source = JNIFromJavaSource.CreateFromFile(input_file)
+ content = jni_from_java_source.GetContent()
+ except ParseError, e:
+ print e
+ sys.exit(1)
+ if output_file:
+ if not os.path.exists(os.path.dirname(os.path.abspath(output_file))):
+ os.makedirs(os.path.dirname(os.path.abspath(output_file)))
+ with file(output_file, 'w') as f:
+ f.write(content)
+ else:
+ print output
+
+
+def main(argv):
+ usage = """usage: %prog [OPTIONS]
+This script will parse the given java source code extracting the native
+declarations and print the header file to stdout (or a file).
+See SampleForTests.java for more details.
+ """
+ option_parser = optparse.OptionParser(usage=usage)
+ option_parser.add_option('-j', dest='jar_file',
+ help='Extract the list of input files from'
+ ' a specified jar file.'
+ ' Uses javap to extract the methods from a'
+ ' pre-compiled class. --input should point'
+ ' to pre-compiled Java .class files.')
+ option_parser.add_option('-n', dest='namespace',
+ help='Uses as a namespace in the generated header,'
+ ' instead of the javap class name.')
+ option_parser.add_option('--input_file',
+ help='Single input file name. The output file name '
+ 'will be derived from it. Must be used with '
+ '--output_dir.')
+ option_parser.add_option('--output_dir',
+ help='The output directory. Must be used with '
+ '--input')
+ options, args = option_parser.parse_args(argv)
+ if options.jar_file:
+ input_file = ExtractJarInputFile(options.jar_file, options.input_file,
+ options.output_dir)
+ else:
+ input_file = options.input_file
+ output_file = None
+ if options.output_dir:
+ root_name = os.path.splitext(os.path.basename(input_file))[0]
+ output_file = os.path.join(options.output_dir, root_name) + '_jni.h'
+ GenerateJNIHeader(input_file, output_file, options.namespace)
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/src/base/android/jni_generator/jni_generator_tests.py b/src/base/android/jni_generator/jni_generator_tests.py
new file mode 100755
index 0000000..5863977
--- /dev/null
+++ b/src/base/android/jni_generator/jni_generator_tests.py
@@ -0,0 +1,1590 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 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.
+
+"""Tests for jni_generator.py.
+
+This test suite contains various tests for the JNI generator.
+It exercises the low-level parser all the way up to the
+code generator and ensures the output matches a golden
+file.
+"""
+
+import difflib
+import os
+import sys
+import unittest
+import jni_generator
+from jni_generator import CalledByNative, NativeMethod, Param
+
+
+class TestGenerator(unittest.TestCase):
+ def assertObjEquals(self, first, second):
+ dict_first = first.__dict__
+ dict_second = second.__dict__
+ self.assertEquals(dict_first.keys(), dict_second.keys())
+ for key, value in dict_first.iteritems():
+ if (type(value) is list and len(value) and
+ isinstance(type(value[0]), object)):
+ self.assertListEquals(value, second.__getattribute__(key))
+ else:
+ actual = second.__getattribute__(key)
+ self.assertEquals(value, actual,
+ 'Key ' + key + ': ' + str(value) + '!=' + str(actual))
+
+ def assertListEquals(self, first, second):
+ self.assertEquals(len(first), len(second))
+ for i in xrange(len(first)):
+ if isinstance(first[i], object):
+ self.assertObjEquals(first[i], second[i])
+ else:
+ self.assertEquals(first[i], second[i])
+
+ def assertTextEquals(self, golden_text, generated_text):
+ stripped_golden = [l.strip() for l in golden_text.split('\n')]
+ stripped_generated = [l.strip() for l in generated_text.split('\n')]
+ if stripped_golden != stripped_generated:
+ print self.id()
+ for line in difflib.context_diff(stripped_golden, stripped_generated):
+ print line
+ print '\n\nGenerated'
+ print '=' * 80
+ print generated_text
+ print '=' * 80
+ self.fail('Golden text mismatch')
+
+ def testNatives(self):
+ test_data = """"
+ interface OnFrameAvailableListener {}
+ private native int nativeInit();
+ private native void nativeDestroy(int nativeChromeBrowserProvider);
+ private native long nativeAddBookmark(
+ int nativeChromeBrowserProvider,
+ String url, String title, boolean isFolder, long parentId);
+ private static native String nativeGetDomainAndRegistry(String url);
+ private static native void nativeCreateHistoricalTabFromState(
+ byte[] state, int tab_index);
+ private native byte[] nativeGetStateAsByteArray(View view);
+ private static native String[] nativeGetAutofillProfileGUIDs();
+ private native void nativeSetRecognitionResults(
+ int sessionId, String[] results);
+ private native long nativeAddBookmarkFromAPI(
+ int nativeChromeBrowserProvider,
+ String url, Long created, Boolean isBookmark,
+ Long date, byte[] favicon, String title, Integer visits);
+ native int nativeFindAll(String find);
+ private static native OnFrameAvailableListener nativeGetInnerClass();
+ private native Bitmap nativeQueryBitmap(
+ int nativeChromeBrowserProvider,
+ String[] projection, String selection,
+ String[] selectionArgs, String sortOrder);
+ private native void nativeGotOrientation(
+ int nativeDataFetcherImplAndroid,
+ double alpha, double beta, double gamma);
+ """
+ jni_generator.JniParams.ExtractImportsAndInnerClasses(test_data)
+ natives = jni_generator.ExtractNatives(test_data)
+ golden_natives = [
+ NativeMethod(return_type='int', static=False,
+ name='Init',
+ params=[],
+ java_class_name=None,
+ type='function'),
+ NativeMethod(return_type='void', static=False, name='Destroy',
+ params=[Param(datatype='int',
+ name='nativeChromeBrowserProvider')],
+ java_class_name=None,
+ type='method',
+ p0_type='ChromeBrowserProvider'),
+ NativeMethod(return_type='long', static=False, name='AddBookmark',
+ params=[Param(datatype='int',
+ name='nativeChromeBrowserProvider'),
+ Param(datatype='String',
+ name='url'),
+ Param(datatype='String',
+ name='title'),
+ Param(datatype='boolean',
+ name='isFolder'),
+ Param(datatype='long',
+ name='parentId')],
+ java_class_name=None,
+ type='method',
+ p0_type='ChromeBrowserProvider'),
+ NativeMethod(return_type='String', static=True,
+ name='GetDomainAndRegistry',
+ params=[Param(datatype='String',
+ name='url')],
+ java_class_name=None,
+ type='function'),
+ NativeMethod(return_type='void', static=True,
+ name='CreateHistoricalTabFromState',
+ params=[Param(datatype='byte[]',
+ name='state'),
+ Param(datatype='int',
+ name='tab_index')],
+ java_class_name=None,
+ type='function'),
+ NativeMethod(return_type='byte[]', static=False,
+ name='GetStateAsByteArray',
+ params=[Param(datatype='View', name='view')],
+ java_class_name=None,
+ type='function'),
+ NativeMethod(return_type='String[]', static=True,
+ name='GetAutofillProfileGUIDs', params=[],
+ java_class_name=None,
+ type='function'),
+ NativeMethod(return_type='void', static=False,
+ name='SetRecognitionResults',
+ params=[Param(datatype='int', name='sessionId'),
+ Param(datatype='String[]', name='results')],
+ java_class_name=None,
+ type='function'),
+ NativeMethod(return_type='long', static=False,
+ name='AddBookmarkFromAPI',
+ params=[Param(datatype='int',
+ name='nativeChromeBrowserProvider'),
+ Param(datatype='String',
+ name='url'),
+ Param(datatype='Long',
+ name='created'),
+ Param(datatype='Boolean',
+ name='isBookmark'),
+ Param(datatype='Long',
+ name='date'),
+ Param(datatype='byte[]',
+ name='favicon'),
+ Param(datatype='String',
+ name='title'),
+ Param(datatype='Integer',
+ name='visits')],
+ java_class_name=None,
+ type='method',
+ p0_type='ChromeBrowserProvider'),
+ NativeMethod(return_type='int', static=False,
+ name='FindAll',
+ params=[Param(datatype='String',
+ name='find')],
+ java_class_name=None,
+ type='function'),
+ NativeMethod(return_type='OnFrameAvailableListener', static=True,
+ name='GetInnerClass',
+ params=[],
+ java_class_name=None,
+ type='function'),
+ NativeMethod(return_type='Bitmap',
+ static=False,
+ name='QueryBitmap',
+ params=[Param(datatype='int',
+ name='nativeChromeBrowserProvider'),
+ Param(datatype='String[]',
+ name='projection'),
+ Param(datatype='String',
+ name='selection'),
+ Param(datatype='String[]',
+ name='selectionArgs'),
+ Param(datatype='String',
+ name='sortOrder'),
+ ],
+ java_class_name=None,
+ type='method',
+ p0_type='ChromeBrowserProvider'),
+ NativeMethod(return_type='void', static=False,
+ name='GotOrientation',
+ params=[Param(datatype='int',
+ name='nativeDataFetcherImplAndroid'),
+ Param(datatype='double',
+ name='alpha'),
+ Param(datatype='double',
+ name='beta'),
+ Param(datatype='double',
+ name='gamma'),
+ ],
+ java_class_name=None,
+ type='method',
+ p0_type='content::DataFetcherImplAndroid'),
+ ]
+ self.assertListEquals(golden_natives, natives)
+ h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
+ natives, [])
+ golden_content = """\
+// Copyright (c) 2012 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 autogenerated by
+// base/android/jni_generator/jni_generator_tests.py
+// For
+// org/chromium/TestJni
+
+#ifndef org_chromium_TestJni_JNI
+#define org_chromium_TestJni_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+using base::android::ScopedJavaLocalRef;
+
+// Step 1: forward declarations.
+namespace {
+const char kTestJniClassPath[] = "org/chromium/TestJni";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_TestJni_clazz = NULL;
+} // namespace
+
+static jint Init(JNIEnv* env, jobject obj);
+
+static jstring GetDomainAndRegistry(JNIEnv* env, jclass clazz,
+ jstring url);
+
+static void CreateHistoricalTabFromState(JNIEnv* env, jclass clazz,
+ jbyteArray state,
+ jint tab_index);
+
+static jbyteArray GetStateAsByteArray(JNIEnv* env, jobject obj,
+ jobject view);
+
+static jobjectArray GetAutofillProfileGUIDs(JNIEnv* env, jclass clazz);
+
+static void SetRecognitionResults(JNIEnv* env, jobject obj,
+ jint sessionId,
+ jobjectArray results);
+
+static jint FindAll(JNIEnv* env, jobject obj,
+ jstring find);
+
+static jobject GetInnerClass(JNIEnv* env, jclass clazz);
+
+// Step 2: method stubs.
+static void Destroy(JNIEnv* env, jobject obj,
+ jint nativeChromeBrowserProvider) {
+ DCHECK(nativeChromeBrowserProvider) << "Destroy";
+ ChromeBrowserProvider* native =
+ reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider);
+ return native->Destroy(env, obj);
+}
+
+static jlong AddBookmark(JNIEnv* env, jobject obj,
+ jint nativeChromeBrowserProvider,
+ jstring url,
+ jstring title,
+ jboolean isFolder,
+ jlong parentId) {
+ DCHECK(nativeChromeBrowserProvider) << "AddBookmark";
+ ChromeBrowserProvider* native =
+ reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider);
+ return native->AddBookmark(env, obj, url, title, isFolder, parentId);
+}
+
+static jlong AddBookmarkFromAPI(JNIEnv* env, jobject obj,
+ jint nativeChromeBrowserProvider,
+ jstring url,
+ jobject created,
+ jobject isBookmark,
+ jobject date,
+ jbyteArray favicon,
+ jstring title,
+ jobject visits) {
+ DCHECK(nativeChromeBrowserProvider) << "AddBookmarkFromAPI";
+ ChromeBrowserProvider* native =
+ reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider);
+ return native->AddBookmarkFromAPI(env, obj, url, created, isBookmark, date,
+ favicon, title, visits);
+}
+
+static jobject QueryBitmap(JNIEnv* env, jobject obj,
+ jint nativeChromeBrowserProvider,
+ jobjectArray projection,
+ jstring selection,
+ jobjectArray selectionArgs,
+ jstring sortOrder) {
+ DCHECK(nativeChromeBrowserProvider) << "QueryBitmap";
+ ChromeBrowserProvider* native =
+ reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider);
+ return native->QueryBitmap(env, obj, projection, selection, selectionArgs,
+ sortOrder).Release();
+}
+
+static void GotOrientation(JNIEnv* env, jobject obj,
+ jint nativeDataFetcherImplAndroid,
+ jdouble alpha,
+ jdouble beta,
+ jdouble gamma) {
+ DCHECK(nativeDataFetcherImplAndroid) << "GotOrientation";
+ DataFetcherImplAndroid* native =
+ reinterpret_cast<DataFetcherImplAndroid*>(nativeDataFetcherImplAndroid);
+ return native->GotOrientation(env, obj, alpha, beta, gamma);
+}
+
+// Step 3: RegisterNatives.
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+
+ g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetUnscopedClass(env, kTestJniClassPath)));
+ static const JNINativeMethod kMethodsTestJni[] = {
+ { "nativeInit",
+"("
+")"
+"I", reinterpret_cast<void*>(Init) },
+ { "nativeDestroy",
+"("
+"I"
+")"
+"V", reinterpret_cast<void*>(Destroy) },
+ { "nativeAddBookmark",
+"("
+"I"
+"Ljava/lang/String;"
+"Ljava/lang/String;"
+"Z"
+"J"
+")"
+"J", reinterpret_cast<void*>(AddBookmark) },
+ { "nativeGetDomainAndRegistry",
+"("
+"Ljava/lang/String;"
+")"
+"Ljava/lang/String;", reinterpret_cast<void*>(GetDomainAndRegistry) },
+ { "nativeCreateHistoricalTabFromState",
+"("
+"[B"
+"I"
+")"
+"V", reinterpret_cast<void*>(CreateHistoricalTabFromState) },
+ { "nativeGetStateAsByteArray",
+"("
+"Landroid/view/View;"
+")"
+"[B", reinterpret_cast<void*>(GetStateAsByteArray) },
+ { "nativeGetAutofillProfileGUIDs",
+"("
+")"
+"[Ljava/lang/String;", reinterpret_cast<void*>(GetAutofillProfileGUIDs) },
+ { "nativeSetRecognitionResults",
+"("
+"I"
+"[Ljava/lang/String;"
+")"
+"V", reinterpret_cast<void*>(SetRecognitionResults) },
+ { "nativeAddBookmarkFromAPI",
+"("
+"I"
+"Ljava/lang/String;"
+"Ljava/lang/Long;"
+"Ljava/lang/Boolean;"
+"Ljava/lang/Long;"
+"[B"
+"Ljava/lang/String;"
+"Ljava/lang/Integer;"
+")"
+"J", reinterpret_cast<void*>(AddBookmarkFromAPI) },
+ { "nativeFindAll",
+"("
+"Ljava/lang/String;"
+")"
+"I", reinterpret_cast<void*>(FindAll) },
+ { "nativeGetInnerClass",
+"("
+")"
+"Lorg/chromium/example/jni_generator/SampleForTests$OnFrameAvailableListener;",
+ reinterpret_cast<void*>(GetInnerClass) },
+ { "nativeQueryBitmap",
+"("
+"I"
+"[Ljava/lang/String;"
+"Ljava/lang/String;"
+"[Ljava/lang/String;"
+"Ljava/lang/String;"
+")"
+"Landroid/graphics/Bitmap;", reinterpret_cast<void*>(QueryBitmap) },
+ { "nativeGotOrientation",
+"("
+"I"
+"D"
+"D"
+"D"
+")"
+"V", reinterpret_cast<void*>(GotOrientation) },
+ };
+ const int kMethodsTestJniSize = arraysize(kMethodsTestJni);
+
+ if (env->RegisterNatives(g_TestJni_clazz,
+ kMethodsTestJni,
+ kMethodsTestJniSize) < 0) {
+ LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
+ return false;
+ }
+
+ return true;
+}
+
+#endif // org_chromium_TestJni_JNI
+"""
+ self.assertTextEquals(golden_content, h.GetContent())
+
+ def testInnerClassNatives(self):
+ test_data = """
+ class MyInnerClass {
+ @NativeCall("MyInnerClass")
+ private native int nativeInit();
+ }
+ """
+ natives = jni_generator.ExtractNatives(test_data)
+ golden_natives = [
+ NativeMethod(return_type='int', static=False,
+ name='Init', params=[],
+ java_class_name='MyInnerClass',
+ type='function')
+ ]
+ self.assertListEquals(golden_natives, natives)
+ h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
+ natives, [])
+ golden_content = """\
+// Copyright (c) 2012 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 autogenerated by
+// base/android/jni_generator/jni_generator_tests.py
+// For
+// org/chromium/TestJni
+
+#ifndef org_chromium_TestJni_JNI
+#define org_chromium_TestJni_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+using base::android::ScopedJavaLocalRef;
+
+// Step 1: forward declarations.
+namespace {
+const char kTestJniClassPath[] = "org/chromium/TestJni";
+const char kMyInnerClassClassPath[] = "org/chromium/TestJni$MyInnerClass";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_TestJni_clazz = NULL;
+} // namespace
+
+static jint Init(JNIEnv* env, jobject obj);
+
+// Step 2: method stubs.
+
+// Step 3: RegisterNatives.
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+
+ g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetUnscopedClass(env, kTestJniClassPath)));
+ static const JNINativeMethod kMethodsMyInnerClass[] = {
+ { "nativeInit",
+"("
+")"
+"I", reinterpret_cast<void*>(Init) },
+ };
+ const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass);
+
+ if (env->RegisterNatives(g_MyInnerClass_clazz,
+ kMethodsMyInnerClass,
+ kMethodsMyInnerClassSize) < 0) {
+ LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
+ return false;
+ }
+
+ return true;
+}
+
+#endif // org_chromium_TestJni_JNI
+"""
+ self.assertTextEquals(golden_content, h.GetContent())
+
+ def testInnerClassNativesMultiple(self):
+ test_data = """
+ class MyInnerClass {
+ @NativeCall("MyInnerClass")
+ private native int nativeInit();
+ }
+ class MyOtherInnerClass {
+ @NativeCall("MyOtherInnerClass")
+ private native int nativeInit();
+ }
+ """
+ natives = jni_generator.ExtractNatives(test_data)
+ golden_natives = [
+ NativeMethod(return_type='int', static=False,
+ name='Init', params=[],
+ java_class_name='MyInnerClass',
+ type='function'),
+ NativeMethod(return_type='int', static=False,
+ name='Init', params=[],
+ java_class_name='MyOtherInnerClass',
+ type='function')
+ ]
+ self.assertListEquals(golden_natives, natives)
+ h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
+ natives, [])
+ golden_content = """\
+// Copyright (c) 2012 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 autogenerated by
+// base/android/jni_generator/jni_generator_tests.py
+// For
+// org/chromium/TestJni
+
+#ifndef org_chromium_TestJni_JNI
+#define org_chromium_TestJni_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+using base::android::ScopedJavaLocalRef;
+
+// Step 1: forward declarations.
+namespace {
+const char kMyOtherInnerClassClassPath[] =
+ "org/chromium/TestJni$MyOtherInnerClass";
+const char kTestJniClassPath[] = "org/chromium/TestJni";
+const char kMyInnerClassClassPath[] = "org/chromium/TestJni$MyInnerClass";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_TestJni_clazz = NULL;
+} // namespace
+
+static jint Init(JNIEnv* env, jobject obj);
+
+static jint Init(JNIEnv* env, jobject obj);
+
+// Step 2: method stubs.
+
+// Step 3: RegisterNatives.
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+
+ g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetUnscopedClass(env, kTestJniClassPath)));
+ static const JNINativeMethod kMethodsMyOtherInnerClass[] = {
+ { "nativeInit",
+"("
+")"
+"I", reinterpret_cast<void*>(Init) },
+ };
+ const int kMethodsMyOtherInnerClassSize =
+ arraysize(kMethodsMyOtherInnerClass);
+
+ if (env->RegisterNatives(g_MyOtherInnerClass_clazz,
+ kMethodsMyOtherInnerClass,
+ kMethodsMyOtherInnerClassSize) < 0) {
+ LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
+ return false;
+ }
+
+ static const JNINativeMethod kMethodsMyInnerClass[] = {
+ { "nativeInit",
+"("
+")"
+"I", reinterpret_cast<void*>(Init) },
+ };
+ const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass);
+
+ if (env->RegisterNatives(g_MyInnerClass_clazz,
+ kMethodsMyInnerClass,
+ kMethodsMyInnerClassSize) < 0) {
+ LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
+ return false;
+ }
+
+ return true;
+}
+
+#endif // org_chromium_TestJni_JNI
+"""
+ self.assertTextEquals(golden_content, h.GetContent())
+
+ def testInnerClassNativesBothInnerAndOuter(self):
+ test_data = """
+ class MyOuterClass {
+ private native int nativeInit();
+ class MyOtherInnerClass {
+ @NativeCall("MyOtherInnerClass")
+ private native int nativeInit();
+ }
+ }
+ """
+ natives = jni_generator.ExtractNatives(test_data)
+ golden_natives = [
+ NativeMethod(return_type='int', static=False,
+ name='Init', params=[],
+ java_class_name=None,
+ type='function'),
+ NativeMethod(return_type='int', static=False,
+ name='Init', params=[],
+ java_class_name='MyOtherInnerClass',
+ type='function')
+ ]
+ self.assertListEquals(golden_natives, natives)
+ h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
+ natives, [])
+ golden_content = """\
+// Copyright (c) 2012 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 autogenerated by
+// base/android/jni_generator/jni_generator_tests.py
+// For
+// org/chromium/TestJni
+
+#ifndef org_chromium_TestJni_JNI
+#define org_chromium_TestJni_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+using base::android::ScopedJavaLocalRef;
+
+// Step 1: forward declarations.
+namespace {
+const char kMyOtherInnerClassClassPath[] =
+ "org/chromium/TestJni$MyOtherInnerClass";
+const char kTestJniClassPath[] = "org/chromium/TestJni";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_TestJni_clazz = NULL;
+} // namespace
+
+static jint Init(JNIEnv* env, jobject obj);
+
+static jint Init(JNIEnv* env, jobject obj);
+
+// Step 2: method stubs.
+
+// Step 3: RegisterNatives.
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+
+ g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetUnscopedClass(env, kTestJniClassPath)));
+ static const JNINativeMethod kMethodsMyOtherInnerClass[] = {
+ { "nativeInit",
+"("
+")"
+"I", reinterpret_cast<void*>(Init) },
+ };
+ const int kMethodsMyOtherInnerClassSize =
+ arraysize(kMethodsMyOtherInnerClass);
+
+ if (env->RegisterNatives(g_MyOtherInnerClass_clazz,
+ kMethodsMyOtherInnerClass,
+ kMethodsMyOtherInnerClassSize) < 0) {
+ LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
+ return false;
+ }
+
+ static const JNINativeMethod kMethodsTestJni[] = {
+ { "nativeInit",
+"("
+")"
+"I", reinterpret_cast<void*>(Init) },
+ };
+ const int kMethodsTestJniSize = arraysize(kMethodsTestJni);
+
+ if (env->RegisterNatives(g_TestJni_clazz,
+ kMethodsTestJni,
+ kMethodsTestJniSize) < 0) {
+ LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
+ return false;
+ }
+
+ return true;
+}
+
+#endif // org_chromium_TestJni_JNI
+"""
+ self.assertTextEquals(golden_content, h.GetContent())
+
+ def testCalledByNatives(self):
+ test_data = """"
+ import android.graphics.Bitmap;
+ import android.view.View;
+ import java.io.InputStream;
+
+ class InnerClass {}
+
+ @CalledByNative
+ InnerClass showConfirmInfoBar(int nativeInfoBar,
+ String buttonOk, String buttonCancel, String title, Bitmap icon) {
+ InfoBar infobar = new ConfirmInfoBar(nativeInfoBar, mContext,
+ buttonOk, buttonCancel,
+ title, icon);
+ return infobar;
+ }
+ @CalledByNative
+ InnerClass showAutoLoginInfoBar(int nativeInfoBar,
+ String realm, String account, String args) {
+ AutoLoginInfoBar infobar = new AutoLoginInfoBar(nativeInfoBar, mContext,
+ realm, account, args);
+ if (infobar.displayedAccountCount() == 0)
+ infobar = null;
+ return infobar;
+ }
+ @CalledByNative("InfoBar")
+ void dismiss();
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private static boolean shouldShowAutoLogin(View view,
+ String realm, String account, String args) {
+ AccountManagerContainer accountManagerContainer =
+ new AccountManagerContainer((Activity)contentView.getContext(),
+ realm, account, args);
+ String[] logins = accountManagerContainer.getAccountLogins(null);
+ return logins.length != 0;
+ }
+ @CalledByNative
+ static InputStream openUrl(String url) {
+ return null;
+ }
+ @CalledByNative
+ private void activateHardwareAcceleration(final boolean activated,
+ final int iPid, final int iType,
+ final int iPrimaryID, final int iSecondaryID) {
+ if (!activated) {
+ return
+ }
+ }
+ @CalledByNativeUnchecked
+ private void uncheckedCall(int iParam);
+ """
+ jni_generator.JniParams.SetFullyQualifiedClass('org/chromium/Foo')
+ jni_generator.JniParams.ExtractImportsAndInnerClasses(test_data)
+ called_by_natives = jni_generator.ExtractCalledByNatives(test_data)
+ golden_called_by_natives = [
+ CalledByNative(
+ return_type='InnerClass',
+ system_class=False,
+ static=False,
+ name='showConfirmInfoBar',
+ method_id_var_name='showConfirmInfoBar',
+ java_class_name='',
+ params=[Param(datatype='int', name='nativeInfoBar'),
+ Param(datatype='String', name='buttonOk'),
+ Param(datatype='String', name='buttonCancel'),
+ Param(datatype='String', name='title'),
+ Param(datatype='Bitmap', name='icon')],
+ env_call=('Object', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='InnerClass',
+ system_class=False,
+ static=False,
+ name='showAutoLoginInfoBar',
+ method_id_var_name='showAutoLoginInfoBar',
+ java_class_name='',
+ params=[Param(datatype='int', name='nativeInfoBar'),
+ Param(datatype='String', name='realm'),
+ Param(datatype='String', name='account'),
+ Param(datatype='String', name='args')],
+ env_call=('Object', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='void',
+ system_class=False,
+ static=False,
+ name='dismiss',
+ method_id_var_name='dismiss',
+ java_class_name='InfoBar',
+ params=[],
+ env_call=('Void', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='boolean',
+ system_class=False,
+ static=True,
+ name='shouldShowAutoLogin',
+ method_id_var_name='shouldShowAutoLogin',
+ java_class_name='',
+ params=[Param(datatype='View', name='view'),
+ Param(datatype='String', name='realm'),
+ Param(datatype='String', name='account'),
+ Param(datatype='String', name='args')],
+ env_call=('Boolean', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='InputStream',
+ system_class=False,
+ static=True,
+ name='openUrl',
+ method_id_var_name='openUrl',
+ java_class_name='',
+ params=[Param(datatype='String', name='url')],
+ env_call=('Object', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='void',
+ system_class=False,
+ static=False,
+ name='activateHardwareAcceleration',
+ method_id_var_name='activateHardwareAcceleration',
+ java_class_name='',
+ params=[Param(datatype='boolean', name='activated'),
+ Param(datatype='int', name='iPid'),
+ Param(datatype='int', name='iType'),
+ Param(datatype='int', name='iPrimaryID'),
+ Param(datatype='int', name='iSecondaryID'),
+ ],
+ env_call=('Void', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='void',
+ system_class=False,
+ static=False,
+ name='uncheckedCall',
+ method_id_var_name='uncheckedCall',
+ java_class_name='',
+ params=[Param(datatype='int', name='iParam')],
+ env_call=('Void', ''),
+ unchecked=True,
+ ),
+ ]
+ self.assertListEquals(golden_called_by_natives, called_by_natives)
+ h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
+ [], called_by_natives)
+ golden_content = """\
+// Copyright (c) 2012 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 autogenerated by
+// base/android/jni_generator/jni_generator_tests.py
+// For
+// org/chromium/TestJni
+
+#ifndef org_chromium_TestJni_JNI
+#define org_chromium_TestJni_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+using base::android::ScopedJavaLocalRef;
+
+// Step 1: forward declarations.
+namespace {
+const char kTestJniClassPath[] = "org/chromium/TestJni";
+const char kInfoBarClassPath[] = "org/chromium/TestJni$InfoBar";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_TestJni_clazz = NULL;
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_InfoBar_clazz = NULL;
+} // namespace
+
+// Step 2: method stubs.
+
+static base::subtle::AtomicWord g_TestJni_showConfirmInfoBar = 0;
+static ScopedJavaLocalRef<jobject> Java_TestJni_showConfirmInfoBar(JNIEnv* env,
+ jobject obj, jint nativeInfoBar,
+ jstring buttonOk,
+ jstring buttonCancel,
+ jstring title,
+ jobject icon) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_TestJni_clazz,
+ "showConfirmInfoBar",
+
+"("
+"I"
+"Ljava/lang/String;"
+"Ljava/lang/String;"
+"Ljava/lang/String;"
+"Landroid/graphics/Bitmap;"
+")"
+"Lorg/chromium/Foo$InnerClass;",
+ &g_TestJni_showConfirmInfoBar);
+
+ jobject ret =
+ env->CallObjectMethod(obj,
+ method_id, nativeInfoBar, buttonOk, buttonCancel, title, icon);
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+static base::subtle::AtomicWord g_TestJni_showAutoLoginInfoBar = 0;
+static ScopedJavaLocalRef<jobject> Java_TestJni_showAutoLoginInfoBar(JNIEnv*
+ env, jobject obj, jint nativeInfoBar,
+ jstring realm,
+ jstring account,
+ jstring args) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_TestJni_clazz,
+ "showAutoLoginInfoBar",
+
+"("
+"I"
+"Ljava/lang/String;"
+"Ljava/lang/String;"
+"Ljava/lang/String;"
+")"
+"Lorg/chromium/Foo$InnerClass;",
+ &g_TestJni_showAutoLoginInfoBar);
+
+ jobject ret =
+ env->CallObjectMethod(obj,
+ method_id, nativeInfoBar, realm, account, args);
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+static base::subtle::AtomicWord g_InfoBar_dismiss = 0;
+static void Java_InfoBar_dismiss(JNIEnv* env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InfoBar_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InfoBar_clazz,
+ "dismiss",
+
+"("
+")"
+"V",
+ &g_InfoBar_dismiss);
+
+ env->CallVoidMethod(obj,
+ method_id);
+ base::android::CheckException(env);
+
+}
+
+static base::subtle::AtomicWord g_TestJni_shouldShowAutoLogin = 0;
+static jboolean Java_TestJni_shouldShowAutoLogin(JNIEnv* env, jobject view,
+ jstring realm,
+ jstring account,
+ jstring args) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_STATIC>(
+ env, g_TestJni_clazz,
+ "shouldShowAutoLogin",
+
+"("
+"Landroid/view/View;"
+"Ljava/lang/String;"
+"Ljava/lang/String;"
+"Ljava/lang/String;"
+")"
+"Z",
+ &g_TestJni_shouldShowAutoLogin);
+
+ jboolean ret =
+ env->CallStaticBooleanMethod(g_TestJni_clazz,
+ method_id, view, realm, account, args);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_TestJni_openUrl = 0;
+static ScopedJavaLocalRef<jobject> Java_TestJni_openUrl(JNIEnv* env, jstring
+ url) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_STATIC>(
+ env, g_TestJni_clazz,
+ "openUrl",
+
+"("
+"Ljava/lang/String;"
+")"
+"Ljava/io/InputStream;",
+ &g_TestJni_openUrl);
+
+ jobject ret =
+ env->CallStaticObjectMethod(g_TestJni_clazz,
+ method_id, url);
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+static base::subtle::AtomicWord g_TestJni_activateHardwareAcceleration = 0;
+static void Java_TestJni_activateHardwareAcceleration(JNIEnv* env, jobject obj,
+ jboolean activated,
+ jint iPid,
+ jint iType,
+ jint iPrimaryID,
+ jint iSecondaryID) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_TestJni_clazz,
+ "activateHardwareAcceleration",
+
+"("
+"Z"
+"I"
+"I"
+"I"
+"I"
+")"
+"V",
+ &g_TestJni_activateHardwareAcceleration);
+
+ env->CallVoidMethod(obj,
+ method_id, activated, iPid, iType, iPrimaryID, iSecondaryID);
+ base::android::CheckException(env);
+
+}
+
+static base::subtle::AtomicWord g_TestJni_uncheckedCall = 0;
+static void Java_TestJni_uncheckedCall(JNIEnv* env, jobject obj, jint iParam) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_TestJni_clazz,
+ "uncheckedCall",
+
+"("
+"I"
+")"
+"V",
+ &g_TestJni_uncheckedCall);
+
+ env->CallVoidMethod(obj,
+ method_id, iParam);
+
+}
+
+// Step 3: RegisterNatives.
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+
+ g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetUnscopedClass(env, kTestJniClassPath)));
+ g_InfoBar_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetUnscopedClass(env, kInfoBarClassPath)));
+ return true;
+}
+
+#endif // org_chromium_TestJni_JNI
+"""
+ self.assertTextEquals(golden_content, h.GetContent())
+
+ def testCalledByNativeParseError(self):
+ try:
+ jni_generator.ExtractCalledByNatives("""
+@CalledByNative
+public static int foo(); // This one is fine
+
+@CalledByNative
+scooby doo
+""")
+ self.fail('Expected a ParseError')
+ except jni_generator.ParseError, e:
+ self.assertEquals(('@CalledByNative', 'scooby doo'), e.context_lines)
+
+ def testFullyQualifiedClassName(self):
+ contents = """
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.content.browser;
+
+import org.chromium.base.BuildInfo;
+"""
+ self.assertEquals('org/chromium/content/browser/Foo',
+ jni_generator.ExtractFullyQualifiedJavaClassName(
+ 'org/chromium/content/browser/Foo.java', contents))
+ self.assertEquals('org/chromium/content/browser/Foo',
+ jni_generator.ExtractFullyQualifiedJavaClassName(
+ 'frameworks/Foo.java', contents))
+ self.assertRaises(SyntaxError,
+ jni_generator.ExtractFullyQualifiedJavaClassName,
+ 'com/foo/Bar', 'no PACKAGE line')
+
+ def testMethodNameMangling(self):
+ self.assertEquals('closeV',
+ jni_generator.GetMangledMethodName('close', [], 'void'))
+ self.assertEquals('readI_AB_I_I',
+ jni_generator.GetMangledMethodName('read',
+ [Param(name='p1',
+ datatype='byte[]'),
+ Param(name='p2',
+ datatype='int'),
+ Param(name='p3',
+ datatype='int'),],
+ 'int'))
+ self.assertEquals('openJIIS_JLS',
+ jni_generator.GetMangledMethodName('open',
+ [Param(name='p1',
+ datatype='java/lang/String'),],
+ 'java/io/InputStream'))
+
+ def testFromJavaP(self):
+ contents = """
+public abstract class java.io.InputStream extends java.lang.Object
+ implements java.io.Closeable{
+ public java.io.InputStream();
+ public int available() throws java.io.IOException;
+ public void close() throws java.io.IOException;
+ public void mark(int);
+ public boolean markSupported();
+ public abstract int read() throws java.io.IOException;
+ public int read(byte[]) throws java.io.IOException;
+ public int read(byte[], int, int) throws java.io.IOException;
+ public synchronized void reset() throws java.io.IOException;
+ public long skip(long) throws java.io.IOException;
+}
+"""
+ jni_from_javap = jni_generator.JNIFromJavaP(contents.split('\n'), None)
+ self.assertEquals(10, len(jni_from_javap.called_by_natives))
+ golden_content = """\
+// Copyright (c) 2012 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 autogenerated by
+// base/android/jni_generator/jni_generator_tests.py
+// For
+// java/io/InputStream
+
+#ifndef java_io_InputStream_JNI
+#define java_io_InputStream_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+using base::android::ScopedJavaLocalRef;
+
+// Step 1: forward declarations.
+namespace {
+const char kInputStreamClassPath[] = "java/io/InputStream";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_InputStream_clazz = NULL;
+} // namespace
+
+namespace JNI_InputStream {
+
+// Step 2: method stubs.
+
+static base::subtle::AtomicWord g_InputStream_available = 0;
+static jint Java_InputStream_available(JNIEnv* env, jobject obj) __attribute__
+ ((unused));
+static jint Java_InputStream_available(JNIEnv* env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "available",
+
+"("
+")"
+"I",
+ &g_InputStream_available);
+
+ jint ret =
+ env->CallIntMethod(obj,
+ method_id);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_InputStream_close = 0;
+static void Java_InputStream_close(JNIEnv* env, jobject obj) __attribute__
+ ((unused));
+static void Java_InputStream_close(JNIEnv* env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "close",
+
+"("
+")"
+"V",
+ &g_InputStream_close);
+
+ env->CallVoidMethod(obj,
+ method_id);
+ base::android::CheckException(env);
+
+}
+
+static base::subtle::AtomicWord g_InputStream_mark = 0;
+static void Java_InputStream_mark(JNIEnv* env, jobject obj, jint p0)
+ __attribute__ ((unused));
+static void Java_InputStream_mark(JNIEnv* env, jobject obj, jint p0) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "mark",
+
+"("
+"I"
+")"
+"V",
+ &g_InputStream_mark);
+
+ env->CallVoidMethod(obj,
+ method_id, p0);
+ base::android::CheckException(env);
+
+}
+
+static base::subtle::AtomicWord g_InputStream_markSupported = 0;
+static jboolean Java_InputStream_markSupported(JNIEnv* env, jobject obj)
+ __attribute__ ((unused));
+static jboolean Java_InputStream_markSupported(JNIEnv* env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "markSupported",
+
+"("
+")"
+"Z",
+ &g_InputStream_markSupported);
+
+ jboolean ret =
+ env->CallBooleanMethod(obj,
+ method_id);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_InputStream_readI = 0;
+static jint Java_InputStream_readI(JNIEnv* env, jobject obj) __attribute__
+ ((unused));
+static jint Java_InputStream_readI(JNIEnv* env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "read",
+
+"("
+")"
+"I",
+ &g_InputStream_readI);
+
+ jint ret =
+ env->CallIntMethod(obj,
+ method_id);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_InputStream_readI_AB = 0;
+static jint Java_InputStream_readI_AB(JNIEnv* env, jobject obj, jbyteArray p0)
+ __attribute__ ((unused));
+static jint Java_InputStream_readI_AB(JNIEnv* env, jobject obj, jbyteArray p0) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "read",
+
+"("
+"[B"
+")"
+"I",
+ &g_InputStream_readI_AB);
+
+ jint ret =
+ env->CallIntMethod(obj,
+ method_id, p0);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_InputStream_readI_AB_I_I = 0;
+static jint Java_InputStream_readI_AB_I_I(JNIEnv* env, jobject obj, jbyteArray
+ p0,
+ jint p1,
+ jint p2) __attribute__ ((unused));
+static jint Java_InputStream_readI_AB_I_I(JNIEnv* env, jobject obj, jbyteArray
+ p0,
+ jint p1,
+ jint p2) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "read",
+
+"("
+"[B"
+"I"
+"I"
+")"
+"I",
+ &g_InputStream_readI_AB_I_I);
+
+ jint ret =
+ env->CallIntMethod(obj,
+ method_id, p0, p1, p2);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_InputStream_reset = 0;
+static void Java_InputStream_reset(JNIEnv* env, jobject obj) __attribute__
+ ((unused));
+static void Java_InputStream_reset(JNIEnv* env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "reset",
+
+"("
+")"
+"V",
+ &g_InputStream_reset);
+
+ env->CallVoidMethod(obj,
+ method_id);
+ base::android::CheckException(env);
+
+}
+
+static base::subtle::AtomicWord g_InputStream_skip = 0;
+static jlong Java_InputStream_skip(JNIEnv* env, jobject obj, jlong p0)
+ __attribute__ ((unused));
+static jlong Java_InputStream_skip(JNIEnv* env, jobject obj, jlong p0) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "skip",
+
+"("
+"J"
+")"
+"J",
+ &g_InputStream_skip);
+
+ jlong ret =
+ env->CallLongMethod(obj,
+ method_id, p0);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_InputStream_Constructor = 0;
+static ScopedJavaLocalRef<jobject> Java_InputStream_Constructor(JNIEnv* env)
+ __attribute__ ((unused));
+static ScopedJavaLocalRef<jobject> Java_InputStream_Constructor(JNIEnv* env) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "<init>",
+
+"("
+")"
+"V",
+ &g_InputStream_Constructor);
+
+ jobject ret =
+ env->NewObject(g_InputStream_clazz,
+ method_id);
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+// Step 3: RegisterNatives.
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+
+ g_InputStream_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetUnscopedClass(env, kInputStreamClassPath)));
+ return true;
+}
+} // namespace JNI_InputStream
+
+#endif // java_io_InputStream_JNI
+"""
+ self.assertTextEquals(golden_content, jni_from_javap.GetContent())
+
+ def testREForNatives(self):
+ # We should not match "native SyncSetupFlow" inside the comment.
+ test_data = """
+ /**
+ * Invoked when the setup process is complete so we can disconnect from the
+ * native-side SyncSetupFlowHandler.
+ */
+ public void destroy() {
+ Log.v(TAG, "Destroying native SyncSetupFlow");
+ if (mNativeSyncSetupFlow != 0) {
+ nativeSyncSetupEnded(mNativeSyncSetupFlow);
+ mNativeSyncSetupFlow = 0;
+ }
+ }
+ private native void nativeSyncSetupEnded(
+ int nativeAndroidSyncSetupFlowHandler);
+ """
+ jni_from_java = jni_generator.JNIFromJavaSource(test_data, 'foo/bar')
+
+ def testRaisesOnNonJNIMethod(self):
+ test_data = """
+ class MyInnerClass {
+ private int Foo(int p0) {
+ }
+ }
+ """
+ self.assertRaises(SyntaxError,
+ jni_generator.JNIFromJavaSource,
+ test_data, 'foo/bar')
+
+ def testJniSelfDocumentingExample(self):
+ script_dir = os.path.dirname(sys.argv[0])
+ content = file(os.path.join(script_dir, 'SampleForTests.java')).read()
+ golden_content = file(os.path.join(script_dir,
+ 'golden_sample_for_tests_jni.h')).read()
+ jni_from_java = jni_generator.JNIFromJavaSource(
+ content, 'org/chromium/example/jni_generator/SampleForTests')
+ self.assertTextEquals(golden_content, jni_from_java.GetContent())
+
+ def testNoWrappingPreprocessorLines(self):
+ test_data = """
+ package com.google.lookhowextremelylongiam.snarf.icankeepthisupallday;
+
+ class ReallyLongClassNamesAreAllTheRage {
+ private static native int nativeTest();
+ }
+ """
+ jni_from_java = jni_generator.JNIFromJavaSource(
+ test_data, ('com/google/lookhowextremelylongiam/snarf/'
+ 'icankeepthisupallday/ReallyLongClassNamesAreAllTheRage'))
+ jni_lines = jni_from_java.GetContent().split('\n')
+ line = filter(lambda line: line.lstrip().startswith('#ifndef'),
+ jni_lines)[0]
+ self.assertTrue(len(line) > 80,
+ ('Expected #ifndef line to be > 80 chars: ', line))
+
+ def testImports(self):
+ import_header = """
+// Copyright (c) 2012 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.
+
+package org.chromium.content.app;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.SurfaceTexture;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Surface;
+
+import java.util.ArrayList;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+import org.chromium.content.app.ContentMain;
+import org.chromium.content.browser.SandboxedProcessConnection;
+import org.chromium.content.common.ISandboxedProcessCallback;
+import org.chromium.content.common.ISandboxedProcessService;
+import org.chromium.content.common.SurfaceCallback;
+import org.chromium.content.common.WillNotRaise.AnException;
+import org.chromium.content.common.WillRaise.AnException;
+
+import static org.chromium.Bar.Zoo;
+
+class Foo {
+ public static class BookmarkNode implements Parcelable {
+ }
+ public interface PasswordListObserver {
+ }
+}
+ """
+ jni_generator.JniParams.SetFullyQualifiedClass(
+ 'org/chromium/content/app/Foo')
+ jni_generator.JniParams.ExtractImportsAndInnerClasses(import_header)
+ self.assertTrue('Lorg/chromium/content/common/ISandboxedProcessService' in
+ jni_generator.JniParams._imports)
+ self.assertTrue('Lorg/chromium/Bar/Zoo' in
+ jni_generator.JniParams._imports)
+ self.assertTrue('Lorg/chromium/content/app/Foo$BookmarkNode' in
+ jni_generator.JniParams._inner_classes)
+ self.assertTrue('Lorg/chromium/content/app/Foo$PasswordListObserver' in
+ jni_generator.JniParams._inner_classes)
+ self.assertEquals('Lorg/chromium/content/app/ContentMain$Inner',
+ jni_generator.JniParams.JavaToJni('ContentMain.Inner'))
+ self.assertRaises(SyntaxError,
+ jni_generator.JniParams.JavaToJni,
+ 'AnException')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/base/android/jni_generator/sample_for_tests.cc b/src/base/android/jni_generator/sample_for_tests.cc
new file mode 100644
index 0000000..5b5cfc5
--- /dev/null
+++ b/src/base/android/jni_generator/sample_for_tests.cc
@@ -0,0 +1,81 @@
+// Copyright (c) 2012 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/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+
+// The main purpose of this is to ensure sample_for_tests_jni.h
+// compiles and the functions declared in it as expected.
+
+using base::android::AttachCurrentThread;
+using base::android::ScopedJavaLocalRef;
+
+namespace base {
+namespace android {
+
+class CPPClass {
+ public:
+ class InnerClass {
+ public:
+ jdouble MethodOtherP0(JNIEnv* env, jobject obj) {
+ return 0.0;
+ }
+ };
+
+ void Destroy(JNIEnv* env, jobject obj) {
+ delete this;
+ }
+
+ jint Method(JNIEnv* env, jobject obj) {
+ return 0;
+ }
+
+ ScopedJavaLocalRef<jstring> InnerMethod(JNIEnv* env, jobject obj) {
+ return ScopedJavaLocalRef<jstring>();
+ }
+};
+
+static jint Init(JNIEnv* env, jobject obj) {
+ return 0;
+}
+
+static jdouble GetDoubleFunction(JNIEnv*, jobject) {
+ return 0;
+}
+
+static jfloat GetFloatFunction(JNIEnv*, jclass) {
+ return 0;
+}
+
+static void SetNonPODDatatype(JNIEnv*, jobject, jobject) {}
+
+static jobject GetNonPODDatatype(JNIEnv*, jobject) {
+ return NULL;
+}
+
+static jint InnerFunction(JNIEnv*, jclass) {
+ return 0;
+}
+
+} // namespace android
+} // namespace base
+
+#include "jni/SampleForTests_jni.h"
+
+int main() {
+ // On a regular application, you'd call AttachCurrentThread(). This sample is
+ // not yet linking with all the libraries.
+ JNIEnv* env = /* AttachCurrentThread() */ NULL;
+
+ // This is how you call a java static method from C++.
+ bool foo = base::android::Java_SampleForTests_staticJavaMethod(env);
+
+ // This is how you call a java method from C++. Note that you must have
+ // obtained the jobject somehow.
+ jobject my_java_object = NULL;
+ int bar = base::android::Java_SampleForTests_javaMethod(
+ env, my_java_object, 1, 2);
+
+ return 0;
+}
diff --git a/src/base/android/jni_helper.cc b/src/base/android/jni_helper.cc
new file mode 100644
index 0000000..c0833c9
--- /dev/null
+++ b/src/base/android/jni_helper.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 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/android/jni_helper.h"
+
+#include "base/android/jni_android.h"
+#include "base/logging.h"
+
+using base::android::AttachCurrentThread;
+
+JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef()
+ : obj_(NULL) {
+}
+
+JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef(
+ const JavaObjectWeakGlobalRef& orig) {
+ Assign(orig);
+}
+
+JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef(JNIEnv* env, jobject obj)
+ : obj_(env->NewWeakGlobalRef(obj)) {
+ DCHECK(obj_);
+}
+
+JavaObjectWeakGlobalRef::~JavaObjectWeakGlobalRef() {
+ reset();
+}
+
+void JavaObjectWeakGlobalRef::operator=(const JavaObjectWeakGlobalRef& rhs) {
+ Assign(rhs);
+}
+
+void JavaObjectWeakGlobalRef::reset() {
+ if (obj_) {
+ AttachCurrentThread()->DeleteWeakGlobalRef(obj_);
+ obj_ = NULL;
+ }
+}
+
+base::android::ScopedJavaLocalRef<jobject>
+ JavaObjectWeakGlobalRef::get(JNIEnv* env) const {
+ return GetRealObject(env, obj_);
+}
+
+base::android::ScopedJavaLocalRef<jobject> GetRealObject(
+ JNIEnv* env, jweak obj) {
+ jobject real = NULL;
+ if (obj) {
+ real = env->NewLocalRef(obj);
+ if (!real)
+ DLOG(ERROR) << "The real object has been deleted!";
+ }
+ return base::android::ScopedJavaLocalRef<jobject>(env, real);
+}
+
+void JavaObjectWeakGlobalRef::Assign(const JavaObjectWeakGlobalRef& other) {
+ JNIEnv* env = AttachCurrentThread();
+ obj_ = env->NewWeakGlobalRef(other.obj_);
+}
diff --git a/src/base/android/jni_helper.h b/src/base/android/jni_helper.h
new file mode 100644
index 0000000..895bf95
--- /dev/null
+++ b/src/base/android/jni_helper.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 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_ANDROID_JNI_HELPER_H_
+#define BASE_ANDROID_JNI_HELPER_H_
+
+#include <jni.h>
+
+#include "base/base_export.h"
+#include "base/android/scoped_java_ref.h"
+
+// Manages WeakGlobalRef lifecycle.
+// This class is not thread-safe w.r.t. get() and reset(). Multiple threads may
+// safely use get() concurrently, but if the user calls reset() (or of course,
+// calls the destructor) they'll need to provide their own synchronization.
+class BASE_EXPORT JavaObjectWeakGlobalRef {
+ public:
+ JavaObjectWeakGlobalRef();
+ JavaObjectWeakGlobalRef(const JavaObjectWeakGlobalRef& orig);
+ JavaObjectWeakGlobalRef(JNIEnv* env, jobject obj);
+ virtual ~JavaObjectWeakGlobalRef();
+
+ void operator=(const JavaObjectWeakGlobalRef& rhs);
+
+ base::android::ScopedJavaLocalRef<jobject> get(JNIEnv* env) const;
+
+ void reset();
+
+ private:
+ void Assign(const JavaObjectWeakGlobalRef& rhs);
+
+ jweak obj_;
+};
+
+// Get the real object stored in the weak reference returned as a
+// ScopedJavaLocalRef.
+BASE_EXPORT base::android::ScopedJavaLocalRef<jobject> GetRealObject(
+ JNIEnv* env, jweak obj);
+
+#endif // BASE_ANDROID_JNI_HELPER_H_
diff --git a/src/base/android/jni_registrar.cc b/src/base/android/jni_registrar.cc
new file mode 100644
index 0000000..696924a
--- /dev/null
+++ b/src/base/android/jni_registrar.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 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/android/jni_registrar.h"
+
+#include "base/logging.h"
+#include "base/android/jni_android.h"
+
+namespace base {
+namespace android {
+
+bool RegisterNativeMethods(JNIEnv* env,
+ const RegistrationMethod* method,
+ size_t count) {
+ const RegistrationMethod* end = method + count;
+ while (method != end) {
+ if (!method->func(env) < 0) {
+ DLOG(ERROR) << method->name << " failed registration!";
+ return false;
+ }
+ method++;
+ }
+ return true;
+}
+
+} // namespace android
+} // namespace base
diff --git a/src/base/android/jni_registrar.h b/src/base/android/jni_registrar.h
new file mode 100644
index 0000000..849d07f
--- /dev/null
+++ b/src/base/android/jni_registrar.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2012 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_ANDROID_JNI_REGISTRAR_H_
+#define BASE_ANDROID_JNI_REGISTRAR_H_
+
+#include <jni.h>
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+namespace android {
+
+struct RegistrationMethod;
+
+// Registers the JNI bindings for the specified |method| definition containing
+// |count| elements. Returns whether the registration of the given methods
+// succeeded.
+BASE_EXPORT bool RegisterNativeMethods(JNIEnv* env,
+ const RegistrationMethod* method,
+ size_t count);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_JNI_REGISTRAR_H_
diff --git a/src/base/android/jni_string.cc b/src/base/android/jni_string.cc
new file mode 100644
index 0000000..ac3975f
--- /dev/null
+++ b/src/base/android/jni_string.cc
@@ -0,0 +1,99 @@
+// Copyright (c) 2012 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/android/jni_string.h"
+
+#include "base/android/jni_android.h"
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+
+namespace {
+
+// Internal version that does not use a scoped local pointer.
+jstring ConvertUTF16ToJavaStringImpl(JNIEnv* env,
+ const base::StringPiece16& str) {
+ jstring result = env->NewString(str.data(), str.length());
+ base::android::CheckException(env);
+ return result;
+}
+
+}
+
+namespace base {
+namespace android {
+
+void ConvertJavaStringToUTF8(JNIEnv* env, jstring str, std::string* result) {
+ if (!str) {
+ LOG(WARNING) << "ConvertJavaStringToUTF8 called with null string.";
+ result->clear();
+ return;
+ }
+ // JNI's GetStringUTFChars() returns strings in Java "modified" UTF8, so
+ // instead get the String in UTF16 and convert using chromium's conversion
+ // function that yields plain (non Java-modified) UTF8.
+ const jchar* chars = env->GetStringChars(str, NULL);
+ DCHECK(chars);
+ UTF16ToUTF8(chars, env->GetStringLength(str), result);
+ env->ReleaseStringChars(str, chars);
+ CheckException(env);
+}
+
+std::string ConvertJavaStringToUTF8(JNIEnv* env, jstring str) {
+ std::string result;
+ ConvertJavaStringToUTF8(env, str, &result);
+ return result;
+}
+
+std::string ConvertJavaStringToUTF8(const JavaRef<jstring>& str) {
+ return ConvertJavaStringToUTF8(AttachCurrentThread(), str.obj());
+}
+
+ScopedJavaLocalRef<jstring> ConvertUTF8ToJavaString(
+ JNIEnv* env,
+ const base::StringPiece& str) {
+ // JNI's NewStringUTF expects "modified" UTF8 so instead create the string
+ // via our own UTF16 conversion utility.
+ // Further, Dalvik requires the string passed into NewStringUTF() to come from
+ // a trusted source. We can't guarantee that all UTF8 will be sanitized before
+ // it gets here, so constructing via UTF16 side-steps this issue.
+ // (Dalvik stores strings internally as UTF16 anyway, so there shouldn't be
+ // a significant performance hit by doing it this way).
+ return ScopedJavaLocalRef<jstring>(env, ConvertUTF16ToJavaStringImpl(
+ env, UTF8ToUTF16(str)));
+}
+
+void ConvertJavaStringToUTF16(JNIEnv* env, jstring str, string16* result) {
+ if (!str) {
+ LOG(WARNING) << "ConvertJavaStringToUTF16 called with null string.";
+ result->clear();
+ return;
+ }
+ const jchar* chars = env->GetStringChars(str, NULL);
+ DCHECK(chars);
+ // GetStringChars isn't required to NULL-terminate the strings
+ // it returns, so the length must be explicitly checked.
+ result->assign(chars, env->GetStringLength(str));
+ env->ReleaseStringChars(str, chars);
+ CheckException(env);
+}
+
+string16 ConvertJavaStringToUTF16(JNIEnv* env, jstring str) {
+ string16 result;
+ ConvertJavaStringToUTF16(env, str, &result);
+ return result;
+}
+
+string16 ConvertJavaStringToUTF16(const JavaRef<jstring>& str) {
+ return ConvertJavaStringToUTF16(AttachCurrentThread(), str.obj());
+}
+
+ScopedJavaLocalRef<jstring> ConvertUTF16ToJavaString(
+ JNIEnv* env,
+ const base::StringPiece16& str) {
+ return ScopedJavaLocalRef<jstring>(env,
+ ConvertUTF16ToJavaStringImpl(env, str));
+}
+
+} // namespace android
+} // namespace base
diff --git a/src/base/android/jni_string.h b/src/base/android/jni_string.h
new file mode 100644
index 0000000..222b78d
--- /dev/null
+++ b/src/base/android/jni_string.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 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_ANDROID_JNI_STRING_H_
+#define BASE_ANDROID_JNI_STRING_H_
+
+#include <jni.h>
+#include <string>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/base_export.h"
+#include "base/string_piece.h"
+
+namespace base {
+namespace android {
+
+// Convert a Java string to UTF8. Returns a std string.
+BASE_EXPORT void ConvertJavaStringToUTF8(JNIEnv* env,
+ jstring str,
+ std::string* result);
+BASE_EXPORT std::string ConvertJavaStringToUTF8(JNIEnv* env, jstring str);
+BASE_EXPORT std::string ConvertJavaStringToUTF8(const JavaRef<jstring>& str);
+
+// Convert a std string to Java string.
+BASE_EXPORT ScopedJavaLocalRef<jstring> ConvertUTF8ToJavaString(
+ JNIEnv* env,
+ const base::StringPiece& str);
+
+// Convert a Java string to UTF16. Returns a string16.
+BASE_EXPORT void ConvertJavaStringToUTF16(JNIEnv* env,
+ jstring str,
+ string16* result);
+BASE_EXPORT string16 ConvertJavaStringToUTF16(JNIEnv* env, jstring str);
+BASE_EXPORT string16 ConvertJavaStringToUTF16(const JavaRef<jstring>& str);
+
+// Convert a string16 to a Java string.
+BASE_EXPORT ScopedJavaLocalRef<jstring> ConvertUTF16ToJavaString(
+ JNIEnv* env,
+ const base::StringPiece16& str);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_JNI_STRING_H_
diff --git a/src/base/android/jni_string_unittest.cc b/src/base/android/jni_string_unittest.cc
new file mode 100644
index 0000000..59a847e
--- /dev/null
+++ b/src/base/android/jni_string_unittest.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 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/android/jni_string.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace android {
+
+TEST(JniString, BasicConversionsUTF8) {
+ const std::string kSimpleString = "SimpleTest8";
+ JNIEnv* env = AttachCurrentThread();
+ std::string result =
+ ConvertJavaStringToUTF8(ConvertUTF8ToJavaString(env, kSimpleString));
+ EXPECT_EQ(kSimpleString, result);
+}
+
+TEST(JniString, BasicConversionsUTF16) {
+ const string16 kSimpleString = UTF8ToUTF16("SimpleTest16");
+ JNIEnv* env = AttachCurrentThread();
+ string16 result =
+ ConvertJavaStringToUTF16(ConvertUTF16ToJavaString(env, kSimpleString));
+ EXPECT_EQ(kSimpleString, result);
+}
+
+} // namespace android
+} // namespace base
diff --git a/src/base/android/locale_utils.cc b/src/base/android/locale_utils.cc
new file mode 100644
index 0000000..60b8f84
--- /dev/null
+++ b/src/base/android/locale_utils.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2012 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/android/locale_utils.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "jni/LocaleUtils_jni.h"
+#include "unicode/uloc.h"
+
+namespace base {
+namespace android {
+
+std::string GetDefaultLocale() {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> locale = Java_LocaleUtils_getDefaultLocale(env);
+ return ConvertJavaStringToUTF8(locale);
+}
+
+namespace {
+
+// Common prototype of ICU uloc_getXXX() functions.
+typedef int32_t (*UlocGetComponentFunc)(const char*, char*, int32_t,
+ UErrorCode*);
+
+std::string GetLocaleComponent(const std::string& locale,
+ UlocGetComponentFunc uloc_func,
+ int32_t max_capacity) {
+ std::string result;
+ UErrorCode error = U_ZERO_ERROR;
+ int32_t actual_length = uloc_func(locale.c_str(),
+ WriteInto(&result, max_capacity),
+ max_capacity,
+ &error);
+ DCHECK(U_SUCCESS(error));
+ DCHECK(actual_length < max_capacity);
+ result.resize(actual_length);
+ return result;
+}
+
+ScopedJavaLocalRef<jobject> NewJavaLocale(
+ JNIEnv* env,
+ ScopedJavaLocalRef<jclass> locale_class,
+ jmethodID constructor_id,
+ const std::string& locale) {
+ // TODO(wangxianzhu): Use new Locale API once Android supports scripts.
+ std::string language = GetLocaleComponent(
+ locale, uloc_getLanguage, ULOC_LANG_CAPACITY);
+ std::string country = GetLocaleComponent(
+ locale, uloc_getCountry, ULOC_COUNTRY_CAPACITY);
+ std::string variant = GetLocaleComponent(
+ locale, uloc_getVariant, ULOC_FULLNAME_CAPACITY);
+ return ScopedJavaLocalRef<jobject>(
+ env, env->NewObject(
+ locale_class.obj(), constructor_id,
+ ConvertUTF8ToJavaString(env, language).obj(),
+ ConvertUTF8ToJavaString(env, country).obj(),
+ ConvertUTF8ToJavaString(env, variant).obj()));
+}
+
+} // namespace
+
+string16 GetDisplayNameForLocale(const std::string& locale,
+ const std::string& display_locale) {
+ JNIEnv* env = AttachCurrentThread();
+
+ ScopedJavaLocalRef<jclass> locale_class = GetClass(env, "java/util/Locale");
+ jmethodID constructor_id = MethodID::Get<MethodID::TYPE_INSTANCE>(
+ env, locale_class.obj(), "<init>",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+ ScopedJavaLocalRef<jobject> java_locale = NewJavaLocale(
+ env, locale_class, constructor_id, locale);
+ ScopedJavaLocalRef<jobject> java_display_locale = NewJavaLocale(
+ env, locale_class, constructor_id, display_locale);
+
+ jmethodID method_id = MethodID::Get<MethodID::TYPE_INSTANCE>(
+ env, locale_class.obj(), "getDisplayName",
+ "(Ljava/util/Locale;)Ljava/lang/String;");
+ ScopedJavaLocalRef<jstring> java_result(
+ env,
+ static_cast<jstring>(env->CallObjectMethod(java_locale.obj(), method_id,
+ java_display_locale.obj())));
+ return ConvertJavaStringToUTF16(java_result);
+}
+
+bool RegisterLocaleUtils(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace base
diff --git a/src/base/android/locale_utils.h b/src/base/android/locale_utils.h
new file mode 100644
index 0000000..cef39f4
--- /dev/null
+++ b/src/base/android/locale_utils.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 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_ANDROID_LOCALE_UTILS_H_
+#define BASE_ANDROID_LOCALE_UTILS_H_
+
+#include <jni.h>
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/string16.h"
+
+namespace base {
+namespace android {
+
+// Return the current default locale of the device.
+BASE_EXPORT std::string GetDefaultLocale();
+
+BASE_EXPORT string16 GetDisplayNameForLocale(const std::string& locale,
+ const std::string& display_locale);
+
+bool RegisterLocaleUtils(JNIEnv* env);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_LOCALE_UTILS_H_
diff --git a/src/base/android/path_service_android.cc b/src/base/android/path_service_android.cc
new file mode 100644
index 0000000..a9bf92d
--- /dev/null
+++ b/src/base/android/path_service_android.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 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/android/path_service_android.h"
+
+#include "base/file_path.h"
+#include "base/path_service.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "jni/PathService_jni.h"
+
+namespace base {
+namespace android {
+
+void Override(JNIEnv* env, jclass clazz, jint what, jstring path) {
+ FilePath file_path(ConvertJavaStringToUTF8(env, path));
+ PathService::Override(what, file_path);
+}
+
+bool RegisterPathService(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace base
diff --git a/src/base/android/path_service_android.h b/src/base/android/path_service_android.h
new file mode 100644
index 0000000..26040f9
--- /dev/null
+++ b/src/base/android/path_service_android.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2012 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_ANDROID_PATH_SERVICE_ANDROID_H_
+#define BASE_ANDROID_PATH_SERVICE_ANDROID_H_
+
+#include <jni.h>
+
+namespace base {
+namespace android {
+
+bool RegisterPathService(JNIEnv* env);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_PATH_SERVICE_ANDROID_H_
diff --git a/src/base/android/path_utils.cc b/src/base/android/path_utils.cc
new file mode 100644
index 0000000..3d86177
--- /dev/null
+++ b/src/base/android/path_utils.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 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/android/path_utils.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/file_path.h"
+
+#include "jni/PathUtils_jni.h"
+
+namespace base {
+namespace android {
+
+bool GetDataDirectory(FilePath* result) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> path =
+ Java_PathUtils_getDataDirectory(env, GetApplicationContext());
+ FilePath data_path(ConvertJavaStringToUTF8(path));
+ *result = data_path;
+ return true;
+}
+
+bool GetCacheDirectory(FilePath* result) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> path =
+ Java_PathUtils_getCacheDirectory(env, GetApplicationContext());
+ FilePath cache_path(ConvertJavaStringToUTF8(path));
+ *result = cache_path;
+ return true;
+}
+
+bool GetDownloadsDirectory(FilePath* result) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> path =
+ Java_PathUtils_getDownloadsDirectory(env, GetApplicationContext());
+ FilePath downloads_path(ConvertJavaStringToUTF8(path));
+ *result = downloads_path;
+ return true;
+}
+
+bool GetNativeLibraryDirectory(FilePath* result) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> path =
+ Java_PathUtils_getNativeLibraryDirectory(env, GetApplicationContext());
+ FilePath library_path(ConvertJavaStringToUTF8(path));
+ *result = library_path;
+ return true;
+}
+
+bool GetExternalStorageDirectory(FilePath* result) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> path =
+ Java_PathUtils_getExternalStorageDirectory(env);
+ FilePath storage_path(ConvertJavaStringToUTF8(path));
+ *result = storage_path;
+ return true;
+}
+
+bool RegisterPathUtils(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace base
diff --git a/src/base/android/path_utils.h b/src/base/android/path_utils.h
new file mode 100644
index 0000000..78f0267
--- /dev/null
+++ b/src/base/android/path_utils.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2012 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_ANDROID_PATH_UTILS_H_
+#define BASE_ANDROID_PATH_UTILS_H_
+
+#include <jni.h>
+
+#include "base/base_export.h"
+
+class FilePath;
+
+namespace base {
+namespace android {
+
+// Retrieves the absolute path to the data directory of the current
+// application. The result is placed in the FilePath pointed to by 'result'.
+// This method is dedicated for base_paths_android.c, Using
+// PathService::Get(base::DIR_ANDROID_APP_DATA, ...) gets the data dir.
+BASE_EXPORT bool GetDataDirectory(FilePath* result);
+
+// Retrieves the absolute path to the cache directory. The result is placed in
+// the FilePath pointed to by 'result'. This method is dedicated for
+// base_paths_android.c, Using PathService::Get(base::DIR_CACHE, ...) gets the
+// cache dir.
+BASE_EXPORT bool GetCacheDirectory(FilePath* result);
+
+// Retrieves the path to the public downloads directory. The result is placed
+// in the FilePath pointed to by 'result'.
+bool GetDownloadsDirectory(FilePath* result);
+
+// Retrieves the path to the native JNI libraries via
+// ApplicationInfo.nativeLibraryDir on the Java side. The result is placed in
+// the FilePath pointed to by 'result'.
+BASE_EXPORT bool GetNativeLibraryDirectory(FilePath* result);
+
+// Retrieves the absolute path to the external storage directory. The result
+// is placed in the FilePath pointed to by 'result'.
+BASE_EXPORT bool GetExternalStorageDirectory(FilePath* result);
+
+bool RegisterPathUtils(JNIEnv* env);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_PATH_UTILS_H_
diff --git a/src/base/android/path_utils_unittest.cc b/src/base/android/path_utils_unittest.cc
new file mode 100644
index 0000000..636e3fa
--- /dev/null
+++ b/src/base/android/path_utils_unittest.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2012 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/android/path_utils.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace android {
+
+typedef testing::Test PathUtilsTest;
+
+TEST_F(PathUtilsTest, TestGetDataDirectory) {
+ // The string comes from the Java side and depends on the APK
+ // we are running in. Assumes that we are packaged in
+ // org.chromium.native_test
+ FilePath path;
+ GetDataDirectory(&path);
+ EXPECT_STREQ("/data/data/org.chromium.native_test/app_chrome",
+ path.value().c_str());
+}
+
+TEST_F(PathUtilsTest, TestGetCacheDirectory) {
+ // The string comes from the Java side and depends on the APK
+ // we are running in. Assumes that we are packaged in
+ // org.chromium.native_test
+ FilePath path;
+ GetCacheDirectory(&path);
+ EXPECT_STREQ("/data/data/org.chromium.native_test/cache",
+ path.value().c_str());
+}
+
+TEST_F(PathUtilsTest, TestGetNativeLibraryDirectory) {
+ // The string comes from the Java side and depends on the APK
+ // we are running in. Assumes that the directory contains
+ // the base tests shared object.
+ FilePath path;
+ GetNativeLibraryDirectory(&path);
+ EXPECT_TRUE(file_util::PathExists(path.Append(("libbase_unittests.so"))));
+}
+
+} // namespace android
+} // namespace base
diff --git a/src/base/android/scoped_java_ref.cc b/src/base/android/scoped_java_ref.cc
new file mode 100644
index 0000000..21b466e
--- /dev/null
+++ b/src/base/android/scoped_java_ref.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 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/android/scoped_java_ref.h"
+
+#include "base/android/jni_android.h"
+#include "base/logging.h"
+
+namespace base {
+namespace android {
+
+JavaRef<jobject>::JavaRef() : obj_(NULL) {}
+
+JavaRef<jobject>::JavaRef(JNIEnv* env, jobject obj) : obj_(obj) {
+ if (obj) {
+ DCHECK(env && env->GetObjectRefType(obj) == JNILocalRefType);
+ }
+}
+
+JavaRef<jobject>::~JavaRef() {
+}
+
+JNIEnv* JavaRef<jobject>::SetNewLocalRef(JNIEnv* env, jobject obj) {
+ if (!env) {
+ env = AttachCurrentThread();
+ } else {
+ DCHECK_EQ(env, AttachCurrentThread()); // Is |env| on correct thread.
+ }
+ if (obj)
+ obj = env->NewLocalRef(obj);
+ if (obj_)
+ env->DeleteLocalRef(obj_);
+ obj_ = obj;
+ return env;
+}
+
+void JavaRef<jobject>::SetNewGlobalRef(JNIEnv* env, jobject obj) {
+ if (!env) {
+ env = AttachCurrentThread();
+ } else {
+ DCHECK_EQ(env, AttachCurrentThread()); // Is |env| on correct thread.
+ }
+ if (obj)
+ obj = env->NewGlobalRef(obj);
+ if (obj_)
+ env->DeleteGlobalRef(obj_);
+ obj_ = obj;
+}
+
+void JavaRef<jobject>::ResetLocalRef(JNIEnv* env) {
+ if (obj_) {
+ DCHECK_EQ(env, AttachCurrentThread()); // Is |env| on correct thread.
+ env->DeleteLocalRef(obj_);
+ obj_ = NULL;
+ }
+}
+
+void JavaRef<jobject>::ResetGlobalRef() {
+ if (obj_) {
+ AttachCurrentThread()->DeleteGlobalRef(obj_);
+ obj_ = NULL;
+ }
+}
+
+jobject JavaRef<jobject>::ReleaseInternal() {
+ jobject obj = obj_;
+ obj_ = NULL;
+ return obj;
+}
+
+} // namespace android
+} // namespace base
diff --git a/src/base/android/scoped_java_ref.h b/src/base/android/scoped_java_ref.h
new file mode 100644
index 0000000..a5d71e2
--- /dev/null
+++ b/src/base/android/scoped_java_ref.h
@@ -0,0 +1,198 @@
+// Copyright (c) 2012 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_ANDROID_SCOPED_JAVA_REF_H_
+#define BASE_ANDROID_SCOPED_JAVA_REF_H_
+
+#include <jni.h>
+#include <stddef.h>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+namespace android {
+
+// Forward declare the generic java reference template class.
+template<typename T> class JavaRef;
+
+// Template specialization of JavaRef, which acts as the base class for all
+// other JavaRef<> template types. This allows you to e.g. pass
+// ScopedJavaLocalRef<jstring> into a function taking const JavaRef<jobject>&
+template<>
+class BASE_EXPORT JavaRef<jobject> {
+ public:
+ jobject obj() const { return obj_; }
+
+ bool is_null() const { return obj_ == NULL; }
+
+ protected:
+ // Initializes a NULL reference.
+ JavaRef();
+
+ // Takes ownership of the |obj| reference passed; requires it to be a local
+ // reference type.
+ JavaRef(JNIEnv* env, jobject obj);
+
+ ~JavaRef();
+
+ // The following are implementation detail convenience methods, for
+ // use by the sub-classes.
+ JNIEnv* SetNewLocalRef(JNIEnv* env, jobject obj);
+ void SetNewGlobalRef(JNIEnv* env, jobject obj);
+ void ResetLocalRef(JNIEnv* env);
+ void ResetGlobalRef();
+ jobject ReleaseInternal();
+
+ private:
+ jobject obj_;
+
+ DISALLOW_COPY_AND_ASSIGN(JavaRef);
+};
+
+// Generic base class for ScopedJavaLocalRef and ScopedJavaGlobalRef. Useful
+// for allowing functions to accept a reference without having to mandate
+// whether it is a local or global type.
+template<typename T>
+class JavaRef : public JavaRef<jobject> {
+ public:
+ T obj() const { return static_cast<T>(JavaRef<jobject>::obj()); }
+
+ protected:
+ JavaRef() {}
+ ~JavaRef() {}
+
+ JavaRef(JNIEnv* env, T obj) : JavaRef<jobject>(env, obj) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(JavaRef);
+};
+
+// Holds a local reference to a Java object. The local reference is scoped
+// to the lifetime of this object.
+// Instances of this class may hold onto any JNIEnv passed into it until
+// destroyed. Therefore, since a JNIEnv is only suitable for use on a single
+// thread, objects of this class must be created, used, and destroyed, on a
+// single thread.
+// Therefore, this class should only be used as a stack-based object and from a
+// single thread. If you wish to have the reference outlive the current
+// callstack (e.g. as a class member) or you wish to pass it across threads,
+// use a ScopedJavaGlobalRef instead.
+template<typename T>
+class ScopedJavaLocalRef : public JavaRef<T> {
+ public:
+ ScopedJavaLocalRef() : env_(NULL) {}
+
+ // Non-explicit copy constructor, to allow ScopedJavaLocalRef to be returned
+ // by value as this is the normal usage pattern.
+ ScopedJavaLocalRef(const ScopedJavaLocalRef<T>& other)
+ : env_(other.env_) {
+ this->SetNewLocalRef(env_, other.obj());
+ }
+
+ template<typename U>
+ explicit ScopedJavaLocalRef(const U& other)
+ : env_(NULL) {
+ this->Reset(other);
+ }
+
+ // Assumes that |obj| is a local reference to a Java object and takes
+ // ownership of this local reference.
+ ScopedJavaLocalRef(JNIEnv* env, T obj) : JavaRef<T>(env, obj), env_(env) {}
+
+ ~ScopedJavaLocalRef() {
+ this->Reset();
+ }
+
+ // Overloaded assignment operator defined for consistency with the implicit
+ // copy constructor.
+ void operator=(const ScopedJavaLocalRef<T>& other) {
+ this->Reset(other);
+ }
+
+ void Reset() {
+ this->ResetLocalRef(env_);
+ }
+
+ template<typename U>
+ void Reset(const ScopedJavaLocalRef<U>& other) {
+ // We can copy over env_ here as |other| instance must be from the same
+ // thread as |this| local ref. (See class comment for multi-threading
+ // limitations, and alternatives).
+ this->Reset(other.env_, other.obj());
+ }
+
+ template<typename U>
+ void Reset(const U& other) {
+ // If |env_| was not yet set (is still NULL) it will be attached to the
+ // current thread in SetNewLocalRef().
+ this->Reset(env_, other.obj());
+ }
+
+ template<typename U>
+ void Reset(JNIEnv* env, U obj) {
+ implicit_cast<T>(obj); // Ensure U is assignable to T
+ env_ = this->SetNewLocalRef(env, obj);
+ }
+
+ // Releases the local reference to the caller. The caller *must* delete the
+ // local reference when it is done with it.
+ T Release() {
+ return static_cast<T>(this->ReleaseInternal());
+ }
+
+ private:
+ // This class is only good for use on the thread it was created on so
+ // it's safe to cache the non-threadsafe JNIEnv* inside this object.
+ JNIEnv* env_;
+};
+
+// Holds a global reference to a Java object. The global reference is scoped
+// to the lifetime of this object. This class does not hold onto any JNIEnv*
+// passed to it, hence it is safe to use across threads (within the constraints
+// imposed by the underlying Java object that it references).
+template<typename T>
+class ScopedJavaGlobalRef : public JavaRef<T> {
+ public:
+ ScopedJavaGlobalRef() {}
+
+ explicit ScopedJavaGlobalRef(const ScopedJavaGlobalRef<T>& other) {
+ this->Reset(other);
+ }
+
+ template<typename U>
+ explicit ScopedJavaGlobalRef(const U& other) {
+ this->Reset(other);
+ }
+
+ ~ScopedJavaGlobalRef() {
+ this->Reset();
+ }
+
+ void Reset() {
+ this->ResetGlobalRef();
+ }
+
+ template<typename U>
+ void Reset(const U& other) {
+ this->Reset(NULL, other.obj());
+ }
+
+ template<typename U>
+ void Reset(JNIEnv* env, U obj) {
+ implicit_cast<T>(obj); // Ensure U is assignable to T
+ this->SetNewGlobalRef(env, obj);
+ }
+
+ // Releases the global reference to the caller. The caller *must* delete the
+ // global reference when it is done with it.
+ T Release() {
+ return static_cast<T>(this->ReleaseInternal());
+ }
+};
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_SCOPED_JAVA_REF_H_
diff --git a/src/base/android/scoped_java_ref_unittest.cc b/src/base/android/scoped_java_ref_unittest.cc
new file mode 100644
index 0000000..36f253c
--- /dev/null
+++ b/src/base/android/scoped_java_ref_unittest.cc
@@ -0,0 +1,122 @@
+// Copyright (c) 2012 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/android/scoped_java_ref.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace android {
+
+namespace {
+int g_local_refs = 0;
+int g_global_refs = 0;
+
+const JNINativeInterface* g_previous_functions;
+
+jobject NewGlobalRef(JNIEnv* env, jobject obj) {
+ ++g_global_refs;
+ return g_previous_functions->NewGlobalRef(env, obj);
+}
+
+void DeleteGlobalRef(JNIEnv* env, jobject obj) {
+ --g_global_refs;
+ return g_previous_functions->DeleteGlobalRef(env, obj);
+}
+
+jobject NewLocalRef(JNIEnv* env, jobject obj) {
+ ++g_local_refs;
+ return g_previous_functions->NewLocalRef(env, obj);
+}
+
+void DeleteLocalRef(JNIEnv* env, jobject obj) {
+ --g_local_refs;
+ return g_previous_functions->DeleteLocalRef(env, obj);
+}
+} // namespace
+
+class ScopedJavaRefTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ g_local_refs = 0;
+ g_global_refs = 0;
+ JNIEnv* env = AttachCurrentThread();
+ g_previous_functions = env->functions;
+ hooked_functions = *g_previous_functions;
+ env->functions = &hooked_functions;
+ // We inject our own functions in JNINativeInterface so we can keep track
+ // of the reference counting ourselves.
+ hooked_functions.NewGlobalRef = &NewGlobalRef;
+ hooked_functions.DeleteGlobalRef = &DeleteGlobalRef;
+ hooked_functions.NewLocalRef = &NewLocalRef;
+ hooked_functions.DeleteLocalRef = &DeleteLocalRef;
+ }
+
+ virtual void TearDown() {
+ JNIEnv* env = AttachCurrentThread();
+ env->functions = g_previous_functions;
+ }
+ // From JellyBean release, the instance of this struct provided in JNIEnv is
+ // read-only, so we deep copy it to allow individual functions to be hooked.
+ JNINativeInterface hooked_functions;
+};
+
+// The main purpose of this is testing the various conversions compile.
+TEST_F(ScopedJavaRefTest, Conversions) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, "string");
+ ScopedJavaGlobalRef<jstring> global(str);
+ {
+ ScopedJavaGlobalRef<jobject> global_obj(str);
+ ScopedJavaLocalRef<jobject> local_obj(global);
+ const JavaRef<jobject>& obj_ref1(str);
+ const JavaRef<jobject>& obj_ref2(global);
+ EXPECT_TRUE(env->IsSameObject(obj_ref1.obj(), obj_ref2.obj()));
+ EXPECT_TRUE(env->IsSameObject(global_obj.obj(), obj_ref2.obj()));
+ }
+ global.Reset(str);
+ const JavaRef<jstring>& str_ref = str;
+ EXPECT_EQ("string", ConvertJavaStringToUTF8(str_ref));
+ str.Reset();
+}
+
+TEST_F(ScopedJavaRefTest, RefCounts) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> str;
+ // The ConvertJavaStringToUTF8 below creates a new string that would normally
+ // return a local ref. We simulate that by starting the g_local_refs count at
+ // 1.
+ g_local_refs = 1;
+ str.Reset(ConvertUTF8ToJavaString(env, "string"));
+ EXPECT_EQ(1, g_local_refs);
+ EXPECT_EQ(0, g_global_refs);
+ {
+ ScopedJavaGlobalRef<jstring> global_str(str);
+ ScopedJavaGlobalRef<jobject> global_obj(global_str);
+ EXPECT_EQ(1, g_local_refs);
+ EXPECT_EQ(2, g_global_refs);
+
+ ScopedJavaLocalRef<jstring> str2(env, str.Release());
+ EXPECT_EQ(1, g_local_refs);
+ {
+ ScopedJavaLocalRef<jstring> str3(str2);
+ EXPECT_EQ(2, g_local_refs);
+ }
+ EXPECT_EQ(1, g_local_refs);
+ str2.Reset();
+ EXPECT_EQ(0, g_local_refs);
+ global_str.Reset();
+ EXPECT_EQ(1, g_global_refs);
+ ScopedJavaGlobalRef<jobject> global_obj2(global_obj);
+ EXPECT_EQ(2, g_global_refs);
+ }
+
+ EXPECT_EQ(0, g_local_refs);
+ EXPECT_EQ(0, g_global_refs);
+}
+
+} // namespace android
+} // namespace base
diff --git a/src/base/at_exit.cc b/src/base/at_exit.cc
new file mode 100644
index 0000000..0fba355
--- /dev/null
+++ b/src/base/at_exit.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/at_exit.h"
+
+#include <stddef.h>
+#include <ostream>
+
+#include "base/bind.h"
+#include "base/callback.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 (for a
+// statically linked version of this library). Testing may use the shadow
+// version of the constructor, and if we are building a dynamic library we may
+// end up with multiple AtExitManagers on the same process. 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_(g_top_manager) {
+// If multiple modules instantiate AtExitManagers they'll end up living in this
+// module... they have to coexist.
+#if !defined(COMPONENT_BUILD)
+ DCHECK(!g_top_manager);
+#endif
+ g_top_manager = this;
+}
+
+AtExitManager::~AtExitManager() {
+ if (!g_top_manager) {
+ NOTREACHED() << "Tried to ~AtExitManager without an AtExitManager";
+ return;
+ }
+ DCHECK_EQ(this, g_top_manager);
+
+ ProcessCallbacksNow();
+ g_top_manager = next_manager_;
+}
+
+// static
+void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) {
+ DCHECK(func);
+ RegisterTask(base::Bind(func, param));
+}
+
+// static
+void AtExitManager::RegisterTask(base::Closure task) {
+ if (!g_top_manager) {
+ NOTREACHED() << "Tried to RegisterCallback without an AtExitManager";
+ return;
+ }
+
+ AutoLock lock(g_top_manager->lock_);
+ g_top_manager->stack_.push(task);
+}
+
+// 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()) {
+ base::Closure task = g_top_manager->stack_.top();
+ task.Run();
+ g_top_manager->stack_.pop();
+ }
+}
+
+AtExitManager::AtExitManager(bool shadow) : next_manager_(g_top_manager) {
+ DCHECK(shadow || !g_top_manager);
+ g_top_manager = this;
+}
+
+} // namespace base
diff --git a/src/base/at_exit.h b/src/base/at_exit.h
new file mode 100644
index 0000000..6b28ae9
--- /dev/null
+++ b/src/base/at_exit.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_AT_EXIT_H_
+#define BASE_AT_EXIT_H_
+
+#include <stack>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/synchronization/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 BASE_EXPORT AtExitManager {
+ 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(void*).
+ static void RegisterCallback(AtExitCallbackType func, void* param);
+
+ // Registers the specified task to be called at exit.
+ static void RegisterTask(base::Closure task);
+
+ // Calls the functions registered with RegisterCallback in LIFO order. It
+ // is possible to register new callbacks after calling this function.
+ static void ProcessCallbacksNow();
+
+ 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.
+ explicit AtExitManager(bool shadow);
+
+ private:
+ base::Lock lock_;
+ std::stack<base::Closure> stack_;
+ AtExitManager* next_manager_; // Stack of managers to allow shadowing.
+
+ DISALLOW_COPY_AND_ASSIGN(AtExitManager);
+};
+
+#if defined(__LB_SHELL__) || defined(UNIT_TEST) || defined(COBALT)
+class ShadowingAtExitManager : public AtExitManager {
+ public:
+ ShadowingAtExitManager() : AtExitManager(true) {}
+};
+#endif // defined(UNIT_TEST)
+
+} // namespace base
+
+#endif // BASE_AT_EXIT_H_
diff --git a/src/base/at_exit_unittest.cc b/src/base/at_exit_unittest.cc
new file mode 100644
index 0000000..cda7340
--- /dev/null
+++ b/src/base/at_exit_unittest.cc
@@ -0,0 +1,87 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/at_exit.h"
+#include "base/bind.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+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
+
+class AtExitTest : public testing::Test {
+ private:
+ // 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.
+ base::ShadowingAtExitManager exit_manager_;
+};
+
+TEST_F(AtExitTest, Basic) {
+ 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_F(AtExitTest, LIFOOrder) {
+ 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_F(AtExitTest, Param) {
+ base::AtExitManager::RegisterCallback(&ExpectParamIsNull, NULL);
+ base::AtExitManager::RegisterCallback(&ExpectParamIsCounter,
+ &g_test_counter_1);
+ base::AtExitManager::ProcessCallbacksNow();
+}
+
+TEST_F(AtExitTest, Task) {
+ ZeroTestCounters();
+ base::AtExitManager::RegisterTask(base::Bind(&ExpectParamIsCounter,
+ &g_test_counter_1));
+ base::AtExitManager::ProcessCallbacksNow();
+}
diff --git a/src/base/atomic_ref_count.h b/src/base/atomic_ref_count.h
new file mode 100644
index 0000000..5130860
--- /dev/null
+++ b/src/base/atomic_ref_count.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is a low level implementation of atomic semantics for reference
+// counting. Please use base/memory/ref_counted.h directly instead.
+//
+// The implementation includes annotations to avoid some false positives
+// when using data race detection tools.
+
+#ifndef BASE_ATOMIC_REF_COUNT_H_
+#define BASE_ATOMIC_REF_COUNT_H_
+
+#include "base/atomicops.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.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) {
+ ANNOTATE_HAPPENS_BEFORE(ptr);
+ bool res = (subtle::Barrier_AtomicIncrement(ptr, -decrement) != 0);
+ if (!res) {
+ ANNOTATE_HAPPENS_AFTER(ptr);
+ }
+ return res;
+}
+
+// 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) {
+ bool res = (subtle::Acquire_Load(ptr) == 1);
+ if (res) {
+ ANNOTATE_HAPPENS_AFTER(ptr);
+ }
+ return res;
+}
+
+// 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) {
+ bool res = (subtle::Acquire_Load(ptr) == 0);
+ if (res) {
+ ANNOTATE_HAPPENS_AFTER(ptr);
+ }
+ return res;
+}
+
+} // namespace base
+
+#endif // BASE_ATOMIC_REF_COUNT_H_
diff --git a/src/base/atomic_sequence_num.h b/src/base/atomic_sequence_num.h
new file mode 100644
index 0000000..7bf2778
--- /dev/null
+++ b/src/base/atomic_sequence_num.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 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;
+
+// Static (POD) AtomicSequenceNumber that MUST be used in global scope (or
+// non-function scope) ONLY. This implementation does not generate any static
+// initializer. Note that it does not implement any constructor which means
+// that its fields are not initialized except when it is stored in the global
+// data section (.data in ELF). If you want to allocate an atomic sequence
+// number on the stack (or heap), please use the AtomicSequenceNumber class
+// declared below.
+class StaticAtomicSequenceNumber {
+ public:
+ inline int GetNext() {
+ return static_cast<int>(
+ base::subtle::NoBarrier_AtomicIncrement(&seq_, 1) - 1);
+ }
+
+ private:
+ friend class AtomicSequenceNumber;
+
+ inline void Reset() {
+ base::subtle::Release_Store(&seq_, 0);
+ }
+
+ base::subtle::Atomic32 seq_;
+};
+
+// AtomicSequenceNumber that can be stored and used safely (i.e. its fields are
+// always initialized as opposed to StaticAtomicSequenceNumber declared above).
+// Please use StaticAtomicSequenceNumber if you want to declare an atomic
+// sequence number in the global scope.
+class AtomicSequenceNumber {
+ public:
+ AtomicSequenceNumber() {
+ seq_.Reset();
+ }
+
+ inline int GetNext() {
+ return seq_.GetNext();
+ }
+
+ private:
+ StaticAtomicSequenceNumber seq_;
+ DISALLOW_COPY_AND_ASSIGN(AtomicSequenceNumber);
+};
+
+} // namespace base
+
+#endif // BASE_ATOMIC_SEQUENCE_NUM_H_
diff --git a/src/base/atomicops.h b/src/base/atomicops.h
new file mode 100644
index 0000000..230a601
--- /dev/null
+++ b/src/base/atomicops.h
@@ -0,0 +1,169 @@
+// Copyright (c) 2012 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 "build/build_config.h"
+
+#if (defined(OS_WIN) && defined(ARCH_CPU_64_BITS)) || defined(__LB_XB360__) || defined(__LB_XB1__)
+// windows.h #defines this (only on x64). This causes problems because the
+// public API also uses MemoryBarrier at the public name for this fence. So, on
+// X64, undef it, and call its documented
+// (http://msdn.microsoft.com/en-us/library/windows/desktop/ms684208.aspx)
+// implementation directly.
+#undef MemoryBarrier
+#endif
+
+namespace base {
+namespace subtle {
+
+typedef int32 Atomic32;
+#ifdef ARCH_CPU_64_BITS
+// We need to be able to go between Atomic64 and AtomicWord implicitly. This
+// means Atomic64 and AtomicWord should be the same type on 64-bit.
+#if defined(OS_NACL)
+// NaCl's intptr_t is not actually 64-bits on 64-bit!
+// http://code.google.com/p/nativeclient/issues/detail?id=1162
+typedef int64_t Atomic64;
+#else
+typedef intptr_t Atomic64;
+#endif
+#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 ARCH_CPU_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 // ARCH_CPU_64_BITS
+
+} // namespace base::subtle
+} // namespace base
+
+// Include our platform specific implementation.
+#if defined(THREAD_SANITIZER)
+#include "base/atomicops_internals_tsan.h"
+#elif defined(OS_WIN) && defined(COMPILER_MSVC) && defined(ARCH_CPU_X86_FAMILY)
+#include "base/atomicops_internals_x86_msvc.h"
+#elif defined(OS_MACOSX)
+#include "base/atomicops_internals_mac.h"
+#elif defined(OS_STARBOARD)
+#include "base/atomicops_internals_starboard.h"
+#elif defined(__LB_SHELL__)
+#define SHELL_BEGIN_ATOMICOPS_NAMESPACES namespace base { namespace subtle {
+#define SHELL_END_ATOMICOPS_NAMESPACES } }
+#include "atomicops_internals_shell.h" // from the platform lib
+#elif (defined(COMPILER_GCC) && defined(ARCH_CPU_ARM_FAMILY)) || \
+ defined(OS_NACL)
+#include "base/atomicops_internals_gcc.h"
+#elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY)
+#include "base/atomicops_internals_x86_gcc.h"
+#elif defined(COMPILER_GCC) && defined(ARCH_CPU_MIPS_FAMILY)
+#include "base/atomicops_internals_mips_gcc.h"
+#else
+#error "Atomic operations are not supported on your platform"
+#endif
+
+// On some platforms we need additional declarations to make
+// AtomicWord compatible with our other Atomic* types.
+#if defined(OS_MACOSX) || defined(OS_OPENBSD)
+#include "base/atomicops_internals_atomicword_compat.h"
+#endif
+
+#endif // BASE_ATOMICOPS_H_
diff --git a/src/base/atomicops_internals_atomicword_compat.h b/src/base/atomicops_internals_atomicword_compat.h
new file mode 100644
index 0000000..e02d11d
--- /dev/null
+++ b/src/base/atomicops_internals_atomicword_compat.h
@@ -0,0 +1,100 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is an internal atomic implementation, use base/atomicops.h instead.
+
+#ifndef BASE_ATOMICOPS_INTERNALS_ATOMICWORD_COMPAT_H_
+#define BASE_ATOMICOPS_INTERNALS_ATOMICWORD_COMPAT_H_
+
+// AtomicWord is a synonym for intptr_t, and Atomic32 is a synonym for int32,
+// which in turn means int. On some LP32 platforms, intptr_t is an int, but
+// on others, it's a long. When AtomicWord and Atomic32 are based on different
+// fundamental types, their pointers are incompatible.
+//
+// This file defines function overloads to allow both AtomicWord and Atomic32
+// data to be used with this interface.
+//
+// On LP64 platforms, AtomicWord and Atomic64 are both always long,
+// so this problem doesn't occur.
+
+#if !defined(ARCH_CPU_64_BITS)
+
+namespace base {
+namespace subtle {
+
+inline AtomicWord NoBarrier_CompareAndSwap(volatile AtomicWord* ptr,
+ AtomicWord old_value,
+ AtomicWord new_value) {
+ return NoBarrier_CompareAndSwap(
+ reinterpret_cast<volatile Atomic32*>(ptr), old_value, new_value);
+}
+
+inline AtomicWord NoBarrier_AtomicExchange(volatile AtomicWord* ptr,
+ AtomicWord new_value) {
+ return NoBarrier_AtomicExchange(
+ reinterpret_cast<volatile Atomic32*>(ptr), new_value);
+}
+
+inline AtomicWord NoBarrier_AtomicIncrement(volatile AtomicWord* ptr,
+ AtomicWord increment) {
+ return NoBarrier_AtomicIncrement(
+ reinterpret_cast<volatile Atomic32*>(ptr), increment);
+}
+
+inline AtomicWord Barrier_AtomicIncrement(volatile AtomicWord* ptr,
+ AtomicWord increment) {
+ return Barrier_AtomicIncrement(
+ reinterpret_cast<volatile Atomic32*>(ptr), increment);
+}
+
+inline AtomicWord Acquire_CompareAndSwap(volatile AtomicWord* ptr,
+ AtomicWord old_value,
+ AtomicWord new_value) {
+ return base::subtle::Acquire_CompareAndSwap(
+ reinterpret_cast<volatile Atomic32*>(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 Atomic32*>(ptr), old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile AtomicWord *ptr, AtomicWord value) {
+ NoBarrier_Store(
+ reinterpret_cast<volatile Atomic32*>(ptr), value);
+}
+
+inline void Acquire_Store(volatile AtomicWord* ptr, AtomicWord value) {
+ return base::subtle::Acquire_Store(
+ reinterpret_cast<volatile Atomic32*>(ptr), value);
+}
+
+inline void Release_Store(volatile AtomicWord* ptr, AtomicWord value) {
+ return base::subtle::Release_Store(
+ reinterpret_cast<volatile Atomic32*>(ptr), value);
+}
+
+inline AtomicWord NoBarrier_Load(volatile const AtomicWord *ptr) {
+ return NoBarrier_Load(
+ reinterpret_cast<volatile const Atomic32*>(ptr));
+}
+
+inline AtomicWord Acquire_Load(volatile const AtomicWord* ptr) {
+ return base::subtle::Acquire_Load(
+ reinterpret_cast<volatile const Atomic32*>(ptr));
+}
+
+inline AtomicWord Release_Load(volatile const AtomicWord* ptr) {
+ return base::subtle::Release_Load(
+ reinterpret_cast<volatile const Atomic32*>(ptr));
+}
+
+} // namespace base::subtle
+} // namespace base
+
+#endif // !defined(ARCH_CPU_64_BITS)
+
+#endif // BASE_ATOMICOPS_INTERNALS_ATOMICWORD_COMPAT_H_
diff --git a/src/base/atomicops_internals_gcc.h b/src/base/atomicops_internals_gcc.h
new file mode 100644
index 0000000..ed1b2d7
--- /dev/null
+++ b/src/base/atomicops_internals_gcc.h
@@ -0,0 +1,106 @@
+// 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, include base/atomicops.h
+// instead. This file is for platforms that use GCC intrinsics rather than
+// platform-specific assembly code for atomic operations.
+
+#ifndef BASE_ATOMICOPS_INTERNALS_GCC_H_
+#define BASE_ATOMICOPS_INTERNALS_GCC_H_
+
+namespace base {
+namespace subtle {
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ Atomic32 prev_value;
+ do {
+ if (__sync_bool_compare_and_swap(ptr, old_value, new_value))
+ 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 (!__sync_bool_compare_and_swap(ptr, old_value, new_value));
+ 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 (__sync_bool_compare_and_swap(ptr, old_value, new_value)) {
+ // 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) {
+ // Since NoBarrier_CompareAndSwap uses __sync_bool_compare_and_swap, which
+ // is a full memory barrier, none is needed here or below in Release.
+ 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() {
+ __sync_synchronize();
+}
+
+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_GCC_H_
+
diff --git a/src/base/atomicops_internals_mac.h b/src/base/atomicops_internals_mac.h
new file mode 100644
index 0000000..658ed54
--- /dev/null
+++ b/src/base/atomicops_internals_mac.h
@@ -0,0 +1,197 @@
+// Copyright (c) 2012 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_MAC_H_
+#define BASE_ATOMICOPS_INTERNALS_MAC_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,
+ reinterpret_cast<volatile int64_t*>(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,
+ reinterpret_cast<volatile int64_t*>(ptr)));
+ return old_value;
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64 *ptr,
+ Atomic64 increment) {
+ return OSAtomicAdd64(increment, reinterpret_cast<volatile int64_t*>(ptr));
+}
+
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64 *ptr,
+ Atomic64 increment) {
+ return OSAtomicAdd64Barrier(increment,
+ reinterpret_cast<volatile int64_t*>(ptr));
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ Atomic64 prev_value;
+ do {
+ if (OSAtomicCompareAndSwap64Barrier(
+ old_value, new_value, reinterpret_cast<volatile int64_t*>(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__)
+
+} // namespace base::subtle
+} // namespace base
+
+#endif // BASE_ATOMICOPS_INTERNALS_MAC_H_
diff --git a/src/base/atomicops_internals_mips_gcc.h b/src/base/atomicops_internals_mips_gcc.h
new file mode 100644
index 0000000..505597e
--- /dev/null
+++ b/src/base/atomicops_internals_mips_gcc.h
@@ -0,0 +1,161 @@
+// Copyright (c) 2012 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_MIPS_GCC_H_
+#define BASE_ATOMICOPS_INTERNALS_MIPS_GCC_H_
+
+#define ATOMICOPS_COMPILER_BARRIER() __asm__ __volatile__("" : : : "memory")
+
+namespace base {
+namespace subtle {
+
+// 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.
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ Atomic32 prev, tmp;
+ __asm__ __volatile__(".set push\n"
+ ".set noreorder\n"
+ "1:\n"
+ "ll %0, %5\n" // prev = *ptr
+ "bne %0, %3, 2f\n" // if (prev != old_value) goto 2
+ "move %2, %4\n" // tmp = new_value
+ "sc %2, %1\n" // *ptr = tmp (with atomic check)
+ "beqz %2, 1b\n" // start again on atomic error
+ "nop\n" // delay slot nop
+ "2:\n"
+ ".set pop\n"
+ : "=&r" (prev), "=m" (*ptr), "=&r" (tmp)
+ : "Ir" (old_value), "r" (new_value), "m" (*ptr)
+ : "memory");
+ return prev;
+}
+
+// Atomically store new_value into *ptr, returning the previous value held in
+// *ptr. This routine implies no memory barriers.
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+ Atomic32 new_value) {
+ Atomic32 temp, old;
+ __asm__ __volatile__(".set push\n"
+ ".set noreorder\n"
+ "1:\n"
+ "ll %1, %2\n" // old = *ptr
+ "move %0, %3\n" // temp = new_value
+ "sc %0, %2\n" // *ptr = temp (with atomic check)
+ "beqz %0, 1b\n" // start again on atomic error
+ "nop\n" // delay slot nop
+ ".set pop\n"
+ : "=&r" (temp), "=&r" (old), "=m" (*ptr)
+ : "r" (new_value), "m" (*ptr)
+ : "memory");
+
+ return old;
+}
+
+// Atomically increment *ptr by "increment". Returns the new value of
+// *ptr with the increment applied. This routine implies no memory barriers.
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ Atomic32 temp, temp2;
+
+ __asm__ __volatile__(".set push\n"
+ ".set noreorder\n"
+ "1:\n"
+ "ll %0, %2\n" // temp = *ptr
+ "addu %1, %0, %3\n" // temp2 = temp + increment
+ "sc %1, %2\n" // *ptr = temp2 (with atomic check)
+ "beqz %1, 1b\n" // start again on atomic error
+ "addu %1, %0, %3\n" // temp2 = temp + increment
+ ".set pop\n"
+ : "=&r" (temp), "=&r" (temp2), "=m" (*ptr)
+ : "Ir" (increment), "m" (*ptr)
+ : "memory");
+ // temp2 now holds the final value.
+ return temp2;
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ ATOMICOPS_COMPILER_BARRIER();
+ Atomic32 res = NoBarrier_AtomicIncrement(ptr, increment);
+ ATOMICOPS_COMPILER_BARRIER();
+ return res;
+}
+
+// "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.
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ ATOMICOPS_COMPILER_BARRIER();
+ Atomic32 res = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+ ATOMICOPS_COMPILER_BARRIER();
+ return res;
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ ATOMICOPS_COMPILER_BARRIER();
+ Atomic32 res = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+ ATOMICOPS_COMPILER_BARRIER();
+ return res;
+}
+
+inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
+ *ptr = value;
+}
+
+inline void MemoryBarrier() {
+ __asm__ __volatile__("sync" : : : "memory");
+}
+
+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
+
+#undef ATOMICOPS_COMPILER_BARRIER
+
+#endif // BASE_ATOMICOPS_INTERNALS_MIPS_GCC_H_
diff --git a/src/base/atomicops_internals_starboard.h b/src/base/atomicops_internals_starboard.h
new file mode 100644
index 0000000..cb1c759
--- /dev/null
+++ b/src/base/atomicops_internals_starboard.h
@@ -0,0 +1,151 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This file is an internal atomic implementation, use base/atomicops.h instead.
+
+// This is the Starboard implementation, which defers all specific
+// implementation decisions for atomic operations to the Starboard port.
+
+#ifndef BASE_ATOMICOPS_INTERNALS_STARBOARD_H_
+#define BASE_ATOMICOPS_INTERNALS_STARBOARD_H_
+
+#include "starboard/atomic.h"
+
+namespace base {
+namespace subtle {
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ return SbAtomicNoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32 *ptr,
+ Atomic32 new_value) {
+ return SbAtomicNoBarrier_Exchange(ptr, new_value);
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32 *ptr,
+ Atomic32 increment) {
+ return SbAtomicNoBarrier_Increment(ptr, increment);
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32 *ptr,
+ Atomic32 increment) {
+ return SbAtomicBarrier_Increment(ptr, increment);
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32 *ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ return SbAtomicAcquire_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32 *ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ return SbAtomicRelease_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile Atomic32 *ptr, Atomic32 value) {
+ SbAtomicNoBarrier_Store(ptr, value);
+}
+
+inline void MemoryBarrier() {
+ SbAtomicMemoryBarrier();
+}
+
+inline void Acquire_Store(volatile Atomic32 *ptr, Atomic32 value) {
+ SbAtomicAcquire_Store(ptr, value);
+}
+
+inline void Release_Store(volatile Atomic32 *ptr, Atomic32 value) {
+ SbAtomicRelease_Store(ptr, value);
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32 *ptr) {
+ return SbAtomicNoBarrier_Load(ptr);
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32 *ptr) {
+ return SbAtomicAcquire_Load(ptr);
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32 *ptr) {
+ return SbAtomicRelease_Load(ptr);
+}
+
+#if SB_IS(64_BIT)
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64 *ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ return SbAtomicNoBarrier_CompareAndSwap64(ptr, old_value, new_value);
+}
+
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64 *ptr,
+ Atomic64 new_value) {
+ return SbAtomicNoBarrier_Exchange64(ptr, new_value);
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64 *ptr,
+ Atomic64 increment) {
+ return SbAtomicNoBarrier_Increment64(ptr, increment);
+}
+
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64 *ptr,
+ Atomic64 increment) {
+ return SbAtomicBarrier_Increment64(ptr, increment);
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ return SbAtomicAcquire_CompareAndSwap64(ptr, old_value, new_value);
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64 *ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ return SbAtomicRelease_CompareAndSwap64(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile Atomic64 *ptr, Atomic64 value) {
+ SbAtomicNoBarrier_Store64(ptr, value);
+}
+
+inline void Acquire_Store(volatile Atomic64 *ptr, Atomic64 value) {
+ SbAtomicAcquire_Store64(ptr, value);
+}
+
+inline void Release_Store(volatile Atomic64 *ptr, Atomic64 value) {
+ SbAtomicRelease_Store64(ptr, value);
+}
+
+inline Atomic64 NoBarrier_Load(volatile const Atomic64 *ptr) {
+ return SbAtomicNoBarrier_Load64(ptr);
+}
+
+inline Atomic64 Acquire_Load(volatile const Atomic64 *ptr) {
+ return SbAtomicAcquire_Load64(ptr);
+}
+
+inline Atomic64 Release_Load(volatile const Atomic64 *ptr) {
+ return SbAtomicRelease_Load64(ptr);
+}
+#endif // SB_IS(64_BIT)
+
+} // namespace base::subtle
+} // namespace base
+
+#endif // BASE_ATOMICOPS_INTERNALS_STARBOARD_H_
diff --git a/src/base/atomicops_internals_tsan.h b/src/base/atomicops_internals_tsan.h
new file mode 100644
index 0000000..44d6400
--- /dev/null
+++ b/src/base/atomicops_internals_tsan.h
@@ -0,0 +1,378 @@
+// Copyright (c) 2012 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 for compiler-based
+// ThreadSanitizer. Use base/atomicops.h instead.
+
+#ifndef BASE_ATOMICOPS_INTERNALS_TSAN_H_
+#define BASE_ATOMICOPS_INTERNALS_TSAN_H_
+
+#include "base/base_export.h"
+
+// This struct is not part of the public API of this module; clients may not
+// use it. (However, it's exported via BASE_EXPORT because clients implicitly
+// do use it at link time by inlining these functions.)
+// 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.
+};
+BASE_EXPORT extern struct AtomicOps_x86CPUFeatureStruct
+ AtomicOps_Internalx86CPUFeatures;
+
+#define ATOMICOPS_COMPILER_BARRIER() __asm__ __volatile__("" : : : "memory")
+
+namespace base {
+namespace subtle {
+
+#ifndef TSAN_INTERFACE_ATOMIC_H
+#define TSAN_INTERFACE_ATOMIC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef char __tsan_atomic8;
+typedef short __tsan_atomic16; // NOLINT
+typedef int __tsan_atomic32;
+typedef long __tsan_atomic64; // NOLINT
+
+#if defined(__SIZEOF_INT128__) \
+ || (__clang_major__ * 100 + __clang_minor__ >= 302)
+typedef __int128 __tsan_atomic128;
+#define __TSAN_HAS_INT128 1
+#else
+typedef char __tsan_atomic128;
+#define __TSAN_HAS_INT128 0
+#endif
+
+typedef enum {
+ __tsan_memory_order_relaxed,
+ __tsan_memory_order_consume,
+ __tsan_memory_order_acquire,
+ __tsan_memory_order_release,
+ __tsan_memory_order_acq_rel,
+ __tsan_memory_order_seq_cst,
+} __tsan_memory_order;
+
+__tsan_atomic8 __tsan_atomic8_load(const volatile __tsan_atomic8 *a,
+ __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_load(const volatile __tsan_atomic16 *a,
+ __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_load(const volatile __tsan_atomic32 *a,
+ __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_load(const volatile __tsan_atomic64 *a,
+ __tsan_memory_order mo);
+__tsan_atomic128 __tsan_atomic128_load(const volatile __tsan_atomic128 *a,
+ __tsan_memory_order mo);
+
+void __tsan_atomic8_store(volatile __tsan_atomic8 *a, __tsan_atomic8 v,
+ __tsan_memory_order mo);
+void __tsan_atomic16_store(volatile __tsan_atomic16 *a, __tsan_atomic16 v,
+ __tsan_memory_order mo);
+void __tsan_atomic32_store(volatile __tsan_atomic32 *a, __tsan_atomic32 v,
+ __tsan_memory_order mo);
+void __tsan_atomic64_store(volatile __tsan_atomic64 *a, __tsan_atomic64 v,
+ __tsan_memory_order mo);
+void __tsan_atomic128_store(volatile __tsan_atomic128 *a, __tsan_atomic128 v,
+ __tsan_memory_order mo);
+
+__tsan_atomic8 __tsan_atomic8_exchange(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 v, __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_exchange(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 v, __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_exchange(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 v, __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_exchange(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo);
+__tsan_atomic128 __tsan_atomic128_exchange(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, __tsan_memory_order mo);
+
+__tsan_atomic8 __tsan_atomic8_fetch_add(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 v, __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_fetch_add(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 v, __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_fetch_add(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 v, __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_fetch_add(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo);
+__tsan_atomic128 __tsan_atomic128_fetch_add(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, __tsan_memory_order mo);
+
+__tsan_atomic8 __tsan_atomic8_fetch_and(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 v, __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_fetch_and(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 v, __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_fetch_and(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 v, __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_fetch_and(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo);
+__tsan_atomic128 __tsan_atomic128_fetch_and(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, __tsan_memory_order mo);
+
+__tsan_atomic8 __tsan_atomic8_fetch_or(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 v, __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_fetch_or(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 v, __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_fetch_or(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 v, __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_fetch_or(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo);
+__tsan_atomic128 __tsan_atomic128_fetch_or(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, __tsan_memory_order mo);
+
+__tsan_atomic8 __tsan_atomic8_fetch_xor(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 v, __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_fetch_xor(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 v, __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_fetch_xor(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 v, __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_fetch_xor(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo);
+__tsan_atomic128 __tsan_atomic128_fetch_xor(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, __tsan_memory_order mo);
+
+__tsan_atomic8 __tsan_atomic8_fetch_nand(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 v, __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_fetch_nand(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 v, __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_fetch_nand(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 v, __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_fetch_nand(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo);
+__tsan_atomic128 __tsan_atomic128_fetch_nand(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, __tsan_memory_order mo);
+
+int __tsan_atomic8_compare_exchange_weak(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 *c, __tsan_atomic8 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+int __tsan_atomic16_compare_exchange_weak(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 *c, __tsan_atomic16 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+int __tsan_atomic32_compare_exchange_weak(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 *c, __tsan_atomic32 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+int __tsan_atomic64_compare_exchange_weak(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 *c, __tsan_atomic64 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+int __tsan_atomic128_compare_exchange_weak(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 *c, __tsan_atomic128 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+
+int __tsan_atomic8_compare_exchange_strong(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 *c, __tsan_atomic8 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+int __tsan_atomic16_compare_exchange_strong(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 *c, __tsan_atomic16 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+int __tsan_atomic32_compare_exchange_strong(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 *c, __tsan_atomic32 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+int __tsan_atomic64_compare_exchange_strong(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 *c, __tsan_atomic64 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+int __tsan_atomic128_compare_exchange_strong(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 *c, __tsan_atomic128 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+
+__tsan_atomic8 __tsan_atomic8_compare_exchange_val(
+ volatile __tsan_atomic8 *a, __tsan_atomic8 c, __tsan_atomic8 v,
+ __tsan_memory_order mo, __tsan_memory_order fail_mo);
+__tsan_atomic16 __tsan_atomic16_compare_exchange_val(
+ volatile __tsan_atomic16 *a, __tsan_atomic16 c, __tsan_atomic16 v,
+ __tsan_memory_order mo, __tsan_memory_order fail_mo);
+__tsan_atomic32 __tsan_atomic32_compare_exchange_val(
+ volatile __tsan_atomic32 *a, __tsan_atomic32 c, __tsan_atomic32 v,
+ __tsan_memory_order mo, __tsan_memory_order fail_mo);
+__tsan_atomic64 __tsan_atomic64_compare_exchange_val(
+ volatile __tsan_atomic64 *a, __tsan_atomic64 c, __tsan_atomic64 v,
+ __tsan_memory_order mo, __tsan_memory_order fail_mo);
+__tsan_atomic128 __tsan_atomic128_compare_exchange_val(
+ volatile __tsan_atomic128 *a, __tsan_atomic128 c, __tsan_atomic128 v,
+ __tsan_memory_order mo, __tsan_memory_order fail_mo);
+
+void __tsan_atomic_thread_fence(__tsan_memory_order mo);
+void __tsan_atomic_signal_fence(__tsan_memory_order mo);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // #ifndef TSAN_INTERFACE_ATOMIC_H
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ Atomic32 cmp = old_value;
+ __tsan_atomic32_compare_exchange_strong(ptr, &cmp, new_value,
+ __tsan_memory_order_relaxed, __tsan_memory_order_relaxed);
+ return cmp;
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32 *ptr,
+ Atomic32 new_value) {
+ return __tsan_atomic32_exchange(ptr, new_value,
+ __tsan_memory_order_relaxed);
+}
+
+inline Atomic32 Acquire_AtomicExchange(volatile Atomic32 *ptr,
+ Atomic32 new_value) {
+ return __tsan_atomic32_exchange(ptr, new_value,
+ __tsan_memory_order_acquire);
+}
+
+inline Atomic32 Release_AtomicExchange(volatile Atomic32 *ptr,
+ Atomic32 new_value) {
+ return __tsan_atomic32_exchange(ptr, new_value,
+ __tsan_memory_order_release);
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32 *ptr,
+ Atomic32 increment) {
+ return increment + __tsan_atomic32_fetch_add(ptr, increment,
+ __tsan_memory_order_relaxed);
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32 *ptr,
+ Atomic32 increment) {
+ return increment + __tsan_atomic32_fetch_add(ptr, increment,
+ __tsan_memory_order_acq_rel);
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32 *ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ Atomic32 cmp = old_value;
+ __tsan_atomic32_compare_exchange_strong(ptr, &cmp, new_value,
+ __tsan_memory_order_acquire, __tsan_memory_order_acquire);
+ return cmp;
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32 *ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ Atomic32 cmp = old_value;
+ __tsan_atomic32_compare_exchange_strong(ptr, &cmp, new_value,
+ __tsan_memory_order_release, __tsan_memory_order_relaxed);
+ return cmp;
+}
+
+inline void NoBarrier_Store(volatile Atomic32 *ptr, Atomic32 value) {
+ __tsan_atomic32_store(ptr, value, __tsan_memory_order_relaxed);
+}
+
+inline void Acquire_Store(volatile Atomic32 *ptr, Atomic32 value) {
+ __tsan_atomic32_store(ptr, value, __tsan_memory_order_relaxed);
+ __tsan_atomic_thread_fence(__tsan_memory_order_seq_cst);
+}
+
+inline void Release_Store(volatile Atomic32 *ptr, Atomic32 value) {
+ __tsan_atomic32_store(ptr, value, __tsan_memory_order_release);
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32 *ptr) {
+ return __tsan_atomic32_load(ptr, __tsan_memory_order_relaxed);
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32 *ptr) {
+ return __tsan_atomic32_load(ptr, __tsan_memory_order_acquire);
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32 *ptr) {
+ __tsan_atomic_thread_fence(__tsan_memory_order_seq_cst);
+ return __tsan_atomic32_load(ptr, __tsan_memory_order_relaxed);
+}
+
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64 *ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ Atomic64 cmp = old_value;
+ __tsan_atomic64_compare_exchange_strong(ptr, &cmp, new_value,
+ __tsan_memory_order_relaxed, __tsan_memory_order_relaxed);
+ return cmp;
+}
+
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64 *ptr,
+ Atomic64 new_value) {
+ return __tsan_atomic64_exchange(ptr, new_value, __tsan_memory_order_relaxed);
+}
+
+inline Atomic64 Acquire_AtomicExchange(volatile Atomic64 *ptr,
+ Atomic64 new_value) {
+ return __tsan_atomic64_exchange(ptr, new_value, __tsan_memory_order_acquire);
+}
+
+inline Atomic64 Release_AtomicExchange(volatile Atomic64 *ptr,
+ Atomic64 new_value) {
+ return __tsan_atomic64_exchange(ptr, new_value, __tsan_memory_order_release);
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64 *ptr,
+ Atomic64 increment) {
+ return increment + __tsan_atomic64_fetch_add(ptr, increment,
+ __tsan_memory_order_relaxed);
+}
+
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64 *ptr,
+ Atomic64 increment) {
+ return increment + __tsan_atomic64_fetch_add(ptr, increment,
+ __tsan_memory_order_acq_rel);
+}
+
+inline void NoBarrier_Store(volatile Atomic64 *ptr, Atomic64 value) {
+ __tsan_atomic64_store(ptr, value, __tsan_memory_order_relaxed);
+}
+
+inline void Acquire_Store(volatile Atomic64 *ptr, Atomic64 value) {
+ __tsan_atomic64_store(ptr, value, __tsan_memory_order_relaxed);
+ __tsan_atomic_thread_fence(__tsan_memory_order_seq_cst);
+}
+
+inline void Release_Store(volatile Atomic64 *ptr, Atomic64 value) {
+ __tsan_atomic64_store(ptr, value, __tsan_memory_order_release);
+}
+
+inline Atomic64 NoBarrier_Load(volatile const Atomic64 *ptr) {
+ return __tsan_atomic64_load(ptr, __tsan_memory_order_relaxed);
+}
+
+inline Atomic64 Acquire_Load(volatile const Atomic64 *ptr) {
+ return __tsan_atomic64_load(ptr, __tsan_memory_order_acquire);
+}
+
+inline Atomic64 Release_Load(volatile const Atomic64 *ptr) {
+ __tsan_atomic_thread_fence(__tsan_memory_order_seq_cst);
+ return __tsan_atomic64_load(ptr, __tsan_memory_order_relaxed);
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ Atomic64 cmp = old_value;
+ __tsan_atomic64_compare_exchange_strong(ptr, &cmp, new_value,
+ __tsan_memory_order_acquire, __tsan_memory_order_acquire);
+ return cmp;
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64 *ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ Atomic64 cmp = old_value;
+ __tsan_atomic64_compare_exchange_strong(ptr, &cmp, new_value,
+ __tsan_memory_order_release, __tsan_memory_order_relaxed);
+ return cmp;
+}
+
+inline void MemoryBarrier() {
+ __tsan_atomic_thread_fence(__tsan_memory_order_seq_cst);
+}
+
+} // namespace base::subtle
+} // namespace base
+
+#undef ATOMICOPS_COMPILER_BARRIER
+
+#endif // BASE_ATOMICOPS_INTERNALS_TSAN_H_
diff --git a/src/base/atomicops_internals_x86_gcc.cc b/src/base/atomicops_internals_x86_gcc.cc
new file mode 100644
index 0000000..933ca51
--- /dev/null
+++ b/src/base/atomicops_internals_x86_gcc.cc
@@ -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_
diff --git a/src/base/atomicops_internals_x86_gcc.h b/src/base/atomicops_internals_x86_gcc.h
new file mode 100644
index 0000000..ac02b17
--- /dev/null
+++ b/src/base/atomicops_internals_x86_gcc.h
@@ -0,0 +1,269 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// 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_
+
+#include "base/base_export.h"
+
+// This struct is not part of the public API of this module; clients may not
+// use it. (However, it's exported via BASE_EXPORT because clients implicitly
+// do use it at link time by inlining these functions.)
+// 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.
+};
+BASE_EXPORT 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;
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ Atomic64 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+ if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
+ __asm__ __volatile__("lfence" : : : "memory");
+ }
+ return x;
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+#endif // defined(__x86_64__)
+
+} // namespace base::subtle
+} // namespace base
+
+#undef ATOMICOPS_COMPILER_BARRIER
+
+#endif // BASE_ATOMICOPS_INTERNALS_X86_GCC_H_
diff --git a/src/base/atomicops_internals_x86_msvc.h b/src/base/atomicops_internals_x86_msvc.h
new file mode 100644
index 0000000..2f428b6
--- /dev/null
+++ b/src/base/atomicops_internals_x86_msvc.h
@@ -0,0 +1,224 @@
+// 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_
+
+#if defined(__LB_XB1__) && !defined(WIN32)
+# include <intrin.h>
+// These are normally defined by windows.h:
+typedef long LONG;
+typedef long long LONGLONG;
+typedef void* PVOID;
+void __faststorefence(void);
+#pragma intrinsic(__faststorefence)
+inline void MemoryBarrier() {
+ __faststorefence();
+}
+#define InterlockedCompareExchange _InterlockedCompareExchange
+#define InterlockedCompareExchangePointer _InterlockedCompareExchangePointer
+#define InterlockedExchangePointer _InterlockedExchangePointer
+#define InterlockedExchange _InterlockedExchange
+#define InterlockedExchangeAdd _InterlockedExchangeAdd
+#define InterlockedExchangeAdd64 _InterlockedExchangeAdd64
+
+#else
+# include <windows.h>
+
+#if defined(ARCH_CPU_64_BITS) || defined(__LB_XB360__)
+// windows.h #defines this (only on x64). This causes problems because the
+// public API also uses MemoryBarrier at the public name for this fence. So, on
+// X64, undef it, and call its documented
+// (http://msdn.microsoft.com/en-us/library/windows/desktop/ms684208.aspx)
+// implementation directly.
+#undef MemoryBarrier
+#endif
+
+#endif
+
+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() {
+#if defined(ARCH_CPU_64_BITS) || defined(__LB_XB360__)
+ // See #undef and note at the top of this file.
+ __faststorefence();
+#else
+ // We use MemoryBarrier from WinNT.h
+ ::MemoryBarrier();
+#endif
+}
+
+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;
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+
+#endif // defined(_WIN64)
+
+} // namespace base::subtle
+} // namespace base
+
+#if defined(__LB_XB1__)
+#undef InterlockedCompareExchange
+#undef InterlockedCompareExchangePointer
+#undef InterlockedExchangePointer
+#undef InterlockedExchange
+#undef InterlockedExchangeAdd
+#undef InterlockedExchangeAdd64
+#endif
+
+#endif // BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_
diff --git a/src/base/atomicops_unittest.cc b/src/base/atomicops_unittest.cc
new file mode 100644
index 0000000..d73a098
--- /dev/null
+++ b/src/base/atomicops_unittest.cc
@@ -0,0 +1,241 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/atomicops.h"
+
+#include <string.h>
+
+#include "base/port.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>();
+}
diff --git a/src/base/auto_reset.h b/src/base/auto_reset.h
new file mode 100644
index 0000000..32f138e
--- /dev/null
+++ b/src/base/auto_reset.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_AUTO_RESET_H_
+#define BASE_AUTO_RESET_H_
+
+#include "base/basictypes.h"
+
+// base::AutoReset<> is useful for setting a variable to a new value only within
+// a particular scope. An base::AutoReset<> object resets a variable to its
+// original value upon destruction, making it an alternative to writing
+// "var = false;" or "var = old_val;" at all of a block's exit points.
+//
+// This should be obvious, but note that an base::AutoReset<> instance should
+// have a shorter lifetime than its scoped_variable, to prevent invalid memory
+// writes when the base::AutoReset<> object is destroyed.
+
+namespace base {
+
+template<typename T>
+class AutoReset {
+ public:
+ AutoReset(T* scoped_variable, T new_value)
+ : scoped_variable_(scoped_variable),
+ original_value_(*scoped_variable) {
+ *scoped_variable_ = new_value;
+ }
+
+ ~AutoReset() { *scoped_variable_ = original_value_; }
+
+ private:
+ T* scoped_variable_;
+ T original_value_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutoReset);
+};
+
+}
+
+#endif // BASE_AUTO_RESET_H_
diff --git a/src/base/base.gyp b/src/base/base.gyp
new file mode 100644
index 0000000..3838ff6
--- /dev/null
+++ b/src/base/base.gyp
@@ -0,0 +1,1322 @@
+# Copyright (c) 2012 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/win_precompile.gypi',
+ 'base.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'base',
+ 'type': '<(component)',
+ 'toolsets': ['host', 'target'],
+ 'variables': {
+ 'base_target': 1,
+ 'enable_wexit_time_destructors': 1,
+ 'optimize': 'max',
+ },
+ 'dependencies': [
+ 'base_static',
+ 'allocator/allocator.gyp:allocator_extension_thunks',
+ '../testing/gtest.gyp:gtest_prod',
+ '../third_party/modp_b64/modp_b64.gyp:modp_b64',
+ 'third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ ],
+ # TODO(gregoryd): direct_dependent_settings should be shared with the
+ # 64-bit target, but it doesn't work due to a bug in gyp
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ 'conditions': [
+ ['use_glib==1', {
+ 'conditions': [
+ ['chromeos==1', {
+ 'sources/': [ ['include', '_chromeos\\.cc$'] ]
+ }],
+ ['linux_use_tcmalloc==0', {
+ 'defines': [
+ 'NO_TCMALLOC',
+ ],
+ 'direct_dependent_settings': {
+ 'defines': [
+ 'NO_TCMALLOC',
+ ],
+ },
+ }],
+ ['toolkit_uses_gtk==1', {
+ 'dependencies': [
+ '../build/linux/system.gyp:gtk',
+ ],
+ 'export_dependent_settings': [
+ '../build/linux/system.gyp:gtk',
+ ],
+ }],
+ ],
+ 'dependencies': [
+ 'symbolize',
+ '../build/linux/system.gyp:glib',
+ '../build/linux/system.gyp:x11',
+ 'xdg_mime',
+ ],
+ 'defines': [
+ 'USE_SYMBOLIZE',
+ ],
+ 'cflags': [
+ '-Wno-write-strings',
+ ],
+ 'export_dependent_settings': [
+ '../build/linux/system.gyp:glib',
+ '../build/linux/system.gyp:x11',
+ ],
+ }, { # use_glib!=1
+ 'sources/': [
+ ['exclude', '/xdg_user_dirs/'],
+ ['exclude', '_nss\\.cc$'],
+ ],
+ }],
+ ['OS == "android" and _toolset == "host"', {
+ # Base for host support is the minimum required to run the
+ # ssl false start blacklist tool. It requires further changes
+ # to generically support host builds (and tests).
+ # Note: when building for host, gyp has OS == "android",
+ # hence the *_android.cc files are included but the actual code
+ # doesn't have OS_ANDROID / ANDROID defined.
+ 'conditions': [
+ # Host build on linux depends on system.gyp::gtk as
+ # default linux build has TOOLKIT_GTK defined.
+ ['host_os == "linux"', {
+ 'sources/': [
+ ['include', '^atomicops_internals_x86_gcc\\.cc$'],
+ ],
+ 'dependencies': [
+ '../build/linux/system.gyp:gtk',
+ ],
+ 'export_dependent_settings': [
+ '../build/linux/system.gyp:gtk',
+ ],
+ }],
+ ['host_os == "mac"', {
+ 'sources/': [
+ ['exclude', '^native_library_linux\\.cc$'],
+ ['exclude', '^process_util_linux\\.cc$'],
+ ['exclude', '^sys_info_linux\\.cc$'],
+ ['exclude', '^sys_string_conversions_linux\\.cc$'],
+ ['exclude', '^worker_pool_linux\\.cc$'],
+ ],
+ }],
+ ],
+ }],
+ ['(OS == "android" or ((OS == "lb_shell" or OS == "starboard") and target_arch == "android")) and _toolset == "target"', {
+ 'dependencies': [
+ 'base_jni_headers',
+ 'symbolize',
+ '../third_party/ashmem/ashmem.gyp:ashmem',
+ '../third_party/icu/icu.gyp:icuuc',
+ ],
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)/base',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '-llog',
+ ],
+ },
+ 'defines': [
+ 'USE_SYMBOLIZE',
+ ],
+ 'sources!': [
+ 'debug/stack_trace_posix.cc',
+ ],
+ 'conditions' : [
+ ['target_arch == "ia32"', {
+ 'sources/': [
+ ['include', '^atomicops_internals_x86_gcc\\.cc$'],
+ ],
+ }],
+ ['target_arch != "android"', {
+ 'includes': [
+ '../build/android/cpufeatures.gypi',
+ ],
+ }],
+ ],
+ }],
+ ['(OS == "android" or ((OS == "lb_shell" or OS == "starboard") and target_arch == "android")) and _toolset == "target" and android_build_type == 0', {
+ 'dependencies': [
+ 'base_java',
+ ],
+ }],
+ ['((OS == "lb_shell" or OS == "starboard") and target_arch == "android")', {
+ 'link_settings': {
+ 'libraries': [
+ # for android_getCpuCount
+ '-lportable',
+ ],
+ },
+ }],
+ ['os_bsd==1', {
+ 'include_dirs': [
+ '/usr/local/include',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '-L/usr/local/lib -lexecinfo',
+ ],
+ },
+ }],
+ ['OS == "linux"', {
+ 'link_settings': {
+ 'libraries': [
+ # We need rt for clock_gettime().
+ '-lrt',
+ # For 'native_library_linux.cc'
+ '-ldl',
+ ],
+ },
+ }],
+ ['OS == "mac"', {
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/AppKit.framework',
+ '$(SDKROOT)/System/Library/Frameworks/ApplicationServices.framework',
+ '$(SDKROOT)/System/Library/Frameworks/Carbon.framework',
+ '$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/IOKit.framework',
+ '$(SDKROOT)/System/Library/Frameworks/Security.framework',
+ ],
+ },
+ 'dependencies': [
+ '../third_party/mach_override/mach_override.gyp:mach_override',
+ ],
+ }],
+ ['OS == "ios"', {
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/CoreGraphics.framework',
+ '$(SDKROOT)/System/Library/Frameworks/CoreText.framework',
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/UIKit.framework',
+ ],
+ },
+ }],
+ ['OS != "win" and OS != "ios"', {
+ 'dependencies': ['../third_party/libevent/libevent.gyp:libevent'],
+ },],
+ ['component=="shared_library"', {
+ 'conditions': [
+ ['OS=="win"', {
+ 'sources!': [
+ 'debug/debug_on_start_win.cc',
+ ],
+ }],
+ ],
+ }],
+ ['OS == "lb_shell"', {
+ 'conditions': [
+ ['target_arch != "android"', {
+ 'dependencies!': [
+ '../third_party/libevent/libevent.gyp:libevent'
+ ],
+ }],
+ ['target_arch == "linux"', {
+ 'defines': [
+ 'USE_SYMBOLIZE',
+ ],
+ 'dependencies': [
+ 'symbolize',
+ ],
+ }],
+ ['target_arch == "ps3" and _toolset == "target"', {
+ # Script for resolving symbols, for use by stack_trace_ps3.cc
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)',
+ 'files': [
+ 'debug/addr2line_ps3.py',
+ ],
+ },
+ ],
+ 'dependencies': [
+ # On PS3, we use both posix emulation and starboard for
+ # threading. We depend on starboard for its TLS implementation.
+ # Eventually this will replace the posix_emulation below.
+ '<(DEPTH)/starboard/starboard.gyp:starboard',
+ ],
+ }],
+ # toolset can be host or target.
+ # (host in the case of e.g. protobuf compiler.)
+ # We only want posix_emulation for target builds.
+ ['_toolset == "target"', {
+ 'dependencies': [
+ '<(lbshell_root)/build/projects/posix_emulation.gyp:posix_emulation',
+ ],
+ }], # _toolset == "target"
+ ],
+ 'sources': [
+ 'debug/debugger_shell.cc',
+ 'debug/stack_trace_shell.cc',
+ 'guid_shell.cc',
+ 'safe_strerror_shell.cc',
+ 'sys_string_conversions_shell.cc',
+ 'test/test_file_util_shell.cc',
+ 'threading/thread_local_shell.cc',
+ 'threading/thread_local_storage_shell.cc',
+ 'threading/worker_pool_shell.cc',
+ ],
+ }], # OS=="lb_shell"
+ ['OS == "starboard"', {
+ 'conditions': [
+ ['_toolset == "target"', {
+ 'dependencies': [
+ '<(DEPTH)/starboard/starboard.gyp:starboard',
+ '<(DEPTH)/starboard/client_porting/eztime/eztime.gyp:eztime',
+ ],
+ }], # _toolset == "target"
+ ],
+ 'dependencies!': [
+ '../third_party/libevent/libevent.gyp:libevent'
+ ],
+ 'sources': [
+ 'base_paths_starboard.cc',
+ 'test/test_file_util_starboard.cc',
+ ],
+ }], # OS=="starboard"
+ ['OS == "lb_shell" or OS == "starboard"', {
+ 'sources!': [
+ 'file_descriptor_shuffle.cc',
+ ],
+ }], # OS == "lb_shell" or OS == "starboard"
+ ],
+ 'sources': [
+ 'third_party/nspr/prcpucfg.h',
+ 'third_party/nspr/prcpucfg_win.h',
+ 'third_party/nspr/prtypes.h',
+ 'third_party/xdg_user_dirs/xdg_user_dir_lookup.cc',
+ 'third_party/xdg_user_dirs/xdg_user_dir_lookup.h',
+ 'auto_reset.h',
+ 'event_recorder.h',
+ 'event_recorder_stubs.cc',
+ 'event_recorder_win.cc',
+ 'linux_util.cc',
+ 'linux_util.h',
+ 'md5.cc',
+ 'md5.h',
+ 'message_pump_android.cc',
+ 'message_pump_android.h',
+ 'message_pump_glib.cc',
+ 'message_pump_glib.h',
+ 'message_pump_gtk.cc',
+ 'message_pump_gtk.h',
+ 'message_pump_io_ios.cc',
+ 'message_pump_io_ios.h',
+ 'message_pump_observer.h',
+ 'message_pump_aurax11.cc',
+ 'message_pump_aurax11.h',
+ 'message_pump_libevent.cc',
+ 'message_pump_libevent.h',
+ 'message_pump_mac.h',
+ 'message_pump_mac.mm',
+ 'metrics/field_trial.cc',
+ 'metrics/field_trial.h',
+ 'posix/file_descriptor_shuffle.cc',
+ 'posix/file_descriptor_shuffle.h',
+ 'sync_socket.h',
+ 'sync_socket_win.cc',
+ 'sync_socket_posix.cc',
+ ],
+ },
+ {
+ 'target_name': 'base_i18n',
+ 'type': '<(component)',
+ 'variables': {
+ 'enable_wexit_time_destructors': 1,
+ 'optimize': 'max',
+ },
+ 'dependencies': [
+ 'base',
+ 'third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ '../third_party/icu/icu.gyp:icui18n',
+ '../third_party/icu/icu.gyp:icuuc',
+ ],
+ 'conditions': [
+ ['toolkit_uses_gtk==1', {
+ 'dependencies': [
+ # i18n/rtl.cc uses gtk
+ '../build/linux/system.gyp:gtk',
+ ],
+ }],
+ ['OS=="lb_shell"', {
+ 'dependencies': [
+ '<(lbshell_root)/build/projects/posix_emulation.gyp:posix_emulation',
+ ],
+ }],
+ ['OS=="starboard"', {
+ 'dependencies': [
+ '<(DEPTH)/starboard/starboard.gyp:starboard',
+ '<(DEPTH)/starboard/client_porting/icu_init/icu_init.gyp:icu_init',
+ ],
+ }],
+ ],
+ 'export_dependent_settings': [
+ 'base',
+ ],
+ 'defines': [
+ 'BASE_I18N_IMPLEMENTATION',
+ ],
+ 'sources': [
+ 'i18n/base_i18n_export.h',
+ 'i18n/bidi_line_iterator.cc',
+ 'i18n/bidi_line_iterator.h',
+ 'i18n/break_iterator.cc',
+ 'i18n/break_iterator.h',
+ 'i18n/char_iterator.cc',
+ 'i18n/char_iterator.h',
+ 'i18n/case_conversion.cc',
+ 'i18n/case_conversion.h',
+ 'i18n/file_util_icu.cc',
+ 'i18n/file_util_icu.h',
+ 'i18n/icu_encoding_detection.cc',
+ 'i18n/icu_encoding_detection.h',
+ 'i18n/icu_string_conversions.cc',
+ 'i18n/icu_string_conversions.h',
+ 'i18n/icu_util.cc',
+ 'i18n/icu_util.h',
+ 'i18n/number_formatting.cc',
+ 'i18n/number_formatting.h',
+ 'i18n/rtl.cc',
+ 'i18n/rtl.h',
+ 'i18n/string_search.cc',
+ 'i18n/string_search.h',
+ 'i18n/time_formatting.cc',
+ 'i18n/time_formatting.h',
+ ],
+ },
+ {
+ 'target_name': 'base_prefs',
+ 'type': '<(component)',
+ 'variables': {
+ 'enable_wexit_time_destructors': 1,
+ 'optimize': 'max',
+ },
+ 'dependencies': [
+ 'base',
+ ],
+ 'export_dependent_settings': [
+ 'base',
+ ],
+ 'defines': [
+ 'BASE_PREFS_IMPLEMENTATION',
+ ],
+ 'sources': [
+ 'prefs/default_pref_store.cc',
+ 'prefs/default_pref_store.h',
+ 'prefs/json_pref_store.cc',
+ 'prefs/json_pref_store.h',
+ 'prefs/overlay_user_pref_store.cc',
+ 'prefs/overlay_user_pref_store.h',
+ 'prefs/persistent_pref_store.h',
+ 'prefs/pref_observer.h',
+ 'prefs/pref_notifier.h',
+ 'prefs/pref_store.cc',
+ 'prefs/pref_store.h',
+ 'prefs/pref_value_map.cc',
+ 'prefs/pref_value_map.h',
+ 'prefs/public/pref_change_registrar.cc',
+ 'prefs/public/pref_change_registrar.h',
+ 'prefs/public/pref_member.cc',
+ 'prefs/public/pref_member.h',
+ 'prefs/public/pref_service_base.h',
+ 'prefs/value_map_pref_store.cc',
+ 'prefs/value_map_pref_store.h',
+ ],
+ 'conditions': [
+ ['OS == "starboard"', {
+ 'sources!': [
+ # Uses ImportantFileWriter, which is not supported by Starboard.
+ 'prefs/json_pref_store.cc',
+ 'prefs/json_pref_store.h',
+ ],
+ }], # OS=="starboard"
+ ],
+ },
+ {
+ # This is the subset of files from base that should not be used with a
+ # dynamic library. Note that this library cannot depend on base because
+ # base depends on base_static.
+ 'target_name': 'base_static',
+ 'type': 'static_library',
+ 'variables': {
+ 'enable_wexit_time_destructors': 1,
+ 'optimize': 'max',
+ },
+ 'toolsets': ['host', 'target'],
+ 'sources': [
+ 'base_switches.cc',
+ 'base_switches.h',
+ 'win/pe_image.cc',
+ 'win/pe_image.h',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'conditions': [
+ ['OS == "lb_shell" or OS=="starboard"', {
+ 'sources/': [
+ ['exclude', '^win/'],
+ ],
+ }],
+ ],
+ },
+ {
+ # TODO(rvargas): Remove this when gyp finally supports a clean model.
+ # See bug 36232.
+ 'target_name': 'base_static_win64',
+ 'type': 'static_library',
+ 'sources': [
+ 'base_switches.cc',
+ 'base_switches.h',
+ 'win/pe_image.cc',
+ 'win/pe_image.h',
+ ],
+ 'sources!': [
+ # base64.cc depends on modp_b64.
+ 'base64.cc',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ 'defines': [
+ 'NACL_WIN64',
+ ],
+ # TODO(rvargas): Bug 78117. Remove this.
+ 'msvs_disabled_warnings': [
+ 4244,
+ ],
+ },
+ # Include this target for a main() function that simply instantiates
+ # and runs a base::TestSuite.
+ {
+ 'target_name': 'run_all_unittests',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'test_support_base',
+ ],
+ 'sources': [
+ 'test/run_all_unittests.cc',
+ ],
+ },
+ {
+ 'target_name': 'base_unittests',
+ 'type': '<(gtest_target_type)',
+ 'sources': [
+ # Tests.
+ 'android/jni_android_unittest.cc',
+ 'android/jni_array_unittest.cc',
+ 'android/jni_string_unittest.cc',
+ 'android/path_utils_unittest.cc',
+ 'android/scoped_java_ref_unittest.cc',
+ 'at_exit_unittest.cc',
+ 'atomicops_unittest.cc',
+ 'base64_unittest.cc',
+ 'bind_helpers_unittest.cc',
+ 'bind_unittest.cc',
+ 'bind_unittest.nc',
+ 'bits_unittest.cc',
+ 'build_time_unittest.cc',
+ 'callback_unittest.cc',
+ 'callback_unittest.nc',
+ 'cancelable_callback_unittest.cc',
+ 'command_line_unittest.cc',
+ 'containers/linked_list_unittest.cc',
+ 'containers/mru_cache_unittest.cc',
+ 'containers/small_map_unittest.cc',
+ 'containers/stack_container_unittest.cc',
+ 'cpu_unittest.cc',
+ 'debug/leak_tracker_unittest.cc',
+ 'debug/stack_trace_unittest.cc',
+ 'debug/trace_event_unittest.cc',
+ 'debug/trace_event_unittest.h',
+ 'debug/trace_event_win_unittest.cc',
+ 'environment_unittest.cc',
+ 'file_path_unittest.cc',
+ 'file_util_proxy_unittest.cc',
+ 'file_util_unittest.cc',
+ 'file_version_info_unittest.cc',
+ 'files/dir_reader_posix_unittest.cc',
+ 'files/important_file_writer_unittest.cc',
+ 'files/scoped_temp_dir_unittest.cc',
+ 'gmock_unittest.cc',
+ 'guid_unittest.cc',
+ 'hi_res_timer_manager_unittest.cc',
+ 'id_map_unittest.cc',
+ 'i18n/break_iterator_unittest.cc',
+ 'i18n/char_iterator_unittest.cc',
+ 'i18n/case_conversion_unittest.cc',
+ 'i18n/file_util_icu_unittest.cc',
+ 'i18n/icu_string_conversions_unittest.cc',
+ 'i18n/number_formatting_unittest.cc',
+ 'i18n/rtl_unittest.cc',
+ 'i18n/string_search_unittest.cc',
+ 'i18n/time_formatting_unittest.cc',
+ 'ios/device_util_unittest.mm',
+ 'json/json_parser_unittest.cc',
+ 'json/json_reader_unittest.cc',
+ 'json/json_value_converter_unittest.cc',
+ 'json/json_value_serializer_unittest.cc',
+ 'json/json_writer_unittest.cc',
+ 'json/string_escape_unittest.cc',
+ 'lazy_instance_unittest.cc',
+ 'logging_unittest.cc',
+ 'mac/bind_objc_block_unittest.mm',
+ 'mac/foundation_util_unittest.mm',
+ 'mac/libdispatch_task_runner_unittest.cc',
+ 'mac/mac_util_unittest.mm',
+ 'mac/objc_property_releaser_unittest.mm',
+ 'mac/scoped_sending_event_unittest.mm',
+ 'md5_unittest.cc',
+ 'memory/aligned_memory_unittest.cc',
+ 'memory/linked_ptr_unittest.cc',
+ 'memory/ref_counted_memory_unittest.cc',
+ 'memory/ref_counted_unittest.cc',
+ 'memory/scoped_nsobject_unittest.mm',
+ 'memory/scoped_ptr_unittest.cc',
+ 'memory/scoped_ptr_unittest.nc',
+ 'memory/scoped_vector_unittest.cc',
+ 'memory/singleton_unittest.cc',
+ 'memory/weak_ptr_unittest.cc',
+ 'memory/weak_ptr_unittest.nc',
+ 'message_loop_proxy_impl_unittest.cc',
+ 'message_loop_proxy_unittest.cc',
+ 'message_loop_unittest.cc',
+ 'message_pump_glib_unittest.cc',
+ 'message_pump_io_ios_unittest.cc',
+ 'message_pump_libevent_unittest.cc',
+ 'metrics/sample_map_unittest.cc',
+ 'metrics/sample_vector_unittest.cc',
+ 'metrics/bucket_ranges_unittest.cc',
+ 'metrics/field_trial_unittest.cc',
+ 'metrics/histogram_unittest.cc',
+ 'metrics/sparse_histogram_unittest.cc',
+ 'metrics/stats_table_unittest.cc',
+ 'metrics/statistics_recorder_unittest.cc',
+ 'object_tracker.h',
+ 'observer_list_unittest.cc',
+ 'os_compat_android_unittest.cc',
+ 'path_service_unittest.cc',
+ 'pickle_unittest.cc',
+ 'platform_file_unittest.cc',
+ 'posix/file_descriptor_shuffle_unittest.cc',
+ 'pr_time_unittest.cc',
+ 'process_util_unittest.cc',
+ 'process_util_unittest_ios.cc',
+ 'process_util_unittest_mac.h',
+ 'process_util_unittest_mac.mm',
+ 'profiler/tracked_time_unittest.cc',
+ 'rand_util_unittest.cc',
+ 'scoped_native_library_unittest.cc',
+ 'scoped_observer.h',
+ 'sha1_unittest.cc',
+ 'shared_memory_unittest.cc',
+ 'stl_util_unittest.cc',
+ 'string16_unittest.cc',
+ 'string_number_conversions_unittest.cc',
+ 'string_piece_unittest.cc',
+ 'string_split_unittest.cc',
+ 'string_tokenizer_unittest.cc',
+ 'string_util_unittest.cc',
+ 'stringize_macros_unittest.cc',
+ 'stringprintf_unittest.cc',
+ 'synchronization/cancellation_flag_unittest.cc',
+ 'synchronization/condition_variable_unittest.cc',
+ 'synchronization/lock_unittest.cc',
+ 'synchronization/waitable_event_unittest.cc',
+ 'synchronization/waitable_event_watcher_unittest.cc',
+ 'sys_info_unittest.cc',
+ 'sys_string_conversions_mac_unittest.mm',
+ 'sys_string_conversions_unittest.cc',
+ 'system_monitor/system_monitor_unittest.cc',
+ 'task_runner_util_unittest.cc',
+ 'template_util_unittest.cc',
+ 'test/sequenced_worker_pool_owner.cc',
+ 'test/sequenced_worker_pool_owner.h',
+ 'test/trace_event_analyzer_unittest.cc',
+ 'test/time_helpers.cc',
+ 'test/time_helpers.h',
+ 'threading/non_thread_safe_unittest.cc',
+ 'threading/platform_thread_unittest.cc',
+ 'threading/sequenced_worker_pool_unittest.cc',
+ 'threading/simple_thread_unittest.cc',
+ 'threading/thread_checker_unittest.cc',
+ 'threading/thread_collision_warner_unittest.cc',
+ 'threading/thread_local_storage_unittest.cc',
+ 'threading/thread_local_unittest.cc',
+ 'threading/thread_unittest.cc',
+ 'threading/watchdog_unittest.cc',
+ 'threading/worker_pool_posix_unittest.cc',
+ 'threading/worker_pool_unittest.cc',
+ 'time_unittest.cc',
+ 'time_win_unittest.cc',
+ 'timer_unittest.cc',
+ 'tools_sanity_unittest.cc',
+ 'tracked_objects_unittest.cc',
+ 'tuple_unittest.cc',
+ 'utf_offset_string_conversions_unittest.cc',
+ 'utf_string_conversions_unittest.cc',
+ 'values_unittest.cc',
+ 'version_unittest.cc',
+ 'vlog_unittest.cc',
+ 'win/dllmain.cc',
+ 'win/enum_variant_unittest.cc',
+ 'win/event_trace_consumer_unittest.cc',
+ 'win/event_trace_controller_unittest.cc',
+ 'win/event_trace_provider_unittest.cc',
+ 'win/i18n_unittest.cc',
+ 'win/iunknown_impl_unittest.cc',
+ 'win/object_watcher_unittest.cc',
+ 'win/pe_image_unittest.cc',
+ 'win/registry_unittest.cc',
+ 'win/sampling_profiler_unittest.cc',
+ 'win/scoped_bstr_unittest.cc',
+ 'win/scoped_comptr_unittest.cc',
+ 'win/scoped_process_information_unittest.cc',
+ 'win/shortcut_unittest.cc',
+ 'win/startup_information_unittest.cc',
+ 'win/scoped_variant_unittest.cc',
+ 'win/win_util_unittest.cc',
+ 'win/wrapped_window_proc_unittest.cc',
+ ],
+ 'dependencies': [
+ 'base',
+ 'base_i18n',
+ 'base_static',
+ 'run_all_unittests',
+ 'test_support_base',
+ 'third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ '../testing/gmock.gyp:gmock',
+ '../testing/gtest.gyp:gtest',
+ '../third_party/icu/icu.gyp:icui18n',
+ '../third_party/icu/icu.gyp:icuuc',
+ ],
+ 'includes': ['../build/nocompile.gypi'],
+ 'variables': {
+ # TODO(ajwong): Is there a way to autodetect this?
+ 'module_dir': 'base'
+ },
+ 'conditions': [
+ ['OS == "android"', {
+ 'dependencies': [
+ 'android/jni_generator/jni_generator.gyp:jni_generator_tests',
+ ],
+ 'conditions': [
+ ['gtest_target_type == "shared_library"', {
+ 'dependencies': [
+ '../testing/android/native_test.gyp:native_test_native_code',
+ ],
+ }],
+ ],
+ 'sources!': [
+ # Broken on Android, and already disabled there.
+ 'debug/stack_trace_unittest.cc',
+ ],
+ }],
+ ['OS == "ios"', {
+ 'sources/': [
+ # Only test the iOS-meaningful portion of process_utils.
+ ['exclude', '^process_util_unittest'],
+ ['include', '^process_util_unittest_ios\\.cc$'],
+ # Requires spawning processes.
+ ['exclude', '^metrics/stats_table_unittest\\.cc$'],
+ # iOS does not use message_pump_libevent.
+ ['exclude', '^message_pump_libevent_unittest\\.cc$'],
+ ],
+ 'conditions': [
+ ['coverage != 0', {
+ 'sources!': [
+ # These sources can't be built with coverage due to a toolchain
+ # bug: http://openradar.appspot.com/radar?id=1499403
+ 'json/json_reader_unittest.cc',
+ 'string_piece_unittest.cc',
+
+ # These tests crash when run with coverage turned on due to an
+ # issue with llvm_gcda_increment_indirect_counter:
+ # http://crbug.com/156058
+ 'debug/trace_event_unittest.cc',
+ 'debug/trace_event_unittest.h',
+ 'logging_unittest.cc',
+ 'string_util_unittest.cc',
+ 'test/trace_event_analyzer_unittest.cc',
+ 'utf_offset_string_conversions_unittest.cc',
+ ],
+ }],
+ ],
+ 'actions': [
+ {
+ 'action_name': 'copy_test_data',
+ 'variables': {
+ 'test_data_files': [
+ 'data/json/bom_feff.json',
+ 'data/file_util_unittest',
+ ],
+ 'test_data_prefix': 'base',
+ },
+ 'includes': [ '../build/copy_test_data_ios.gypi' ],
+ },
+ ],
+ }],
+ ['cobalt==1', {
+ 'actions': [
+ {
+ 'action_name': 'copy_test_data',
+ 'variables': {
+ 'input_files': [
+ 'data/json',
+ 'data/file_util_unittest',
+ ],
+ 'output_dir': 'base/data',
+ },
+ 'includes': [ '../cobalt/build/copy_test_data.gypi' ],
+ },
+ ],
+ }],
+ ['use_glib==1', {
+ 'sources!': [
+ 'file_version_info_unittest.cc',
+ ],
+ 'conditions': [
+ [ 'linux_use_tcmalloc==1', {
+ 'dependencies': [
+ 'allocator/allocator.gyp:allocator',
+ ],
+ },
+ ],
+ [ 'toolkit_uses_gtk==1', {
+ 'sources': [
+ 'nix/xdg_util_unittest.cc',
+ ],
+ 'dependencies': [
+ '../build/linux/system.gyp:gtk',
+ ]
+ }],
+ ],
+ 'dependencies': [
+ '../build/linux/system.gyp:glib',
+ '../build/linux/system.gyp:ssl',
+ '../tools/xdisplaycheck/xdisplaycheck.gyp:xdisplaycheck',
+ ],
+ }, { # use_glib!=1
+ 'sources!': [
+ 'message_pump_glib_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/icu/icu.gyp:icudata',
+ ],
+ 'sources!': [
+ 'file_descriptor_shuffle_unittest.cc',
+ 'files/dir_reader_posix_unittest.cc',
+ 'threading/worker_pool_posix_unittest.cc',
+ 'message_pump_libevent_unittest.cc',
+ ],
+ }, { # OS != "win"
+ 'dependencies': [
+ '../third_party/libevent/libevent.gyp:libevent'
+ ],
+ 'sources/': [
+ ['exclude', '^win/'],
+ ],
+ 'sources!': [
+ 'debug/trace_event_win_unittest.cc',
+ 'time_win_unittest.cc',
+ 'win/win_util_unittest.cc',
+ ],
+ }],
+ ['OS == "lb_shell" or OS == "starboard"', {
+ 'sources!': [
+ 'environment_unittest.cc',
+ 'files/important_file_writer_unittest.cc',
+ # We don't use field trials (an experiments framework) in Cobalt,
+ # and these tests depend on the current date being set correctly,
+ # so do not run them.
+ 'metrics/field_trial_unittest.cc',
+ 'metrics/stats_table_unittest.cc',
+ 'process_util_unittest.cc',
+ 'scoped_native_library_unittest.cc',
+ 'shared_memory_unittest.cc',
+ 'synchronization/waitable_event_watcher_unittest.cc',
+ ],
+ 'sources': [
+ 'circular_buffer_shell_unittest.cc',
+ 'optional_unittest.cc',
+ 'state_machine_shell_unittest.cc',
+ ],
+ 'conditions': [
+ ['target_arch != "android" or OS == "starboard"', {
+ 'dependencies!': [
+ '../third_party/libevent/libevent.gyp:libevent'
+ ],
+ 'sources!': [
+ 'message_pump_libevent_unittest.cc',
+ ],
+ }],
+ ],
+ }],
+ ], # conditions
+ 'target_conditions': [
+ ['OS == "ios"', {
+ 'sources/': [
+ # Pull in specific Mac files for iOS (which have been filtered out
+ # by file name rules).
+ ['include', '^mac/objc_property_releaser_unittest\\.mm$'],
+ ['include', '^mac/bind_objc_block_unittest\\.mm$'],
+ ['include', '^sys_string_conversions_mac_unittest\\.mm$'],
+ ],
+ }],
+ ], # target_conditions
+ 'msvs_disabled_warnings': [
+ 4800, # forcing value to bool 'true' or 'false' (performance warning)
+ ],
+ },
+ {
+ 'target_name': 'test_support_base',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'base',
+ 'base_static',
+ 'base_i18n',
+ '../testing/gmock.gyp:gmock',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'export_dependent_settings': [
+ 'base',
+ ],
+ 'conditions': [
+ ['cobalt==1', {
+ 'dependencies': [
+ # Platform delegate is used to perform system specific
+ # initialization logic. This is a temporary solution and the code
+ # should eventually move into the chromium/base code.
+ '<(DEPTH)/cobalt/deprecated/deprecated.gyp:platform_delegate',
+ ],
+ }],
+ ['toolkit_uses_gtk==1', {
+ 'dependencies': [
+ # test_suite initializes GTK.
+ '../build/linux/system.gyp:gtk',
+ ],
+ }],
+ ['os_posix==0', {
+ 'sources!': [
+ 'test/scoped_locale.cc',
+ 'test/scoped_locale.h',
+ ],
+ }],
+ ['os_bsd==1', {
+ 'sources!': [
+ 'test/test_file_util_linux.cc',
+ ],
+ }],
+ ['OS=="win"', {
+ 'direct_dependent_settings': {
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'DelayLoadDLLs': [
+ 'propsys.dll',
+ ],
+ },
+ },
+ },
+ }],
+ ],
+ 'sources': [
+ 'perftimer.cc',
+ 'test/main_hook.cc',
+ 'test/main_hook.h',
+ 'test/main_hook_ios.mm',
+ 'test/mock_chrome_application_mac.h',
+ 'test/mock_chrome_application_mac.mm',
+ 'test/mock_devices_changed_observer.cc',
+ 'test/mock_devices_changed_observer.h',
+ 'test/mock_time_provider.cc',
+ 'test/mock_time_provider.h',
+ 'test/multiprocess_test.cc',
+ 'test/multiprocess_test.h',
+ 'test/multiprocess_test_android.cc',
+ 'test/perf_test_suite.cc',
+ 'test/perf_test_suite.h',
+ 'test/scoped_locale.cc',
+ 'test/scoped_locale.h',
+ 'test/scoped_path_override.cc',
+ 'test/scoped_path_override.h',
+ 'test/sequenced_task_runner_test_template.cc',
+ 'test/sequenced_task_runner_test_template.h',
+ 'test/task_runner_test_template.cc',
+ 'test/task_runner_test_template.h',
+ 'test/test_file_util.h',
+ 'test/test_file_util_linux.cc',
+ 'test/test_file_util_mac.cc',
+ 'test/test_file_util_posix.cc',
+ 'test/test_file_util_win.cc',
+ 'test/test_listener_ios.h',
+ 'test/test_listener_ios.mm',
+ 'test/test_reg_util_win.cc',
+ 'test/test_reg_util_win.h',
+ 'test/test_shortcut_win.cc',
+ 'test/test_shortcut_win.h',
+ 'test/test_suite.cc',
+ 'test/test_suite.h',
+ 'test/test_support_android.cc',
+ 'test/test_support_android.h',
+ 'test/test_support_ios.h',
+ 'test/test_support_ios.mm',
+ 'test/test_switches.cc',
+ 'test/test_switches.h',
+ 'test/test_timeouts.cc',
+ 'test/test_timeouts.h',
+ 'test/thread_test_helper.cc',
+ 'test/thread_test_helper.h',
+ 'test/trace_event_analyzer.cc',
+ 'test/trace_event_analyzer.h',
+ 'test/values_test_util.cc',
+ 'test/values_test_util.h',
+ ],
+ 'target_conditions': [
+ ['OS == "ios"', {
+ 'sources/': [
+ # Pull in specific Mac files for iOS (which have been filtered out
+ # by file name rules).
+ ['include', '^test/test_file_util_mac\\.cc$'],
+ ],
+ }],
+ ['OS == "lb_shell" or OS=="starboard"', {
+ 'sources/': [
+ ['exclude', 'test/multiprocess_test'],
+ ],
+ 'sources!': [
+ 'perftimer.cc',
+ ],
+ 'sources': [
+ 'perftimer_starboard.cc',
+ ],
+ }],
+ ], # target_conditions
+ },
+ {
+ 'target_name': 'test_support_perf',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'base',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ 'perftimer.cc',
+ 'test/run_all_perftests.cc',
+ ],
+ 'direct_dependent_settings': {
+ 'defines': [
+ 'PERF_TEST',
+ ],
+ },
+ 'conditions': [
+ ['toolkit_uses_gtk==1', {
+ 'dependencies': [
+ # Needed to handle the #include chain:
+ # base/test/perf_test_suite.h
+ # base/test/test_suite.h
+ # gtk/gtk.h
+ '../build/linux/system.gyp:gtk',
+ ],
+ }],
+ ['OS=="starboard"', {
+ 'sources!': [
+ 'perftimer.cc',
+ ],
+ 'sources': [
+ 'perftimer_starboard.cc',
+ ],
+ }],
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS!="ios"', {
+ 'targets': [
+ {
+ 'target_name': 'check_example',
+ 'type': 'executable',
+ 'sources': [
+ 'check_example.cc',
+ ],
+ 'dependencies': [
+ 'base',
+ ],
+ },
+ ],
+ }],
+ ['OS == "win"', {
+ 'targets': [
+ {
+ 'target_name': 'base_nacl_win64',
+ 'type': '<(component)',
+ 'variables': {
+ 'base_target': 1,
+ },
+ 'dependencies': [
+ 'base_static_win64',
+ 'allocator/allocator.gyp:allocator_extension_thunks_win64',
+ 'third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations_win64',
+ ],
+ # TODO(gregoryd): direct_dependent_settings should be shared with the
+ # 32-bit target, but it doesn't work due to a bug in gyp
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ 'defines': [
+ '<@(nacl_win64_defines)',
+ ],
+ 'sources!': [
+ # base64.cc depends on modp_b64.
+ 'base64.cc',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ 'conditions': [
+ ['component == "shared_library"', {
+ 'sources!': [
+ 'debug/debug_on_start_win.cc',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'base_i18n_nacl_win64',
+ 'type': '<(component)',
+ # TODO(gregoryd): direct_dependent_settings should be shared with the
+ # 32-bit target, but it doesn't work due to a bug in gyp
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ 'defines': [
+ '<@(nacl_win64_defines)',
+ 'BASE_I18N_IMPLEMENTATION',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'i18n/icu_util_nacl_win64.cc',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ },
+ ],
+ }],
+ ['os_posix==1 and OS!="mac" and OS!="ios" and (OS!="lb_shell" or target_arch in ("android", "linux"))', {
+ 'targets': [
+ {
+ 'target_name': 'symbolize',
+ 'type': 'static_library',
+ 'toolsets': ['host', 'target'],
+ 'variables': {
+ 'chromium_code': 0,
+ },
+ 'conditions': [
+ ['OS == "solaris"', {
+ 'include_dirs': [
+ '/usr/gnu/include',
+ '/usr/gnu/include/libelf',
+ ],
+ },],
+ ],
+ 'cflags': [
+ '-Wno-sign-compare',
+ ],
+ 'cflags!': [
+ '-Wextra',
+ ],
+ 'sources': [
+ 'third_party/symbolize/config.h',
+ 'third_party/symbolize/demangle.cc',
+ 'third_party/symbolize/demangle.h',
+ 'third_party/symbolize/glog/logging.h',
+ 'third_party/symbolize/glog/raw_logging.h',
+ 'third_party/symbolize/symbolize.cc',
+ 'third_party/symbolize/symbolize.h',
+ 'third_party/symbolize/utilities.h',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ {
+ 'target_name': 'xdg_mime',
+ 'type': 'static_library',
+ 'toolsets': ['host', 'target'],
+ 'variables': {
+ 'chromium_code': 0,
+ },
+ 'cflags!': [
+ '-Wextra',
+ ],
+ 'sources': [
+ 'third_party/xdg_mime/xdgmime.c',
+ 'third_party/xdg_mime/xdgmime.h',
+ 'third_party/xdg_mime/xdgmimealias.c',
+ 'third_party/xdg_mime/xdgmimealias.h',
+ 'third_party/xdg_mime/xdgmimecache.c',
+ 'third_party/xdg_mime/xdgmimecache.h',
+ 'third_party/xdg_mime/xdgmimeglob.c',
+ 'third_party/xdg_mime/xdgmimeglob.h',
+ 'third_party/xdg_mime/xdgmimeicon.c',
+ 'third_party/xdg_mime/xdgmimeicon.h',
+ 'third_party/xdg_mime/xdgmimeint.c',
+ 'third_party/xdg_mime/xdgmimeint.h',
+ 'third_party/xdg_mime/xdgmimemagic.c',
+ 'third_party/xdg_mime/xdgmimemagic.h',
+ 'third_party/xdg_mime/xdgmimeparent.c',
+ 'third_party/xdg_mime/xdgmimeparent.h',
+ ],
+ },
+ ],
+ }],
+ ['OS == "android" or ((OS == "lb_shell" or OS == "starboard") and target_arch == "android")', {
+ 'targets': [
+ {
+ 'target_name': 'base_jni_headers',
+ 'type': 'none',
+ 'sources': [
+ 'android/java/src/org/chromium/base/BuildInfo.java',
+ 'android/java/src/org/chromium/base/CpuFeatures.java',
+ 'android/java/src/org/chromium/base/LocaleUtils.java',
+ 'android/java/src/org/chromium/base/PathService.java',
+ 'android/java/src/org/chromium/base/PathUtils.java',
+ 'android/java/src/org/chromium/base/SystemMessageHandler.java',
+ 'android/java/src/org/chromium/base/SystemMonitor.java',
+ ],
+ 'variables': {
+ 'jni_gen_dir': 'base',
+ },
+ 'includes': [ '../build/jni_generator.gypi' ],
+ },
+ {
+ 'target_name': 'base_java',
+ 'type': 'none',
+ 'variables': {
+ 'package_name': 'base',
+ 'java_in_dir': '../base/android/java',
+ },
+ 'includes': [ '../build/java.gypi' ],
+ },
+ {
+ 'target_name': 'base_java_test_support',
+ 'type': 'none',
+ 'dependencies': [
+ 'base_java',
+ ],
+ 'variables': {
+ 'package_name': 'base_javatests',
+ 'java_in_dir': '../base/test/android/javatests',
+ },
+ 'includes': [ '../build/java.gypi' ],
+ },
+ ],
+ }],
+ ['OS == "win"', {
+ 'targets': [
+ {
+ 'target_name': 'debug_message',
+ 'type': 'executable',
+ 'sources': [
+ 'debug_message.cc',
+ ],
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'SubSystem': '2', # Set /SUBSYSTEM:WINDOWS
+ },
+ },
+ },
+ ],
+ }],
+ ['cobalt==1', {
+ 'targets': [
+ {
+ 'target_name': 'base_unittests_deploy',
+ 'type': 'none',
+ 'dependencies': [
+ 'base_unittests',
+ ],
+ 'variables': {
+ 'executable_name': 'base_unittests',
+ },
+ 'includes': [ '../cobalt/build/deploy.gypi' ],
+ },
+ ],
+ }],
+ # Special target to wrap a gtest_target_type == shared_library
+ # base_unittests into an android apk for execution.
+ # TODO(jrg): lib.target comes from _InstallableTargetInstallPath()
+ # in the gyp make generator. What is the correct way to extract
+ # this path from gyp and into 'raw' for input to antfiles?
+ # Hard-coding in the gypfile seems a poor choice.
+ ['(OS == "android" and gtest_target_type == "shared_library") or ((OS == "lb_shell" or OS == "starboard") and target_arch == "android")', {
+ 'targets': [
+ {
+ 'target_name': 'base_unittests_apk',
+ 'type': 'none',
+ 'dependencies': [
+ 'base_java',
+ 'base_unittests',
+ ],
+ 'variables': {
+ 'test_suite_name': 'base_unittests',
+ 'input_shlib_path': '<(SHARED_LIB_DIR)/<(SHARED_LIB_PREFIX)base_unittests<(SHARED_LIB_SUFFIX)',
+ },
+ 'includes': [ '../build/apk_test.gypi' ],
+ },
+ ],
+ }],
+ ['test_isolation_mode != "noop"', {
+ 'targets': [
+ {
+ 'target_name': 'base_unittests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'base_unittests',
+ ],
+ 'includes': [
+ '../build/isolate.gypi',
+ 'base_unittests.isolate',
+ ],
+ 'sources': [
+ 'base_unittests.isolate',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/src/base/base.gypi b/src/base/base.gypi
new file mode 100644
index 0000000..fde1a96
--- /dev/null
+++ b/src/base/base.gypi
@@ -0,0 +1,858 @@
+# Copyright (c) 2012 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.
+
+{
+ 'target_defaults': {
+ 'variables': {
+ 'base_target': 0,
+ },
+ 'target_conditions': [
+ # This part is shared between the targets defined below.
+ ['base_target==1', {
+ 'sources': [
+ '../build/build_config.h',
+ 'third_party/dmg_fp/dmg_fp.h',
+ 'third_party/dmg_fp/g_fmt.cc',
+ 'third_party/dmg_fp/dtoa_wrapper.cc',
+ 'third_party/icu/icu_utf.cc',
+ 'third_party/icu/icu_utf.h',
+ 'third_party/nspr/prtime.cc',
+ 'third_party/nspr/prtime.h',
+ 'third_party/nspr/prcpucfg_linux.h',
+ 'third_party/xdg_mime/xdgmime.h',
+ 'allocator/allocator_extension.cc',
+ 'allocator/allocator_extension.h',
+ 'allocator/type_profiler_control.cc',
+ 'allocator/type_profiler_control.h',
+ 'android/base_jni_registrar.cc',
+ 'android/base_jni_registrar.h',
+ 'android/build_info.cc',
+ 'android/build_info.h',
+ 'android/cpu_features.cc',
+ 'android/scoped_java_ref.cc',
+ 'android/scoped_java_ref.h',
+ 'android/jni_android.cc',
+ 'android/jni_android.h',
+ 'android/jni_array.cc',
+ 'android/jni_array.h',
+ 'android/jni_helper.cc',
+ 'android/jni_helper.h',
+ 'android/jni_registrar.cc',
+ 'android/jni_registrar.h',
+ 'android/jni_string.cc',
+ 'android/jni_string.h',
+ 'android/locale_utils.cc',
+ 'android/locale_utils.h',
+ 'android/path_service_android.cc',
+ 'android/path_service_android.h',
+ 'android/path_utils.cc',
+ 'android/path_utils.h',
+ 'at_exit.cc',
+ 'at_exit.h',
+ 'atomic_ref_count.h',
+ 'atomic_sequence_num.h',
+ 'atomicops.h',
+ 'atomicops_internals_gcc.h',
+ 'atomicops_internals_mac.h',
+ 'atomicops_internals_tsan.h',
+ 'atomicops_internals_x86_gcc.cc',
+ 'atomicops_internals_x86_gcc.h',
+ 'atomicops_internals_x86_msvc.h',
+ 'base_export.h',
+ 'base_paths.cc',
+ 'base_paths.h',
+ 'base_paths_android.cc',
+ 'base_paths_android.h',
+ 'base_paths_mac.h',
+ 'base_paths_mac.mm',
+ 'base_paths_posix.cc',
+ 'base_paths_posix.h',
+ 'base_paths_win.cc',
+ 'base_paths_win.h',
+ 'base_switches.h',
+ 'base64.cc',
+ 'base64.h',
+ 'basictypes.h',
+ 'bind.h',
+ 'bind_helpers.cc',
+ 'bind_helpers.h',
+ 'bind_internal.h',
+ 'bind_internal_functor.h',
+ 'bind_internal_win.h',
+ 'bits.h',
+ 'build_time.cc',
+ 'build_time.h',
+ 'callback.h',
+ 'callback_helpers.h',
+ 'callback_internal.cc',
+ 'callback_internal.h',
+ 'cancelable_callback.h',
+ 'chromeos/chromeos_version.cc',
+ 'chromeos/chromeos_version.h',
+ 'command_line.cc',
+ 'command_line.h',
+ 'compiler_specific.h',
+ 'containers/linked_hash_map.h',
+ 'containers/linked_list.h',
+ 'containers/mru_cache.h',
+ 'containers/small_map.h',
+ 'containers/stack_container.h',
+ 'cpu.cc',
+ 'cpu.h',
+ 'critical_closure.h',
+ 'critical_closure_ios.mm',
+ 'debug/alias.cc',
+ 'debug/alias.h',
+ 'debug/debug_on_start_win.cc',
+ 'debug/debug_on_start_win.h',
+ 'debug/debugger.cc',
+ 'debug/debugger.h',
+ 'debug/debugger_posix.cc',
+ 'debug/debugger_win.cc',
+ # This file depends on files from the 'allocator' target,
+ # but this target does not depend on 'allocator' (see
+ # allocator.gyp for details).
+ 'debug/leak_annotations.h',
+ 'debug/leak_tracker.h',
+ 'debug/profiler.cc',
+ 'debug/profiler.h',
+ 'debug/stack_trace.cc',
+ 'debug/stack_trace.h',
+ 'debug/stack_trace_android.cc',
+ 'debug/stack_trace_ios.mm',
+ 'debug/stack_trace_posix.cc',
+ 'debug/stack_trace_win.cc',
+ 'debug/trace_event.cc',
+ 'debug/trace_event.h',
+ 'debug/trace_event_android.cc',
+ 'debug/trace_event_impl.cc',
+ 'debug/trace_event_impl.h',
+ 'debug/trace_event_win.cc',
+ 'environment.cc',
+ 'environment.h',
+ 'file_descriptor_posix.h',
+ 'file_path.cc',
+ 'file_path.h',
+ 'file_util.cc',
+ 'file_util.h',
+ 'file_util_android.cc',
+ 'file_util_linux.cc',
+ 'file_util_mac.mm',
+ 'file_util_posix.cc',
+ 'file_util_win.cc',
+ 'file_util_proxy.cc',
+ 'file_util_proxy.h',
+ 'file_version_info.h',
+ 'file_version_info_mac.h',
+ 'file_version_info_mac.mm',
+ 'file_version_info_win.cc',
+ 'file_version_info_win.h',
+ 'files/dir_reader_fallback.h',
+ 'files/dir_reader_dirent.h',
+ 'files/dir_reader_linux.h',
+ 'files/dir_reader_posix.h',
+ 'files/file_path_watcher.cc',
+ 'files/file_path_watcher.h',
+ 'files/file_path_watcher_kqueue.cc',
+ 'files/file_path_watcher_linux.cc',
+ 'files/file_path_watcher_stub.cc',
+ 'files/file_path_watcher_win.cc',
+ 'files/important_file_writer.h',
+ 'files/important_file_writer.cc',
+ 'files/scoped_temp_dir.cc',
+ 'files/scoped_temp_dir.h',
+ 'float_util.h',
+ 'format_macros.h',
+ 'gtest_prod_util.h',
+ 'guid.cc',
+ 'guid.h',
+ 'guid_posix.cc',
+ 'guid_win.cc',
+ 'hash.cc',
+ 'hash.h',
+ 'hash_tables.h',
+ 'hi_res_timer_manager_posix.cc',
+ 'hi_res_timer_manager_win.cc',
+ 'hi_res_timer_manager.h',
+ 'id_map.h',
+ 'ios/device_util.h',
+ 'ios/device_util.mm',
+ 'ios/ios_util.h',
+ 'ios/ios_util.mm',
+ 'ios/scoped_critical_action.h',
+ 'ios/scoped_critical_action.mm',
+ 'json/json_file_value_serializer.cc',
+ 'json/json_file_value_serializer.h',
+ 'json/json_parser.cc',
+ 'json/json_parser.h',
+ 'json/json_reader.cc',
+ 'json/json_reader.h',
+ 'json/json_string_value_serializer.cc',
+ 'json/json_string_value_serializer.h',
+ 'json/json_value_converter.h',
+ 'json/json_writer.cc',
+ 'json/json_writer.h',
+ 'json/string_escape.cc',
+ 'json/string_escape.h',
+ 'lazy_instance.cc',
+ 'lazy_instance.h',
+ 'location.cc',
+ 'location.h',
+ 'logging.cc',
+ 'logging.h',
+ 'logging_win.cc',
+ 'logging_win.h',
+ 'mac/authorization_util.h',
+ 'mac/authorization_util.mm',
+ 'mac/bind_objc_block.h',
+ 'mac/bind_objc_block.mm',
+ 'mac/bundle_locations.h',
+ 'mac/bundle_locations.mm',
+ 'mac/cocoa_protocols.h',
+ 'mac/crash_logging.h',
+ 'mac/crash_logging.mm',
+ 'mac/foundation_util.h',
+ 'mac/foundation_util.mm',
+ 'mac/launchd.cc',
+ 'mac/launchd.h',
+ 'mac/libdispatch_task_runner.cc',
+ 'mac/libdispatch_task_runner.h',
+ 'mac/mac_logging.h',
+ 'mac/mac_logging.cc',
+ 'mac/mac_util.h',
+ 'mac/mac_util.mm',
+ 'mac/objc_property_releaser.h',
+ 'mac/objc_property_releaser.mm',
+ 'mac/os_crash_dumps.cc',
+ 'mac/os_crash_dumps.h',
+ 'mac/scoped_aedesc.h',
+ 'mac/scoped_authorizationref.h',
+ 'mac/scoped_cftyperef.h',
+ 'mac/scoped_ioobject.h',
+ 'mac/scoped_launch_data.h',
+ 'mac/scoped_mach_port.cc',
+ 'mac/scoped_mach_port.h',
+ 'mac/scoped_nsautorelease_pool.h',
+ 'mac/scoped_nsautorelease_pool.mm',
+ 'mac/scoped_nsexception_enabler.h',
+ 'mac/scoped_nsexception_enabler.mm',
+ 'mac/scoped_sending_event.h',
+ 'mac/scoped_sending_event.mm',
+ 'mach_ipc_mac.h',
+ 'mach_ipc_mac.mm',
+ 'memory/aligned_memory.cc',
+ 'memory/aligned_memory.h',
+ 'memory/linked_ptr.h',
+ 'memory/manual_constructor.h',
+ 'memory/raw_scoped_refptr_mismatch_checker.h',
+ 'memory/ref_counted.cc',
+ 'memory/ref_counted.h',
+ 'memory/ref_counted_memory.cc',
+ 'memory/ref_counted_memory.h',
+ 'memory/scoped_handle.h',
+ 'memory/scoped_nsobject.h',
+ 'memory/scoped_open_process.h',
+ 'memory/scoped_policy.h',
+ 'memory/scoped_ptr.h',
+ 'memory/scoped_vector.h',
+ 'memory/singleton.cc',
+ 'memory/singleton.h',
+ 'memory/weak_ptr.cc',
+ 'memory/weak_ptr.h',
+ 'message_loop.cc',
+ 'message_loop.h',
+ 'message_loop_proxy.cc',
+ 'message_loop_proxy.h',
+ 'message_loop_proxy_impl.cc',
+ 'message_loop_proxy_impl.h',
+ 'message_pump.cc',
+ 'message_pump.h',
+ 'message_pump_android.cc',
+ 'message_pump_android.h',
+ 'message_pump_default.cc',
+ 'message_pump_default.h',
+ 'message_pump_win.cc',
+ 'message_pump_win.h',
+ 'metrics/sample_map.cc',
+ 'metrics/sample_map.h',
+ 'metrics/sample_vector.cc',
+ 'metrics/sample_vector.h',
+ 'metrics/bucket_ranges.cc',
+ 'metrics/bucket_ranges.h',
+ 'metrics/histogram.cc',
+ 'metrics/histogram.h',
+ 'metrics/histogram_base.cc',
+ 'metrics/histogram_base.h',
+ 'metrics/histogram_flattener.h',
+ 'metrics/histogram_samples.cc',
+ 'metrics/histogram_samples.h',
+ 'metrics/histogram_snapshot_manager.cc',
+ 'metrics/histogram_snapshot_manager.h',
+ 'metrics/sparse_histogram.cc',
+ 'metrics/sparse_histogram.h',
+ 'metrics/statistics_recorder.cc',
+ 'metrics/statistics_recorder.h',
+ 'metrics/stats_counters.cc',
+ 'metrics/stats_counters.h',
+ 'metrics/stats_table.cc',
+ 'metrics/stats_table.h',
+ 'move.h',
+ 'native_library.h',
+ 'native_library_mac.mm',
+ 'native_library_posix.cc',
+ 'native_library_win.cc',
+ 'observer_list.h',
+ 'observer_list_threadsafe.h',
+ 'os_compat_android.cc',
+ 'os_compat_android.h',
+ 'os_compat_nacl.cc',
+ 'os_compat_nacl.h',
+ 'path_service.cc',
+ 'path_service.h',
+ 'pending_task.cc',
+ 'pending_task.h',
+ 'pickle.cc',
+ 'pickle.h',
+ 'platform_file.cc',
+ 'platform_file.h',
+ 'platform_file_posix.cc',
+ 'platform_file_win.cc',
+ 'port.h',
+ 'posix/eintr_wrapper.h',
+ 'posix/global_descriptors.cc',
+ 'posix/global_descriptors.h',
+ 'posix/unix_domain_socket.cc',
+ 'posix/unix_domain_socket.h',
+ 'process.h',
+ 'process_info.h',
+ 'process_info_mac.cc',
+ 'process_info_win.cc',
+ 'process_linux.cc',
+ 'process_posix.cc',
+ 'process_util.cc',
+ 'process_util.h',
+ 'process_util_freebsd.cc',
+ 'process_util_ios.mm',
+ 'process_util_linux.cc',
+ 'process_util_mac.mm',
+ 'process_util_openbsd.cc',
+ 'process_util_posix.cc',
+ 'process_util_win.cc',
+ 'process_win.cc',
+ 'profiler/scoped_profile.cc',
+ 'profiler/scoped_profile.h',
+ 'profiler/alternate_timer.cc',
+ 'profiler/alternate_timer.h',
+ 'profiler/tracked_time.cc',
+ 'profiler/tracked_time.h',
+ 'rand_util.cc',
+ 'rand_util.h',
+ 'rand_util_nacl.cc',
+ 'rand_util_posix.cc',
+ 'rand_util_win.cc',
+ 'run_loop.cc',
+ 'run_loop.h',
+ 'safe_strerror_posix.cc',
+ 'safe_strerror_posix.h',
+ 'scoped_native_library.cc',
+ 'scoped_native_library.h',
+ 'sequenced_task_runner.cc',
+ 'sequenced_task_runner.h',
+ 'sequenced_task_runner_helpers.h',
+ 'sha1.h',
+ 'sha1_portable.cc',
+ 'sha1_win.cc',
+ 'shared_memory.h',
+ 'shared_memory_android.cc',
+ 'shared_memory_nacl.cc',
+ 'shared_memory_posix.cc',
+ 'shared_memory_win.cc',
+ 'single_thread_task_runner.h',
+ 'stl_util.h',
+ 'string_number_conversions.cc',
+ 'string_number_conversions.h',
+ 'string_piece.cc',
+ 'string_piece.h',
+ 'string_split.cc',
+ 'string_split.h',
+ 'string_tokenizer.h',
+ 'string_util.cc',
+ 'string_util.h',
+ 'string_util_posix.h',
+ 'string_util_win.h',
+ 'string16.cc',
+ 'string16.h',
+ 'stringize_macros.h',
+ 'stringprintf.cc',
+ 'stringprintf.h',
+ 'supports_user_data.cc',
+ 'supports_user_data.h',
+ 'synchronization/cancellation_flag.cc',
+ 'synchronization/cancellation_flag.h',
+ 'synchronization/condition_variable.h',
+ 'synchronization/condition_variable_posix.cc',
+ 'synchronization/condition_variable_win.cc',
+ 'synchronization/lock.cc',
+ 'synchronization/lock.h',
+ 'synchronization/lock_impl.h',
+ 'synchronization/lock_impl_posix.cc',
+ 'synchronization/lock_impl_win.cc',
+ 'synchronization/waitable_event.h',
+ 'synchronization/waitable_event_posix.cc',
+ 'synchronization/waitable_event_watcher.h',
+ 'synchronization/waitable_event_watcher_posix.cc',
+ 'synchronization/waitable_event_watcher_win.cc',
+ 'synchronization/waitable_event_win.cc',
+ 'system_monitor/system_monitor.cc',
+ 'system_monitor/system_monitor.h',
+ 'system_monitor/system_monitor_android.cc',
+ 'system_monitor/system_monitor_android.h',
+ 'system_monitor/system_monitor_ios.mm',
+ 'system_monitor/system_monitor_mac.mm',
+ 'system_monitor/system_monitor_posix.cc',
+ 'system_monitor/system_monitor_win.cc',
+ 'sys_byteorder.h',
+ 'sys_info.cc',
+ 'sys_info.h',
+ 'sys_info_android.cc',
+ 'sys_info_chromeos.cc',
+ 'sys_info_freebsd.cc',
+ 'sys_info_ios.mm',
+ 'sys_info_linux.cc',
+ 'sys_info_mac.cc',
+ 'sys_info_openbsd.cc',
+ 'sys_info_posix.cc',
+ 'sys_info_win.cc',
+ 'sys_string_conversions.h',
+ 'sys_string_conversions_mac.mm',
+ 'sys_string_conversions_posix.cc',
+ 'sys_string_conversions_win.cc',
+ 'task_runner.cc',
+ 'task_runner.h',
+ 'task_runner_util.h',
+ 'template_util.h',
+ 'thread_task_runner_handle.cc',
+ 'thread_task_runner_handle.h',
+ 'threading/non_thread_safe.h',
+ 'threading/non_thread_safe_impl.cc',
+ 'threading/non_thread_safe_impl.h',
+ 'threading/platform_thread.h',
+ 'threading/platform_thread_mac.mm',
+ 'threading/platform_thread_posix.cc',
+ 'threading/platform_thread_win.cc',
+ 'threading/post_task_and_reply_impl.cc',
+ 'threading/post_task_and_reply_impl.h',
+ 'threading/sequenced_worker_pool.cc',
+ 'threading/sequenced_worker_pool.h',
+ 'threading/simple_thread.cc',
+ 'threading/simple_thread.h',
+ 'threading/thread.cc',
+ 'threading/thread.h',
+ 'threading/thread_checker.h',
+ 'threading/thread_checker_impl.cc',
+ 'threading/thread_checker_impl.h',
+ 'threading/thread_collision_warner.cc',
+ 'threading/thread_collision_warner.h',
+ 'threading/thread_local.h',
+ 'threading/thread_local_posix.cc',
+ 'threading/thread_local_storage.h',
+ 'threading/thread_local_storage_posix.cc',
+ 'threading/thread_local_storage_win.cc',
+ 'threading/thread_local_win.cc',
+ 'threading/thread_restrictions.h',
+ 'threading/thread_restrictions.cc',
+ 'threading/watchdog.cc',
+ 'threading/watchdog.h',
+ 'threading/worker_pool.h',
+ 'threading/worker_pool.cc',
+ 'threading/worker_pool_posix.cc',
+ 'threading/worker_pool_posix.h',
+ 'threading/worker_pool_win.cc',
+ 'time.cc',
+ 'time.h',
+ 'time_mac.cc',
+ 'time_posix.cc',
+ 'time_win.cc',
+ 'timer.cc',
+ 'timer.h',
+ 'tracked_objects.cc',
+ 'tracked_objects.h',
+ 'tracking_info.cc',
+ 'tracking_info.h',
+ 'tuple.h',
+ 'utf_offset_string_conversions.cc',
+ 'utf_offset_string_conversions.h',
+ 'utf_string_conversion_utils.cc',
+ 'utf_string_conversion_utils.h',
+ 'utf_string_conversions.cc',
+ 'utf_string_conversions.h',
+ 'values.cc',
+ 'values.h',
+ 'value_conversions.cc',
+ 'value_conversions.h',
+ 'version.cc',
+ 'version.h',
+ 'vlog.cc',
+ 'vlog.h',
+ 'nix/mime_util_xdg.cc',
+ 'nix/mime_util_xdg.h',
+ 'nix/xdg_util.cc',
+ 'nix/xdg_util.h',
+ 'win/enum_variant.h',
+ 'win/enum_variant.cc',
+ 'win/event_trace_consumer.h',
+ 'win/event_trace_controller.cc',
+ 'win/event_trace_controller.h',
+ 'win/event_trace_provider.cc',
+ 'win/event_trace_provider.h',
+ 'win/i18n.cc',
+ 'win/i18n.h',
+ 'win/iat_patch_function.cc',
+ 'win/iat_patch_function.h',
+ 'win/iunknown_impl.h',
+ 'win/iunknown_impl.cc',
+ 'win/metro.cc',
+ 'win/metro.h',
+ 'win/object_watcher.cc',
+ 'win/object_watcher.h',
+ 'win/registry.cc',
+ 'win/registry.h',
+ 'win/resource_util.cc',
+ 'win/resource_util.h',
+ 'win/sampling_profiler.cc',
+ 'win/sampling_profiler.h',
+ 'win/scoped_bstr.cc',
+ 'win/scoped_bstr.h',
+ 'win/scoped_co_mem.h',
+ 'win/scoped_com_initializer.h',
+ 'win/scoped_comptr.h',
+ 'win/scoped_gdi_object.h',
+ 'win/scoped_handle.cc',
+ 'win/scoped_handle.h',
+ 'win/scoped_hdc.h',
+ 'win/scoped_hglobal.h',
+ 'win/scoped_process_information.cc',
+ 'win/scoped_process_information.h',
+ 'win/scoped_select_object.h',
+ 'win/shortcut.cc',
+ 'win/shortcut.h',
+ 'win/startup_information.cc',
+ 'win/startup_information.h',
+ 'win/scoped_variant.cc',
+ 'win/scoped_variant.h',
+ 'win/text_services_message_filter.cc',
+ 'win/text_services_message_filter.h',
+ 'win/windows_version.cc',
+ 'win/windows_version.h',
+ 'win/win_util.cc',
+ 'win/win_util.h',
+ 'win/wrapped_window_proc.cc',
+ 'win/wrapped_window_proc.h',
+ ],
+ 'defines': [
+ 'BASE_IMPLEMENTATION',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'msvs_disabled_warnings': [
+ 4018,
+ ],
+ 'conditions': [
+ ['OS=="lb_shell"', {
+ 'sources': [
+ 'base_paths_shell.cc',
+ 'circular_buffer_shell.h',
+ 'circular_buffer_shell.cc',
+ 'message_pump_shell.cc',
+ 'message_pump_shell.h',
+ 'native_library_shell.cc', # Stub implementation
+ 'object_watcher_shell.cc',
+ 'object_watcher_shell.h',
+ 'optional.cc',
+ 'optional.h',
+ 'optional_internal.h',
+ 'shared_memory_shell.cc', # Stub implementation
+ 'state_machine_shell.h',
+ 'state_machine_shell.cc',
+ 'synchronization/condition_variable_shell.cc',
+ 'synchronization/lock_impl_shell.cc',
+ 'synchronization/waitable_event_shell.cc',
+ 'threading/thread_checker_impl_atomic.cc',
+ '<!@(find <(lbshell_root)/src/platform/<(target_arch)/chromium/base -type f)',
+ ],
+ 'sources!': [
+ 'threading/thread_checker_impl.cc',
+ ],
+ 'sources/': [
+ ['include', 'sys_string_conversions_linux.cc'],
+ ],
+ }],
+ [ 'OS=="lb_shell" and target_arch!="linux" and target_arch!="android"', {
+ 'sources!': [
+ 'string16.cc', # wchar_t is 2-bytes wide, string16 == wstring here.
+ ],
+ }],
+ [ 'OS=="lb_shell" and target_arch=="android"', {
+ 'sources/' : [
+ ['exclude', 'message_pump_shell.cc'],
+ ['exclude', 'message_pump_shell.h'],
+ ['exclude', 'native_library_shell.cc'],
+ ['exclude', 'process_util.cc'],
+ ['exclude', '<(lbshell_root)/src/object_watcher_shell.cc'],
+ ['exclude', '<(lbshell_root)/src/object_watcher_shell.h'],
+ ['include', 'message_pump_libevent.cc'],
+ ['include', 'message_pump_libevent.h'],
+ ['include', 'native_library_posix.cc'],
+ ],
+ }],
+ # For Starboard, we are going to build this back up as necessary.
+ ['OS=="starboard"', {
+ 'sources': [
+ 'base_paths_starboard.cc',
+ 'circular_buffer_shell.cc',
+ 'circular_buffer_shell.h',
+ 'debug/debugger_starboard.cc',
+ 'debug/stack_trace_starboard.cc',
+ 'file_util_starboard.cc',
+ 'guid_starboard.cc',
+ 'message_pump_io_starboard.cc',
+ 'message_pump_io_starboard.h',
+ 'message_pump_ui_starboard.cc',
+ 'message_pump_ui_starboard.h',
+ 'optional.cc',
+ 'optional.h',
+ 'optional_internal.h',
+ 'platform_file_starboard.cc',
+ 'process_starboard.cc',
+ 'process_util_starboard.cc',
+ 'rand_util_starboard.cc',
+ 'state_machine_shell.cc',
+ 'state_machine_shell.h',
+ 'synchronization/condition_variable_starboard.cc',
+ 'synchronization/lock_impl_starboard.cc',
+ 'synchronization/waitable_event_starboard.cc',
+ 'sys_info_starboard.cc',
+ 'sys_string_conversions_starboard.cc',
+ 'system_monitor/system_monitor_starboard.cc',
+ 'threading/platform_thread_starboard.cc',
+ 'threading/thread_checker_impl_atomic.cc',
+ 'threading/thread_local_starboard.cc',
+ 'threading/thread_local_storage_starboard.cc',
+ 'threading/worker_pool_starboard.cc',
+ 'time_starboard.cc',
+ ],
+ 'sources!': [
+ # Uses file_util::ReplaceFile, which isn't implemented in
+ # Starboard, and isn't otherwise used in Cobalt.
+ 'files/important_file_writer.cc',
+ 'files/important_file_writer.h',
+
+ # All the common functions have been removed for Starboard, so we
+ # don't even want to compile this.
+ 'process_util.cc',
+
+ # Tricky to support cross-platform, and not used by Cobalt.
+ 'scoped_native_library.cc',
+ 'scoped_native_library.h',
+
+ # We use thread_checker_impl_atomic.cc instead.
+ 'threading/thread_checker_impl.cc',
+ ],
+ 'conditions': [
+ ['target_os!="linux" and target_arch!="android"', {
+ 'sources!': [
+ # Since wchar_t is 2-bytes wide, string16 == wstring here.
+ 'string16.cc',
+ ],
+ }],
+ ],
+ }], # OS == "starboard"
+ [ 'OS=="lb_shell" or OS=="starboard"', {
+ 'sources!': [
+ 'environment.cc',
+ 'file_descriptor_shuffle.cc',
+ 'files/file_path_watcher.cc',
+ 'files/file_path_watcher_kqueue.cc',
+ 'files/file_path_watcher_stub.cc',
+ 'message_pump_libevent.cc',
+ 'message_pump_libevent.h',
+ 'metrics/stats_table.cc',
+ 'metrics/stats_table.h',
+ ],
+ }], # OS == "lb_shell" or OS == "starboard"
+ ],
+ 'target_conditions': [
+ ['<(use_glib)==0 or >(nacl_untrusted_build)==1', {
+ 'sources/': [
+ ['exclude', '^nix/'],
+ ],
+ 'sources!': [
+ 'atomicops_internals_x86_gcc.cc',
+ 'message_pump_glib.cc',
+ 'message_pump_aurax11.cc',
+ ],
+ }],
+ ['<(toolkit_uses_gtk)==0 or >(nacl_untrusted_build)==1', {
+ 'sources!': ['message_pump_gtk.cc'],
+ }],
+ ['(OS != "linux" and <(os_bsd) != 1 and OS != "android") or >(nacl_untrusted_build)==1', {
+ 'sources!': [
+ # Not automatically excluded by the *linux.cc rules.
+ 'linux_util.cc',
+ ],
+ },
+ ],
+ ['>(nacl_untrusted_build)==1', {
+ 'sources!': [
+ 'allocator/type_profiler_control.cc',
+ 'allocator/type_profiler_control.h',
+ 'base_paths.cc',
+ 'command_line.cc',
+ 'cpu.cc',
+ 'debug/stack_trace_posix.cc',
+ 'file_util.cc',
+ 'file_util_posix.cc',
+ 'file_util_proxy.cc',
+ 'files/file_path_watcher_kqueue.cc',
+ 'native_library_posix.cc',
+ 'path_service.cc',
+ 'platform_file_posix.cc',
+ 'posix/unix_domain_socket.cc',
+ 'process_posix.cc',
+ 'process_util.cc',
+ 'process_util_posix.cc',
+ 'rand_util_posix.cc',
+ 'scoped_native_library.cc',
+ 'files/scoped_temp_dir.cc',
+ 'shared_memory_posix.cc',
+ 'sys_info_posix.cc',
+ 'threading/sequenced_worker_pool.cc',
+ 'third_party/dynamic_annotations/dynamic_annotations.c',
+ ],
+ # Metrics won't work in the NaCl sandbox.
+ 'sources/': [ ['exclude', '^metrics/'] ],
+ }],
+ ['OS == "android" and >(nacl_untrusted_build)==0', {
+ 'sources!': [
+ 'base_paths_posix.cc',
+ 'files/file_path_watcher_kqueue.cc',
+ 'files/file_path_watcher_stub.cc',
+ 'system_monitor/system_monitor_posix.cc',
+ ],
+ 'sources/': [
+ ['include', '^files/file_path_watcher_linux\\.cc$'],
+ ['include', '^process_util_linux\\.cc$'],
+ ['include', '^sys_info_linux\\.cc$'],
+ ['include', '^sys_string_conversions_posix\\.cc$'],
+ ['include', '^worker_pool_linux\\.cc$'],
+ ],
+ }],
+ ['OS == "ios"', {
+ 'sources/': [
+ # Pull in specific Mac files for iOS (which have been filtered out
+ # by file name rules).
+ ['include', '^atomicops_internals_mac\\.'],
+ ['include', '^base_paths_mac\\.'],
+ ['include', '^file_util_mac\\.'],
+ ['include', '^file_version_info_mac\\.'],
+ ['include', '^mac/bind_objc_block\\.'],
+ ['include', '^mac/bundle_locations\\.'],
+ ['include', '^mac/foundation_util\\.'],
+ ['include', '^mac/mac_logging\\.'],
+ ['include', '^mac/objc_property_releaser\\.'],
+ ['include', '^mac/scoped_mach_port\\.'],
+ ['include', '^mac/scoped_nsautorelease_pool\\.'],
+ ['include', '^message_pump_mac\\.'],
+ ['include', '^threading/platform_thread_mac\\.'],
+ ['include', '^sys_string_conversions_mac\\.'],
+ ['include', '^time_mac\\.'],
+ ['include', '^worker_pool_mac\\.'],
+ # Exclude all process_util except the minimal implementation
+ # needed on iOS (mostly for unit tests).
+ ['exclude', '^process_util'],
+ ['include', '^process_util_ios\\.mm$'],
+ ],
+ 'sources!': [
+ 'message_pump_libevent.cc'
+ ],
+ }],
+ ['OS != "mac" or >(nacl_untrusted_build)==1', {
+ 'sources!': [
+ 'mac/scoped_aedesc.h'
+ ],
+ }],
+ # For now, just test the *BSD platforms enough to exclude them.
+ # Subsequent changes will include them further.
+ ['OS != "freebsd" or >(nacl_untrusted_build)==1', {
+ 'sources/': [ ['exclude', '_freebsd\\.cc$'] ],
+ },
+ ],
+ ['OS != "openbsd" or >(nacl_untrusted_build)==1', {
+ 'sources/': [ ['exclude', '_openbsd\\.cc$'] ],
+ },
+ ],
+ ['OS != "win" or >(nacl_untrusted_build)==1', {
+ 'sources/': [ ['exclude', '^win/'] ],
+ },
+ ],
+ ['(OS != "android" or >(nacl_untrusted_build)==1) and (OS != "lb_shell" or "<(target_arch)" != "android")', {
+ 'sources/': [ ['exclude', '^android/'] ],
+ },
+ ],
+ ['OS == "win" and >(nacl_untrusted_build)==0', {
+ 'include_dirs': [
+ '<(DEPTH)/third_party/wtl/include',
+ ],
+ 'sources!': [
+ 'event_recorder_stubs.cc',
+ 'files/file_path_watcher_kqueue.cc',
+ 'files/file_path_watcher_stub.cc',
+ 'message_pump_libevent.cc',
+ 'posix/file_descriptor_shuffle.cc',
+ # Not using sha1_win.cc because it may have caused a
+ # regression to page cycler moz.
+ 'sha1_win.cc',
+ 'string16.cc',
+ ],
+ },],
+ ['OS == "linux" and >(nacl_untrusted_build)==0', {
+ 'sources!': [
+ 'files/file_path_watcher_kqueue.cc',
+ 'files/file_path_watcher_stub.cc',
+ ],
+ }],
+ ['(OS == "mac" or OS == "ios") and >(nacl_untrusted_build)==0', {
+ 'sources/': [
+ ['exclude', '^files/file_path_watcher_stub\\.cc$'],
+ ['exclude', '^base_paths_posix\\.cc$'],
+ ['exclude', '^native_library_posix\\.cc$'],
+ ['exclude', '^sys_string_conversions_posix\\.cc$'],
+ ],
+ }],
+ ['<(os_bsd)==1 and >(nacl_untrusted_build)==0', {
+ 'sources/': [
+ ['exclude', '^files/file_path_watcher_linux\\.cc$'],
+ ['exclude', '^files/file_path_watcher_stub\\.cc$'],
+ ['exclude', '^file_util_linux\\.cc$'],
+ ['exclude', '^process_linux\\.cc$'],
+ ['exclude', '^process_util_linux\\.cc$'],
+ ['exclude', '^sys_info_linux\\.cc$'],
+ ],
+ }],
+ ['<(chromeos)!=1 or >(nacl_untrusted_build)==1', {
+ 'sources/': [
+ ['exclude', '^chromeos/'],
+ ],
+ }],
+ # Remove all unnecessary files for build_nexe.py to avoid exceeding
+ # command-line-string limitation when building NaCl on Windows.
+ ['OS == "win" and >(nacl_untrusted_build)==1', {
+ 'sources/': [ ['exclude', '\\.h$'] ],
+ }],
+ ],
+ }],
+ ],
+ },
+}
diff --git a/src/base/base64.cc b/src/base/base64.cc
new file mode 100644
index 0000000..1907978
--- /dev/null
+++ b/src/base/base64.cc
@@ -0,0 +1,43 @@
+// Copyright (c) 2012 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/base64.h"
+
+#include "third_party/modp_b64/modp_b64.h"
+
+namespace base {
+
+bool Base64Encode(const StringPiece& input, std::string* output) {
+ std::string temp;
+ temp.resize(modp_b64_encode_len(input.size())); // makes room for null byte
+
+ // null terminates result since result is base64 text!
+ int input_size = static_cast<int>(input.size());
+
+ // modp_b64_encode_len() returns at least 1, so temp[0] is safe to use.
+ int output_size = modp_b64_encode(&(temp[0]), input.data(), input_size);
+ if (output_size < 0)
+ return false;
+
+ temp.resize(output_size); // strips off null byte
+ output->swap(temp);
+ return true;
+}
+
+bool Base64Decode(const StringPiece& input, std::string* output) {
+ std::string temp;
+ temp.resize(modp_b64_decode_len(input.size()));
+
+ // does not null terminate result since result is binary data!
+ int input_size = static_cast<int>(input.size());
+ int output_size = modp_b64_decode(&(temp[0]), input.data(), input_size);
+ if (output_size < 0)
+ return false;
+
+ temp.resize(output_size);
+ output->swap(temp);
+ return true;
+}
+
+} // namespace base
diff --git a/src/base/base64.h b/src/base/base64.h
new file mode 100644
index 0000000..983d5e2
--- /dev/null
+++ b/src/base/base64.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_BASE64_H__
+#define BASE_BASE64_H__
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/string_piece.h"
+
+namespace base {
+
+// Encodes the input string in base64. Returns true if successful and false
+// otherwise. The output string is only modified if successful.
+BASE_EXPORT bool Base64Encode(const StringPiece& input, std::string* output);
+
+// Decodes the base64 input string. Returns true if successful and false
+// otherwise. The output string is only modified if successful.
+BASE_EXPORT bool Base64Decode(const StringPiece& input, std::string* output);
+
+} // namespace base
+
+#endif // BASE_BASE64_H__
diff --git a/src/base/base64_unittest.cc b/src/base/base64_unittest.cc
new file mode 100644
index 0000000..9a5dd81
--- /dev/null
+++ b/src/base/base64_unittest.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 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/base64.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(Base64Test, Basic) {
+ const std::string kText = "hello world";
+ const std::string kBase64Text = "aGVsbG8gd29ybGQ=";
+
+ std::string encoded;
+ std::string decoded;
+ bool ok;
+
+ ok = Base64Encode(kText, &encoded);
+ EXPECT_TRUE(ok);
+ EXPECT_EQ(kBase64Text, encoded);
+
+ ok = Base64Decode(encoded, &decoded);
+ EXPECT_TRUE(ok);
+ EXPECT_EQ(kText, decoded);
+}
+
+} // namespace base
diff --git a/src/base/base_export.h b/src/base/base_export.h
new file mode 100644
index 0000000..fdfa5f9
--- /dev/null
+++ b/src/base/base_export.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2012 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_EXPORT_H_
+#define BASE_BASE_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(_MSC_VER)
+#if defined(BASE_IMPLEMENTATION)
+#define BASE_EXPORT __declspec(dllexport)
+#define BASE_EXPORT_PRIVATE __declspec(dllexport)
+#else
+#define BASE_EXPORT __declspec(dllimport)
+#define BASE_EXPORT_PRIVATE __declspec(dllimport)
+#endif // defined(BASE_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(BASE_IMPLEMENTATION)
+#define BASE_EXPORT __attribute__((visibility("default")))
+#define BASE_EXPORT_PRIVATE __attribute__((visibility("default")))
+#else
+#define BASE_EXPORT
+#define BASE_EXPORT_PRIVATE
+#endif // defined(BASE_IMPLEMENTATION)
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define BASE_EXPORT
+#define BASE_EXPORT_PRIVATE
+#endif
+
+#endif // BASE_BASE_EXPORT_H_
diff --git a/src/base/base_paths.cc b/src/base/base_paths.cc
new file mode 100644
index 0000000..80105b6
--- /dev/null
+++ b/src/base/base_paths.cc
@@ -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 case 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
diff --git a/src/base/base_paths.h b/src/base/base_paths.h
new file mode 100644
index 0000000..7bc23a8
--- /dev/null
+++ b/src/base/base_paths.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 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 "build/build_config.h"
+
+#if defined(OS_WIN)
+#include "base/base_paths_win.h"
+#elif defined(OS_MACOSX)
+#include "base/base_paths_mac.h"
+#elif defined(OS_ANDROID) || defined(__LB_ANDROID__)
+#include "base/base_paths_android.h"
+#elif defined(OS_STARBOARD)
+#include "base/base_paths_starboard.h"
+#endif
+
+#if defined(OS_POSIX)
+#include "base/base_paths_posix.h"
+#endif
+
+namespace base {
+
+enum BasePathKey {
+ PATH_START = 0,
+
+ DIR_CURRENT, // current directory
+ DIR_EXE, // directory containing FILE_EXE
+ DIR_MODULE, // directory containing FILE_MODULE
+ DIR_TEMP, // temporary directory
+ 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.
+ DIR_USER_DESKTOP, // The current user's Desktop.
+
+ PATH_END
+};
+
+} // namespace base
+
+#endif // BASE_BASE_PATHS_H_
diff --git a/src/base/base_paths_android.cc b/src/base/base_paths_android.cc
new file mode 100644
index 0000000..9905200
--- /dev/null
+++ b/src/base/base_paths_android.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2012 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 base::PathProviderAndroid which replaces base::PathProviderPosix for
+// Android in base/path_service.cc.
+
+#include <unistd.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/path_utils.h"
+#include "base/base_paths.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/process_util.h"
+
+namespace base {
+
+bool PathProviderAndroid(int key, FilePath* result) {
+ switch (key) {
+ case base::FILE_EXE: {
+ char bin_dir[PATH_MAX + 1];
+ int bin_dir_size = readlink(kProcSelfExe, bin_dir, PATH_MAX);
+ if (bin_dir_size < 0 || bin_dir_size > PATH_MAX) {
+ NOTREACHED() << "Unable to resolve " << kProcSelfExe << ".";
+ return false;
+ }
+ bin_dir[bin_dir_size] = 0;
+ *result = FilePath(bin_dir);
+ return true;
+ }
+ case base::FILE_MODULE:
+ // dladdr didn't work in Android as only the file name was returned.
+ NOTIMPLEMENTED();
+ return false;
+ case base::DIR_MODULE:
+ return base::android::GetNativeLibraryDirectory(result);
+ case base::DIR_SOURCE_ROOT:
+ // This const is only used for tests.
+ return base::android::GetExternalStorageDirectory(result);
+ case base::DIR_USER_DESKTOP:
+ // Android doesn't support GetUserDesktop.
+ NOTIMPLEMENTED();
+ return false;
+ case base::DIR_CACHE:
+ return base::android::GetCacheDirectory(result);
+ case base::DIR_ANDROID_APP_DATA:
+ return base::android::GetDataDirectory(result);
+ case base::DIR_HOME:
+ *result = file_util::GetHomeDir();
+ return true;
+ case base::DIR_ANDROID_EXTERNAL_STORAGE:
+ return base::android::GetExternalStorageDirectory(result);
+ default:
+ // Note: the path system expects this function to override the default
+ // behavior. So no need to log an error if we don't support a given
+ // path. The system will just use the default.
+ return false;
+ }
+}
+
+} // namespace base
diff --git a/src/base/base_paths_android.h b/src/base/base_paths_android.h
new file mode 100644
index 0000000..7a9ac4a
--- /dev/null
+++ b/src/base/base_paths_android.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2012 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_ANDROID_H_
+#define BASE_BASE_PATHS_ANDROID_H_
+
+// This file declares Android-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_ANDROID_START = 300,
+
+ DIR_ANDROID_APP_DATA, // Directory where to put Android app's data.
+ DIR_ANDROID_EXTERNAL_STORAGE, // Android external storage directory.
+
+ PATH_ANDROID_END
+};
+
+} // namespace base
+
+#endif // BASE_BASE_PATHS_ANDROID_H_
diff --git a/src/base/base_paths_mac.h b/src/base/base_paths_mac.h
new file mode 100644
index 0000000..ac75402
--- /dev/null
+++ b/src/base/base_paths_mac.h
@@ -0,0 +1,24 @@
+// 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,
+
+ DIR_APP_DATA, // ~/Library/Application Support
+
+ PATH_MAC_END
+};
+
+} // namespace base
+
+#endif // BASE_BASE_PATHS_MAC_H_
diff --git a/src/base/base_paths_mac.mm b/src/base/base_paths_mac.mm
new file mode 100644
index 0000000..630f742
--- /dev/null
+++ b/src/base/base_paths_mac.mm
@@ -0,0 +1,112 @@
+// Copyright (c) 2012 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 base::PathProviderMac which replaces base::PathProviderPosix for Mac
+// in base/path_service.cc.
+
+#include <dlfcn.h>
+#import <Foundation/Foundation.h>
+#include <mach-o/dyld.h>
+
+#include "base/base_paths.h"
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "build/build_config.h"
+
+namespace {
+
+void GetNSExecutablePath(FilePath* path) {
+ DCHECK(path);
+ // Executable path can have relative references ("..") depending on
+ // how the app was launched.
+ uint32_t executable_length = 0;
+ _NSGetExecutablePath(NULL, &executable_length);
+ DCHECK_GT(executable_length, 1u);
+ std::string executable_path;
+ int rv = _NSGetExecutablePath(WriteInto(&executable_path, executable_length),
+ &executable_length);
+ DCHECK_EQ(rv, 0);
+ *path = FilePath(executable_path);
+}
+
+// Returns true if the module for |address| is found. |path| will contain
+// the path to the module. Note that |path| may not be absolute.
+bool GetModulePathForAddress(FilePath* path,
+ const void* address) WARN_UNUSED_RESULT;
+
+bool GetModulePathForAddress(FilePath* path, const void* address) {
+ Dl_info info;
+ if (dladdr(address, &info) == 0)
+ return false;
+ *path = FilePath(info.dli_fname);
+ return true;
+}
+
+} // namespace
+
+namespace base {
+
+bool PathProviderMac(int key, FilePath* result) {
+ switch (key) {
+ case base::FILE_EXE:
+ GetNSExecutablePath(result);
+ return true;
+ case base::FILE_MODULE:
+ return GetModulePathForAddress(result,
+ reinterpret_cast<const void*>(&base::PathProviderMac));
+ case base::DIR_APP_DATA: {
+ bool success = base::mac::GetUserDirectory(NSApplicationSupportDirectory,
+ result);
+#if defined(OS_IOS)
+ // On IOS, this directory does not exist unless it is created explicitly.
+ if (success && !file_util::PathExists(*result))
+ success = file_util::CreateDirectory(*result);
+#endif // defined(OS_IOS)
+ return success;
+ }
+ case base::DIR_SOURCE_ROOT:
+ // Go through PathService to catch overrides.
+ if (!PathService::Get(base::FILE_EXE, result))
+ return false;
+
+ // Start with the executable's directory.
+ *result = result->DirName();
+
+#if !defined(OS_IOS)
+ if (base::mac::AmIBundled()) {
+ // The bundled app executables (Chromium, TestShell, etc) live five
+ // levels down, eg:
+ // src/xcodebuild/{Debug|Release}/Chromium.app/Contents/MacOS/Chromium
+ *result = result->DirName().DirName().DirName().DirName().DirName();
+ } else {
+ // Unit tests execute two levels deep from the source root, eg:
+ // src/xcodebuild/{Debug|Release}/base_unittests
+ *result = result->DirName().DirName();
+ }
+#endif
+ return true;
+ case base::DIR_USER_DESKTOP:
+#if defined(OS_IOS)
+ // iOS does not have desktop directories.
+ NOTIMPLEMENTED();
+ return false;
+#else
+ return base::mac::GetUserDirectory(NSDesktopDirectory, result);
+#endif
+ case base::DIR_CACHE:
+ return base::mac::GetUserDirectory(NSCachesDirectory, result);
+ case base::DIR_HOME:
+ *result = base::mac::NSStringToFilePath(NSHomeDirectory());
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace base
diff --git a/src/base/base_paths_posix.cc b/src/base/base_paths_posix.cc
new file mode 100644
index 0000000..834dee3
--- /dev/null
+++ b/src/base/base_paths_posix.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2012 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 base::PathProviderPosix, default path provider on POSIX OSes that
+// don't have their own base_paths_OS.cc implementation (i.e. all but Mac and
+// Android).
+
+#include <ostream>
+#include <string>
+
+#include "base/base_paths.h"
+#include "base/environment.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "base/nix/xdg_util.h"
+#include "build/build_config.h"
+
+#if defined(OS_FREEBSD)
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#elif defined(OS_SOLARIS)
+#include <stdlib.h>
+#endif
+
+namespace base {
+
+bool PathProviderPosix(int key, FilePath* result) {
+ FilePath path;
+ switch (key) {
+ case base::FILE_EXE:
+ case base::FILE_MODULE: { // TODO(evanm): is this correct?
+#if defined(OS_LINUX)
+ FilePath bin_dir;
+ if (!file_util::ReadSymbolicLink(FilePath(kProcSelfExe), &bin_dir)) {
+ NOTREACHED() << "Unable to resolve " << kProcSelfExe << ".";
+ return false;
+ }
+ *result = bin_dir;
+ return true;
+#elif defined(OS_FREEBSD)
+ int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
+ char bin_dir[PATH_MAX + 1];
+ size_t length = sizeof(bin_dir);
+ // Upon return, |length| is the number of bytes written to |bin_dir|
+ // including the string terminator.
+ int error = sysctl(name, 4, bin_dir, &length, NULL, 0);
+ if (error < 0 || length <= 1) {
+ NOTREACHED() << "Unable to resolve path.";
+ return false;
+ }
+ *result = FilePath(FilePath::StringType(bin_dir, length - 1));
+ return true;
+#elif defined(OS_SOLARIS)
+ char bin_dir[PATH_MAX + 1];
+ if (realpath(getexecname(), bin_dir) == NULL) {
+ NOTREACHED() << "Unable to resolve " << getexecname() << ".";
+ return false;
+ }
+ *result = FilePath(bin_dir);
+ return true;
+#elif defined(OS_OPENBSD)
+ // There is currently no way to get the executable path on OpenBSD
+ char* cpath;
+ if ((cpath = getenv("CHROME_EXE_PATH")) != NULL)
+ *result = FilePath(cpath);
+ else
+ *result = FilePath("/usr/local/chrome/chrome");
+ return true;
+#endif
+ }
+ case base::DIR_SOURCE_ROOT: {
+ // Allow passing this in the environment, for more flexibility in build
+ // tree configurations (sub-project builds, gyp --output_dir, etc.)
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ std::string cr_source_root;
+ if (env->GetVar("CR_SOURCE_ROOT", &cr_source_root)) {
+ path = FilePath(cr_source_root);
+ if (file_util::PathExists(path)) {
+ *result = path;
+ return true;
+ } else {
+ DLOG(WARNING) << "CR_SOURCE_ROOT is set, but it appears to not "
+ << "point to a directory.";
+ }
+ }
+ // On POSIX, unit tests execute two levels deep from the source root.
+ // For example: out/{Debug|Release}/net_unittest
+ if (PathService::Get(base::DIR_EXE, &path)) {
+ *result = path.DirName().DirName();
+ return true;
+ }
+
+ DLOG(ERROR) << "Couldn't find your source root. "
+ << "Try running from your chromium/src directory.";
+ return false;
+ }
+ case base::DIR_USER_DESKTOP:
+ *result = base::nix::GetXDGUserDirectory("DESKTOP", "Desktop");
+ return true;
+ case base::DIR_CACHE: {
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ FilePath cache_dir(base::nix::GetXDGDirectory(env.get(), "XDG_CACHE_HOME",
+ ".cache"));
+ *result = cache_dir;
+ return true;
+ }
+ case base::DIR_HOME:
+ *result = file_util::GetHomeDir();
+ return true;
+ }
+ return false;
+}
+
+} // namespace base
diff --git a/src/base/base_paths_posix.h b/src/base/base_paths_posix.h
new file mode 100644
index 0000000..811c8cb
--- /dev/null
+++ b/src/base/base_paths_posix.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 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_POSIX_H_
+#define BASE_BASE_PATHS_POSIX_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_POSIX_START = 400,
+
+ DIR_CACHE, // Directory where to put cache data. Note this is
+ // *not* where the browser cache lives, but the
+ // browser cache can be a subdirectory.
+ // This is $XDG_CACHE_HOME on Linux and
+ // ~/Library/Caches on Mac.
+ DIR_HOME, // $HOME on POSIX-like systems.
+
+ PATH_POSIX_END
+};
+
+} // namespace base
+
+#endif // BASE_BASE_PATHS_POSIX_H_
diff --git a/src/base/base_paths_shell.cc b/src/base/base_paths_shell.cc
new file mode 100644
index 0000000..aae1d80
--- /dev/null
+++ b/src/base/base_paths_shell.cc
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2012 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "base/base_paths.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "cobalt/deprecated/platform_delegate.h"
+
+namespace base {
+
+// this is where we can control the path for placement
+// of a lot of file resources for lbshell.
+bool PathProviderShell(int key, FilePath* result) {
+ cobalt::deprecated::PlatformDelegate* plat =
+ cobalt::deprecated::PlatformDelegate::Get();
+ switch (key) {
+ case base::DIR_EXE:
+ case base::DIR_MODULE:
+ DCHECK(!plat->game_content_path().empty());
+ *result = FilePath(plat->game_content_path());
+ return true;
+
+#if defined(ENABLE_DIR_SOURCE_ROOT_ACCESS)
+ case base::DIR_SOURCE_ROOT:
+ if (plat->dir_source_root().length() > 0) {
+ *result = FilePath(plat->dir_source_root());
+ return true;
+ } else {
+ DLOG(INFO) << "DIR_SOURCE_ROOT not defined - skipping.";
+ return false;
+ }
+#endif // ENABLE_DIR_SOURCE_ROOT_ACCESS
+
+ case base::DIR_TEMP:
+ DCHECK(!plat->temp_path().empty());
+ *result = FilePath(plat->temp_path());
+ return true;
+
+ case base::DIR_CACHE:
+ DCHECK(!plat->temp_path().empty());
+ *result = FilePath(plat->temp_path()).Append("cache");
+ return true;
+ case base::DIR_HOME: {
+#if defined(COBALT_LINUX)
+ const char* home_dir = getenv("HOME");
+ if (home_dir && home_dir[0]) {
+ *result = FilePath(home_dir);
+ return true;
+ } else {
+ return PathProviderShell(base::DIR_TEMP, result);
+ }
+#else
+ return PathProviderShell(base::DIR_EXE, result);
+#endif // defined(COBALT_LINUX)
+ }
+ }
+
+ return false;
+}
+
+} // namespace base
+
diff --git a/src/base/base_paths_starboard.cc b/src/base/base_paths_starboard.cc
new file mode 100644
index 0000000..2b8a14a
--- /dev/null
+++ b/src/base/base_paths_starboard.cc
@@ -0,0 +1,98 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/base_paths.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "starboard/system.h"
+
+namespace base {
+
+// This is where we can control the path for placement of a lot of file
+// resources for cobalt.
+bool PathProviderStarboard(int key, FilePath *result) {
+ char path[SB_FILE_MAX_PATH];
+ switch (key) {
+ case base::FILE_EXE: {
+ bool success = SbSystemGetPath(kSbSystemPathExecutableFile, path,
+ SB_ARRAY_SIZE_INT(path));
+ DCHECK(success);
+ if (success) {
+ *result = FilePath(path);
+ return true;
+ }
+ DLOG(ERROR) << "FILE_EXE not defined.";
+ return false;
+ }
+
+ case base::DIR_EXE:
+ case base::DIR_MODULE: {
+ bool success = SbSystemGetPath(kSbSystemPathContentDirectory, path,
+ SB_ARRAY_SIZE_INT(path));
+ DCHECK(success);
+ if (success) {
+ *result = FilePath(path);
+ return true;
+ }
+ DLOG(ERROR) << "DIR_EXE/DIR_MODULE not defined.";
+ return false;
+ }
+
+#if defined(ENABLE_DIR_SOURCE_ROOT_ACCESS)
+ case base::DIR_SOURCE_ROOT: {
+ bool success = SbSystemGetPath(kSbSystemPathSourceDirectory, path,
+ SB_ARRAY_SIZE_INT(path));
+ DCHECK(success);
+ if (success) {
+ *result = FilePath(path);
+ return true;
+ }
+ DLOG(ERROR) << "DIR_SOURCE_ROOT not defined.";
+ return false;
+ }
+#endif // ENABLE_DIR_SOURCE_ROOT_ACCESS
+
+ case base::DIR_CACHE: {
+ bool success = SbSystemGetPath(kSbSystemPathCacheDirectory, path,
+ SB_ARRAY_SIZE_INT(path));
+ if (success) {
+ *result = FilePath(path);
+ return true;
+ }
+ DLOG(INFO) << "DIR_CACHE not defined.";
+ return false;
+ }
+
+ case base::DIR_TEMP: {
+ bool success = SbSystemGetPath(kSbSystemPathTempDirectory, path,
+ SB_ARRAY_SIZE_INT(path));
+ DCHECK(success);
+ if (success) {
+ *result = FilePath(path);
+ return true;
+ }
+ DLOG(ERROR) << "DIR_TEMP not defined.";
+ return false;
+ }
+
+ case base::DIR_HOME:
+ // TODO: Add a home directory to SbSystemPathId and get it from there.
+ return PathProviderStarboard(base::DIR_CACHE, result);
+ }
+
+ NOTREACHED() << "key = " << key;
+ return false;
+}
+
+}
diff --git a/src/base/base_paths_starboard.h b/src/base/base_paths_starboard.h
new file mode 100644
index 0000000..cfa9099
--- /dev/null
+++ b/src/base/base_paths_starboard.h
@@ -0,0 +1,39 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef BASE_BASE_PATHS_STARBOARD_H_
+#define BASE_BASE_PATHS_STARBOARD_H_
+
+// This file declares Starboard-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_STARBOARD_START = 500,
+
+ DIR_CACHE, // Directory where to put cache data. Note this is
+ // *not* where the browser cache lives, but the
+ // browser cache can be a subdirectory.
+
+ DIR_HOME, // The root of the primary directory for a user (and their
+ // programs) to place their personal files.
+
+ PATH_STARBOARD_END
+};
+
+} // namespace base
+
+#endif // BASE_BASE_PATHS_STARBOARD_H_
diff --git a/src/base/base_paths_win.cc b/src/base/base_paths_win.cc
new file mode 100644
index 0000000..a06d908
--- /dev/null
+++ b/src/base/base_paths_win.cc
@@ -0,0 +1,209 @@
+// Copyright (c) 2012 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 <shlobj.h>
+
+#include "base/base_paths.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/win/scoped_co_mem.h"
+#include "base/win/windows_version.h"
+
+// http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
+extern "C" IMAGE_DOS_HEADER __ImageBase;
+
+namespace {
+
+bool GetQuickLaunchPath(bool default_user, FilePath* result) {
+ if (default_user) {
+ wchar_t system_buffer[MAX_PATH];
+ system_buffer[0] = 0;
+ // As per MSDN, passing -1 for |hToken| indicates the Default user:
+ // http://msdn.microsoft.com/library/windows/desktop/bb762181.aspx
+ if (FAILED(SHGetFolderPath(NULL, CSIDL_APPDATA,
+ reinterpret_cast<HANDLE>(-1), SHGFP_TYPE_CURRENT,
+ system_buffer))) {
+ return false;
+ }
+ *result = FilePath(system_buffer);
+ } else if (!PathService::Get(base::DIR_APP_DATA, result)) {
+ // For the current user, grab the APPDATA directory directly from the
+ // PathService cache.
+ return false;
+ }
+ // According to various sources, appending
+ // "Microsoft\Internet Explorer\Quick Launch" to %appdata% is the only
+ // reliable way to get the quick launch folder across all versions of Windows.
+ // http://stackoverflow.com/questions/76080/how-do-you-reliably-get-the-quick-
+ // http://www.microsoft.com/technet/scriptcenter/resources/qanda/sept05/hey0901.mspx
+ *result = result->AppendASCII("Microsoft");
+ *result = result->AppendASCII("Internet Explorer");
+ *result = result->AppendASCII("Quick Launch");
+ return true;
+}
+
+} // namespace
+
+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;
+ 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_FILESX86:
+ if (base::win::OSInfo::GetInstance()->architecture() !=
+ base::win::OSInfo::X86_ARCHITECTURE) {
+ if (FAILED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILESX86, NULL,
+ SHGFP_TYPE_CURRENT, system_buffer)))
+ return false;
+ cur = FilePath(system_buffer);
+ break;
+ }
+ // Fall through to base::DIR_PROGRAM_FILES if we're on an X86 machine.
+ 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_COMMON_APP_DATA:
+ if (FAILED(SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL,
+ SHGFP_TYPE_CURRENT, system_buffer)))
+ return false;
+ cur = FilePath(system_buffer);
+ break;
+ case base::DIR_PROFILE:
+ if (FAILED(SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT,
+ system_buffer)))
+ return false;
+ cur = FilePath(system_buffer);
+ break;
+ case base::DIR_LOCAL_APP_DATA_LOW:
+ if (win::GetVersion() < win::VERSION_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;
+ cur = FilePath(system_buffer).DirName().AppendASCII("LocalLow");
+ 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: {
+ FilePath executableDir;
+ // 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, &executableDir);
+ cur = executableDir.DirName().DirName();
+ break;
+ }
+ case base::DIR_APP_SHORTCUTS: {
+ if (win::GetVersion() < win::VERSION_WIN8)
+ return false;
+
+ base::win::ScopedCoMem<wchar_t> path_buf;
+ if (FAILED(SHGetKnownFolderPath(FOLDERID_ApplicationShortcuts, 0, NULL,
+ &path_buf)))
+ return false;
+
+ cur = FilePath(string16(path_buf));
+ break;
+ }
+ case base::DIR_USER_DESKTOP:
+ if (FAILED(SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY, NULL,
+ SHGFP_TYPE_CURRENT, system_buffer))) {
+ return false;
+ }
+ cur = FilePath(system_buffer);
+ break;
+ case base::DIR_COMMON_DESKTOP:
+ if (FAILED(SHGetFolderPath(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, NULL,
+ SHGFP_TYPE_CURRENT, system_buffer))) {
+ return false;
+ }
+ cur = FilePath(system_buffer);
+ break;
+ case base::DIR_USER_QUICK_LAUNCH:
+ if (!GetQuickLaunchPath(false, &cur))
+ return false;
+ break;
+ case base::DIR_DEFAULT_USER_QUICK_LAUNCH:
+ if (!GetQuickLaunchPath(true, &cur))
+ return false;
+ break;
+ case base::DIR_TASKBAR_PINS:
+ if (!PathService::Get(base::DIR_USER_QUICK_LAUNCH, &cur))
+ return false;
+ cur = cur.AppendASCII("User Pinned");
+ cur = cur.AppendASCII("TaskBar");
+ break;
+ default:
+ return false;
+ }
+
+ *result = cur;
+ return true;
+}
+
+} // namespace base
diff --git a/src/base/base_paths_win.h b/src/base/base_paths_win.h
new file mode 100644
index 0000000..11bc111
--- /dev/null
+++ b/src/base/base_paths_win.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2012 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,
+
+ DIR_WINDOWS, // Windows directory, usually "c:\windows"
+ DIR_SYSTEM, // Usually c:\windows\system32"
+ DIR_PROGRAM_FILES, // Usually c:\program files
+ DIR_PROGRAM_FILESX86, // Usually c:\program files or c:\program files (x86)
+
+ 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_PROFILE, // Usually "C:\Documents and settings\<user>.
+ 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_COMMON_APP_DATA, // W2K, XP, W2K3: "C:\Documents and Settings\
+ // All Users\Application Data".
+ // Vista, W2K8 and above: "C:\ProgramData".
+ DIR_APP_SHORTCUTS, // Where tiles on the start screen are stored, only
+ // for Windows 8. Maps to "Local\AppData\Microsoft\
+ // Windows\Application Shortcuts\".
+ DIR_COMMON_DESKTOP, // Directory for the common desktop (visible
+ // on all user's Desktop).
+ DIR_USER_QUICK_LAUNCH, // Directory for the quick launch shortcuts.
+ DIR_DEFAULT_USER_QUICK_LAUNCH, // Directory for the quick launch shortcuts
+ // of the Default user.
+ DIR_TASKBAR_PINS, // Directory for the shortcuts pinned to taskbar via
+ // base::win::TaskbarPinShortcutLink().
+
+ PATH_WIN_END
+};
+
+} // namespace base
+
+#endif // BASE_BASE_PATHS_WIN_H__
diff --git a/src/base/base_switches.cc b/src/base/base_switches.cc
new file mode 100644
index 0000000..7c7b061
--- /dev/null
+++ b/src/base/base_switches.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 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 base/debug/debug_on_start_win.h, the process will
+// (on Windows only) 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 char kDebugOnStart[] = "debug-on-start";
+
+// Disables the crash reporting.
+const char kDisableBreakpad[] = "disable-breakpad";
+
+// Enable DCHECKs in release mode.
+const char kEnableDCHECK[] = "enable-dcheck";
+
+// Generates full memory crash dump.
+const char kFullMemoryCrashReport[] = "full-memory-crash-report";
+
+// Suppresses all error dialogs when present.
+const char kNoErrorDialogs[] = "noerrdialogs";
+
+// When running certain tests that spawn child processes, this switch indicates
+// to the test framework that the current process is a child process.
+const char kTestChildProcess[] = "test-child-process";
+
+// Gives the default maximal active V-logging level; 0 is the default.
+// Normally positive values are used for V-logging levels.
+const char kV[] = "v";
+
+// Gives the per-module maximal V-logging levels to override the value
+// given by --v. E.g. "my_module=2,foo*=3" would change the logging
+// level for all code in source files "my_module.*" and "foo*.*"
+// ("-inl" suffixes are also disregarded for this matching).
+//
+// Any pattern containing a forward or backward slash will be tested
+// against the whole pathname and not just the module. E.g.,
+// "*/foo/bar/*=2" would change the logging level for all code in
+// source files under a "foo/bar" directory.
+const char kVModule[] = "vmodule";
+
+// Will wait for 60 seconds for a debugger to come to attach to the process.
+const char kWaitForDebugger[] = "wait-for-debugger";
+
+} // namespace switches
diff --git a/src/base/base_switches.h b/src/base/base_switches.h
new file mode 100644
index 0000000..558a5cf
--- /dev/null
+++ b/src/base/base_switches.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 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 char kDebugOnStart[];
+extern const char kDisableBreakpad[];
+extern const char kEnableDCHECK[];
+extern const char kFullMemoryCrashReport[];
+extern const char kNoErrorDialogs[];
+extern const char kTestChildProcess[];
+extern const char kV[];
+extern const char kVModule[];
+extern const char kWaitForDebugger[];
+
+} // namespace switches
+
+#endif // BASE_BASE_SWITCHES_H_
diff --git a/src/base/base_unittests.isolate b/src/base/base_unittests.isolate
new file mode 100644
index 0000000..f13e66b
--- /dev/null
+++ b/src/base/base_unittests.isolate
@@ -0,0 +1,50 @@
+# Copyright (c) 2012 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': {
+ 'isolate_dependency_tracked': [
+ '../testing/test_env.py',
+ '../tools/swarm_client/run_isolated.py',
+ '../tools/swarm_client/run_test_cases.py',
+ '<(PRODUCT_DIR)/base_unittests<(EXECUTABLE_SUFFIX)',
+ 'data/json/bom_feff.json',
+ ],
+ 'isolate_dependency_untracked': [
+ 'data/file_util_unittest/',
+ ],
+ },
+ 'conditions': [
+ ['OS=="linux"', {
+ 'variables': {
+ 'command': [
+ '../testing/xvfb.py',
+ '<(PRODUCT_DIR)',
+ '../tools/swarm_client/run_test_cases.py',
+ '<(PRODUCT_DIR)/base_unittests<(EXECUTABLE_SUFFIX)',
+ ],
+ 'isolate_dependency_tracked': [
+ '../testing/xvfb.py',
+ '<(PRODUCT_DIR)/xdisplaycheck<(EXECUTABLE_SUFFIX)',
+ ],
+ },
+ }, {
+ 'variables': {
+ 'command': [
+ '../testing/test_env.py',
+ '../tools/swarm_client/run_test_cases.py',
+ '<(PRODUCT_DIR)/base_unittests<(EXECUTABLE_SUFFIX)',
+ ],
+ },
+ }],
+ ['OS=="win"', {
+ 'variables': {
+ 'isolate_dependency_tracked': [
+ '<(PRODUCT_DIR)/icudt.dll',
+ 'data/file_version_info_unittest/FileVersionInfoTest1.dll',
+ 'data/file_version_info_unittest/FileVersionInfoTest2.dll',
+ ],
+ },
+ }],
+ ],
+}
diff --git a/src/base/base_untrusted.gyp b/src/base/base_untrusted.gyp
new file mode 100644
index 0000000..8b64c7d
--- /dev/null
+++ b/src/base/base_untrusted.gyp
@@ -0,0 +1,39 @@
+# Copyright (c) 2012 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_untrusted.gypi',
+ 'base.gypi',
+ ],
+ 'conditions': [
+ ['disable_nacl==0 and disable_nacl_untrusted==0', {
+ 'targets': [
+ {
+ 'target_name': 'base_untrusted',
+ 'type': 'none',
+ 'variables': {
+ 'base_target': 1,
+ 'nacl_untrusted_build': 1,
+ 'nlib_target': 'libbase_untrusted.a',
+ 'build_glibc': 1,
+ 'build_newlib': 1,
+ 'sources': [
+ 'string16.cc',
+ 'sync_socket_nacl.cc',
+ 'third_party/nspr/prtime.cc',
+ 'time_posix.cc',
+ ],
+ },
+ 'dependencies': [
+ '<(DEPTH)/native_client/tools.gyp:prep_toolchain',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/src/base/basictypes.h b/src/base/basictypes.h
new file mode 100644
index 0000000..c393fd7
--- /dev/null
+++ b/src/base/basictypes.h
@@ -0,0 +1,365 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#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;
+typedef int int32;
+
+// The NSPR system headers define 64-bit as |long| when possible, except on
+// Mac OS X. In order to not have typedef mismatches, we do the same on LP64.
+//
+// On Mac OS X, |long long| is used for 64-bit types for compatibility with
+// <inttypes.h> format macros even in the LP64 model.
+#if defined(__LP64__) && !defined(OS_MACOSX) && !defined(OS_OPENBSD)
+typedef long int64;
+#else
+typedef long long int64;
+#endif
+
+// 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;
+typedef unsigned int uint32;
+
+// See the comment above about NSPR and 64-bit.
+#if defined(__LP64__) && !defined(OS_MACOSX) && !defined(OS_OPENBSD)
+typedef unsigned long uint64;
+#else
+typedef unsigned long long uint64;
+#endif
+
+// 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;
+
+#if defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4310) // Cast truncates constant value.
+#endif
+
+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));
+
+#if defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+
+// 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.
+// NOTE: The usage of this macro was baned from our code base, but some
+// third_party libraries are yet using it.
+// TODO(tfarina): Figure out how to fix the usage of this macro in the
+// third_party libraries and get rid of it.
+#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
+
+#if defined(COMPILER_GHS)
+// GHS does not support local types as template arguments, so we must fall back to the unsafe version
+#define arraysize ARRAYSIZE_UNSAFE
+#else
+#define arraysize(array) (sizeof(ArraySizeHelper(array)))
+#endif
+
+// 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 outer 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.
+
+
+// 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;
+}
+
+// Used to explicitly mark the return value of a function as unused. If you are
+// really sure you don't want to do anything with the return value of a function
+// that has been marked WARN_UNUSED_RESULT, wrap it with this. Example:
+//
+// scoped_ptr<MyType> my_var = ...;
+// if (TakeOwnership(my_var.get()) == SUCCESS)
+// ignore_result(my_var.release());
+//
+template<typename T>
+inline void ignore_result(const T&) {
+}
+
+// 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 };
+
+// Use these to declare and define a static local variable (static T;) so that
+// it is leaked so that its destructors are not called at exit. If you need
+// thread-safe initialization, use base/lazy_instance.h instead.
+#define CR_DEFINE_STATIC_LOCAL(type, name, arguments) \
+ static type& name = *new type arguments
+
+} // base
+
+#endif // BASE_BASICTYPES_H_
diff --git a/src/base/bind.h b/src/base/bind.h
new file mode 100644
index 0000000..5cf124d
--- /dev/null
+++ b/src/base/bind.h
@@ -0,0 +1,517 @@
+// This file was GENERATED by command:
+// pump.py bind.h.pump
+// DO NOT EDIT BY HAND!!!
+
+
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_BIND_H_
+#define BASE_BIND_H_
+
+#include "base/bind_internal.h"
+#include "base/callback_internal.h"
+
+// -----------------------------------------------------------------------------
+// Usage documentation
+// -----------------------------------------------------------------------------
+//
+// See base/callback.h for documentation.
+//
+//
+// -----------------------------------------------------------------------------
+// Implementation notes
+// -----------------------------------------------------------------------------
+//
+// If you're reading the implementation, before proceeding further, you should
+// read the top comment of base/bind_internal.h for a definition of common
+// terms and concepts.
+//
+// RETURN TYPES
+//
+// Though Bind()'s result is meant to be stored in a Callback<> type, it
+// cannot actually return the exact type without requiring a large amount
+// of extra template specializations. The problem is that in order to
+// discern the correct specialization of Callback<>, Bind would need to
+// unwrap the function signature to determine the signature's arity, and
+// whether or not it is a method.
+//
+// Each unique combination of (arity, function_type, num_prebound) where
+// function_type is one of {function, method, const_method} would require
+// one specialization. We eventually have to do a similar number of
+// specializations anyways in the implementation (see the Invoker<>,
+// classes). However, it is avoidable in Bind if we return the result
+// via an indirection like we do below.
+//
+// TODO(ajwong): We might be able to avoid this now, but need to test.
+//
+// It is possible to move most of the COMPILE_ASSERT asserts into BindState<>,
+// but it feels a little nicer to have the asserts here so people do not
+// need to crack open bind_internal.h. On the other hand, it makes Bind()
+// harder to read.
+
+namespace base {
+
+template <typename Functor>
+base::Callback<
+ typename internal::BindState<
+ typename internal::FunctorTraits<Functor>::RunnableType,
+ typename internal::FunctorTraits<Functor>::RunType,
+ void()>
+ ::UnboundRunType>
+Bind(Functor functor) {
+ // Typedefs for how to store and run the functor.
+ typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType;
+ typedef typename internal::FunctorTraits<Functor>::RunType RunType;
+
+ // Use RunnableType::RunType instead of RunType above because our
+ // checks should below for bound references need to know what the actual
+ // functor is going to interpret the argument as.
+ typedef internal::FunctionTraits<typename RunnableType::RunType>
+ BoundFunctorTraits;
+
+ typedef internal::BindState<RunnableType, RunType, void()> BindState;
+
+
+ return Callback<typename BindState::UnboundRunType>(
+ new BindState(internal::MakeRunnable(functor)));
+}
+
+template <typename Functor, typename P1>
+base::Callback<
+ typename internal::BindState<
+ typename internal::FunctorTraits<Functor>::RunnableType,
+ typename internal::FunctorTraits<Functor>::RunType,
+ void(typename internal::CallbackParamTraits<P1>::StorageType)>
+ ::UnboundRunType>
+Bind(Functor functor, const P1& p1) {
+ // Typedefs for how to store and run the functor.
+ typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType;
+ typedef typename internal::FunctorTraits<Functor>::RunType RunType;
+
+ // Use RunnableType::RunType instead of RunType above because our
+ // checks should below for bound references need to know what the actual
+ // functor is going to interpret the argument as.
+ typedef internal::FunctionTraits<typename RunnableType::RunType>
+ BoundFunctorTraits;
+
+ // Do not allow binding a non-const reference parameter. Non-const reference
+ // parameters are disallowed by the Google style guide. Also, binding a
+ // non-const reference parameter can make for subtle bugs because the
+ // invoked function will receive a reference to the stored copy of the
+ // argument and not the original.
+ COMPILE_ASSERT(
+ !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value ),
+ do_not_bind_functions_with_nonconst_ref);
+
+ // For methods, we need to be careful for parameter 1. We do not require
+ // a scoped_refptr because BindState<> itself takes care of AddRef() for
+ // methods. We also disallow binding of an array as the method's target
+ // object.
+ COMPILE_ASSERT(
+ internal::HasIsMethodTag<RunnableType>::value ||
+ !internal::NeedsScopedRefptrButGetsRawPtr<P1>::value,
+ p1_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::HasIsMethodTag<RunnableType>::value ||
+ !is_array<P1>::value,
+ first_bound_argument_to_method_cannot_be_array);
+ typedef internal::BindState<RunnableType, RunType,
+ void(typename internal::CallbackParamTraits<P1>::StorageType)> BindState;
+
+
+ return Callback<typename BindState::UnboundRunType>(
+ new BindState(internal::MakeRunnable(functor), p1));
+}
+
+template <typename Functor, typename P1, typename P2>
+base::Callback<
+ typename internal::BindState<
+ typename internal::FunctorTraits<Functor>::RunnableType,
+ typename internal::FunctorTraits<Functor>::RunType,
+ void(typename internal::CallbackParamTraits<P1>::StorageType,
+ typename internal::CallbackParamTraits<P2>::StorageType)>
+ ::UnboundRunType>
+Bind(Functor functor, const P1& p1, const P2& p2) {
+ // Typedefs for how to store and run the functor.
+ typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType;
+ typedef typename internal::FunctorTraits<Functor>::RunType RunType;
+
+ // Use RunnableType::RunType instead of RunType above because our
+ // checks should below for bound references need to know what the actual
+ // functor is going to interpret the argument as.
+ typedef internal::FunctionTraits<typename RunnableType::RunType>
+ BoundFunctorTraits;
+
+ // Do not allow binding a non-const reference parameter. Non-const reference
+ // parameters are disallowed by the Google style guide. Also, binding a
+ // non-const reference parameter can make for subtle bugs because the
+ // invoked function will receive a reference to the stored copy of the
+ // argument and not the original.
+ COMPILE_ASSERT(
+ !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A2Type>::value ),
+ do_not_bind_functions_with_nonconst_ref);
+
+ // For methods, we need to be careful for parameter 1. We do not require
+ // a scoped_refptr because BindState<> itself takes care of AddRef() for
+ // methods. We also disallow binding of an array as the method's target
+ // object.
+ COMPILE_ASSERT(
+ internal::HasIsMethodTag<RunnableType>::value ||
+ !internal::NeedsScopedRefptrButGetsRawPtr<P1>::value,
+ p1_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::HasIsMethodTag<RunnableType>::value ||
+ !is_array<P1>::value,
+ first_bound_argument_to_method_cannot_be_array);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P2>::value,
+ p2_is_refcounted_type_and_needs_scoped_refptr);
+ typedef internal::BindState<RunnableType, RunType,
+ void(typename internal::CallbackParamTraits<P1>::StorageType,
+ typename internal::CallbackParamTraits<P2>::StorageType)> BindState;
+
+
+ return Callback<typename BindState::UnboundRunType>(
+ new BindState(internal::MakeRunnable(functor), p1, p2));
+}
+
+template <typename Functor, typename P1, typename P2, typename P3>
+base::Callback<
+ typename internal::BindState<
+ typename internal::FunctorTraits<Functor>::RunnableType,
+ typename internal::FunctorTraits<Functor>::RunType,
+ void(typename internal::CallbackParamTraits<P1>::StorageType,
+ typename internal::CallbackParamTraits<P2>::StorageType,
+ typename internal::CallbackParamTraits<P3>::StorageType)>
+ ::UnboundRunType>
+Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3) {
+ // Typedefs for how to store and run the functor.
+ typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType;
+ typedef typename internal::FunctorTraits<Functor>::RunType RunType;
+
+ // Use RunnableType::RunType instead of RunType above because our
+ // checks should below for bound references need to know what the actual
+ // functor is going to interpret the argument as.
+ typedef internal::FunctionTraits<typename RunnableType::RunType>
+ BoundFunctorTraits;
+
+ // Do not allow binding a non-const reference parameter. Non-const reference
+ // parameters are disallowed by the Google style guide. Also, binding a
+ // non-const reference parameter can make for subtle bugs because the
+ // invoked function will receive a reference to the stored copy of the
+ // argument and not the original.
+ COMPILE_ASSERT(
+ !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A2Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A3Type>::value ),
+ do_not_bind_functions_with_nonconst_ref);
+
+ // For methods, we need to be careful for parameter 1. We do not require
+ // a scoped_refptr because BindState<> itself takes care of AddRef() for
+ // methods. We also disallow binding of an array as the method's target
+ // object.
+ COMPILE_ASSERT(
+ internal::HasIsMethodTag<RunnableType>::value ||
+ !internal::NeedsScopedRefptrButGetsRawPtr<P1>::value,
+ p1_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::HasIsMethodTag<RunnableType>::value ||
+ !is_array<P1>::value,
+ first_bound_argument_to_method_cannot_be_array);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P2>::value,
+ p2_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P3>::value,
+ p3_is_refcounted_type_and_needs_scoped_refptr);
+ typedef internal::BindState<RunnableType, RunType,
+ void(typename internal::CallbackParamTraits<P1>::StorageType,
+ typename internal::CallbackParamTraits<P2>::StorageType,
+ typename internal::CallbackParamTraits<P3>::StorageType)> BindState;
+
+
+ return Callback<typename BindState::UnboundRunType>(
+ new BindState(internal::MakeRunnable(functor), p1, p2, p3));
+}
+
+template <typename Functor, typename P1, typename P2, typename P3, typename P4>
+base::Callback<
+ typename internal::BindState<
+ typename internal::FunctorTraits<Functor>::RunnableType,
+ typename internal::FunctorTraits<Functor>::RunType,
+ void(typename internal::CallbackParamTraits<P1>::StorageType,
+ typename internal::CallbackParamTraits<P2>::StorageType,
+ typename internal::CallbackParamTraits<P3>::StorageType,
+ typename internal::CallbackParamTraits<P4>::StorageType)>
+ ::UnboundRunType>
+Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4) {
+ // Typedefs for how to store and run the functor.
+ typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType;
+ typedef typename internal::FunctorTraits<Functor>::RunType RunType;
+
+ // Use RunnableType::RunType instead of RunType above because our
+ // checks should below for bound references need to know what the actual
+ // functor is going to interpret the argument as.
+ typedef internal::FunctionTraits<typename RunnableType::RunType>
+ BoundFunctorTraits;
+
+ // Do not allow binding a non-const reference parameter. Non-const reference
+ // parameters are disallowed by the Google style guide. Also, binding a
+ // non-const reference parameter can make for subtle bugs because the
+ // invoked function will receive a reference to the stored copy of the
+ // argument and not the original.
+ COMPILE_ASSERT(
+ !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A2Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A3Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A4Type>::value ),
+ do_not_bind_functions_with_nonconst_ref);
+
+ // For methods, we need to be careful for parameter 1. We do not require
+ // a scoped_refptr because BindState<> itself takes care of AddRef() for
+ // methods. We also disallow binding of an array as the method's target
+ // object.
+ COMPILE_ASSERT(
+ internal::HasIsMethodTag<RunnableType>::value ||
+ !internal::NeedsScopedRefptrButGetsRawPtr<P1>::value,
+ p1_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::HasIsMethodTag<RunnableType>::value ||
+ !is_array<P1>::value,
+ first_bound_argument_to_method_cannot_be_array);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P2>::value,
+ p2_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P3>::value,
+ p3_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P4>::value,
+ p4_is_refcounted_type_and_needs_scoped_refptr);
+ typedef internal::BindState<RunnableType, RunType,
+ void(typename internal::CallbackParamTraits<P1>::StorageType,
+ typename internal::CallbackParamTraits<P2>::StorageType,
+ typename internal::CallbackParamTraits<P3>::StorageType,
+ typename internal::CallbackParamTraits<P4>::StorageType)> BindState;
+
+
+ return Callback<typename BindState::UnboundRunType>(
+ new BindState(internal::MakeRunnable(functor), p1, p2, p3, p4));
+}
+
+template <typename Functor, typename P1, typename P2, typename P3, typename P4,
+ typename P5>
+base::Callback<
+ typename internal::BindState<
+ typename internal::FunctorTraits<Functor>::RunnableType,
+ typename internal::FunctorTraits<Functor>::RunType,
+ void(typename internal::CallbackParamTraits<P1>::StorageType,
+ typename internal::CallbackParamTraits<P2>::StorageType,
+ typename internal::CallbackParamTraits<P3>::StorageType,
+ typename internal::CallbackParamTraits<P4>::StorageType,
+ typename internal::CallbackParamTraits<P5>::StorageType)>
+ ::UnboundRunType>
+Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4,
+ const P5& p5) {
+ // Typedefs for how to store and run the functor.
+ typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType;
+ typedef typename internal::FunctorTraits<Functor>::RunType RunType;
+
+ // Use RunnableType::RunType instead of RunType above because our
+ // checks should below for bound references need to know what the actual
+ // functor is going to interpret the argument as.
+ typedef internal::FunctionTraits<typename RunnableType::RunType>
+ BoundFunctorTraits;
+
+ // Do not allow binding a non-const reference parameter. Non-const reference
+ // parameters are disallowed by the Google style guide. Also, binding a
+ // non-const reference parameter can make for subtle bugs because the
+ // invoked function will receive a reference to the stored copy of the
+ // argument and not the original.
+ COMPILE_ASSERT(
+ !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A2Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A3Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A4Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A5Type>::value ),
+ do_not_bind_functions_with_nonconst_ref);
+
+ // For methods, we need to be careful for parameter 1. We do not require
+ // a scoped_refptr because BindState<> itself takes care of AddRef() for
+ // methods. We also disallow binding of an array as the method's target
+ // object.
+ COMPILE_ASSERT(
+ internal::HasIsMethodTag<RunnableType>::value ||
+ !internal::NeedsScopedRefptrButGetsRawPtr<P1>::value,
+ p1_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::HasIsMethodTag<RunnableType>::value ||
+ !is_array<P1>::value,
+ first_bound_argument_to_method_cannot_be_array);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P2>::value,
+ p2_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P3>::value,
+ p3_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P4>::value,
+ p4_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P5>::value,
+ p5_is_refcounted_type_and_needs_scoped_refptr);
+ typedef internal::BindState<RunnableType, RunType,
+ void(typename internal::CallbackParamTraits<P1>::StorageType,
+ typename internal::CallbackParamTraits<P2>::StorageType,
+ typename internal::CallbackParamTraits<P3>::StorageType,
+ typename internal::CallbackParamTraits<P4>::StorageType,
+ typename internal::CallbackParamTraits<P5>::StorageType)> BindState;
+
+
+ return Callback<typename BindState::UnboundRunType>(
+ new BindState(internal::MakeRunnable(functor), p1, p2, p3, p4, p5));
+}
+
+template <typename Functor, typename P1, typename P2, typename P3, typename P4,
+ typename P5, typename P6>
+base::Callback<
+ typename internal::BindState<
+ typename internal::FunctorTraits<Functor>::RunnableType,
+ typename internal::FunctorTraits<Functor>::RunType,
+ void(typename internal::CallbackParamTraits<P1>::StorageType,
+ typename internal::CallbackParamTraits<P2>::StorageType,
+ typename internal::CallbackParamTraits<P3>::StorageType,
+ typename internal::CallbackParamTraits<P4>::StorageType,
+ typename internal::CallbackParamTraits<P5>::StorageType,
+ typename internal::CallbackParamTraits<P6>::StorageType)>
+ ::UnboundRunType>
+Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4,
+ const P5& p5, const P6& p6) {
+ // Typedefs for how to store and run the functor.
+ typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType;
+ typedef typename internal::FunctorTraits<Functor>::RunType RunType;
+
+ // Use RunnableType::RunType instead of RunType above because our
+ // checks should below for bound references need to know what the actual
+ // functor is going to interpret the argument as.
+ typedef internal::FunctionTraits<typename RunnableType::RunType>
+ BoundFunctorTraits;
+
+ // Do not allow binding a non-const reference parameter. Non-const reference
+ // parameters are disallowed by the Google style guide. Also, binding a
+ // non-const reference parameter can make for subtle bugs because the
+ // invoked function will receive a reference to the stored copy of the
+ // argument and not the original.
+ COMPILE_ASSERT(
+ !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A2Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A3Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A4Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A5Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A6Type>::value ),
+ do_not_bind_functions_with_nonconst_ref);
+
+ // For methods, we need to be careful for parameter 1. We do not require
+ // a scoped_refptr because BindState<> itself takes care of AddRef() for
+ // methods. We also disallow binding of an array as the method's target
+ // object.
+ COMPILE_ASSERT(
+ internal::HasIsMethodTag<RunnableType>::value ||
+ !internal::NeedsScopedRefptrButGetsRawPtr<P1>::value,
+ p1_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::HasIsMethodTag<RunnableType>::value ||
+ !is_array<P1>::value,
+ first_bound_argument_to_method_cannot_be_array);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P2>::value,
+ p2_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P3>::value,
+ p3_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P4>::value,
+ p4_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P5>::value,
+ p5_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P6>::value,
+ p6_is_refcounted_type_and_needs_scoped_refptr);
+ typedef internal::BindState<RunnableType, RunType,
+ void(typename internal::CallbackParamTraits<P1>::StorageType,
+ typename internal::CallbackParamTraits<P2>::StorageType,
+ typename internal::CallbackParamTraits<P3>::StorageType,
+ typename internal::CallbackParamTraits<P4>::StorageType,
+ typename internal::CallbackParamTraits<P5>::StorageType,
+ typename internal::CallbackParamTraits<P6>::StorageType)> BindState;
+
+
+ return Callback<typename BindState::UnboundRunType>(
+ new BindState(internal::MakeRunnable(functor), p1, p2, p3, p4, p5, p6));
+}
+
+template <typename Functor, typename P1, typename P2, typename P3, typename P4,
+ typename P5, typename P6, typename P7>
+base::Callback<
+ typename internal::BindState<
+ typename internal::FunctorTraits<Functor>::RunnableType,
+ typename internal::FunctorTraits<Functor>::RunType,
+ void(typename internal::CallbackParamTraits<P1>::StorageType,
+ typename internal::CallbackParamTraits<P2>::StorageType,
+ typename internal::CallbackParamTraits<P3>::StorageType,
+ typename internal::CallbackParamTraits<P4>::StorageType,
+ typename internal::CallbackParamTraits<P5>::StorageType,
+ typename internal::CallbackParamTraits<P6>::StorageType,
+ typename internal::CallbackParamTraits<P7>::StorageType)>
+ ::UnboundRunType>
+Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4,
+ const P5& p5, const P6& p6, const P7& p7) {
+ // Typedefs for how to store and run the functor.
+ typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType;
+ typedef typename internal::FunctorTraits<Functor>::RunType RunType;
+
+ // Use RunnableType::RunType instead of RunType above because our
+ // checks should below for bound references need to know what the actual
+ // functor is going to interpret the argument as.
+ typedef internal::FunctionTraits<typename RunnableType::RunType>
+ BoundFunctorTraits;
+
+ // Do not allow binding a non-const reference parameter. Non-const reference
+ // parameters are disallowed by the Google style guide. Also, binding a
+ // non-const reference parameter can make for subtle bugs because the
+ // invoked function will receive a reference to the stored copy of the
+ // argument and not the original.
+ COMPILE_ASSERT(
+ !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A2Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A3Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A4Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A5Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A6Type>::value ||
+ is_non_const_reference<typename BoundFunctorTraits::A7Type>::value ),
+ do_not_bind_functions_with_nonconst_ref);
+
+ // For methods, we need to be careful for parameter 1. We do not require
+ // a scoped_refptr because BindState<> itself takes care of AddRef() for
+ // methods. We also disallow binding of an array as the method's target
+ // object.
+ COMPILE_ASSERT(
+ internal::HasIsMethodTag<RunnableType>::value ||
+ !internal::NeedsScopedRefptrButGetsRawPtr<P1>::value,
+ p1_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::HasIsMethodTag<RunnableType>::value ||
+ !is_array<P1>::value,
+ first_bound_argument_to_method_cannot_be_array);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P2>::value,
+ p2_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P3>::value,
+ p3_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P4>::value,
+ p4_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P5>::value,
+ p5_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P6>::value,
+ p6_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P7>::value,
+ p7_is_refcounted_type_and_needs_scoped_refptr);
+ typedef internal::BindState<RunnableType, RunType,
+ void(typename internal::CallbackParamTraits<P1>::StorageType,
+ typename internal::CallbackParamTraits<P2>::StorageType,
+ typename internal::CallbackParamTraits<P3>::StorageType,
+ typename internal::CallbackParamTraits<P4>::StorageType,
+ typename internal::CallbackParamTraits<P5>::StorageType,
+ typename internal::CallbackParamTraits<P6>::StorageType,
+ typename internal::CallbackParamTraits<P7>::StorageType)> BindState;
+
+
+ return Callback<typename BindState::UnboundRunType>(
+ new BindState(internal::MakeRunnable(functor), p1, p2, p3, p4, p5, p6,
+ p7));
+}
+
+} // namespace base
+
+#endif // BASE_BIND_H_
diff --git a/src/base/bind.h.pump b/src/base/bind.h.pump
new file mode 100644
index 0000000..b321649
--- /dev/null
+++ b/src/base/bind.h.pump
@@ -0,0 +1,153 @@
+$$ This is a pump file for generating file templates. Pump is a python
+$$ script that is part of the Google Test suite of utilities. Description
+$$ can be found here:
+$$
+$$ http://code.google.com/p/googletest/wiki/PumpManual
+$$
+
+$$
+$$ MAX_ARITY controls the number of arguments that Bind() supports.
+$$ The amount of code, and more importantly, the number of template types
+$$ generated by pump grows at O(MAX_ARITY^2).
+$$
+$$ We tried going to 11 and found it imposed an extra 10 penalty on windows
+$$ cycle times compared to our original baseline of 6.
+$$
+$$ Currently 7 is chosen as a compromise between supporting a convenient
+$$ number of arguments and keeping compile times low. At 7, we have 115
+$$ templates being generated by pump.
+$$
+$$ Be careful when adjusting this number. If people find a need to bind
+$$ a larger number of arguments, consider refactoring the function to use
+$$ a param struct instead of raising the MAX_ARITY.
+$$
+$$ See http://crbug.com/98542 for more context.
+$$
+$var MAX_ARITY = 7
+
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_BIND_H_
+#define BASE_BIND_H_
+
+#include "base/bind_internal.h"
+#include "base/callback_internal.h"
+
+// -----------------------------------------------------------------------------
+// Usage documentation
+// -----------------------------------------------------------------------------
+//
+// See base/callback.h for documentation.
+//
+//
+// -----------------------------------------------------------------------------
+// Implementation notes
+// -----------------------------------------------------------------------------
+//
+// If you're reading the implementation, before proceeding further, you should
+// read the top comment of base/bind_internal.h for a definition of common
+// terms and concepts.
+//
+// RETURN TYPES
+//
+// Though Bind()'s result is meant to be stored in a Callback<> type, it
+// cannot actually return the exact type without requiring a large amount
+// of extra template specializations. The problem is that in order to
+// discern the correct specialization of Callback<>, Bind would need to
+// unwrap the function signature to determine the signature's arity, and
+// whether or not it is a method.
+//
+// Each unique combination of (arity, function_type, num_prebound) where
+// function_type is one of {function, method, const_method} would require
+// one specialization. We eventually have to do a similar number of
+// specializations anyways in the implementation (see the Invoker<>,
+// classes). However, it is avoidable in Bind if we return the result
+// via an indirection like we do below.
+//
+// TODO(ajwong): We might be able to avoid this now, but need to test.
+//
+// It is possible to move most of the COMPILE_ASSERT asserts into BindState<>,
+// but it feels a little nicer to have the asserts here so people do not
+// need to crack open bind_internal.h. On the other hand, it makes Bind()
+// harder to read.
+
+namespace base {
+
+$range ARITY 0..MAX_ARITY
+$for ARITY [[
+$range ARG 1..ARITY
+
+template <typename Functor[[]]
+$if ARITY > 0 [[, ]] $for ARG , [[typename P$(ARG)]]>
+base::Callback<
+ typename internal::BindState<
+ typename internal::FunctorTraits<Functor>::RunnableType,
+ typename internal::FunctorTraits<Functor>::RunType,
+ void($for ARG , [[typename internal::CallbackParamTraits<P$(ARG)>::StorageType]])>
+ ::UnboundRunType>
+Bind(Functor functor
+$if ARITY > 0 [[, ]] $for ARG , [[const P$(ARG)& p$(ARG)]]) {
+ // Typedefs for how to store and run the functor.
+ typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType;
+ typedef typename internal::FunctorTraits<Functor>::RunType RunType;
+
+ // Use RunnableType::RunType instead of RunType above because our
+ // checks should below for bound references need to know what the actual
+ // functor is going to interpret the argument as.
+ typedef internal::FunctionTraits<typename RunnableType::RunType>
+ BoundFunctorTraits;
+
+$if ARITY > 0 [[
+
+ // Do not allow binding a non-const reference parameter. Non-const reference
+ // parameters are disallowed by the Google style guide. Also, binding a
+ // non-const reference parameter can make for subtle bugs because the
+ // invoked function will receive a reference to the stored copy of the
+ // argument and not the original.
+ COMPILE_ASSERT(
+ !($for ARG || [[
+is_non_const_reference<typename BoundFunctorTraits::A$(ARG)Type>::value ]]),
+ do_not_bind_functions_with_nonconst_ref);
+
+]]
+
+
+$for ARG [[
+
+
+$if ARG == 1 [[
+ // For methods, we need to be careful for parameter 1. We do not require
+ // a scoped_refptr because BindState<> itself takes care of AddRef() for
+ // methods. We also disallow binding of an array as the method's target
+ // object.
+ COMPILE_ASSERT(
+ internal::HasIsMethodTag<RunnableType>::value ||
+ !internal::NeedsScopedRefptrButGetsRawPtr<P$(ARG)>::value,
+ p$(ARG)_is_refcounted_type_and_needs_scoped_refptr);
+ COMPILE_ASSERT(!internal::HasIsMethodTag<RunnableType>::value ||
+ !is_array<P$(ARG)>::value,
+ first_bound_argument_to_method_cannot_be_array);
+]] $else [[
+ COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr<P$(ARG)>::value,
+ p$(ARG)_is_refcounted_type_and_needs_scoped_refptr);
+]] $$ $if ARG
+
+]] $$ $for ARG
+
+ typedef internal::BindState<RunnableType, RunType, [[]]
+void($for ARG , [[typename internal::CallbackParamTraits<P$(ARG)>::StorageType]])> [[]]
+BindState;
+
+
+ return Callback<typename BindState::UnboundRunType>(
+ new BindState(internal::MakeRunnable(functor)[[]]
+$if ARITY > 0 [[, ]] $for ARG , [[p$(ARG)]]));
+}
+
+]] $$ for ARITY
+
+} // namespace base
+
+#endif // BASE_BIND_H_
diff --git a/src/base/bind_helpers.cc b/src/base/bind_helpers.cc
new file mode 100644
index 0000000..f2fc3bb
--- /dev/null
+++ b/src/base/bind_helpers.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 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/bind_helpers.h"
+
+#include "base/callback.h"
+
+namespace base {
+
+void DoNothing() {
+}
+
+ScopedClosureRunner::ScopedClosureRunner(const Closure& closure)
+ : closure_(closure) {
+}
+
+ScopedClosureRunner::~ScopedClosureRunner() {
+ if (!closure_.is_null())
+ closure_.Run();
+}
+
+Closure ScopedClosureRunner::Release() {
+ Closure result = closure_;
+ closure_.Reset();
+ return result;
+}
+
+} // namespace base
diff --git a/src/base/bind_helpers.h b/src/base/bind_helpers.h
new file mode 100644
index 0000000..b60a56e
--- /dev/null
+++ b/src/base/bind_helpers.h
@@ -0,0 +1,563 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This defines a set of argument wrappers and related factory methods that
+// can be used specify the refcounting and reference semantics of arguments
+// that are bound by the Bind() function in base/bind.h.
+//
+// It also defines a set of simple functions and utilities that people want
+// when using Callback<> and Bind().
+//
+//
+// ARGUMENT BINDING WRAPPERS
+//
+// The wrapper functions are base::Unretained(), base::Owned(), bass::Passed(),
+// base::ConstRef(), and base::IgnoreResult().
+//
+// Unretained() allows Bind() to bind a non-refcounted class, and to disable
+// refcounting on arguments that are refcounted objects.
+//
+// Owned() transfers ownership of an object to the Callback resulting from
+// bind; the object will be deleted when the Callback is deleted.
+//
+// Passed() is for transferring movable-but-not-copyable types (eg. scoped_ptr)
+// through a Callback. Logically, this signifies a destructive transfer of
+// the state of the argument into the target function. Invoking
+// Callback::Run() twice on a Callback that was created with a Passed()
+// argument will CHECK() because the first invocation would have already
+// transferred ownership to the target function.
+//
+// ConstRef() allows binding a constant reference to an argument rather
+// than a copy.
+//
+// IgnoreResult() is used to adapt a function or Callback with a return type to
+// one with a void return. This is most useful if you have a function with,
+// say, a pesky ignorable bool return that you want to use with PostTask or
+// something else that expect a Callback with a void return.
+//
+// EXAMPLE OF Unretained():
+//
+// class Foo {
+// public:
+// void func() { cout << "Foo:f" << endl; }
+// };
+//
+// // In some function somewhere.
+// Foo foo;
+// Closure foo_callback =
+// Bind(&Foo::func, Unretained(&foo));
+// foo_callback.Run(); // Prints "Foo:f".
+//
+// Without the Unretained() wrapper on |&foo|, the above call would fail
+// to compile because Foo does not support the AddRef() and Release() methods.
+//
+//
+// EXAMPLE OF Owned():
+//
+// void foo(int* arg) { cout << *arg << endl }
+//
+// int* pn = new int(1);
+// Closure foo_callback = Bind(&foo, Owned(pn));
+//
+// foo_callback.Run(); // Prints "1"
+// foo_callback.Run(); // Prints "1"
+// *n = 2;
+// foo_callback.Run(); // Prints "2"
+//
+// foo_callback.Reset(); // |pn| is deleted. Also will happen when
+// // |foo_callback| goes out of scope.
+//
+// Without Owned(), someone would have to know to delete |pn| when the last
+// reference to the Callback is deleted.
+//
+//
+// EXAMPLE OF ConstRef():
+//
+// void foo(int arg) { cout << arg << endl }
+//
+// int n = 1;
+// Closure no_ref = Bind(&foo, n);
+// Closure has_ref = Bind(&foo, ConstRef(n));
+//
+// no_ref.Run(); // Prints "1"
+// has_ref.Run(); // Prints "1"
+//
+// n = 2;
+// no_ref.Run(); // Prints "1"
+// has_ref.Run(); // Prints "2"
+//
+// Note that because ConstRef() takes a reference on |n|, |n| must outlive all
+// its bound callbacks.
+//
+//
+// EXAMPLE OF IgnoreResult():
+//
+// int DoSomething(int arg) { cout << arg << endl; }
+//
+// // Assign to a Callback with a void return type.
+// Callback<void(int)> cb = Bind(IgnoreResult(&DoSomething));
+// cb->Run(1); // Prints "1".
+//
+// // Prints "1" on |ml|.
+// ml->PostTask(FROM_HERE, Bind(IgnoreResult(&DoSomething), 1);
+//
+//
+// EXAMPLE OF Passed():
+//
+// void TakesOwnership(scoped_ptr<Foo> arg) { }
+// scoped_ptr<Foo> CreateFoo() { return scoped_ptr<Foo>(new Foo()); }
+//
+// scoped_ptr<Foo> f(new Foo());
+//
+// // |cb| is given ownership of Foo(). |f| is now NULL.
+// // You can use f.Pass() in place of &f, but it's more verbose.
+// Closure cb = Bind(&TakesOwnership, Passed(&f));
+//
+// // Run was never called so |cb| still owns Foo() and deletes
+// // it on Reset().
+// cb.Reset();
+//
+// // |cb| is given a new Foo created by CreateFoo().
+// cb = Bind(&TakesOwnership, Passed(CreateFoo()));
+//
+// // |arg| in TakesOwnership() is given ownership of Foo(). |cb|
+// // no longer owns Foo() and, if reset, would not delete Foo().
+// cb.Run(); // Foo() is now transferred to |arg| and deleted.
+// cb.Run(); // This CHECK()s since Foo() already been used once.
+//
+// Passed() is particularly useful with PostTask() when you are transferring
+// ownership of an argument into a task, but don't necessarily know if the
+// task will always be executed. This can happen if the task is cancellable
+// or if it is posted to a MessageLoopProxy.
+//
+//
+// SIMPLE FUNCTIONS AND UTILITIES.
+//
+// DoNothing() - Useful for creating a Closure that does nothing when called.
+// DeletePointer<T>() - Useful for creating a Closure that will delete a
+// pointer when invoked. Only use this when necessary.
+// In most cases MessageLoop::DeleteSoon() is a better
+// fit.
+// ScopedClosureRunner - Scoper object that runs the wrapped closure when it
+// goes out of scope. It's conceptually similar to
+// scoped_ptr<> but calls Run() instead of deleting
+// the pointer.
+
+#ifndef BASE_BIND_HELPERS_H_
+#define BASE_BIND_HELPERS_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "base/template_util.h"
+
+namespace base {
+namespace internal {
+
+// Use the Substitution Failure Is Not An Error (SFINAE) trick to inspect T
+// for the existence of AddRef() and Release() functions of the correct
+// signature.
+//
+// http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error
+// http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence
+// http://stackoverflow.com/questions/4358584/sfinae-approach-comparison
+// http://stackoverflow.com/questions/1966362/sfinae-to-check-for-inherited-member-functions
+//
+// The last link in particular show the method used below.
+//
+// For SFINAE to work with inherited methods, we need to pull some extra tricks
+// with multiple inheritance. In the more standard formulation, the overloads
+// of Check would be:
+//
+// template <typename C>
+// Yes NotTheCheckWeWant(Helper<&C::TargetFunc>*);
+//
+// template <typename C>
+// No NotTheCheckWeWant(...);
+//
+// static const bool value = sizeof(NotTheCheckWeWant<T>(0)) == sizeof(Yes);
+//
+// The problem here is that template resolution will not match
+// C::TargetFunc if TargetFunc does not exist directly in C. That is, if
+// TargetFunc in inherited from an ancestor, &C::TargetFunc will not match,
+// |value| will be false. This formulation only checks for whether or
+// not TargetFunc exist directly in the class being introspected.
+//
+// To get around this, we play a dirty trick with multiple inheritance.
+// First, We create a class BaseMixin that declares each function that we
+// want to probe for. Then we create a class Base that inherits from both T
+// (the class we wish to probe) and BaseMixin. Note that the function
+// signature in BaseMixin does not need to match the signature of the function
+// we are probing for; thus it's easiest to just use void(void).
+//
+// Now, if TargetFunc exists somewhere in T, then &Base::TargetFunc has an
+// ambiguous resolution between BaseMixin and T. This lets us write the
+// following:
+//
+// template <typename C>
+// No GoodCheck(Helper<&C::TargetFunc>*);
+//
+// template <typename C>
+// Yes GoodCheck(...);
+//
+// static const bool value = sizeof(GoodCheck<Base>(0)) == sizeof(Yes);
+//
+// Notice here that the variadic version of GoodCheck() returns Yes here
+// instead of No like the previous one. Also notice that we calculate |value|
+// by specializing GoodCheck() on Base instead of T.
+//
+// We've reversed the roles of the variadic, and Helper overloads.
+// GoodCheck(Helper<&C::TargetFunc>*), when C = Base, fails to be a valid
+// substitution if T::TargetFunc exists. Thus GoodCheck<Base>(0) will resolve
+// to the variadic version if T has TargetFunc. If T::TargetFunc does not
+// exist, then &C::TargetFunc is not ambiguous, and the overload resolution
+// will prefer GoodCheck(Helper<&C::TargetFunc>*).
+//
+// This method of SFINAE will correctly probe for inherited names, but it cannot
+// typecheck those names. It's still a good enough sanity check though.
+//
+// Works on gcc-4.2, gcc-4.4, and Visual Studio 2008.
+//
+// TODO(ajwong): Move to ref_counted.h or template_util.h when we've vetted
+// this works well.
+//
+// TODO(ajwong): Make this check for Release() as well.
+// See http://crbug.com/82038.
+template <typename T>
+class SupportsAddRefAndRelease {
+ typedef char Yes[1];
+ typedef char No[2];
+
+ struct BaseMixin {
+ void AddRef();
+ };
+
+// MSVC warns when you try to use Base if T has a private destructor, the
+// common pattern for refcounted types. It does this even though no attempt to
+// instantiate Base is made. We disable the warning for this definition.
+#if defined(OS_WIN) || defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4624)
+#endif
+ struct Base : public T, public BaseMixin {
+ };
+#if defined(OS_WIN) || defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+
+ template <void(BaseMixin::*)(void)> struct Helper {};
+
+ template <typename C>
+ static No& Check(Helper<&C::AddRef>*);
+
+ template <typename >
+ static Yes& Check(...);
+
+ public:
+ static const bool value = sizeof(Check<Base>(0)) == sizeof(Yes);
+};
+
+// Helpers to assert that arguments of a recounted type are bound with a
+// scoped_refptr.
+template <bool IsClasstype, typename T>
+struct UnsafeBindtoRefCountedArgHelper : false_type {
+};
+
+template <typename T>
+struct UnsafeBindtoRefCountedArgHelper<true, T>
+ : integral_constant<bool, SupportsAddRefAndRelease<T>::value> {
+};
+
+template <typename T>
+struct UnsafeBindtoRefCountedArg : false_type {
+};
+
+template <typename T>
+struct UnsafeBindtoRefCountedArg<T*>
+ : UnsafeBindtoRefCountedArgHelper<is_class<T>::value, T> {
+};
+
+template <typename T>
+class HasIsMethodTag {
+ typedef char Yes[1];
+ typedef char No[2];
+
+ template <typename U>
+ static Yes& Check(typename U::IsMethod*);
+
+ template <typename U>
+ static No& Check(...);
+
+ public:
+ static const bool value = sizeof(Check<T>(0)) == sizeof(Yes);
+};
+
+template <typename T>
+class UnretainedWrapper {
+ public:
+ explicit UnretainedWrapper(T* o) : ptr_(o) {}
+ T* get() const { return ptr_; }
+ private:
+ T* ptr_;
+};
+
+template <typename T>
+class ConstRefWrapper {
+ public:
+ explicit ConstRefWrapper(const T& o) : ptr_(&o) {}
+ const T& get() const { return *ptr_; }
+ private:
+ const T* ptr_;
+};
+
+template <typename T>
+struct IgnoreResultHelper {
+ explicit IgnoreResultHelper(T functor) : functor_(functor) {}
+
+ T functor_;
+};
+
+template <typename T>
+struct IgnoreResultHelper<Callback<T> > {
+ explicit IgnoreResultHelper(const Callback<T>& functor) : functor_(functor) {}
+
+ const Callback<T>& functor_;
+};
+
+// An alternate implementation is to avoid the destructive copy, and instead
+// specialize ParamTraits<> for OwnedWrapper<> to change the StorageType to
+// a class that is essentially a scoped_ptr<>.
+//
+// The current implementation has the benefit though of leaving ParamTraits<>
+// fully in callback_internal.h as well as avoiding type conversions during
+// storage.
+template <typename T>
+class OwnedWrapper {
+ public:
+ explicit OwnedWrapper(T* o) : ptr_(o) {}
+ ~OwnedWrapper() { delete ptr_; }
+ T* get() const { return ptr_; }
+ OwnedWrapper(const OwnedWrapper& other) {
+ ptr_ = other.ptr_;
+ other.ptr_ = NULL;
+ }
+
+ private:
+ mutable T* ptr_;
+};
+
+// PassedWrapper is a copyable adapter for a scoper that ignores const.
+//
+// It is needed to get around the fact that Bind() takes a const reference to
+// all its arguments. Because Bind() takes a const reference to avoid
+// unnecessary copies, it is incompatible with movable-but-not-copyable
+// types; doing a destructive "move" of the type into Bind() would violate
+// the const correctness.
+//
+// This conundrum cannot be solved without either C++11 rvalue references or
+// a O(2^n) blowup of Bind() templates to handle each combination of regular
+// types and movable-but-not-copyable types. Thus we introduce a wrapper type
+// that is copyable to transmit the correct type information down into
+// BindState<>. Ignoring const in this type makes sense because it is only
+// created when we are explicitly trying to do a destructive move.
+//
+// Two notes:
+// 1) PassedWrapper supports any type that has a "Pass()" function.
+// This is intentional. The whitelisting of which specific types we
+// support is maintained by CallbackParamTraits<>.
+// 2) is_valid_ is distinct from NULL because it is valid to bind a "NULL"
+// scoper to a Callback and allow the Callback to execute once.
+template <typename T>
+class PassedWrapper {
+ public:
+ explicit PassedWrapper(T scoper) : is_valid_(true), scoper_(scoper.Pass()) {}
+ PassedWrapper(const PassedWrapper& other)
+ : is_valid_(other.is_valid_), scoper_(other.scoper_.Pass()) {
+ }
+ T Pass() const {
+ CHECK(is_valid_);
+ is_valid_ = false;
+ return scoper_.Pass();
+ }
+
+ private:
+ mutable bool is_valid_;
+ mutable T scoper_;
+};
+
+// Unwrap the stored parameters for the wrappers above.
+template <typename T>
+struct UnwrapTraits {
+ typedef const T& ForwardType;
+ static ForwardType Unwrap(const T& o) { return o; }
+};
+
+template <typename T>
+struct UnwrapTraits<UnretainedWrapper<T> > {
+ typedef T* ForwardType;
+ static ForwardType Unwrap(UnretainedWrapper<T> unretained) {
+ return unretained.get();
+ }
+};
+
+template <typename T>
+struct UnwrapTraits<ConstRefWrapper<T> > {
+ typedef const T& ForwardType;
+ static ForwardType Unwrap(ConstRefWrapper<T> const_ref) {
+ return const_ref.get();
+ }
+};
+
+template <typename T>
+struct UnwrapTraits<scoped_refptr<T> > {
+ typedef T* ForwardType;
+ static ForwardType Unwrap(const scoped_refptr<T>& o) { return o.get(); }
+};
+
+template <typename T>
+struct UnwrapTraits<WeakPtr<T> > {
+ typedef const WeakPtr<T>& ForwardType;
+ static ForwardType Unwrap(const WeakPtr<T>& o) { return o; }
+};
+
+template <typename T>
+struct UnwrapTraits<OwnedWrapper<T> > {
+ typedef T* ForwardType;
+ static ForwardType Unwrap(const OwnedWrapper<T>& o) {
+ return o.get();
+ }
+};
+
+template <typename T>
+struct UnwrapTraits<PassedWrapper<T> > {
+ typedef T ForwardType;
+ static T Unwrap(PassedWrapper<T>& o) {
+ return o.Pass();
+ }
+};
+
+// Utility for handling different refcounting semantics in the Bind()
+// function.
+template <bool is_method, typename T>
+struct MaybeRefcount;
+
+template <typename T>
+struct MaybeRefcount<false, T> {
+ static void AddRef(const T&) {}
+ static void Release(const T&) {}
+};
+
+template <typename T, size_t n>
+struct MaybeRefcount<false, T[n]> {
+ static void AddRef(const T*) {}
+ static void Release(const T*) {}
+};
+
+template <typename T>
+struct MaybeRefcount<true, T> {
+ static void AddRef(const T&) {}
+ static void Release(const T&) {}
+};
+
+template <typename T>
+struct MaybeRefcount<true, T*> {
+ static void AddRef(T* o) { o->AddRef(); }
+ static void Release(T* o) { o->Release(); }
+};
+
+// No need to additionally AddRef() and Release() since we are storing a
+// scoped_refptr<> inside the storage object already.
+template <typename T>
+struct MaybeRefcount<true, scoped_refptr<T> > {
+ static void AddRef(const scoped_refptr<T>&) {}
+ static void Release(const scoped_refptr<T>&) {}
+};
+
+template <typename T>
+struct MaybeRefcount<true, const T*> {
+ static void AddRef(const T* o) { o->AddRef(); }
+ static void Release(const T* o) { o->Release(); }
+};
+
+// IsWeakMethod is a helper that determine if we are binding a WeakPtr<> to a
+// method. It is used internally by Bind() to select the correct
+// InvokeHelper that will no-op itself in the event the WeakPtr<> for
+// the target object is invalidated.
+//
+// P1 should be the type of the object that will be received of the method.
+template <bool IsMethod, typename P1>
+struct IsWeakMethod : public false_type {};
+
+template <typename T>
+struct IsWeakMethod<true, WeakPtr<T> > : public true_type {};
+
+template <typename T>
+struct IsWeakMethod<true, ConstRefWrapper<WeakPtr<T> > > : public true_type {};
+
+} // namespace internal
+
+template <typename T>
+static inline internal::UnretainedWrapper<T> Unretained(T* o) {
+ return internal::UnretainedWrapper<T>(o);
+}
+
+template <typename T>
+static inline internal::ConstRefWrapper<T> ConstRef(const T& o) {
+ return internal::ConstRefWrapper<T>(o);
+}
+
+template <typename T>
+static inline internal::OwnedWrapper<T> Owned(T* o) {
+ return internal::OwnedWrapper<T>(o);
+}
+
+// We offer 2 syntaxes for calling Passed(). The first takes a temporary and
+// is best suited for use with the return value of a function. The second
+// takes a pointer to the scoper and is just syntactic sugar to avoid having
+// to write Passed(scoper.Pass()).
+template <typename T>
+static inline internal::PassedWrapper<T> Passed(T scoper) {
+ return internal::PassedWrapper<T>(scoper.Pass());
+}
+template <typename T>
+static inline internal::PassedWrapper<T> Passed(T* scoper) {
+ return internal::PassedWrapper<T>(scoper->Pass());
+}
+
+template <typename T>
+static inline internal::IgnoreResultHelper<T> IgnoreResult(T data) {
+ return internal::IgnoreResultHelper<T>(data);
+}
+
+template <typename T>
+static inline internal::IgnoreResultHelper<Callback<T> >
+IgnoreResult(const Callback<T>& data) {
+ return internal::IgnoreResultHelper<Callback<T> >(data);
+}
+
+BASE_EXPORT void DoNothing();
+
+template<typename T>
+void DeletePointer(T* obj) {
+ delete obj;
+}
+
+// ScopedClosureRunner is akin to scoped_ptr for Closures. It ensures that the
+// Closure is executed and deleted no matter how the current scope exits.
+class BASE_EXPORT ScopedClosureRunner {
+ public:
+ explicit ScopedClosureRunner(const Closure& closure);
+ ~ScopedClosureRunner();
+
+ Closure Release();
+
+ private:
+ Closure closure_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedClosureRunner);
+};
+
+} // namespace base
+
+#endif // BASE_BIND_HELPERS_H_
diff --git a/src/base/bind_helpers_unittest.cc b/src/base/bind_helpers_unittest.cc
new file mode 100644
index 0000000..3ef2d75
--- /dev/null
+++ b/src/base/bind_helpers_unittest.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 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/bind_helpers.h"
+
+#include "base/callback.h"
+#include "base/bind.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void Increment(int* value) {
+ (*value)++;
+}
+
+TEST(BindHelpersTest, TestScopedClosureRunnerExitScope) {
+ int run_count = 0;
+ {
+ base::ScopedClosureRunner runner(base::Bind(&Increment, &run_count));
+ EXPECT_EQ(0, run_count);
+ }
+ EXPECT_EQ(1, run_count);
+}
+
+TEST(BindHelpersTest, TestScopedClosureRunnerRelease) {
+ int run_count = 0;
+ base::Closure c;
+ {
+ base::ScopedClosureRunner runner(base::Bind(&Increment, &run_count));
+ c = runner.Release();
+ EXPECT_EQ(0, run_count);
+ }
+ EXPECT_EQ(0, run_count);
+ c.Run();
+ EXPECT_EQ(1, run_count);
+}
+
+} // namespace
diff --git a/src/base/bind_internal.h b/src/base/bind_internal.h
new file mode 100644
index 0000000..b87879f
--- /dev/null
+++ b/src/base/bind_internal.h
@@ -0,0 +1,2800 @@
+// This file was GENERATED by command:
+// pump.py bind_internal.h.pump
+// DO NOT EDIT BY HAND!!!
+
+
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_BIND_INTERNAL_H_
+#define BASE_BIND_INTERNAL_H_
+
+#include "base/bind_helpers.h"
+#include "base/callback_internal.h"
+#include "base/memory/raw_scoped_refptr_mismatch_checker.h"
+#include "base/memory/weak_ptr.h"
+#include "base/template_util.h"
+#include "build/build_config.h"
+
+#if defined(__LB_SHELL__) || defined(OS_STARBOARD)
+// Check for C++11 support
+#if (_MSC_VER >= 1700) || (__cplusplus > 199711L)
+#include "base/bind_internal_functor.h"
+#endif
+#endif
+
+#if defined(OS_WIN)
+#include "base/bind_internal_win.h"
+#endif
+
+namespace base {
+namespace internal {
+
+// See base/callback.h for user documentation.
+//
+//
+// CONCEPTS:
+// Runnable -- A type (really a type class) that has a single Run() method
+// and a RunType typedef that corresponds to the type of Run().
+// A Runnable can declare that it should treated like a method
+// call by including a typedef named IsMethod. The value of
+// this typedef is NOT inspected, only the existence. When a
+// Runnable declares itself a method, Bind() will enforce special
+// refcounting + WeakPtr handling semantics for the first
+// parameter which is expected to be an object.
+// Functor -- A copyable type representing something that should be called.
+// All function pointers, Callback<>, and Runnables are functors
+// even if the invocation syntax differs.
+// RunType -- A function type (as opposed to function _pointer_ type) for
+// a Run() function. Usually just a convenience typedef.
+// (Bound)ArgsType -- A function type that is being (ab)used to store the
+// types of set of arguments. The "return" type is always
+// void here. We use this hack so that we do not need
+// a new type name for each arity of type. (eg.,
+// BindState1, BindState2). This makes forward
+// declarations and friending much much easier.
+//
+// Types:
+// RunnableAdapter<> -- Wraps the various "function" pointer types into an
+// object that adheres to the Runnable interface.
+// There are |3*ARITY| RunnableAdapter types.
+// FunctionTraits<> -- Type traits that unwrap a function signature into a
+// a set of easier to use typedefs. Used mainly for
+// compile time asserts.
+// There are |ARITY| FunctionTraits types.
+// ForceVoidReturn<> -- Helper class for translating function signatures to
+// equivalent forms with a "void" return type.
+// There are |ARITY| ForceVoidReturn types.
+// FunctorTraits<> -- Type traits used determine the correct RunType and
+// RunnableType for a Functor. This is where function
+// signature adapters are applied.
+// There are |ARITY| ForceVoidReturn types.
+// MakeRunnable<> -- Takes a Functor and returns an object in the Runnable
+// type class that represents the underlying Functor.
+// There are |O(1)| MakeRunnable types.
+// InvokeHelper<> -- Take a Runnable + arguments and actully invokes it.
+// Handle the differing syntaxes needed for WeakPtr<> support,
+// and for ignoring return values. This is separate from
+// Invoker to avoid creating multiple version of Invoker<>
+// which grows at O(n^2) with the arity.
+// There are |k*ARITY| InvokeHelper types.
+// Invoker<> -- Unwraps the curried parameters and executes the Runnable.
+// There are |(ARITY^2 + ARITY)/2| Invoketypes.
+// BindState<> -- Stores the curried parameters, and is the main entry point
+// into the Bind() system, doing most of the type resolution.
+// There are ARITY BindState types.
+
+// RunnableAdapter<>
+//
+// The RunnableAdapter<> templates provide a uniform interface for invoking
+// a function pointer, method pointer, or const method pointer. The adapter
+// exposes a Run() method with an appropriate signature. Using this wrapper
+// allows for writing code that supports all three pointer types without
+// undue repetition. Without it, a lot of code would need to be repeated 3
+// times.
+//
+// For method pointers and const method pointers the first argument to Run()
+// is considered to be the received of the method. This is similar to STL's
+// mem_fun().
+//
+// This class also exposes a RunType typedef that is the function type of the
+// Run() function.
+//
+// If and only if the wrapper contains a method or const method pointer, an
+// IsMethod typedef is exposed. The existence of this typedef (NOT the value)
+// marks that the wrapper should be considered a method wrapper.
+
+template <typename Functor>
+class RunnableAdapter;
+
+// Function: Arity 0.
+template <typename R>
+class RunnableAdapter<R(*)()> {
+ public:
+ typedef R (RunType)();
+
+ explicit RunnableAdapter(R(*function)())
+ : function_(function) {
+ }
+
+ R Run() {
+ return function_();
+ }
+
+ private:
+ R (*function_)();
+};
+
+// Method: Arity 0.
+template <typename R, typename T>
+class RunnableAdapter<R(T::*)()> {
+ public:
+ typedef R (RunType)(T*);
+ typedef true_type IsMethod;
+
+ explicit RunnableAdapter(R(T::*method)())
+ : method_(method) {
+ }
+
+ R Run(T* object) {
+ return (object->*method_)();
+ }
+
+ private:
+ R (T::*method_)();
+};
+
+// Const Method: Arity 0.
+template <typename R, typename T>
+class RunnableAdapter<R(T::*)() const> {
+ public:
+ typedef R (RunType)(const T*);
+ typedef true_type IsMethod;
+
+ explicit RunnableAdapter(R(T::*method)() const)
+ : method_(method) {
+ }
+
+ R Run(const T* object) {
+ return (object->*method_)();
+ }
+
+ private:
+ R (T::*method_)() const;
+};
+
+// Function: Arity 1.
+template <typename R, typename A1>
+class RunnableAdapter<R(*)(A1)> {
+ public:
+ typedef R (RunType)(A1);
+
+ explicit RunnableAdapter(R(*function)(A1))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1) {
+ return function_(CallbackForward(a1));
+ }
+
+ private:
+ R (*function_)(A1);
+};
+
+// Method: Arity 1.
+template <typename R, typename T, typename A1>
+class RunnableAdapter<R(T::*)(A1)> {
+ public:
+ typedef R (RunType)(T*, A1);
+ typedef true_type IsMethod;
+
+ explicit RunnableAdapter(R(T::*method)(A1))
+ : method_(method) {
+ }
+
+ R Run(T* object, typename CallbackParamTraits<A1>::ForwardType a1) {
+ return (object->*method_)(CallbackForward(a1));
+ }
+
+ private:
+ R (T::*method_)(A1);
+};
+
+// Const Method: Arity 1.
+template <typename R, typename T, typename A1>
+class RunnableAdapter<R(T::*)(A1) const> {
+ public:
+ typedef R (RunType)(const T*, A1);
+ typedef true_type IsMethod;
+
+ explicit RunnableAdapter(R(T::*method)(A1) const)
+ : method_(method) {
+ }
+
+ R Run(const T* object, typename CallbackParamTraits<A1>::ForwardType a1) {
+ return (object->*method_)(CallbackForward(a1));
+ }
+
+ private:
+ R (T::*method_)(A1) const;
+};
+
+// Function: Arity 2.
+template <typename R, typename A1, typename A2>
+class RunnableAdapter<R(*)(A1, A2)> {
+ public:
+ typedef R (RunType)(A1, A2);
+
+ explicit RunnableAdapter(R(*function)(A1, A2))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2) {
+ return function_(CallbackForward(a1), CallbackForward(a2));
+ }
+
+ private:
+ R (*function_)(A1, A2);
+};
+
+// Method: Arity 2.
+template <typename R, typename T, typename A1, typename A2>
+class RunnableAdapter<R(T::*)(A1, A2)> {
+ public:
+ typedef R (RunType)(T*, A1, A2);
+ typedef true_type IsMethod;
+
+ explicit RunnableAdapter(R(T::*method)(A1, A2))
+ : method_(method) {
+ }
+
+ R Run(T* object, typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2) {
+ return (object->*method_)(CallbackForward(a1), CallbackForward(a2));
+ }
+
+ private:
+ R (T::*method_)(A1, A2);
+};
+
+// Const Method: Arity 2.
+template <typename R, typename T, typename A1, typename A2>
+class RunnableAdapter<R(T::*)(A1, A2) const> {
+ public:
+ typedef R (RunType)(const T*, A1, A2);
+ typedef true_type IsMethod;
+
+ explicit RunnableAdapter(R(T::*method)(A1, A2) const)
+ : method_(method) {
+ }
+
+ R Run(const T* object, typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2) {
+ return (object->*method_)(CallbackForward(a1), CallbackForward(a2));
+ }
+
+ private:
+ R (T::*method_)(A1, A2) const;
+};
+
+// Function: Arity 3.
+template <typename R, typename A1, typename A2, typename A3>
+class RunnableAdapter<R(*)(A1, A2, A3)> {
+ public:
+ typedef R (RunType)(A1, A2, A3);
+
+ explicit RunnableAdapter(R(*function)(A1, A2, A3))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3) {
+ return function_(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3));
+ }
+
+ private:
+ R (*function_)(A1, A2, A3);
+};
+
+// Method: Arity 3.
+template <typename R, typename T, typename A1, typename A2, typename A3>
+class RunnableAdapter<R(T::*)(A1, A2, A3)> {
+ public:
+ typedef R (RunType)(T*, A1, A2, A3);
+ typedef true_type IsMethod;
+
+ explicit RunnableAdapter(R(T::*method)(A1, A2, A3))
+ : method_(method) {
+ }
+
+ R Run(T* object, typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3) {
+ return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3));
+ }
+
+ private:
+ R (T::*method_)(A1, A2, A3);
+};
+
+// Const Method: Arity 3.
+template <typename R, typename T, typename A1, typename A2, typename A3>
+class RunnableAdapter<R(T::*)(A1, A2, A3) const> {
+ public:
+ typedef R (RunType)(const T*, A1, A2, A3);
+ typedef true_type IsMethod;
+
+ explicit RunnableAdapter(R(T::*method)(A1, A2, A3) const)
+ : method_(method) {
+ }
+
+ R Run(const T* object, typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3) {
+ return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3));
+ }
+
+ private:
+ R (T::*method_)(A1, A2, A3) const;
+};
+
+// Function: Arity 4.
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+class RunnableAdapter<R(*)(A1, A2, A3, A4)> {
+ public:
+ typedef R (RunType)(A1, A2, A3, A4);
+
+ explicit RunnableAdapter(R(*function)(A1, A2, A3, A4))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4) {
+ return function_(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4));
+ }
+
+ private:
+ R (*function_)(A1, A2, A3, A4);
+};
+
+// Method: Arity 4.
+template <typename R, typename T, typename A1, typename A2, typename A3,
+ typename A4>
+class RunnableAdapter<R(T::*)(A1, A2, A3, A4)> {
+ public:
+ typedef R (RunType)(T*, A1, A2, A3, A4);
+ typedef true_type IsMethod;
+
+ explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4))
+ : method_(method) {
+ }
+
+ R Run(T* object, typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4) {
+ return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4));
+ }
+
+ private:
+ R (T::*method_)(A1, A2, A3, A4);
+};
+
+// Const Method: Arity 4.
+template <typename R, typename T, typename A1, typename A2, typename A3,
+ typename A4>
+class RunnableAdapter<R(T::*)(A1, A2, A3, A4) const> {
+ public:
+ typedef R (RunType)(const T*, A1, A2, A3, A4);
+ typedef true_type IsMethod;
+
+ explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4) const)
+ : method_(method) {
+ }
+
+ R Run(const T* object, typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4) {
+ return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4));
+ }
+
+ private:
+ R (T::*method_)(A1, A2, A3, A4) const;
+};
+
+// Function: Arity 5.
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5>
+class RunnableAdapter<R(*)(A1, A2, A3, A4, A5)> {
+ public:
+ typedef R (RunType)(A1, A2, A3, A4, A5);
+
+ explicit RunnableAdapter(R(*function)(A1, A2, A3, A4, A5))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4,
+ typename CallbackParamTraits<A5>::ForwardType a5) {
+ return function_(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4), CallbackForward(a5));
+ }
+
+ private:
+ R (*function_)(A1, A2, A3, A4, A5);
+};
+
+// Method: Arity 5.
+template <typename R, typename T, typename A1, typename A2, typename A3,
+ typename A4, typename A5>
+class RunnableAdapter<R(T::*)(A1, A2, A3, A4, A5)> {
+ public:
+ typedef R (RunType)(T*, A1, A2, A3, A4, A5);
+ typedef true_type IsMethod;
+
+ explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5))
+ : method_(method) {
+ }
+
+ R Run(T* object, typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4,
+ typename CallbackParamTraits<A5>::ForwardType a5) {
+ return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4), CallbackForward(a5));
+ }
+
+ private:
+ R (T::*method_)(A1, A2, A3, A4, A5);
+};
+
+// Const Method: Arity 5.
+template <typename R, typename T, typename A1, typename A2, typename A3,
+ typename A4, typename A5>
+class RunnableAdapter<R(T::*)(A1, A2, A3, A4, A5) const> {
+ public:
+ typedef R (RunType)(const T*, A1, A2, A3, A4, A5);
+ typedef true_type IsMethod;
+
+ explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5) const)
+ : method_(method) {
+ }
+
+ R Run(const T* object, typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4,
+ typename CallbackParamTraits<A5>::ForwardType a5) {
+ return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4), CallbackForward(a5));
+ }
+
+ private:
+ R (T::*method_)(A1, A2, A3, A4, A5) const;
+};
+
+// Function: Arity 6.
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6>
+class RunnableAdapter<R(*)(A1, A2, A3, A4, A5, A6)> {
+ public:
+ typedef R (RunType)(A1, A2, A3, A4, A5, A6);
+
+ explicit RunnableAdapter(R(*function)(A1, A2, A3, A4, A5, A6))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4,
+ typename CallbackParamTraits<A5>::ForwardType a5,
+ typename CallbackParamTraits<A6>::ForwardType a6) {
+ return function_(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4), CallbackForward(a5),
+ CallbackForward(a6));
+ }
+
+ private:
+ R (*function_)(A1, A2, A3, A4, A5, A6);
+};
+
+// Method: Arity 6.
+template <typename R, typename T, typename A1, typename A2, typename A3,
+ typename A4, typename A5, typename A6>
+class RunnableAdapter<R(T::*)(A1, A2, A3, A4, A5, A6)> {
+ public:
+ typedef R (RunType)(T*, A1, A2, A3, A4, A5, A6);
+ typedef true_type IsMethod;
+
+ explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5, A6))
+ : method_(method) {
+ }
+
+ R Run(T* object, typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4,
+ typename CallbackParamTraits<A5>::ForwardType a5,
+ typename CallbackParamTraits<A6>::ForwardType a6) {
+ return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4), CallbackForward(a5),
+ CallbackForward(a6));
+ }
+
+ private:
+ R (T::*method_)(A1, A2, A3, A4, A5, A6);
+};
+
+// Const Method: Arity 6.
+template <typename R, typename T, typename A1, typename A2, typename A3,
+ typename A4, typename A5, typename A6>
+class RunnableAdapter<R(T::*)(A1, A2, A3, A4, A5, A6) const> {
+ public:
+ typedef R (RunType)(const T*, A1, A2, A3, A4, A5, A6);
+ typedef true_type IsMethod;
+
+ explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5, A6) const)
+ : method_(method) {
+ }
+
+ R Run(const T* object, typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4,
+ typename CallbackParamTraits<A5>::ForwardType a5,
+ typename CallbackParamTraits<A6>::ForwardType a6) {
+ return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4), CallbackForward(a5),
+ CallbackForward(a6));
+ }
+
+ private:
+ R (T::*method_)(A1, A2, A3, A4, A5, A6) const;
+};
+
+// Function: Arity 7.
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7>
+class RunnableAdapter<R(*)(A1, A2, A3, A4, A5, A6, A7)> {
+ public:
+ typedef R (RunType)(A1, A2, A3, A4, A5, A6, A7);
+
+ explicit RunnableAdapter(R(*function)(A1, A2, A3, A4, A5, A6, A7))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4,
+ typename CallbackParamTraits<A5>::ForwardType a5,
+ typename CallbackParamTraits<A6>::ForwardType a6,
+ typename CallbackParamTraits<A7>::ForwardType a7) {
+ return function_(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4), CallbackForward(a5),
+ CallbackForward(a6), CallbackForward(a7));
+ }
+
+ private:
+ R (*function_)(A1, A2, A3, A4, A5, A6, A7);
+};
+
+// Method: Arity 7.
+template <typename R, typename T, typename A1, typename A2, typename A3,
+ typename A4, typename A5, typename A6, typename A7>
+class RunnableAdapter<R(T::*)(A1, A2, A3, A4, A5, A6, A7)> {
+ public:
+ typedef R (RunType)(T*, A1, A2, A3, A4, A5, A6, A7);
+ typedef true_type IsMethod;
+
+ explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5, A6, A7))
+ : method_(method) {
+ }
+
+ R Run(T* object, typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4,
+ typename CallbackParamTraits<A5>::ForwardType a5,
+ typename CallbackParamTraits<A6>::ForwardType a6,
+ typename CallbackParamTraits<A7>::ForwardType a7) {
+ return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4), CallbackForward(a5),
+ CallbackForward(a6), CallbackForward(a7));
+ }
+
+ private:
+ R (T::*method_)(A1, A2, A3, A4, A5, A6, A7);
+};
+
+// Const Method: Arity 7.
+template <typename R, typename T, typename A1, typename A2, typename A3,
+ typename A4, typename A5, typename A6, typename A7>
+class RunnableAdapter<R(T::*)(A1, A2, A3, A4, A5, A6, A7) const> {
+ public:
+ typedef R (RunType)(const T*, A1, A2, A3, A4, A5, A6, A7);
+ typedef true_type IsMethod;
+
+ explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5, A6, A7) const)
+ : method_(method) {
+ }
+
+ R Run(const T* object, typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4,
+ typename CallbackParamTraits<A5>::ForwardType a5,
+ typename CallbackParamTraits<A6>::ForwardType a6,
+ typename CallbackParamTraits<A7>::ForwardType a7) {
+ return (object->*method_)(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4), CallbackForward(a5),
+ CallbackForward(a6), CallbackForward(a7));
+ }
+
+ private:
+ R (T::*method_)(A1, A2, A3, A4, A5, A6, A7) const;
+};
+
+
+// FunctionTraits<>
+//
+// Breaks a function signature apart into typedefs for easier introspection.
+template <typename Sig>
+struct FunctionTraits;
+
+template <typename R>
+struct FunctionTraits<R()> {
+ typedef R ReturnType;
+};
+
+template <typename R, typename A1>
+struct FunctionTraits<R(A1)> {
+ typedef R ReturnType;
+ typedef A1 A1Type;
+};
+
+template <typename R, typename A1, typename A2>
+struct FunctionTraits<R(A1, A2)> {
+ typedef R ReturnType;
+ typedef A1 A1Type;
+ typedef A2 A2Type;
+};
+
+template <typename R, typename A1, typename A2, typename A3>
+struct FunctionTraits<R(A1, A2, A3)> {
+ typedef R ReturnType;
+ typedef A1 A1Type;
+ typedef A2 A2Type;
+ typedef A3 A3Type;
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+struct FunctionTraits<R(A1, A2, A3, A4)> {
+ typedef R ReturnType;
+ typedef A1 A1Type;
+ typedef A2 A2Type;
+ typedef A3 A3Type;
+ typedef A4 A4Type;
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5>
+struct FunctionTraits<R(A1, A2, A3, A4, A5)> {
+ typedef R ReturnType;
+ typedef A1 A1Type;
+ typedef A2 A2Type;
+ typedef A3 A3Type;
+ typedef A4 A4Type;
+ typedef A5 A5Type;
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6>
+struct FunctionTraits<R(A1, A2, A3, A4, A5, A6)> {
+ typedef R ReturnType;
+ typedef A1 A1Type;
+ typedef A2 A2Type;
+ typedef A3 A3Type;
+ typedef A4 A4Type;
+ typedef A5 A5Type;
+ typedef A6 A6Type;
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7>
+struct FunctionTraits<R(A1, A2, A3, A4, A5, A6, A7)> {
+ typedef R ReturnType;
+ typedef A1 A1Type;
+ typedef A2 A2Type;
+ typedef A3 A3Type;
+ typedef A4 A4Type;
+ typedef A5 A5Type;
+ typedef A6 A6Type;
+ typedef A7 A7Type;
+};
+
+
+// ForceVoidReturn<>
+//
+// Set of templates that support forcing the function return type to void.
+template <typename Sig>
+struct ForceVoidReturn;
+
+template <typename R>
+struct ForceVoidReturn<R()> {
+ typedef void(RunType)();
+};
+
+template <typename R, typename A1>
+struct ForceVoidReturn<R(A1)> {
+ typedef void(RunType)(A1);
+};
+
+template <typename R, typename A1, typename A2>
+struct ForceVoidReturn<R(A1, A2)> {
+ typedef void(RunType)(A1, A2);
+};
+
+template <typename R, typename A1, typename A2, typename A3>
+struct ForceVoidReturn<R(A1, A2, A3)> {
+ typedef void(RunType)(A1, A2, A3);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+struct ForceVoidReturn<R(A1, A2, A3, A4)> {
+ typedef void(RunType)(A1, A2, A3, A4);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5>
+struct ForceVoidReturn<R(A1, A2, A3, A4, A5)> {
+ typedef void(RunType)(A1, A2, A3, A4, A5);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6>
+struct ForceVoidReturn<R(A1, A2, A3, A4, A5, A6)> {
+ typedef void(RunType)(A1, A2, A3, A4, A5, A6);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7>
+struct ForceVoidReturn<R(A1, A2, A3, A4, A5, A6, A7)> {
+ typedef void(RunType)(A1, A2, A3, A4, A5, A6, A7);
+};
+
+
+// FunctorTraits<>
+//
+// See description at top of file.
+template <typename T>
+struct FunctorTraits {
+ typedef RunnableAdapter<T> RunnableType;
+ typedef typename RunnableType::RunType RunType;
+};
+
+template <typename T>
+struct FunctorTraits<IgnoreResultHelper<T> > {
+ typedef typename FunctorTraits<T>::RunnableType RunnableType;
+ typedef typename ForceVoidReturn<
+ typename RunnableType::RunType>::RunType RunType;
+};
+
+template <typename T>
+struct FunctorTraits<Callback<T> > {
+ typedef Callback<T> RunnableType;
+ typedef typename Callback<T>::RunType RunType;
+};
+
+
+// MakeRunnable<>
+//
+// Converts a passed in functor to a RunnableType using type inference.
+
+template <typename T>
+typename FunctorTraits<T>::RunnableType MakeRunnable(const T& t) {
+ return RunnableAdapter<T>(t);
+}
+
+template <typename T>
+typename FunctorTraits<T>::RunnableType
+MakeRunnable(const IgnoreResultHelper<T>& t) {
+ return MakeRunnable(t.functor_);
+}
+
+template <typename T>
+const typename FunctorTraits<Callback<T> >::RunnableType&
+MakeRunnable(const Callback<T>& t) {
+ DCHECK(!t.is_null());
+ return t;
+}
+
+
+// InvokeHelper<>
+//
+// There are 3 logical InvokeHelper<> specializations: normal, void-return,
+// WeakCalls.
+//
+// The normal type just calls the underlying runnable.
+//
+// We need a InvokeHelper to handle void return types in order to support
+// IgnoreResult(). Normally, if the Runnable's RunType had a void return,
+// the template system would just accept "return functor.Run()" ignoring
+// the fact that a void function is being used with return. This piece of
+// sugar breaks though when the Runnable's RunType is not void. Thus, we
+// need a partial specialization to change the syntax to drop the "return"
+// from the invocation call.
+//
+// WeakCalls similarly need special syntax that is applied to the first
+// argument to check if they should no-op themselves.
+template <bool IsWeakCall, typename ReturnType, typename Runnable,
+ typename ArgsType>
+struct InvokeHelper;
+
+template <typename ReturnType, typename Runnable>
+struct InvokeHelper<false, ReturnType, Runnable,
+ void()> {
+ static ReturnType MakeItSo(Runnable runnable) {
+ return runnable.Run();
+ }
+};
+
+template <typename Runnable>
+struct InvokeHelper<false, void, Runnable,
+ void()> {
+ static void MakeItSo(Runnable runnable) {
+ runnable.Run();
+ }
+};
+
+template <typename ReturnType, typename Runnable,typename A1>
+struct InvokeHelper<false, ReturnType, Runnable,
+ void(A1)> {
+ static ReturnType MakeItSo(Runnable runnable, A1 a1) {
+ return runnable.Run(CallbackForward(a1));
+ }
+};
+
+template <typename Runnable,typename A1>
+struct InvokeHelper<false, void, Runnable,
+ void(A1)> {
+ static void MakeItSo(Runnable runnable, A1 a1) {
+ runnable.Run(CallbackForward(a1));
+ }
+};
+
+template <typename Runnable, typename A1>
+struct InvokeHelper<true, void, Runnable,
+ void(A1)> {
+ static void MakeItSo(Runnable runnable, A1 a1) {
+ if (!a1.get()) {
+ return;
+ }
+
+ runnable.Run(CallbackForward(a1));
+ }
+};
+
+template <typename ReturnType, typename Runnable,typename A1, typename A2>
+struct InvokeHelper<false, ReturnType, Runnable,
+ void(A1, A2)> {
+ static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2) {
+ return runnable.Run(CallbackForward(a1), CallbackForward(a2));
+ }
+};
+
+template <typename Runnable,typename A1, typename A2>
+struct InvokeHelper<false, void, Runnable,
+ void(A1, A2)> {
+ static void MakeItSo(Runnable runnable, A1 a1, A2 a2) {
+ runnable.Run(CallbackForward(a1), CallbackForward(a2));
+ }
+};
+
+template <typename Runnable, typename A1, typename A2>
+struct InvokeHelper<true, void, Runnable,
+ void(A1, A2)> {
+ static void MakeItSo(Runnable runnable, A1 a1, A2 a2) {
+ if (!a1.get()) {
+ return;
+ }
+
+ runnable.Run(CallbackForward(a1), CallbackForward(a2));
+ }
+};
+
+template <typename ReturnType, typename Runnable,typename A1, typename A2,
+ typename A3>
+struct InvokeHelper<false, ReturnType, Runnable,
+ void(A1, A2, A3)> {
+ static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3) {
+ return runnable.Run(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3));
+ }
+};
+
+template <typename Runnable,typename A1, typename A2, typename A3>
+struct InvokeHelper<false, void, Runnable,
+ void(A1, A2, A3)> {
+ static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3) {
+ runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3));
+ }
+};
+
+template <typename Runnable, typename A1, typename A2, typename A3>
+struct InvokeHelper<true, void, Runnable,
+ void(A1, A2, A3)> {
+ static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3) {
+ if (!a1.get()) {
+ return;
+ }
+
+ runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3));
+ }
+};
+
+template <typename ReturnType, typename Runnable,typename A1, typename A2,
+ typename A3, typename A4>
+struct InvokeHelper<false, ReturnType, Runnable,
+ void(A1, A2, A3, A4)> {
+ static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4) {
+ return runnable.Run(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4));
+ }
+};
+
+template <typename Runnable,typename A1, typename A2, typename A3, typename A4>
+struct InvokeHelper<false, void, Runnable,
+ void(A1, A2, A3, A4)> {
+ static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4) {
+ runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3),
+ CallbackForward(a4));
+ }
+};
+
+template <typename Runnable, typename A1, typename A2, typename A3, typename A4>
+struct InvokeHelper<true, void, Runnable,
+ void(A1, A2, A3, A4)> {
+ static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4) {
+ if (!a1.get()) {
+ return;
+ }
+
+ runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3),
+ CallbackForward(a4));
+ }
+};
+
+template <typename ReturnType, typename Runnable,typename A1, typename A2,
+ typename A3, typename A4, typename A5>
+struct InvokeHelper<false, ReturnType, Runnable,
+ void(A1, A2, A3, A4, A5)> {
+ static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4,
+ A5 a5) {
+ return runnable.Run(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4), CallbackForward(a5));
+ }
+};
+
+template <typename Runnable,typename A1, typename A2, typename A3, typename A4,
+ typename A5>
+struct InvokeHelper<false, void, Runnable,
+ void(A1, A2, A3, A4, A5)> {
+ static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) {
+ runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3),
+ CallbackForward(a4), CallbackForward(a5));
+ }
+};
+
+template <typename Runnable, typename A1, typename A2, typename A3,
+ typename A4, typename A5>
+struct InvokeHelper<true, void, Runnable,
+ void(A1, A2, A3, A4, A5)> {
+ static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) {
+ if (!a1.get()) {
+ return;
+ }
+
+ runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3),
+ CallbackForward(a4), CallbackForward(a5));
+ }
+};
+
+template <typename ReturnType, typename Runnable,typename A1, typename A2,
+ typename A3, typename A4, typename A5, typename A6>
+struct InvokeHelper<false, ReturnType, Runnable,
+ void(A1, A2, A3, A4, A5, A6)> {
+ static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4,
+ A5 a5, A6 a6) {
+ return runnable.Run(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4), CallbackForward(a5),
+ CallbackForward(a6));
+ }
+};
+
+template <typename Runnable,typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6>
+struct InvokeHelper<false, void, Runnable,
+ void(A1, A2, A3, A4, A5, A6)> {
+ static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5,
+ A6 a6) {
+ runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3),
+ CallbackForward(a4), CallbackForward(a5), CallbackForward(a6));
+ }
+};
+
+template <typename Runnable, typename A1, typename A2, typename A3,
+ typename A4, typename A5, typename A6>
+struct InvokeHelper<true, void, Runnable,
+ void(A1, A2, A3, A4, A5, A6)> {
+ static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5,
+ A6 a6) {
+ if (!a1.get()) {
+ return;
+ }
+
+ runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3),
+ CallbackForward(a4), CallbackForward(a5), CallbackForward(a6));
+ }
+};
+
+template <typename ReturnType, typename Runnable,typename A1, typename A2,
+ typename A3, typename A4, typename A5, typename A6, typename A7>
+struct InvokeHelper<false, ReturnType, Runnable,
+ void(A1, A2, A3, A4, A5, A6, A7)> {
+ static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4,
+ A5 a5, A6 a6, A7 a7) {
+ return runnable.Run(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4), CallbackForward(a5),
+ CallbackForward(a6), CallbackForward(a7));
+ }
+};
+
+template <typename Runnable,typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7>
+struct InvokeHelper<false, void, Runnable,
+ void(A1, A2, A3, A4, A5, A6, A7)> {
+ static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5,
+ A6 a6, A7 a7) {
+ runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3),
+ CallbackForward(a4), CallbackForward(a5), CallbackForward(a6),
+ CallbackForward(a7));
+ }
+};
+
+template <typename Runnable, typename A1, typename A2, typename A3,
+ typename A4, typename A5, typename A6, typename A7>
+struct InvokeHelper<true, void, Runnable,
+ void(A1, A2, A3, A4, A5, A6, A7)> {
+ static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5,
+ A6 a6, A7 a7) {
+ if (!a1.get()) {
+ return;
+ }
+
+ runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3),
+ CallbackForward(a4), CallbackForward(a5), CallbackForward(a6),
+ CallbackForward(a7));
+ }
+};
+
+#if !defined(_MSC_VER)
+
+template <typename ReturnType, typename Runnable, typename ArgsType>
+struct InvokeHelper<true, ReturnType, Runnable, ArgsType> {
+ // WeakCalls are only supported for functions with a void return type.
+ // Otherwise, the function result would be undefined if the the WeakPtr<>
+ // is invalidated.
+ COMPILE_ASSERT(is_void<ReturnType>::value,
+ weak_ptrs_can_only_bind_to_methods_without_return_values);
+};
+
+#endif
+
+// Invoker<>
+//
+// See description at the top of the file.
+template <int NumBound, typename Storage, typename RunType>
+struct Invoker;
+
+// Arity 0 -> 0.
+template <typename StorageType, typename R>
+struct Invoker<0, StorageType, R()> {
+ typedef R(RunType)(BindStateBase*);
+
+ typedef R(UnboundRunType)();
+
+ static R Run(BindStateBase* base) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void()>
+ ::MakeItSo(storage->runnable_);
+ }
+};
+
+// Arity 1 -> 1.
+template <typename StorageType, typename R,typename X1>
+struct Invoker<0, StorageType, R(X1)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X1>::ForwardType);
+
+ typedef R(UnboundRunType)(X1);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X1>::ForwardType x1) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename CallbackParamTraits<X1>::ForwardType x1)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1));
+ }
+};
+
+// Arity 1 -> 0.
+template <typename StorageType, typename R,typename X1>
+struct Invoker<1, StorageType, R(X1)> {
+ typedef R(RunType)(BindStateBase*);
+
+ typedef R(UnboundRunType)();
+
+ static R Run(BindStateBase* base) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1));
+ }
+};
+
+// Arity 2 -> 2.
+template <typename StorageType, typename R,typename X1, typename X2>
+struct Invoker<0, StorageType, R(X1, X2)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X1>::ForwardType,
+ typename CallbackParamTraits<X2>::ForwardType);
+
+ typedef R(UnboundRunType)(X1, X2);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X1>::ForwardType x1,
+ typename CallbackParamTraits<X2>::ForwardType x2) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename CallbackParamTraits<X1>::ForwardType x1,
+ typename CallbackParamTraits<X2>::ForwardType x2)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2));
+ }
+};
+
+// Arity 2 -> 1.
+template <typename StorageType, typename R,typename X1, typename X2>
+struct Invoker<1, StorageType, R(X1, X2)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X2>::ForwardType);
+
+ typedef R(UnboundRunType)(X2);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X2>::ForwardType x2) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X2>::ForwardType x2)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2));
+ }
+};
+
+// Arity 2 -> 0.
+template <typename StorageType, typename R,typename X1, typename X2>
+struct Invoker<2, StorageType, R(X1, X2)> {
+ typedef R(RunType)(BindStateBase*);
+
+ typedef R(UnboundRunType)();
+
+ static R Run(BindStateBase* base) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2));
+ }
+};
+
+// Arity 3 -> 3.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3>
+struct Invoker<0, StorageType, R(X1, X2, X3)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X1>::ForwardType,
+ typename CallbackParamTraits<X2>::ForwardType,
+ typename CallbackParamTraits<X3>::ForwardType);
+
+ typedef R(UnboundRunType)(X1, X2, X3);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X1>::ForwardType x1,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename CallbackParamTraits<X1>::ForwardType x1,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3));
+ }
+};
+
+// Arity 3 -> 2.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3>
+struct Invoker<1, StorageType, R(X1, X2, X3)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X2>::ForwardType,
+ typename CallbackParamTraits<X3>::ForwardType);
+
+ typedef R(UnboundRunType)(X2, X3);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3));
+ }
+};
+
+// Arity 3 -> 1.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3>
+struct Invoker<2, StorageType, R(X1, X2, X3)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X3>::ForwardType);
+
+ typedef R(UnboundRunType)(X3);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X3>::ForwardType x3) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X3>::ForwardType x3)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3));
+ }
+};
+
+// Arity 3 -> 0.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3>
+struct Invoker<3, StorageType, R(X1, X2, X3)> {
+ typedef R(RunType)(BindStateBase*);
+
+ typedef R(UnboundRunType)();
+
+ static R Run(BindStateBase* base) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+ typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ typename Bound3UnwrapTraits::ForwardType x3 =
+ Bound3UnwrapTraits::Unwrap(storage->p3_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename Bound3UnwrapTraits::ForwardType)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3));
+ }
+};
+
+// Arity 4 -> 4.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4>
+struct Invoker<0, StorageType, R(X1, X2, X3, X4)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X1>::ForwardType,
+ typename CallbackParamTraits<X2>::ForwardType,
+ typename CallbackParamTraits<X3>::ForwardType,
+ typename CallbackParamTraits<X4>::ForwardType);
+
+ typedef R(UnboundRunType)(X1, X2, X3, X4);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X1>::ForwardType x1,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename CallbackParamTraits<X1>::ForwardType x1,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4));
+ }
+};
+
+// Arity 4 -> 3.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4>
+struct Invoker<1, StorageType, R(X1, X2, X3, X4)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X2>::ForwardType,
+ typename CallbackParamTraits<X3>::ForwardType,
+ typename CallbackParamTraits<X4>::ForwardType);
+
+ typedef R(UnboundRunType)(X2, X3, X4);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4));
+ }
+};
+
+// Arity 4 -> 2.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4>
+struct Invoker<2, StorageType, R(X1, X2, X3, X4)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X3>::ForwardType,
+ typename CallbackParamTraits<X4>::ForwardType);
+
+ typedef R(UnboundRunType)(X3, X4);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4));
+ }
+};
+
+// Arity 4 -> 1.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4>
+struct Invoker<3, StorageType, R(X1, X2, X3, X4)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X4>::ForwardType);
+
+ typedef R(UnboundRunType)(X4);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X4>::ForwardType x4) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+ typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ typename Bound3UnwrapTraits::ForwardType x3 =
+ Bound3UnwrapTraits::Unwrap(storage->p3_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename Bound3UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X4>::ForwardType x4)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4));
+ }
+};
+
+// Arity 4 -> 0.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4>
+struct Invoker<4, StorageType, R(X1, X2, X3, X4)> {
+ typedef R(RunType)(BindStateBase*);
+
+ typedef R(UnboundRunType)();
+
+ static R Run(BindStateBase* base) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+ typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+ typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ typename Bound3UnwrapTraits::ForwardType x3 =
+ Bound3UnwrapTraits::Unwrap(storage->p3_);
+ typename Bound4UnwrapTraits::ForwardType x4 =
+ Bound4UnwrapTraits::Unwrap(storage->p4_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename Bound3UnwrapTraits::ForwardType,
+ typename Bound4UnwrapTraits::ForwardType)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4));
+ }
+};
+
+// Arity 5 -> 5.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5>
+struct Invoker<0, StorageType, R(X1, X2, X3, X4, X5)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X1>::ForwardType,
+ typename CallbackParamTraits<X2>::ForwardType,
+ typename CallbackParamTraits<X3>::ForwardType,
+ typename CallbackParamTraits<X4>::ForwardType,
+ typename CallbackParamTraits<X5>::ForwardType);
+
+ typedef R(UnboundRunType)(X1, X2, X3, X4, X5);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X1>::ForwardType x1,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename CallbackParamTraits<X1>::ForwardType x1,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5));
+ }
+};
+
+// Arity 5 -> 4.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5>
+struct Invoker<1, StorageType, R(X1, X2, X3, X4, X5)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X2>::ForwardType,
+ typename CallbackParamTraits<X3>::ForwardType,
+ typename CallbackParamTraits<X4>::ForwardType,
+ typename CallbackParamTraits<X5>::ForwardType);
+
+ typedef R(UnboundRunType)(X2, X3, X4, X5);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5));
+ }
+};
+
+// Arity 5 -> 3.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5>
+struct Invoker<2, StorageType, R(X1, X2, X3, X4, X5)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X3>::ForwardType,
+ typename CallbackParamTraits<X4>::ForwardType,
+ typename CallbackParamTraits<X5>::ForwardType);
+
+ typedef R(UnboundRunType)(X3, X4, X5);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5));
+ }
+};
+
+// Arity 5 -> 2.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5>
+struct Invoker<3, StorageType, R(X1, X2, X3, X4, X5)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X4>::ForwardType,
+ typename CallbackParamTraits<X5>::ForwardType);
+
+ typedef R(UnboundRunType)(X4, X5);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+ typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ typename Bound3UnwrapTraits::ForwardType x3 =
+ Bound3UnwrapTraits::Unwrap(storage->p3_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename Bound3UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5));
+ }
+};
+
+// Arity 5 -> 1.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5>
+struct Invoker<4, StorageType, R(X1, X2, X3, X4, X5)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X5>::ForwardType);
+
+ typedef R(UnboundRunType)(X5);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X5>::ForwardType x5) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+ typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+ typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ typename Bound3UnwrapTraits::ForwardType x3 =
+ Bound3UnwrapTraits::Unwrap(storage->p3_);
+ typename Bound4UnwrapTraits::ForwardType x4 =
+ Bound4UnwrapTraits::Unwrap(storage->p4_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename Bound3UnwrapTraits::ForwardType,
+ typename Bound4UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X5>::ForwardType x5)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5));
+ }
+};
+
+// Arity 5 -> 0.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5>
+struct Invoker<5, StorageType, R(X1, X2, X3, X4, X5)> {
+ typedef R(RunType)(BindStateBase*);
+
+ typedef R(UnboundRunType)();
+
+ static R Run(BindStateBase* base) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+ typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+ typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+ typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ typename Bound3UnwrapTraits::ForwardType x3 =
+ Bound3UnwrapTraits::Unwrap(storage->p3_);
+ typename Bound4UnwrapTraits::ForwardType x4 =
+ Bound4UnwrapTraits::Unwrap(storage->p4_);
+ typename Bound5UnwrapTraits::ForwardType x5 =
+ Bound5UnwrapTraits::Unwrap(storage->p5_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename Bound3UnwrapTraits::ForwardType,
+ typename Bound4UnwrapTraits::ForwardType,
+ typename Bound5UnwrapTraits::ForwardType)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5));
+ }
+};
+
+// Arity 6 -> 6.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5, typename X6>
+struct Invoker<0, StorageType, R(X1, X2, X3, X4, X5, X6)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X1>::ForwardType,
+ typename CallbackParamTraits<X2>::ForwardType,
+ typename CallbackParamTraits<X3>::ForwardType,
+ typename CallbackParamTraits<X4>::ForwardType,
+ typename CallbackParamTraits<X5>::ForwardType,
+ typename CallbackParamTraits<X6>::ForwardType);
+
+ typedef R(UnboundRunType)(X1, X2, X3, X4, X5, X6);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X1>::ForwardType x1,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename CallbackParamTraits<X1>::ForwardType x1,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5),
+ CallbackForward(x6));
+ }
+};
+
+// Arity 6 -> 5.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5, typename X6>
+struct Invoker<1, StorageType, R(X1, X2, X3, X4, X5, X6)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X2>::ForwardType,
+ typename CallbackParamTraits<X3>::ForwardType,
+ typename CallbackParamTraits<X4>::ForwardType,
+ typename CallbackParamTraits<X5>::ForwardType,
+ typename CallbackParamTraits<X6>::ForwardType);
+
+ typedef R(UnboundRunType)(X2, X3, X4, X5, X6);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5),
+ CallbackForward(x6));
+ }
+};
+
+// Arity 6 -> 4.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5, typename X6>
+struct Invoker<2, StorageType, R(X1, X2, X3, X4, X5, X6)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X3>::ForwardType,
+ typename CallbackParamTraits<X4>::ForwardType,
+ typename CallbackParamTraits<X5>::ForwardType,
+ typename CallbackParamTraits<X6>::ForwardType);
+
+ typedef R(UnboundRunType)(X3, X4, X5, X6);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5),
+ CallbackForward(x6));
+ }
+};
+
+// Arity 6 -> 3.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5, typename X6>
+struct Invoker<3, StorageType, R(X1, X2, X3, X4, X5, X6)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X4>::ForwardType,
+ typename CallbackParamTraits<X5>::ForwardType,
+ typename CallbackParamTraits<X6>::ForwardType);
+
+ typedef R(UnboundRunType)(X4, X5, X6);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+ typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ typename Bound3UnwrapTraits::ForwardType x3 =
+ Bound3UnwrapTraits::Unwrap(storage->p3_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename Bound3UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5),
+ CallbackForward(x6));
+ }
+};
+
+// Arity 6 -> 2.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5, typename X6>
+struct Invoker<4, StorageType, R(X1, X2, X3, X4, X5, X6)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X5>::ForwardType,
+ typename CallbackParamTraits<X6>::ForwardType);
+
+ typedef R(UnboundRunType)(X5, X6);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+ typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+ typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ typename Bound3UnwrapTraits::ForwardType x3 =
+ Bound3UnwrapTraits::Unwrap(storage->p3_);
+ typename Bound4UnwrapTraits::ForwardType x4 =
+ Bound4UnwrapTraits::Unwrap(storage->p4_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename Bound3UnwrapTraits::ForwardType,
+ typename Bound4UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5),
+ CallbackForward(x6));
+ }
+};
+
+// Arity 6 -> 1.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5, typename X6>
+struct Invoker<5, StorageType, R(X1, X2, X3, X4, X5, X6)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X6>::ForwardType);
+
+ typedef R(UnboundRunType)(X6);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X6>::ForwardType x6) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+ typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+ typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+ typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ typename Bound3UnwrapTraits::ForwardType x3 =
+ Bound3UnwrapTraits::Unwrap(storage->p3_);
+ typename Bound4UnwrapTraits::ForwardType x4 =
+ Bound4UnwrapTraits::Unwrap(storage->p4_);
+ typename Bound5UnwrapTraits::ForwardType x5 =
+ Bound5UnwrapTraits::Unwrap(storage->p5_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename Bound3UnwrapTraits::ForwardType,
+ typename Bound4UnwrapTraits::ForwardType,
+ typename Bound5UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X6>::ForwardType x6)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5),
+ CallbackForward(x6));
+ }
+};
+
+// Arity 6 -> 0.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5, typename X6>
+struct Invoker<6, StorageType, R(X1, X2, X3, X4, X5, X6)> {
+ typedef R(RunType)(BindStateBase*);
+
+ typedef R(UnboundRunType)();
+
+ static R Run(BindStateBase* base) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+ typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+ typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+ typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits;
+ typedef typename StorageType::Bound6UnwrapTraits Bound6UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ typename Bound3UnwrapTraits::ForwardType x3 =
+ Bound3UnwrapTraits::Unwrap(storage->p3_);
+ typename Bound4UnwrapTraits::ForwardType x4 =
+ Bound4UnwrapTraits::Unwrap(storage->p4_);
+ typename Bound5UnwrapTraits::ForwardType x5 =
+ Bound5UnwrapTraits::Unwrap(storage->p5_);
+ typename Bound6UnwrapTraits::ForwardType x6 =
+ Bound6UnwrapTraits::Unwrap(storage->p6_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename Bound3UnwrapTraits::ForwardType,
+ typename Bound4UnwrapTraits::ForwardType,
+ typename Bound5UnwrapTraits::ForwardType,
+ typename Bound6UnwrapTraits::ForwardType)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5),
+ CallbackForward(x6));
+ }
+};
+
+// Arity 7 -> 7.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5, typename X6, typename X7>
+struct Invoker<0, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X1>::ForwardType,
+ typename CallbackParamTraits<X2>::ForwardType,
+ typename CallbackParamTraits<X3>::ForwardType,
+ typename CallbackParamTraits<X4>::ForwardType,
+ typename CallbackParamTraits<X5>::ForwardType,
+ typename CallbackParamTraits<X6>::ForwardType,
+ typename CallbackParamTraits<X7>::ForwardType);
+
+ typedef R(UnboundRunType)(X1, X2, X3, X4, X5, X6, X7);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X1>::ForwardType x1,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6,
+ typename CallbackParamTraits<X7>::ForwardType x7) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename CallbackParamTraits<X1>::ForwardType x1,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6,
+ typename CallbackParamTraits<X7>::ForwardType x7)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5),
+ CallbackForward(x6), CallbackForward(x7));
+ }
+};
+
+// Arity 7 -> 6.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5, typename X6, typename X7>
+struct Invoker<1, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X2>::ForwardType,
+ typename CallbackParamTraits<X3>::ForwardType,
+ typename CallbackParamTraits<X4>::ForwardType,
+ typename CallbackParamTraits<X5>::ForwardType,
+ typename CallbackParamTraits<X6>::ForwardType,
+ typename CallbackParamTraits<X7>::ForwardType);
+
+ typedef R(UnboundRunType)(X2, X3, X4, X5, X6, X7);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6,
+ typename CallbackParamTraits<X7>::ForwardType x7) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X2>::ForwardType x2,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6,
+ typename CallbackParamTraits<X7>::ForwardType x7)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5),
+ CallbackForward(x6), CallbackForward(x7));
+ }
+};
+
+// Arity 7 -> 5.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5, typename X6, typename X7>
+struct Invoker<2, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X3>::ForwardType,
+ typename CallbackParamTraits<X4>::ForwardType,
+ typename CallbackParamTraits<X5>::ForwardType,
+ typename CallbackParamTraits<X6>::ForwardType,
+ typename CallbackParamTraits<X7>::ForwardType);
+
+ typedef R(UnboundRunType)(X3, X4, X5, X6, X7);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6,
+ typename CallbackParamTraits<X7>::ForwardType x7) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X3>::ForwardType x3,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6,
+ typename CallbackParamTraits<X7>::ForwardType x7)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5),
+ CallbackForward(x6), CallbackForward(x7));
+ }
+};
+
+// Arity 7 -> 4.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5, typename X6, typename X7>
+struct Invoker<3, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X4>::ForwardType,
+ typename CallbackParamTraits<X5>::ForwardType,
+ typename CallbackParamTraits<X6>::ForwardType,
+ typename CallbackParamTraits<X7>::ForwardType);
+
+ typedef R(UnboundRunType)(X4, X5, X6, X7);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6,
+ typename CallbackParamTraits<X7>::ForwardType x7) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+ typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ typename Bound3UnwrapTraits::ForwardType x3 =
+ Bound3UnwrapTraits::Unwrap(storage->p3_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename Bound3UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X4>::ForwardType x4,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6,
+ typename CallbackParamTraits<X7>::ForwardType x7)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5),
+ CallbackForward(x6), CallbackForward(x7));
+ }
+};
+
+// Arity 7 -> 3.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5, typename X6, typename X7>
+struct Invoker<4, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X5>::ForwardType,
+ typename CallbackParamTraits<X6>::ForwardType,
+ typename CallbackParamTraits<X7>::ForwardType);
+
+ typedef R(UnboundRunType)(X5, X6, X7);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6,
+ typename CallbackParamTraits<X7>::ForwardType x7) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+ typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+ typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ typename Bound3UnwrapTraits::ForwardType x3 =
+ Bound3UnwrapTraits::Unwrap(storage->p3_);
+ typename Bound4UnwrapTraits::ForwardType x4 =
+ Bound4UnwrapTraits::Unwrap(storage->p4_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename Bound3UnwrapTraits::ForwardType,
+ typename Bound4UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X5>::ForwardType x5,
+ typename CallbackParamTraits<X6>::ForwardType x6,
+ typename CallbackParamTraits<X7>::ForwardType x7)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5),
+ CallbackForward(x6), CallbackForward(x7));
+ }
+};
+
+// Arity 7 -> 2.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5, typename X6, typename X7>
+struct Invoker<5, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X6>::ForwardType,
+ typename CallbackParamTraits<X7>::ForwardType);
+
+ typedef R(UnboundRunType)(X6, X7);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X6>::ForwardType x6,
+ typename CallbackParamTraits<X7>::ForwardType x7) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+ typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+ typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+ typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ typename Bound3UnwrapTraits::ForwardType x3 =
+ Bound3UnwrapTraits::Unwrap(storage->p3_);
+ typename Bound4UnwrapTraits::ForwardType x4 =
+ Bound4UnwrapTraits::Unwrap(storage->p4_);
+ typename Bound5UnwrapTraits::ForwardType x5 =
+ Bound5UnwrapTraits::Unwrap(storage->p5_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename Bound3UnwrapTraits::ForwardType,
+ typename Bound4UnwrapTraits::ForwardType,
+ typename Bound5UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X6>::ForwardType x6,
+ typename CallbackParamTraits<X7>::ForwardType x7)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5),
+ CallbackForward(x6), CallbackForward(x7));
+ }
+};
+
+// Arity 7 -> 1.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5, typename X6, typename X7>
+struct Invoker<6, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> {
+ typedef R(RunType)(BindStateBase*,
+ typename CallbackParamTraits<X7>::ForwardType);
+
+ typedef R(UnboundRunType)(X7);
+
+ static R Run(BindStateBase* base,
+ typename CallbackParamTraits<X7>::ForwardType x7) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+ typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+ typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+ typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits;
+ typedef typename StorageType::Bound6UnwrapTraits Bound6UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ typename Bound3UnwrapTraits::ForwardType x3 =
+ Bound3UnwrapTraits::Unwrap(storage->p3_);
+ typename Bound4UnwrapTraits::ForwardType x4 =
+ Bound4UnwrapTraits::Unwrap(storage->p4_);
+ typename Bound5UnwrapTraits::ForwardType x5 =
+ Bound5UnwrapTraits::Unwrap(storage->p5_);
+ typename Bound6UnwrapTraits::ForwardType x6 =
+ Bound6UnwrapTraits::Unwrap(storage->p6_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename Bound3UnwrapTraits::ForwardType,
+ typename Bound4UnwrapTraits::ForwardType,
+ typename Bound5UnwrapTraits::ForwardType,
+ typename Bound6UnwrapTraits::ForwardType,
+ typename CallbackParamTraits<X7>::ForwardType x7)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5),
+ CallbackForward(x6), CallbackForward(x7));
+ }
+};
+
+// Arity 7 -> 0.
+template <typename StorageType, typename R,typename X1, typename X2,
+ typename X3, typename X4, typename X5, typename X6, typename X7>
+struct Invoker<7, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> {
+ typedef R(RunType)(BindStateBase*);
+
+ typedef R(UnboundRunType)();
+
+ static R Run(BindStateBase* base) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+ typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
+ typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
+ typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits;
+ typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits;
+ typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits;
+ typedef typename StorageType::Bound6UnwrapTraits Bound6UnwrapTraits;
+ typedef typename StorageType::Bound7UnwrapTraits Bound7UnwrapTraits;
+
+ typename Bound1UnwrapTraits::ForwardType x1 =
+ Bound1UnwrapTraits::Unwrap(storage->p1_);
+ typename Bound2UnwrapTraits::ForwardType x2 =
+ Bound2UnwrapTraits::Unwrap(storage->p2_);
+ typename Bound3UnwrapTraits::ForwardType x3 =
+ Bound3UnwrapTraits::Unwrap(storage->p3_);
+ typename Bound4UnwrapTraits::ForwardType x4 =
+ Bound4UnwrapTraits::Unwrap(storage->p4_);
+ typename Bound5UnwrapTraits::ForwardType x5 =
+ Bound5UnwrapTraits::Unwrap(storage->p5_);
+ typename Bound6UnwrapTraits::ForwardType x6 =
+ Bound6UnwrapTraits::Unwrap(storage->p6_);
+ typename Bound7UnwrapTraits::ForwardType x7 =
+ Bound7UnwrapTraits::Unwrap(storage->p7_);
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(typename Bound1UnwrapTraits::ForwardType,
+ typename Bound2UnwrapTraits::ForwardType,
+ typename Bound3UnwrapTraits::ForwardType,
+ typename Bound4UnwrapTraits::ForwardType,
+ typename Bound5UnwrapTraits::ForwardType,
+ typename Bound6UnwrapTraits::ForwardType,
+ typename Bound7UnwrapTraits::ForwardType)>
+ ::MakeItSo(storage->runnable_, CallbackForward(x1),
+ CallbackForward(x2), CallbackForward(x3),
+ CallbackForward(x4), CallbackForward(x5),
+ CallbackForward(x6), CallbackForward(x7));
+ }
+};
+
+
+// BindState<>
+//
+// This stores all the state passed into Bind() and is also where most
+// of the template resolution magic occurs.
+//
+// Runnable is the functor we are binding arguments to.
+// RunType is type of the Run() function that the Invoker<> should use.
+// Normally, this is the same as the RunType of the Runnable, but it can
+// be different if an adapter like IgnoreResult() has been used.
+//
+// BoundArgsType contains the storage type for all the bound arguments by
+// (ab)using a function type.
+template <typename Runnable, typename RunType, typename BoundArgsType>
+struct BindState;
+
+template <typename Runnable, typename RunType>
+struct BindState<Runnable, RunType, void()> : public BindStateBase {
+ typedef Runnable RunnableType;
+ typedef false_type IsWeakCall;
+ typedef Invoker<0, BindState, RunType> InvokerType;
+ typedef typename InvokerType::UnboundRunType UnboundRunType;
+ explicit BindState(const Runnable& runnable)
+ : runnable_(runnable) {
+ }
+
+ virtual ~BindState() { }
+
+ RunnableType runnable_;
+};
+
+template <typename Runnable, typename RunType, typename P1>
+struct BindState<Runnable, RunType, void(P1)> : public BindStateBase {
+ typedef Runnable RunnableType;
+ typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall;
+ typedef Invoker<1, BindState, RunType> InvokerType;
+ typedef typename InvokerType::UnboundRunType UnboundRunType;
+
+ // Convenience typedefs for bound argument types.
+ typedef UnwrapTraits<P1> Bound1UnwrapTraits;
+
+ BindState(const Runnable& runnable, const P1& p1)
+ : runnable_(runnable),
+ p1_(p1) {
+ MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_);
+ }
+
+ virtual ~BindState() { MaybeRefcount<HasIsMethodTag<Runnable>::value,
+ P1>::Release(p1_); }
+
+ RunnableType runnable_;
+ P1 p1_;
+};
+
+template <typename Runnable, typename RunType, typename P1, typename P2>
+struct BindState<Runnable, RunType, void(P1, P2)> : public BindStateBase {
+ typedef Runnable RunnableType;
+ typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall;
+ typedef Invoker<2, BindState, RunType> InvokerType;
+ typedef typename InvokerType::UnboundRunType UnboundRunType;
+
+ // Convenience typedefs for bound argument types.
+ typedef UnwrapTraits<P1> Bound1UnwrapTraits;
+ typedef UnwrapTraits<P2> Bound2UnwrapTraits;
+
+ BindState(const Runnable& runnable, const P1& p1, const P2& p2)
+ : runnable_(runnable),
+ p1_(p1),
+ p2_(p2) {
+ MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_);
+ }
+
+ virtual ~BindState() { MaybeRefcount<HasIsMethodTag<Runnable>::value,
+ P1>::Release(p1_); }
+
+ RunnableType runnable_;
+ P1 p1_;
+ P2 p2_;
+};
+
+template <typename Runnable, typename RunType, typename P1, typename P2,
+ typename P3>
+struct BindState<Runnable, RunType, void(P1, P2, P3)> : public BindStateBase {
+ typedef Runnable RunnableType;
+ typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall;
+ typedef Invoker<3, BindState, RunType> InvokerType;
+ typedef typename InvokerType::UnboundRunType UnboundRunType;
+
+ // Convenience typedefs for bound argument types.
+ typedef UnwrapTraits<P1> Bound1UnwrapTraits;
+ typedef UnwrapTraits<P2> Bound2UnwrapTraits;
+ typedef UnwrapTraits<P3> Bound3UnwrapTraits;
+
+ BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3)
+ : runnable_(runnable),
+ p1_(p1),
+ p2_(p2),
+ p3_(p3) {
+ MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_);
+ }
+
+ virtual ~BindState() { MaybeRefcount<HasIsMethodTag<Runnable>::value,
+ P1>::Release(p1_); }
+
+ RunnableType runnable_;
+ P1 p1_;
+ P2 p2_;
+ P3 p3_;
+};
+
+template <typename Runnable, typename RunType, typename P1, typename P2,
+ typename P3, typename P4>
+struct BindState<Runnable, RunType, void(P1, P2, P3,
+ P4)> : public BindStateBase {
+ typedef Runnable RunnableType;
+ typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall;
+ typedef Invoker<4, BindState, RunType> InvokerType;
+ typedef typename InvokerType::UnboundRunType UnboundRunType;
+
+ // Convenience typedefs for bound argument types.
+ typedef UnwrapTraits<P1> Bound1UnwrapTraits;
+ typedef UnwrapTraits<P2> Bound2UnwrapTraits;
+ typedef UnwrapTraits<P3> Bound3UnwrapTraits;
+ typedef UnwrapTraits<P4> Bound4UnwrapTraits;
+
+ BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3,
+ const P4& p4)
+ : runnable_(runnable),
+ p1_(p1),
+ p2_(p2),
+ p3_(p3),
+ p4_(p4) {
+ MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_);
+ }
+
+ virtual ~BindState() { MaybeRefcount<HasIsMethodTag<Runnable>::value,
+ P1>::Release(p1_); }
+
+ RunnableType runnable_;
+ P1 p1_;
+ P2 p2_;
+ P3 p3_;
+ P4 p4_;
+};
+
+template <typename Runnable, typename RunType, typename P1, typename P2,
+ typename P3, typename P4, typename P5>
+struct BindState<Runnable, RunType, void(P1, P2, P3, P4,
+ P5)> : public BindStateBase {
+ typedef Runnable RunnableType;
+ typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall;
+ typedef Invoker<5, BindState, RunType> InvokerType;
+ typedef typename InvokerType::UnboundRunType UnboundRunType;
+
+ // Convenience typedefs for bound argument types.
+ typedef UnwrapTraits<P1> Bound1UnwrapTraits;
+ typedef UnwrapTraits<P2> Bound2UnwrapTraits;
+ typedef UnwrapTraits<P3> Bound3UnwrapTraits;
+ typedef UnwrapTraits<P4> Bound4UnwrapTraits;
+ typedef UnwrapTraits<P5> Bound5UnwrapTraits;
+
+ BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3,
+ const P4& p4, const P5& p5)
+ : runnable_(runnable),
+ p1_(p1),
+ p2_(p2),
+ p3_(p3),
+ p4_(p4),
+ p5_(p5) {
+ MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_);
+ }
+
+ virtual ~BindState() { MaybeRefcount<HasIsMethodTag<Runnable>::value,
+ P1>::Release(p1_); }
+
+ RunnableType runnable_;
+ P1 p1_;
+ P2 p2_;
+ P3 p3_;
+ P4 p4_;
+ P5 p5_;
+};
+
+template <typename Runnable, typename RunType, typename P1, typename P2,
+ typename P3, typename P4, typename P5, typename P6>
+struct BindState<Runnable, RunType, void(P1, P2, P3, P4, P5,
+ P6)> : public BindStateBase {
+ typedef Runnable RunnableType;
+ typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall;
+ typedef Invoker<6, BindState, RunType> InvokerType;
+ typedef typename InvokerType::UnboundRunType UnboundRunType;
+
+ // Convenience typedefs for bound argument types.
+ typedef UnwrapTraits<P1> Bound1UnwrapTraits;
+ typedef UnwrapTraits<P2> Bound2UnwrapTraits;
+ typedef UnwrapTraits<P3> Bound3UnwrapTraits;
+ typedef UnwrapTraits<P4> Bound4UnwrapTraits;
+ typedef UnwrapTraits<P5> Bound5UnwrapTraits;
+ typedef UnwrapTraits<P6> Bound6UnwrapTraits;
+
+ BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3,
+ const P4& p4, const P5& p5, const P6& p6)
+ : runnable_(runnable),
+ p1_(p1),
+ p2_(p2),
+ p3_(p3),
+ p4_(p4),
+ p5_(p5),
+ p6_(p6) {
+ MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_);
+ }
+
+ virtual ~BindState() { MaybeRefcount<HasIsMethodTag<Runnable>::value,
+ P1>::Release(p1_); }
+
+ RunnableType runnable_;
+ P1 p1_;
+ P2 p2_;
+ P3 p3_;
+ P4 p4_;
+ P5 p5_;
+ P6 p6_;
+};
+
+template <typename Runnable, typename RunType, typename P1, typename P2,
+ typename P3, typename P4, typename P5, typename P6, typename P7>
+struct BindState<Runnable, RunType, void(P1, P2, P3, P4, P5, P6,
+ P7)> : public BindStateBase {
+ typedef Runnable RunnableType;
+ typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall;
+ typedef Invoker<7, BindState, RunType> InvokerType;
+ typedef typename InvokerType::UnboundRunType UnboundRunType;
+
+ // Convenience typedefs for bound argument types.
+ typedef UnwrapTraits<P1> Bound1UnwrapTraits;
+ typedef UnwrapTraits<P2> Bound2UnwrapTraits;
+ typedef UnwrapTraits<P3> Bound3UnwrapTraits;
+ typedef UnwrapTraits<P4> Bound4UnwrapTraits;
+ typedef UnwrapTraits<P5> Bound5UnwrapTraits;
+ typedef UnwrapTraits<P6> Bound6UnwrapTraits;
+ typedef UnwrapTraits<P7> Bound7UnwrapTraits;
+
+ BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3,
+ const P4& p4, const P5& p5, const P6& p6, const P7& p7)
+ : runnable_(runnable),
+ p1_(p1),
+ p2_(p2),
+ p3_(p3),
+ p4_(p4),
+ p5_(p5),
+ p6_(p6),
+ p7_(p7) {
+ MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_);
+ }
+
+ virtual ~BindState() { MaybeRefcount<HasIsMethodTag<Runnable>::value,
+ P1>::Release(p1_); }
+
+ RunnableType runnable_;
+ P1 p1_;
+ P2 p2_;
+ P3 p3_;
+ P4 p4_;
+ P5 p5_;
+ P6 p6_;
+ P7 p7_;
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_BIND_INTERNAL_H_
diff --git a/src/base/bind_internal.h.pump b/src/base/bind_internal.h.pump
new file mode 100644
index 0000000..4760af7
--- /dev/null
+++ b/src/base/bind_internal.h.pump
@@ -0,0 +1,504 @@
+$$ This is a pump file for generating file templates. Pump is a python
+$$ script that is part of the Google Test suite of utilities. Description
+$$ can be found here:
+$$
+$$ http://code.google.com/p/googletest/wiki/PumpManual
+$$
+
+$$ See comment for MAX_ARITY in base/bind.h.pump.
+$var MAX_ARITY = 7
+$range ARITY 0..MAX_ARITY
+
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_BIND_INTERNAL_H_
+#define BASE_BIND_INTERNAL_H_
+
+#include "base/bind_helpers.h"
+#include "base/callback_internal.h"
+#include "base/memory/raw_scoped_refptr_mismatch_checker.h"
+#include "base/memory/weak_ptr.h"
+#include "base/template_util.h"
+#include "build/build_config.h"
+
+#if defined(__LB_SHELL__) || defined(OS_STARBOARD)
+// Check for C++11 support
+#if (_MSC_VER >= 1700) || (__cplusplus > 199711L)
+#include "base/bind_internal_functor.h"
+#endif
+#endif
+
+#if defined(OS_WIN)
+#include "base/bind_internal_win.h"
+#endif
+
+namespace base {
+namespace internal {
+
+// See base/callback.h for user documentation.
+//
+//
+// CONCEPTS:
+// Runnable -- A type (really a type class) that has a single Run() method
+// and a RunType typedef that corresponds to the type of Run().
+// A Runnable can declare that it should treated like a method
+// call by including a typedef named IsMethod. The value of
+// this typedef is NOT inspected, only the existence. When a
+// Runnable declares itself a method, Bind() will enforce special
+// refcounting + WeakPtr handling semantics for the first
+// parameter which is expected to be an object.
+// Functor -- A copyable type representing something that should be called.
+// All function pointers, Callback<>, and Runnables are functors
+// even if the invocation syntax differs.
+// RunType -- A function type (as opposed to function _pointer_ type) for
+// a Run() function. Usually just a convenience typedef.
+// (Bound)ArgsType -- A function type that is being (ab)used to store the
+// types of set of arguments. The "return" type is always
+// void here. We use this hack so that we do not need
+// a new type name for each arity of type. (eg.,
+// BindState1, BindState2). This makes forward
+// declarations and friending much much easier.
+//
+// Types:
+// RunnableAdapter<> -- Wraps the various "function" pointer types into an
+// object that adheres to the Runnable interface.
+// There are |3*ARITY| RunnableAdapter types.
+// FunctionTraits<> -- Type traits that unwrap a function signature into a
+// a set of easier to use typedefs. Used mainly for
+// compile time asserts.
+// There are |ARITY| FunctionTraits types.
+// ForceVoidReturn<> -- Helper class for translating function signatures to
+// equivalent forms with a "void" return type.
+// There are |ARITY| ForceVoidReturn types.
+// FunctorTraits<> -- Type traits used determine the correct RunType and
+// RunnableType for a Functor. This is where function
+// signature adapters are applied.
+// There are |ARITY| ForceVoidReturn types.
+// MakeRunnable<> -- Takes a Functor and returns an object in the Runnable
+// type class that represents the underlying Functor.
+// There are |O(1)| MakeRunnable types.
+// InvokeHelper<> -- Take a Runnable + arguments and actully invokes it.
+// Handle the differing syntaxes needed for WeakPtr<> support,
+// and for ignoring return values. This is separate from
+// Invoker to avoid creating multiple version of Invoker<>
+// which grows at O(n^2) with the arity.
+// There are |k*ARITY| InvokeHelper types.
+// Invoker<> -- Unwraps the curried parameters and executes the Runnable.
+// There are |(ARITY^2 + ARITY)/2| Invoketypes.
+// BindState<> -- Stores the curried parameters, and is the main entry point
+// into the Bind() system, doing most of the type resolution.
+// There are ARITY BindState types.
+
+// RunnableAdapter<>
+//
+// The RunnableAdapter<> templates provide a uniform interface for invoking
+// a function pointer, method pointer, or const method pointer. The adapter
+// exposes a Run() method with an appropriate signature. Using this wrapper
+// allows for writing code that supports all three pointer types without
+// undue repetition. Without it, a lot of code would need to be repeated 3
+// times.
+//
+// For method pointers and const method pointers the first argument to Run()
+// is considered to be the received of the method. This is similar to STL's
+// mem_fun().
+//
+// This class also exposes a RunType typedef that is the function type of the
+// Run() function.
+//
+// If and only if the wrapper contains a method or const method pointer, an
+// IsMethod typedef is exposed. The existence of this typedef (NOT the value)
+// marks that the wrapper should be considered a method wrapper.
+
+template <typename Functor>
+class RunnableAdapter;
+
+$for ARITY [[
+$range ARG 1..ARITY
+
+// Function: Arity $(ARITY).
+template <typename R[[]]
+$if ARITY > 0[[, ]] $for ARG , [[typename A$(ARG)]]>
+class RunnableAdapter<R(*)($for ARG , [[A$(ARG)]])> {
+ public:
+ typedef R (RunType)($for ARG , [[A$(ARG)]]);
+
+ explicit RunnableAdapter(R(*function)($for ARG , [[A$(ARG)]]))
+ : function_(function) {
+ }
+
+ R Run($for ARG , [[typename CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) {
+ return function_($for ARG , [[CallbackForward(a$(ARG))]]);
+ }
+
+ private:
+ R (*function_)($for ARG , [[A$(ARG)]]);
+};
+
+// Method: Arity $(ARITY).
+template <typename R, typename T[[]]
+$if ARITY > 0[[, ]] $for ARG , [[typename A$(ARG)]]>
+class RunnableAdapter<R(T::*)($for ARG , [[A$(ARG)]])> {
+ public:
+ typedef R (RunType)(T*[[]]
+$if ARITY > 0[[, ]] $for ARG , [[A$(ARG)]]);
+ typedef true_type IsMethod;
+
+ explicit RunnableAdapter(R(T::*method)($for ARG , [[A$(ARG)]]))
+ : method_(method) {
+ }
+
+ R Run(T* object[[]]
+$if ARITY > 0[[, ]] $for ARG, [[typename CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) {
+ return (object->*method_)($for ARG , [[CallbackForward(a$(ARG))]]);
+ }
+
+ private:
+ R (T::*method_)($for ARG , [[A$(ARG)]]);
+};
+
+// Const Method: Arity $(ARITY).
+template <typename R, typename T[[]]
+$if ARITY > 0[[, ]] $for ARG , [[typename A$(ARG)]]>
+class RunnableAdapter<R(T::*)($for ARG , [[A$(ARG)]]) const> {
+ public:
+ typedef R (RunType)(const T*[[]]
+$if ARITY > 0[[, ]] $for ARG , [[A$(ARG)]]);
+ typedef true_type IsMethod;
+
+ explicit RunnableAdapter(R(T::*method)($for ARG , [[A$(ARG)]]) const)
+ : method_(method) {
+ }
+
+ R Run(const T* object[[]]
+$if ARITY > 0[[, ]] $for ARG, [[typename CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) {
+ return (object->*method_)($for ARG , [[CallbackForward(a$(ARG))]]);
+ }
+
+ private:
+ R (T::*method_)($for ARG , [[A$(ARG)]]) const;
+};
+
+]] $$ for ARITY
+
+
+// FunctionTraits<>
+//
+// Breaks a function signature apart into typedefs for easier introspection.
+template <typename Sig>
+struct FunctionTraits;
+
+$for ARITY [[
+$range ARG 1..ARITY
+
+template <typename R[[]]
+$if ARITY > 0[[, ]] $for ARG , [[typename A$(ARG)]]>
+struct FunctionTraits<R($for ARG , [[A$(ARG)]])> {
+ typedef R ReturnType;
+$for ARG [[
+
+ typedef A$(ARG) A$(ARG)Type;
+]]
+
+};
+
+]]
+
+
+// ForceVoidReturn<>
+//
+// Set of templates that support forcing the function return type to void.
+template <typename Sig>
+struct ForceVoidReturn;
+
+$for ARITY [[
+$range ARG 1..ARITY
+
+template <typename R[[]]
+$if ARITY > 0[[, ]] $for ARG , [[typename A$(ARG)]]>
+struct ForceVoidReturn<R($for ARG , [[A$(ARG)]])> {
+ typedef void(RunType)($for ARG , [[A$(ARG)]]);
+};
+
+]] $$ for ARITY
+
+
+// FunctorTraits<>
+//
+// See description at top of file.
+template <typename T>
+struct FunctorTraits {
+ typedef RunnableAdapter<T> RunnableType;
+ typedef typename RunnableType::RunType RunType;
+};
+
+template <typename T>
+struct FunctorTraits<IgnoreResultHelper<T> > {
+ typedef typename FunctorTraits<T>::RunnableType RunnableType;
+ typedef typename ForceVoidReturn<
+ typename RunnableType::RunType>::RunType RunType;
+};
+
+template <typename T>
+struct FunctorTraits<Callback<T> > {
+ typedef Callback<T> RunnableType;
+ typedef typename Callback<T>::RunType RunType;
+};
+
+
+// MakeRunnable<>
+//
+// Converts a passed in functor to a RunnableType using type inference.
+
+template <typename T>
+typename FunctorTraits<T>::RunnableType MakeRunnable(const T& t) {
+ return RunnableAdapter<T>(t);
+}
+
+template <typename T>
+typename FunctorTraits<T>::RunnableType
+MakeRunnable(const IgnoreResultHelper<T>& t) {
+ return MakeRunnable(t.functor_);
+}
+
+template <typename T>
+const typename FunctorTraits<Callback<T> >::RunnableType&
+MakeRunnable(const Callback<T>& t) {
+ DCHECK(!t.is_null());
+ return t;
+}
+
+
+// InvokeHelper<>
+//
+// There are 3 logical InvokeHelper<> specializations: normal, void-return,
+// WeakCalls.
+//
+// The normal type just calls the underlying runnable.
+//
+// We need a InvokeHelper to handle void return types in order to support
+// IgnoreResult(). Normally, if the Runnable's RunType had a void return,
+// the template system would just accept "return functor.Run()" ignoring
+// the fact that a void function is being used with return. This piece of
+// sugar breaks though when the Runnable's RunType is not void. Thus, we
+// need a partial specialization to change the syntax to drop the "return"
+// from the invocation call.
+//
+// WeakCalls similarly need special syntax that is applied to the first
+// argument to check if they should no-op themselves.
+template <bool IsWeakCall, typename ReturnType, typename Runnable,
+ typename ArgsType>
+struct InvokeHelper;
+
+$for ARITY [[
+$range ARG 1..ARITY
+
+template <typename ReturnType, typename Runnable[[]]
+$if ARITY > 0 [[,]] $for ARG , [[typename A$(ARG)]]>
+struct InvokeHelper<false, ReturnType, Runnable,
+ void($for ARG , [[A$(ARG)]])> {
+ static ReturnType MakeItSo(Runnable runnable[[]]
+$if ARITY > 0[[, ]] $for ARG , [[A$(ARG) a$(ARG)]]) {
+ return runnable.Run($for ARG , [[CallbackForward(a$(ARG))]]);
+ }
+};
+
+template <typename Runnable[[]]
+$if ARITY > 0 [[,]] $for ARG , [[typename A$(ARG)]]>
+struct InvokeHelper<false, void, Runnable,
+ void($for ARG , [[A$(ARG)]])> {
+ static void MakeItSo(Runnable runnable[[]]
+$if ARITY > 0[[, ]] $for ARG , [[A$(ARG) a$(ARG)]]) {
+ runnable.Run($for ARG , [[CallbackForward(a$(ARG))]]);
+ }
+};
+
+$if ARITY > 0 [[
+
+template <typename Runnable[[]], $for ARG , [[typename A$(ARG)]]>
+struct InvokeHelper<true, void, Runnable,
+ void($for ARG , [[A$(ARG)]])> {
+ static void MakeItSo(Runnable runnable[[]]
+$if ARITY > 0[[, ]] $for ARG , [[A$(ARG) a$(ARG)]]) {
+ if (!a1.get()) {
+ return;
+ }
+
+ runnable.Run($for ARG , [[CallbackForward(a$(ARG))]]);
+ }
+};
+
+]]
+
+]] $$ for ARITY
+
+#if !defined(_MSC_VER)
+
+template <typename ReturnType, typename Runnable, typename ArgsType>
+struct InvokeHelper<true, ReturnType, Runnable, ArgsType> {
+ // WeakCalls are only supported for functions with a void return type.
+ // Otherwise, the function result would be undefined if the the WeakPtr<>
+ // is invalidated.
+ COMPILE_ASSERT(is_void<ReturnType>::value,
+ weak_ptrs_can_only_bind_to_methods_without_return_values);
+};
+
+#endif
+
+// Invoker<>
+//
+// See description at the top of the file.
+template <int NumBound, typename Storage, typename RunType>
+struct Invoker;
+
+$for ARITY [[
+
+$$ Number of bound arguments.
+$range BOUND 0..ARITY
+$for BOUND [[
+
+$var UNBOUND = ARITY - BOUND
+$range ARG 1..ARITY
+$range BOUND_ARG 1..BOUND
+$range UNBOUND_ARG (ARITY - UNBOUND + 1)..ARITY
+
+// Arity $(ARITY) -> $(UNBOUND).
+template <typename StorageType, typename R[[]]
+$if ARITY > 0 [[,]][[]]
+$for ARG , [[typename X$(ARG)]]>
+struct Invoker<$(BOUND), StorageType, R($for ARG , [[X$(ARG)]])> {
+ typedef R(RunType)(BindStateBase*[[]]
+$if UNBOUND != 0 [[, ]]
+$for UNBOUND_ARG , [[typename CallbackParamTraits<X$(UNBOUND_ARG)>::ForwardType]]);
+
+ typedef R(UnboundRunType)($for UNBOUND_ARG , [[X$(UNBOUND_ARG)]]);
+
+ static R Run(BindStateBase* base[[]]
+$if UNBOUND != 0 [[, ]][[]]
+$for UNBOUND_ARG , [[
+typename CallbackParamTraits<X$(UNBOUND_ARG)>::ForwardType x$(UNBOUND_ARG)
+]][[]]
+) {
+ StorageType* storage = static_cast<StorageType*>(base);
+
+ // Local references to make debugger stepping easier. If in a debugger,
+ // you really want to warp ahead and step through the
+ // InvokeHelper<>::MakeItSo() call below.
+$for BOUND_ARG
+[[
+
+ typedef typename StorageType::Bound$(BOUND_ARG)UnwrapTraits Bound$(BOUND_ARG)UnwrapTraits;
+]]
+
+
+$for BOUND_ARG
+[[
+
+ typename Bound$(BOUND_ARG)UnwrapTraits::ForwardType x$(BOUND_ARG) =
+ Bound$(BOUND_ARG)UnwrapTraits::Unwrap(storage->p$(BOUND_ARG)_);
+]]
+
+ return InvokeHelper<StorageType::IsWeakCall::value, R,
+ typename StorageType::RunnableType,
+ void(
+$for BOUND_ARG , [[
+typename Bound$(BOUND_ARG)UnwrapTraits::ForwardType
+]]
+
+$if UNBOUND > 0 [[$if BOUND > 0 [[, ]]]][[]]
+
+$for UNBOUND_ARG , [[
+typename CallbackParamTraits<X$(UNBOUND_ARG)>::ForwardType x$(UNBOUND_ARG)
+]]
+)>
+ ::MakeItSo(storage->runnable_
+$if ARITY > 0[[, ]] $for ARG , [[CallbackForward(x$(ARG))]]);
+ }
+};
+
+]] $$ for BOUND
+]] $$ for ARITY
+
+
+// BindState<>
+//
+// This stores all the state passed into Bind() and is also where most
+// of the template resolution magic occurs.
+//
+// Runnable is the functor we are binding arguments to.
+// RunType is type of the Run() function that the Invoker<> should use.
+// Normally, this is the same as the RunType of the Runnable, but it can
+// be different if an adapter like IgnoreResult() has been used.
+//
+// BoundArgsType contains the storage type for all the bound arguments by
+// (ab)using a function type.
+template <typename Runnable, typename RunType, typename BoundArgsType>
+struct BindState;
+
+$for ARITY [[
+$range ARG 1..ARITY
+
+template <typename Runnable, typename RunType[[]]
+$if ARITY > 0[[, ]] $for ARG , [[typename P$(ARG)]]>
+struct BindState<Runnable, RunType, void($for ARG , [[P$(ARG)]])> : public BindStateBase {
+ typedef Runnable RunnableType;
+
+$if ARITY > 0 [[
+ typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall;
+]] $else [[
+ typedef false_type IsWeakCall;
+]]
+
+ typedef Invoker<$(ARITY), BindState, RunType> InvokerType;
+ typedef typename InvokerType::UnboundRunType UnboundRunType;
+
+$if ARITY > 0 [[
+
+ // Convenience typedefs for bound argument types.
+
+$for ARG [[
+ typedef UnwrapTraits<P$(ARG)> Bound$(ARG)UnwrapTraits;
+
+]] $$ for ARG
+
+
+]] $$ if ARITY > 0
+
+$$ The extra [[ ]] is needed to massage spacing. Silly pump.py.
+[[ ]]$if ARITY == 0 [[explicit ]]BindState(const Runnable& runnable
+$if ARITY > 0 [[, ]] $for ARG , [[const P$(ARG)& p$(ARG)]])
+ : runnable_(runnable)[[]]
+$if ARITY == 0 [[
+ {
+
+]] $else [[
+, $for ARG , [[
+
+ p$(ARG)_(p$(ARG))
+]] {
+ MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_);
+
+]]
+ }
+
+ virtual ~BindState() {
+$if ARITY > 0 [[
+ MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::Release(p1_);
+]]
+ }
+
+ RunnableType runnable_;
+
+$for ARG [[
+ P$(ARG) p$(ARG)_;
+
+]]
+};
+
+]] $$ for ARITY
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_BIND_INTERNAL_H_
diff --git a/src/base/bind_internal_functor.h b/src/base/bind_internal_functor.h
new file mode 100644
index 0000000..ede69bc
--- /dev/null
+++ b/src/base/bind_internal_functor.h
@@ -0,0 +1,221 @@
+// This file was GENERATED by command:
+// pump.py bind_internal_functor.h.pump
+// DO NOT EDIT BY HAND!!!
+
+
+// Copyright (c) 2013 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_BIND_INTERNAL_FUNCTOR_H_
+#define BASE_BIND_INTERNAL_FUNCTOR_H_
+
+namespace base {
+namespace internal {
+
+// The RunnableAdapterFunctor<> template is used in a template specialization
+// for RunnableAdapter<> which enables support for functor objects, including
+// C++11 lambdas. The functor signature is deduced from the type of the
+// operator() method using decltype().
+//
+// All supported functors are assumed to have a const qualifier in their
+// operator() method.
+
+template <typename Functor>
+class RunnableAdapter;
+
+template <typename Functor, typename Sig>
+class RunnableAdapterFunctor;
+
+template <typename Functor>
+class RunnableAdapter
+ : public RunnableAdapterFunctor<Functor, decltype(&Functor::operator())> {
+ public:
+ typedef RunnableAdapterFunctor<Functor,
+ decltype(&Functor::operator())> BaseType;
+
+ explicit RunnableAdapter(Functor functor)
+ : BaseType(functor) {}
+};
+
+// Functor: Arity 0.
+template <typename F, typename T, typename R>
+class RunnableAdapterFunctor<F, R(T::*)() const> {
+ public:
+ typedef R (RunType)();
+
+ explicit RunnableAdapterFunctor(F functor)
+ : functor_(functor) {
+ }
+
+ R Run() {
+ return functor_();
+ }
+
+ private:
+ F functor_;
+};
+
+// Functor: Arity 1.
+template <typename F, typename T, typename R, typename A1>
+class RunnableAdapterFunctor<F, R(T::*)(A1) const> {
+ public:
+ typedef R (RunType)(A1);
+
+ explicit RunnableAdapterFunctor(F functor)
+ : functor_(functor) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1) {
+ return functor_(CallbackForward(a1));
+ }
+
+ private:
+ F functor_;
+};
+
+// Functor: Arity 2.
+template <typename F, typename T, typename R, typename A1, typename A2>
+class RunnableAdapterFunctor<F, R(T::*)(A1, A2) const> {
+ public:
+ typedef R (RunType)(A1, A2);
+
+ explicit RunnableAdapterFunctor(F functor)
+ : functor_(functor) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2) {
+ return functor_(CallbackForward(a1), CallbackForward(a2));
+ }
+
+ private:
+ F functor_;
+};
+
+// Functor: Arity 3.
+template <typename F, typename T, typename R, typename A1, typename A2,
+ typename A3>
+class RunnableAdapterFunctor<F, R(T::*)(A1, A2, A3) const> {
+ public:
+ typedef R (RunType)(A1, A2, A3);
+
+ explicit RunnableAdapterFunctor(F functor)
+ : functor_(functor) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3) {
+ return functor_(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3));
+ }
+
+ private:
+ F functor_;
+};
+
+// Functor: Arity 4.
+template <typename F, typename T, typename R, typename A1, typename A2,
+ typename A3, typename A4>
+class RunnableAdapterFunctor<F, R(T::*)(A1, A2, A3, A4) const> {
+ public:
+ typedef R (RunType)(A1, A2, A3, A4);
+
+ explicit RunnableAdapterFunctor(F functor)
+ : functor_(functor) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4) {
+ return functor_(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4));
+ }
+
+ private:
+ F functor_;
+};
+
+// Functor: Arity 5.
+template <typename F, typename T, typename R, typename A1, typename A2,
+ typename A3, typename A4, typename A5>
+class RunnableAdapterFunctor<F, R(T::*)(A1, A2, A3, A4, A5) const> {
+ public:
+ typedef R (RunType)(A1, A2, A3, A4, A5);
+
+ explicit RunnableAdapterFunctor(F functor)
+ : functor_(functor) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4,
+ typename CallbackParamTraits<A5>::ForwardType a5) {
+ return functor_(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4), CallbackForward(a5));
+ }
+
+ private:
+ F functor_;
+};
+
+// Functor: Arity 6.
+template <typename F, typename T, typename R, typename A1, typename A2,
+ typename A3, typename A4, typename A5, typename A6>
+class RunnableAdapterFunctor<F, R(T::*)(A1, A2, A3, A4, A5, A6) const> {
+ public:
+ typedef R (RunType)(A1, A2, A3, A4, A5, A6);
+
+ explicit RunnableAdapterFunctor(F functor)
+ : functor_(functor) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4,
+ typename CallbackParamTraits<A5>::ForwardType a5,
+ typename CallbackParamTraits<A6>::ForwardType a6) {
+ return functor_(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4), CallbackForward(a5),
+ CallbackForward(a6));
+ }
+
+ private:
+ F functor_;
+};
+
+// Functor: Arity 7.
+template <typename F, typename T, typename R, typename A1, typename A2,
+ typename A3, typename A4, typename A5, typename A6, typename A7>
+class RunnableAdapterFunctor<F, R(T::*)(A1, A2, A3, A4, A5, A6, A7) const> {
+ public:
+ typedef R (RunType)(A1, A2, A3, A4, A5, A6, A7);
+
+ explicit RunnableAdapterFunctor(F functor)
+ : functor_(functor) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4,
+ typename CallbackParamTraits<A5>::ForwardType a5,
+ typename CallbackParamTraits<A6>::ForwardType a6,
+ typename CallbackParamTraits<A7>::ForwardType a7) {
+ return functor_(CallbackForward(a1), CallbackForward(a2),
+ CallbackForward(a3), CallbackForward(a4), CallbackForward(a5),
+ CallbackForward(a6), CallbackForward(a7));
+ }
+
+ private:
+ F functor_;
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_BIND_INTERNAL_FUNCTOR_H_
diff --git a/src/base/bind_internal_functor.h.pump b/src/base/bind_internal_functor.h.pump
new file mode 100644
index 0000000..919521e
--- /dev/null
+++ b/src/base/bind_internal_functor.h.pump
@@ -0,0 +1,74 @@
+$$ This is a pump file for generating file templates. Pump is a python
+$$ script that is part of the Google Test suite of utilities. Description
+$$ can be found here:
+$$
+$$ http://code.google.com/p/googletest/wiki/PumpManual
+$$
+
+$$ See comment for MAX_ARITY in base/bind.h.pump.
+$var MAX_ARITY = 7
+$range ARITY 0..MAX_ARITY
+
+// Copyright (c) 2013 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_BIND_INTERNAL_FUNCTOR_H_
+#define BASE_BIND_INTERNAL_FUNCTOR_H_
+
+namespace base {
+namespace internal {
+
+// The RunnableAdapterFunctor<> template is used in a template specialization
+// for RunnableAdapter<> which enables support for functor objects, including
+// C++11 lambdas. The functor signature is deduced from the type of the
+// operator() method using decltype().
+//
+// All supported functors are assumed to have a const qualifier in their
+// operator() method.
+
+template <typename Functor>
+class RunnableAdapter;
+
+template <typename Functor, typename Sig>
+class RunnableAdapterFunctor;
+
+template <typename Functor>
+class RunnableAdapter
+ : public RunnableAdapterFunctor<Functor, decltype(&Functor::operator())> {
+ public:
+ typedef RunnableAdapterFunctor<Functor,
+ decltype(&Functor::operator())> BaseType;
+
+ explicit RunnableAdapter(Functor functor)
+ : BaseType(functor) {}
+};
+
+$for ARITY [[
+$range ARG 1..ARITY
+
+// Functor: Arity $(ARITY).
+template <typename F, typename T, typename R[[]]
+$if ARITY > 0[[, ]] $for ARG , [[typename A$(ARG)]]>
+class RunnableAdapterFunctor<F, R(T::*)($for ARG , [[A$(ARG)]]) const> {
+ public:
+ typedef R (RunType)($for ARG , [[A$(ARG)]]);
+
+ explicit RunnableAdapterFunctor(F functor)
+ : functor_(functor) {
+ }
+
+ R Run($for ARG , [[typename CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) {
+ return functor_($for ARG , [[CallbackForward(a$(ARG))]]);
+ }
+
+ private:
+ F functor_;
+};
+
+]] $$ for ARITY
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_BIND_INTERNAL_FUNCTOR_H_
diff --git a/src/base/bind_internal_win.h b/src/base/bind_internal_win.h
new file mode 100644
index 0000000..7a8486a
--- /dev/null
+++ b/src/base/bind_internal_win.h
@@ -0,0 +1,368 @@
+// This file was GENERATED by command:
+// pump.py bind_internal_win.h.pump
+// DO NOT EDIT BY HAND!!!
+
+
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Specializations of RunnableAdapter<> for Windows specific calling
+// conventions. Please see base/bind_internal.h for more info.
+
+#ifndef BASE_BIND_INTERNAL_WIN_H_
+#define BASE_BIND_INTERNAL_WIN_H_
+
+// In the x64 architecture in Windows, __fastcall, __stdcall, etc, are all
+// the same as __cdecl which would turn the following specializations into
+// multiple definitions.
+#if !defined(ARCH_CPU_X86_64)
+
+namespace base {
+namespace internal {
+
+template <typename Functor>
+class RunnableAdapter;
+
+// __stdcall Function: Arity 0.
+template <typename R>
+class RunnableAdapter<R(__stdcall *)()> {
+ public:
+ typedef R (RunType)();
+
+ explicit RunnableAdapter(R(__stdcall *function)())
+ : function_(function) {
+ }
+
+ R Run() {
+ return function_();
+ }
+
+ private:
+ R (__stdcall *function_)();
+};
+
+// __fastcall Function: Arity 0.
+template <typename R>
+class RunnableAdapter<R(__fastcall *)()> {
+ public:
+ typedef R (RunType)();
+
+ explicit RunnableAdapter(R(__fastcall *function)())
+ : function_(function) {
+ }
+
+ R Run() {
+ return function_();
+ }
+
+ private:
+ R (__fastcall *function_)();
+};
+
+// __stdcall Function: Arity 1.
+template <typename R, typename A1>
+class RunnableAdapter<R(__stdcall *)(A1)> {
+ public:
+ typedef R (RunType)(A1);
+
+ explicit RunnableAdapter(R(__stdcall *function)(A1))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1) {
+ return function_(a1);
+ }
+
+ private:
+ R (__stdcall *function_)(A1);
+};
+
+// __fastcall Function: Arity 1.
+template <typename R, typename A1>
+class RunnableAdapter<R(__fastcall *)(A1)> {
+ public:
+ typedef R (RunType)(A1);
+
+ explicit RunnableAdapter(R(__fastcall *function)(A1))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1) {
+ return function_(a1);
+ }
+
+ private:
+ R (__fastcall *function_)(A1);
+};
+
+// __stdcall Function: Arity 2.
+template <typename R, typename A1, typename A2>
+class RunnableAdapter<R(__stdcall *)(A1, A2)> {
+ public:
+ typedef R (RunType)(A1, A2);
+
+ explicit RunnableAdapter(R(__stdcall *function)(A1, A2))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2) {
+ return function_(a1, a2);
+ }
+
+ private:
+ R (__stdcall *function_)(A1, A2);
+};
+
+// __fastcall Function: Arity 2.
+template <typename R, typename A1, typename A2>
+class RunnableAdapter<R(__fastcall *)(A1, A2)> {
+ public:
+ typedef R (RunType)(A1, A2);
+
+ explicit RunnableAdapter(R(__fastcall *function)(A1, A2))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2) {
+ return function_(a1, a2);
+ }
+
+ private:
+ R (__fastcall *function_)(A1, A2);
+};
+
+// __stdcall Function: Arity 3.
+template <typename R, typename A1, typename A2, typename A3>
+class RunnableAdapter<R(__stdcall *)(A1, A2, A3)> {
+ public:
+ typedef R (RunType)(A1, A2, A3);
+
+ explicit RunnableAdapter(R(__stdcall *function)(A1, A2, A3))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3) {
+ return function_(a1, a2, a3);
+ }
+
+ private:
+ R (__stdcall *function_)(A1, A2, A3);
+};
+
+// __fastcall Function: Arity 3.
+template <typename R, typename A1, typename A2, typename A3>
+class RunnableAdapter<R(__fastcall *)(A1, A2, A3)> {
+ public:
+ typedef R (RunType)(A1, A2, A3);
+
+ explicit RunnableAdapter(R(__fastcall *function)(A1, A2, A3))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3) {
+ return function_(a1, a2, a3);
+ }
+
+ private:
+ R (__fastcall *function_)(A1, A2, A3);
+};
+
+// __stdcall Function: Arity 4.
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+class RunnableAdapter<R(__stdcall *)(A1, A2, A3, A4)> {
+ public:
+ typedef R (RunType)(A1, A2, A3, A4);
+
+ explicit RunnableAdapter(R(__stdcall *function)(A1, A2, A3, A4))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4) {
+ return function_(a1, a2, a3, a4);
+ }
+
+ private:
+ R (__stdcall *function_)(A1, A2, A3, A4);
+};
+
+// __fastcall Function: Arity 4.
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+class RunnableAdapter<R(__fastcall *)(A1, A2, A3, A4)> {
+ public:
+ typedef R (RunType)(A1, A2, A3, A4);
+
+ explicit RunnableAdapter(R(__fastcall *function)(A1, A2, A3, A4))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4) {
+ return function_(a1, a2, a3, a4);
+ }
+
+ private:
+ R (__fastcall *function_)(A1, A2, A3, A4);
+};
+
+// __stdcall Function: Arity 5.
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5>
+class RunnableAdapter<R(__stdcall *)(A1, A2, A3, A4, A5)> {
+ public:
+ typedef R (RunType)(A1, A2, A3, A4, A5);
+
+ explicit RunnableAdapter(R(__stdcall *function)(A1, A2, A3, A4, A5))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4,
+ typename CallbackParamTraits<A5>::ForwardType a5) {
+ return function_(a1, a2, a3, a4, a5);
+ }
+
+ private:
+ R (__stdcall *function_)(A1, A2, A3, A4, A5);
+};
+
+// __fastcall Function: Arity 5.
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5>
+class RunnableAdapter<R(__fastcall *)(A1, A2, A3, A4, A5)> {
+ public:
+ typedef R (RunType)(A1, A2, A3, A4, A5);
+
+ explicit RunnableAdapter(R(__fastcall *function)(A1, A2, A3, A4, A5))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4,
+ typename CallbackParamTraits<A5>::ForwardType a5) {
+ return function_(a1, a2, a3, a4, a5);
+ }
+
+ private:
+ R (__fastcall *function_)(A1, A2, A3, A4, A5);
+};
+
+// __stdcall Function: Arity 6.
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6>
+class RunnableAdapter<R(__stdcall *)(A1, A2, A3, A4, A5, A6)> {
+ public:
+ typedef R (RunType)(A1, A2, A3, A4, A5, A6);
+
+ explicit RunnableAdapter(R(__stdcall *function)(A1, A2, A3, A4, A5, A6))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4,
+ typename CallbackParamTraits<A5>::ForwardType a5,
+ typename CallbackParamTraits<A6>::ForwardType a6) {
+ return function_(a1, a2, a3, a4, a5, a6);
+ }
+
+ private:
+ R (__stdcall *function_)(A1, A2, A3, A4, A5, A6);
+};
+
+// __fastcall Function: Arity 6.
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6>
+class RunnableAdapter<R(__fastcall *)(A1, A2, A3, A4, A5, A6)> {
+ public:
+ typedef R (RunType)(A1, A2, A3, A4, A5, A6);
+
+ explicit RunnableAdapter(R(__fastcall *function)(A1, A2, A3, A4, A5, A6))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4,
+ typename CallbackParamTraits<A5>::ForwardType a5,
+ typename CallbackParamTraits<A6>::ForwardType a6) {
+ return function_(a1, a2, a3, a4, a5, a6);
+ }
+
+ private:
+ R (__fastcall *function_)(A1, A2, A3, A4, A5, A6);
+};
+
+// __stdcall Function: Arity 7.
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7>
+class RunnableAdapter<R(__stdcall *)(A1, A2, A3, A4, A5, A6, A7)> {
+ public:
+ typedef R (RunType)(A1, A2, A3, A4, A5, A6, A7);
+
+ explicit RunnableAdapter(R(__stdcall *function)(A1, A2, A3, A4, A5, A6, A7))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4,
+ typename CallbackParamTraits<A5>::ForwardType a5,
+ typename CallbackParamTraits<A6>::ForwardType a6,
+ typename CallbackParamTraits<A7>::ForwardType a7) {
+ return function_(a1, a2, a3, a4, a5, a6, a7);
+ }
+
+ private:
+ R (__stdcall *function_)(A1, A2, A3, A4, A5, A6, A7);
+};
+
+// __fastcall Function: Arity 7.
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7>
+class RunnableAdapter<R(__fastcall *)(A1, A2, A3, A4, A5, A6, A7)> {
+ public:
+ typedef R (RunType)(A1, A2, A3, A4, A5, A6, A7);
+
+ explicit RunnableAdapter(R(__fastcall *function)(A1, A2, A3, A4, A5, A6, A7))
+ : function_(function) {
+ }
+
+ R Run(typename CallbackParamTraits<A1>::ForwardType a1,
+ typename CallbackParamTraits<A2>::ForwardType a2,
+ typename CallbackParamTraits<A3>::ForwardType a3,
+ typename CallbackParamTraits<A4>::ForwardType a4,
+ typename CallbackParamTraits<A5>::ForwardType a5,
+ typename CallbackParamTraits<A6>::ForwardType a6,
+ typename CallbackParamTraits<A7>::ForwardType a7) {
+ return function_(a1, a2, a3, a4, a5, a6, a7);
+ }
+
+ private:
+ R (__fastcall *function_)(A1, A2, A3, A4, A5, A6, A7);
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // !defined(ARCH_CPU_X86_64)
+
+#endif // BASE_BIND_INTERNAL_WIN_H_
diff --git a/src/base/bind_internal_win.h.pump b/src/base/bind_internal_win.h.pump
new file mode 100644
index 0000000..cd108b6
--- /dev/null
+++ b/src/base/bind_internal_win.h.pump
@@ -0,0 +1,81 @@
+$$ This is a pump file for generating file templates. Pump is a python
+$$ script that is part of the Google Test suite of utilities. Description
+$$ can be found here:
+$$
+$$ http://code.google.com/p/googletest/wiki/PumpManual
+$$
+
+$$ See comment for MAX_ARITY in base/bind.h.pump.
+$var MAX_ARITY = 7
+
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Specializations of RunnableAdapter<> for Windows specific calling
+// conventions. Please see base/bind_internal.h for more info.
+
+#ifndef BASE_BIND_INTERNAL_WIN_H_
+#define BASE_BIND_INTERNAL_WIN_H_
+
+// In the x64 architecture in Windows, __fastcall, __stdcall, etc, are all
+// the same as __cdecl which would turn the following specializations into
+// multiple definitions.
+#if !defined(ARCH_CPU_X86_64)
+
+namespace base {
+namespace internal {
+
+template <typename Functor>
+class RunnableAdapter;
+
+$range ARITY 0..MAX_ARITY
+$for ARITY [[
+$range ARG 1..ARITY
+
+// __stdcall Function: Arity $(ARITY).
+template <typename R[[]]
+$if ARITY > 0[[, ]] $for ARG , [[typename A$(ARG)]]>
+class RunnableAdapter<R(__stdcall *)($for ARG , [[A$(ARG)]])> {
+ public:
+ typedef R (RunType)($for ARG , [[A$(ARG)]]);
+
+ explicit RunnableAdapter(R(__stdcall *function)($for ARG , [[A$(ARG)]]))
+ : function_(function) {
+ }
+
+ R Run($for ARG , [[typename CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) {
+ return function_($for ARG , [[a$(ARG)]]);
+ }
+
+ private:
+ R (__stdcall *function_)($for ARG , [[A$(ARG)]]);
+};
+
+// __fastcall Function: Arity $(ARITY).
+template <typename R[[]]
+$if ARITY > 0[[, ]] $for ARG , [[typename A$(ARG)]]>
+class RunnableAdapter<R(__fastcall *)($for ARG , [[A$(ARG)]])> {
+ public:
+ typedef R (RunType)($for ARG , [[A$(ARG)]]);
+
+ explicit RunnableAdapter(R(__fastcall *function)($for ARG , [[A$(ARG)]]))
+ : function_(function) {
+ }
+
+ R Run($for ARG , [[typename CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) {
+ return function_($for ARG , [[a$(ARG)]]);
+ }
+
+ private:
+ R (__fastcall *function_)($for ARG , [[A$(ARG)]]);
+};
+
+]] $$for ARITY
+
+} // namespace internal
+} // namespace base
+
+#endif // !defined(ARCH_CPU_X86_64)
+
+#endif // BASE_BIND_INTERNAL_WIN_H_
diff --git a/src/base/bind_unittest.cc b/src/base/bind_unittest.cc
new file mode 100644
index 0000000..1d808a6
--- /dev/null
+++ b/src/base/bind_unittest.cc
@@ -0,0 +1,814 @@
+// Copyright (c) 2012 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/bind.h"
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::Mock;
+using ::testing::Return;
+using ::testing::StrictMock;
+
+namespace base {
+namespace {
+
+class IncompleteType;
+
+class NoRef {
+ public:
+ NoRef() {}
+
+ MOCK_METHOD0(VoidMethod0, void(void));
+ MOCK_CONST_METHOD0(VoidConstMethod0, void(void));
+
+ MOCK_METHOD0(IntMethod0, int(void));
+ MOCK_CONST_METHOD0(IntConstMethod0, int(void));
+
+ private:
+ // Particularly important in this test to ensure no copies are made.
+ DISALLOW_COPY_AND_ASSIGN(NoRef);
+};
+
+class HasRef : public NoRef {
+ public:
+ HasRef() {}
+
+ MOCK_CONST_METHOD0(AddRef, void(void));
+ MOCK_CONST_METHOD0(Release, bool(void));
+
+ private:
+ // Particularly important in this test to ensure no copies are made.
+ DISALLOW_COPY_AND_ASSIGN(HasRef);
+};
+
+class HasRefPrivateDtor : public HasRef {
+ private:
+ ~HasRefPrivateDtor() {}
+};
+
+static const int kParentValue = 1;
+static const int kChildValue = 2;
+
+class Parent {
+ public:
+ void AddRef(void) const {}
+ void Release(void) const {}
+ virtual void VirtualSet() { value = kParentValue; }
+ void NonVirtualSet() { value = kParentValue; }
+ int value;
+};
+
+class Child : public Parent {
+ public:
+ virtual void VirtualSet() OVERRIDE { value = kChildValue; }
+ void NonVirtualSet() { value = kChildValue; }
+};
+
+class NoRefParent {
+ public:
+ virtual void VirtualSet() { value = kParentValue; }
+ void NonVirtualSet() { value = kParentValue; }
+ int value;
+};
+
+class NoRefChild : public NoRefParent {
+ virtual void VirtualSet() OVERRIDE { value = kChildValue; }
+ void NonVirtualSet() { value = kChildValue; }
+};
+
+// Used for probing the number of copies that occur if a type must be coerced
+// during argument forwarding in the Run() methods.
+struct DerivedCopyCounter {
+ DerivedCopyCounter(int* copies, int* assigns)
+ : copies_(copies), assigns_(assigns) {
+ }
+ int* copies_;
+ int* assigns_;
+};
+
+// Used for probing the number of copies in an argument.
+class CopyCounter {
+ public:
+ CopyCounter(int* copies, int* assigns)
+ : copies_(copies), assigns_(assigns) {
+ }
+
+ CopyCounter(const CopyCounter& other)
+ : copies_(other.copies_),
+ assigns_(other.assigns_) {
+ (*copies_)++;
+ }
+
+ // Probing for copies from coercion.
+ CopyCounter(const DerivedCopyCounter& other)
+ : copies_(other.copies_),
+ assigns_(other.assigns_) {
+ (*copies_)++;
+ }
+
+ const CopyCounter& operator=(const CopyCounter& rhs) {
+ copies_ = rhs.copies_;
+ assigns_ = rhs.assigns_;
+
+ if (assigns_) {
+ (*assigns_)++;
+ }
+
+ return *this;
+ }
+
+ int copies() const {
+ return *copies_;
+ }
+
+ int assigns() const {
+ return *assigns_;
+ }
+
+ private:
+ int* copies_;
+ int* assigns_;
+};
+
+class DeleteCounter {
+ public:
+ explicit DeleteCounter(int* deletes)
+ : deletes_(deletes) {
+ }
+
+ ~DeleteCounter() {
+ (*deletes_)++;
+ }
+
+ void VoidMethod0() {}
+
+ private:
+ int* deletes_;
+};
+
+template <typename T>
+T PassThru(T scoper) {
+ return scoper.Pass();
+}
+
+// Some test functions that we can Bind to.
+template <typename T>
+T PolymorphicIdentity(T t) {
+ return t;
+}
+
+template <typename T>
+void VoidPolymorphic1(T t) {
+}
+
+int Identity(int n) {
+ return n;
+}
+
+int ArrayGet(const int array[], int n) {
+ return array[n];
+}
+
+int Sum(int a, int b, int c, int d, int e, int f) {
+ return a + b + c + d + e + f;
+}
+
+const char* CStringIdentity(const char* s) {
+ return s;
+}
+
+int GetCopies(const CopyCounter& counter) {
+ return counter.copies();
+}
+
+int UnwrapNoRefParent(NoRefParent p) {
+ return p.value;
+}
+
+int UnwrapNoRefParentPtr(NoRefParent* p) {
+ return p->value;
+}
+
+int UnwrapNoRefParentConstRef(const NoRefParent& p) {
+ return p.value;
+}
+
+void RefArgSet(int &n) {
+ n = 2;
+}
+
+void PtrArgSet(int *n) {
+ *n = 2;
+}
+
+int FunctionWithWeakFirstParam(WeakPtr<NoRef> o, int n) {
+ return n;
+}
+
+void TakesACallback(const Closure& callback) {
+ callback.Run();
+}
+
+class BindTest : public ::testing::Test {
+ public:
+ BindTest() {
+ const_has_ref_ptr_ = &has_ref_;
+ const_no_ref_ptr_ = &no_ref_;
+ static_func_mock_ptr = &static_func_mock_;
+ }
+
+ virtual ~BindTest() {
+ }
+
+ static void VoidFunc0(void) {
+ static_func_mock_ptr->VoidMethod0();
+ }
+
+ static int IntFunc0(void) { return static_func_mock_ptr->IntMethod0(); }
+
+ protected:
+ StrictMock<NoRef> no_ref_;
+ StrictMock<HasRef> has_ref_;
+ const HasRef* const_has_ref_ptr_;
+ const NoRef* const_no_ref_ptr_;
+ StrictMock<NoRef> static_func_mock_;
+
+ // Used by the static functions to perform expectations.
+ static StrictMock<NoRef>* static_func_mock_ptr;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BindTest);
+};
+
+StrictMock<NoRef>* BindTest::static_func_mock_ptr;
+
+// Sanity check that we can instantiate a callback for each arity.
+TEST_F(BindTest, ArityTest) {
+ Callback<int(void)> c0 = Bind(&Sum, 32, 16, 8, 4, 2, 1);
+ EXPECT_EQ(63, c0.Run());
+
+ Callback<int(int)> c1 = Bind(&Sum, 32, 16, 8, 4, 2);
+ EXPECT_EQ(75, c1.Run(13));
+
+ Callback<int(int,int)> c2 = Bind(&Sum, 32, 16, 8, 4);
+ EXPECT_EQ(85, c2.Run(13, 12));
+
+ Callback<int(int,int,int)> c3 = Bind(&Sum, 32, 16, 8);
+ EXPECT_EQ(92, c3.Run(13, 12, 11));
+
+ Callback<int(int,int,int,int)> c4 = Bind(&Sum, 32, 16);
+ EXPECT_EQ(94, c4.Run(13, 12, 11, 10));
+
+ Callback<int(int,int,int,int,int)> c5 = Bind(&Sum, 32);
+ EXPECT_EQ(87, c5.Run(13, 12, 11, 10, 9));
+
+ Callback<int(int,int,int,int,int,int)> c6 = Bind(&Sum);
+ EXPECT_EQ(69, c6.Run(13, 12, 11, 10, 9, 14));
+}
+
+// Test the Currying ability of the Callback system.
+TEST_F(BindTest, CurryingTest) {
+ Callback<int(int,int,int,int,int,int)> c6 = Bind(&Sum);
+ EXPECT_EQ(69, c6.Run(13, 12, 11, 10, 9, 14));
+
+ Callback<int(int,int,int,int,int)> c5 = Bind(c6, 32);
+ EXPECT_EQ(87, c5.Run(13, 12, 11, 10, 9));
+
+ Callback<int(int,int,int,int)> c4 = Bind(c5, 16);
+ EXPECT_EQ(94, c4.Run(13, 12, 11, 10));
+
+ Callback<int(int,int,int)> c3 = Bind(c4, 8);
+ EXPECT_EQ(92, c3.Run(13, 12, 11));
+
+ Callback<int(int,int)> c2 = Bind(c3, 4);
+ EXPECT_EQ(85, c2.Run(13, 12));
+
+ Callback<int(int)> c1 = Bind(c2, 2);
+ EXPECT_EQ(75, c1.Run(13));
+
+ Callback<int(void)> c0 = Bind(c1, 1);
+ EXPECT_EQ(63, c0.Run());
+}
+
+// Test that currying the rvalue result of another Bind() works correctly.
+// - rvalue should be usable as argument to Bind().
+// - multiple runs of resulting Callback remain valid.
+TEST_F(BindTest, CurryingRvalueResultOfBind) {
+ int n = 0;
+ Closure cb = base::Bind(&TakesACallback, base::Bind(&PtrArgSet, &n));
+
+ // If we implement Bind() such that the return value has auto_ptr-like
+ // semantics, the second call here will fail because ownership of
+ // the internal BindState<> would have been transfered to a *temporary*
+ // constructon of a Callback object on the first call.
+ cb.Run();
+ EXPECT_EQ(2, n);
+
+ n = 0;
+ cb.Run();
+ EXPECT_EQ(2, n);
+}
+
+// Function type support.
+// - Normal function.
+// - Normal function bound with non-refcounted first argument.
+// - Method bound to non-const object.
+// - Method bound to scoped_refptr.
+// - Const method bound to non-const object.
+// - Const method bound to const object.
+// - Derived classes can be used with pointers to non-virtual base functions.
+// - Derived classes can be used with pointers to virtual base functions (and
+// preserve virtual dispatch).
+TEST_F(BindTest, FunctionTypeSupport) {
+ EXPECT_CALL(static_func_mock_, VoidMethod0());
+ EXPECT_CALL(has_ref_, AddRef()).Times(5);
+ EXPECT_CALL(has_ref_, Release()).Times(5);
+ EXPECT_CALL(has_ref_, VoidMethod0()).Times(2);
+ EXPECT_CALL(has_ref_, VoidConstMethod0()).Times(2);
+
+ Closure normal_cb = Bind(&VoidFunc0);
+ Callback<NoRef*(void)> normal_non_refcounted_cb =
+ Bind(&PolymorphicIdentity<NoRef*>, &no_ref_);
+ normal_cb.Run();
+ EXPECT_EQ(&no_ref_, normal_non_refcounted_cb.Run());
+
+ Closure method_cb = Bind(&HasRef::VoidMethod0, &has_ref_);
+ Closure method_refptr_cb = Bind(&HasRef::VoidMethod0,
+ make_scoped_refptr(&has_ref_));
+ Closure const_method_nonconst_obj_cb = Bind(&HasRef::VoidConstMethod0,
+ &has_ref_);
+ Closure const_method_const_obj_cb = Bind(&HasRef::VoidConstMethod0,
+ const_has_ref_ptr_);
+ method_cb.Run();
+ method_refptr_cb.Run();
+ const_method_nonconst_obj_cb.Run();
+ const_method_const_obj_cb.Run();
+
+ Child child;
+ child.value = 0;
+ Closure virtual_set_cb = Bind(&Parent::VirtualSet, &child);
+ virtual_set_cb.Run();
+ EXPECT_EQ(kChildValue, child.value);
+
+ child.value = 0;
+ Closure non_virtual_set_cb = Bind(&Parent::NonVirtualSet, &child);
+ non_virtual_set_cb.Run();
+ EXPECT_EQ(kParentValue, child.value);
+}
+
+// Return value support.
+// - Function with return value.
+// - Method with return value.
+// - Const method with return value.
+TEST_F(BindTest, ReturnValues) {
+ EXPECT_CALL(static_func_mock_, IntMethod0()).WillOnce(Return(1337));
+ EXPECT_CALL(has_ref_, AddRef()).Times(3);
+ EXPECT_CALL(has_ref_, Release()).Times(3);
+ EXPECT_CALL(has_ref_, IntMethod0()).WillOnce(Return(31337));
+ EXPECT_CALL(has_ref_, IntConstMethod0())
+ .WillOnce(Return(41337))
+ .WillOnce(Return(51337));
+
+ Callback<int(void)> normal_cb = Bind(&IntFunc0);
+ Callback<int(void)> method_cb = Bind(&HasRef::IntMethod0, &has_ref_);
+ Callback<int(void)> const_method_nonconst_obj_cb =
+ Bind(&HasRef::IntConstMethod0, &has_ref_);
+ Callback<int(void)> const_method_const_obj_cb =
+ Bind(&HasRef::IntConstMethod0, const_has_ref_ptr_);
+ EXPECT_EQ(1337, normal_cb.Run());
+ EXPECT_EQ(31337, method_cb.Run());
+ EXPECT_EQ(41337, const_method_nonconst_obj_cb.Run());
+ EXPECT_EQ(51337, const_method_const_obj_cb.Run());
+}
+
+// IgnoreResult adapter test.
+// - Function with return value.
+// - Method with return value.
+// - Const Method with return.
+// - Method with return value bound to WeakPtr<>.
+// - Const Method with return bound to WeakPtr<>.
+TEST_F(BindTest, IgnoreResult) {
+ EXPECT_CALL(static_func_mock_, IntMethod0()).WillOnce(Return(1337));
+ EXPECT_CALL(has_ref_, AddRef()).Times(2);
+ EXPECT_CALL(has_ref_, Release()).Times(2);
+ EXPECT_CALL(has_ref_, IntMethod0()).WillOnce(Return(10));
+ EXPECT_CALL(has_ref_, IntConstMethod0()).WillOnce(Return(11));
+ EXPECT_CALL(no_ref_, IntMethod0()).WillOnce(Return(12));
+ EXPECT_CALL(no_ref_, IntConstMethod0()).WillOnce(Return(13));
+
+ Closure normal_func_cb = Bind(IgnoreResult(&IntFunc0));
+ normal_func_cb.Run();
+
+ Closure non_void_method_cb =
+ Bind(IgnoreResult(&HasRef::IntMethod0), &has_ref_);
+ non_void_method_cb.Run();
+
+ Closure non_void_const_method_cb =
+ Bind(IgnoreResult(&HasRef::IntConstMethod0), &has_ref_);
+ non_void_const_method_cb.Run();
+
+ WeakPtrFactory<NoRef> weak_factory(&no_ref_);
+ WeakPtrFactory<const NoRef> const_weak_factory(const_no_ref_ptr_);
+
+ Closure non_void_weak_method_cb =
+ Bind(IgnoreResult(&NoRef::IntMethod0), weak_factory.GetWeakPtr());
+ non_void_weak_method_cb.Run();
+
+ Closure non_void_weak_const_method_cb =
+ Bind(IgnoreResult(&NoRef::IntConstMethod0), weak_factory.GetWeakPtr());
+ non_void_weak_const_method_cb.Run();
+
+ weak_factory.InvalidateWeakPtrs();
+ non_void_weak_const_method_cb.Run();
+ non_void_weak_method_cb.Run();
+}
+
+// Argument binding tests.
+// - Argument binding to primitive.
+// - Argument binding to primitive pointer.
+// - Argument binding to a literal integer.
+// - Argument binding to a literal string.
+// - Argument binding with template function.
+// - Argument binding to an object.
+// - Argument binding to pointer to incomplete type.
+// - Argument gets type converted.
+// - Pointer argument gets converted.
+// - Const Reference forces conversion.
+TEST_F(BindTest, ArgumentBinding) {
+ int n = 2;
+
+ Callback<int(void)> bind_primitive_cb = Bind(&Identity, n);
+ EXPECT_EQ(n, bind_primitive_cb.Run());
+
+ Callback<int*(void)> bind_primitive_pointer_cb =
+ Bind(&PolymorphicIdentity<int*>, &n);
+ EXPECT_EQ(&n, bind_primitive_pointer_cb.Run());
+
+ Callback<int(void)> bind_int_literal_cb = Bind(&Identity, 3);
+ EXPECT_EQ(3, bind_int_literal_cb.Run());
+
+ Callback<const char*(void)> bind_string_literal_cb =
+ Bind(&CStringIdentity, "hi");
+ EXPECT_STREQ("hi", bind_string_literal_cb.Run());
+
+ Callback<int(void)> bind_template_function_cb =
+ Bind(&PolymorphicIdentity<int>, 4);
+ EXPECT_EQ(4, bind_template_function_cb.Run());
+
+ NoRefParent p;
+ p.value = 5;
+ Callback<int(void)> bind_object_cb = Bind(&UnwrapNoRefParent, p);
+ EXPECT_EQ(5, bind_object_cb.Run());
+
+ IncompleteType* incomplete_ptr = reinterpret_cast<IncompleteType*>(123);
+ Callback<IncompleteType*(void)> bind_incomplete_ptr_cb =
+ Bind(&PolymorphicIdentity<IncompleteType*>, incomplete_ptr);
+ EXPECT_EQ(incomplete_ptr, bind_incomplete_ptr_cb.Run());
+
+ NoRefChild c;
+ c.value = 6;
+ Callback<int(void)> bind_promotes_cb = Bind(&UnwrapNoRefParent, c);
+ EXPECT_EQ(6, bind_promotes_cb.Run());
+
+ c.value = 7;
+ Callback<int(void)> bind_pointer_promotes_cb =
+ Bind(&UnwrapNoRefParentPtr, &c);
+ EXPECT_EQ(7, bind_pointer_promotes_cb.Run());
+
+ c.value = 8;
+ Callback<int(void)> bind_const_reference_promotes_cb =
+ Bind(&UnwrapNoRefParentConstRef, c);
+ EXPECT_EQ(8, bind_const_reference_promotes_cb.Run());
+}
+
+// Unbound argument type support tests.
+// - Unbound value.
+// - Unbound pointer.
+// - Unbound reference.
+// - Unbound const reference.
+// - Unbound unsized array.
+// - Unbound sized array.
+// - Unbound array-of-arrays.
+TEST_F(BindTest, UnboundArgumentTypeSupport) {
+ Callback<void(int)> unbound_value_cb = Bind(&VoidPolymorphic1<int>);
+ Callback<void(int*)> unbound_pointer_cb = Bind(&VoidPolymorphic1<int*>);
+ Callback<void(int&)> unbound_ref_cb = Bind(&VoidPolymorphic1<int&>);
+ Callback<void(const int&)> unbound_const_ref_cb =
+ Bind(&VoidPolymorphic1<const int&>);
+ Callback<void(int[])> unbound_unsized_array_cb =
+ Bind(&VoidPolymorphic1<int[]>);
+ Callback<void(int[2])> unbound_sized_array_cb =
+ Bind(&VoidPolymorphic1<int[2]>);
+ Callback<void(int[][2])> unbound_array_of_arrays_cb =
+ Bind(&VoidPolymorphic1<int[][2]>);
+}
+
+// Function with unbound reference parameter.
+// - Original parameter is modified by callback.
+TEST_F(BindTest, UnboundReferenceSupport) {
+ int n = 0;
+ Callback<void(int&)> unbound_ref_cb = Bind(&RefArgSet);
+ unbound_ref_cb.Run(n);
+ EXPECT_EQ(2, n);
+}
+
+// Functions that take reference parameters.
+// - Forced reference parameter type still stores a copy.
+// - Forced const reference parameter type still stores a copy.
+TEST_F(BindTest, ReferenceArgumentBinding) {
+ int n = 1;
+ int& ref_n = n;
+ const int& const_ref_n = n;
+
+ Callback<int(void)> ref_copies_cb = Bind(&Identity, ref_n);
+ EXPECT_EQ(n, ref_copies_cb.Run());
+ n++;
+ EXPECT_EQ(n - 1, ref_copies_cb.Run());
+
+ Callback<int(void)> const_ref_copies_cb = Bind(&Identity, const_ref_n);
+ EXPECT_EQ(n, const_ref_copies_cb.Run());
+ n++;
+ EXPECT_EQ(n - 1, const_ref_copies_cb.Run());
+}
+
+// Check that we can pass in arrays and have them be stored as a pointer.
+// - Array of values stores a pointer.
+// - Array of const values stores a pointer.
+TEST_F(BindTest, ArrayArgumentBinding) {
+ int array[4] = {1, 1, 1, 1};
+ const int (*const_array_ptr)[4] = &array;
+
+ Callback<int(void)> array_cb = Bind(&ArrayGet, array, 1);
+ EXPECT_EQ(1, array_cb.Run());
+
+ Callback<int(void)> const_array_cb = Bind(&ArrayGet, *const_array_ptr, 1);
+ EXPECT_EQ(1, const_array_cb.Run());
+
+ array[1] = 3;
+ EXPECT_EQ(3, array_cb.Run());
+ EXPECT_EQ(3, const_array_cb.Run());
+}
+
+// Verify SupportsAddRefAndRelease correctly introspects the class type for
+// AddRef() and Release().
+// - Class with AddRef() and Release()
+// - Class without AddRef() and Release()
+// - Derived Class with AddRef() and Release()
+// - Derived Class without AddRef() and Release()
+// - Derived Class with AddRef() and Release() and a private destructor.
+TEST_F(BindTest, SupportsAddRefAndRelease) {
+ EXPECT_TRUE(internal::SupportsAddRefAndRelease<HasRef>::value);
+ EXPECT_FALSE(internal::SupportsAddRefAndRelease<NoRef>::value);
+
+ // StrictMock<T> is a derived class of T. So, we use StrictMock<HasRef> and
+ // StrictMock<NoRef> to test that SupportsAddRefAndRelease works over
+ // inheritance.
+ EXPECT_TRUE(internal::SupportsAddRefAndRelease<StrictMock<HasRef> >::value);
+ EXPECT_FALSE(internal::SupportsAddRefAndRelease<StrictMock<NoRef> >::value);
+
+ // This matters because the implementation creates a dummy class that
+ // inherits from the template type.
+ EXPECT_TRUE(internal::SupportsAddRefAndRelease<HasRefPrivateDtor>::value);
+}
+
+// Unretained() wrapper support.
+// - Method bound to Unretained() non-const object.
+// - Const method bound to Unretained() non-const object.
+// - Const method bound to Unretained() const object.
+TEST_F(BindTest, Unretained) {
+ EXPECT_CALL(no_ref_, VoidMethod0());
+ EXPECT_CALL(no_ref_, VoidConstMethod0()).Times(2);
+
+ Callback<void(void)> method_cb =
+ Bind(&NoRef::VoidMethod0, Unretained(&no_ref_));
+ method_cb.Run();
+
+ Callback<void(void)> const_method_cb =
+ Bind(&NoRef::VoidConstMethod0, Unretained(&no_ref_));
+ const_method_cb.Run();
+
+ Callback<void(void)> const_method_const_ptr_cb =
+ Bind(&NoRef::VoidConstMethod0, Unretained(const_no_ref_ptr_));
+ const_method_const_ptr_cb.Run();
+}
+
+// WeakPtr() support.
+// - Method bound to WeakPtr<> to non-const object.
+// - Const method bound to WeakPtr<> to non-const object.
+// - Const method bound to WeakPtr<> to const object.
+// - Normal Function with WeakPtr<> as P1 can have return type and is
+// not canceled.
+TEST_F(BindTest, WeakPtr) {
+ EXPECT_CALL(no_ref_, VoidMethod0());
+ EXPECT_CALL(no_ref_, VoidConstMethod0()).Times(2);
+
+ WeakPtrFactory<NoRef> weak_factory(&no_ref_);
+ WeakPtrFactory<const NoRef> const_weak_factory(const_no_ref_ptr_);
+
+ Closure method_cb =
+ Bind(&NoRef::VoidMethod0, weak_factory.GetWeakPtr());
+ method_cb.Run();
+
+ Closure const_method_cb =
+ Bind(&NoRef::VoidConstMethod0, const_weak_factory.GetWeakPtr());
+ const_method_cb.Run();
+
+ Closure const_method_const_ptr_cb =
+ Bind(&NoRef::VoidConstMethod0, const_weak_factory.GetWeakPtr());
+ const_method_const_ptr_cb.Run();
+
+ Callback<int(int)> normal_func_cb =
+ Bind(&FunctionWithWeakFirstParam, weak_factory.GetWeakPtr());
+ EXPECT_EQ(1, normal_func_cb.Run(1));
+
+ weak_factory.InvalidateWeakPtrs();
+ const_weak_factory.InvalidateWeakPtrs();
+
+ method_cb.Run();
+ const_method_cb.Run();
+ const_method_const_ptr_cb.Run();
+
+ // Still runs even after the pointers are invalidated.
+ EXPECT_EQ(2, normal_func_cb.Run(2));
+}
+
+// ConstRef() wrapper support.
+// - Binding w/o ConstRef takes a copy.
+// - Binding a ConstRef takes a reference.
+// - Binding ConstRef to a function ConstRef does not copy on invoke.
+TEST_F(BindTest, ConstRef) {
+ int n = 1;
+
+ Callback<int(void)> copy_cb = Bind(&Identity, n);
+ Callback<int(void)> const_ref_cb = Bind(&Identity, ConstRef(n));
+ EXPECT_EQ(n, copy_cb.Run());
+ EXPECT_EQ(n, const_ref_cb.Run());
+ n++;
+ EXPECT_EQ(n - 1, copy_cb.Run());
+ EXPECT_EQ(n, const_ref_cb.Run());
+
+ int copies = 0;
+ int assigns = 0;
+ CopyCounter counter(&copies, &assigns);
+ Callback<int(void)> all_const_ref_cb =
+ Bind(&GetCopies, ConstRef(counter));
+ EXPECT_EQ(0, all_const_ref_cb.Run());
+ EXPECT_EQ(0, copies);
+ EXPECT_EQ(0, assigns);
+}
+
+// Test Owned() support.
+TEST_F(BindTest, Owned) {
+ int deletes = 0;
+ DeleteCounter* counter = new DeleteCounter(&deletes);
+
+ // If we don't capture, delete happens on Callback destruction/reset.
+ // return the same value.
+ Callback<DeleteCounter*(void)> no_capture_cb =
+ Bind(&PolymorphicIdentity<DeleteCounter*>, Owned(counter));
+ ASSERT_EQ(counter, no_capture_cb.Run());
+ ASSERT_EQ(counter, no_capture_cb.Run());
+ EXPECT_EQ(0, deletes);
+ no_capture_cb.Reset(); // This should trigger a delete.
+ EXPECT_EQ(1, deletes);
+
+ deletes = 0;
+ counter = new DeleteCounter(&deletes);
+ base::Closure own_object_cb =
+ Bind(&DeleteCounter::VoidMethod0, Owned(counter));
+ own_object_cb.Run();
+ EXPECT_EQ(0, deletes);
+ own_object_cb.Reset();
+ EXPECT_EQ(1, deletes);
+}
+
+// Passed() wrapper support.
+// - Passed() can be constructed from a pointer to scoper.
+// - Passed() can be constructed from a scoper rvalue.
+// - Using Passed() gives Callback Ownership.
+// - Ownership is transferred from Callback to callee on the first Run().
+// - Callback supports unbound arguments.
+TEST_F(BindTest, ScopedPtr) {
+ int deletes = 0;
+
+ // Tests the Passed() function's support for pointers.
+ scoped_ptr<DeleteCounter> ptr(new DeleteCounter(&deletes));
+ Callback<scoped_ptr<DeleteCounter>(void)> unused_callback =
+ Bind(&PassThru<scoped_ptr<DeleteCounter> >, Passed(&ptr));
+ EXPECT_FALSE(ptr.get());
+ EXPECT_EQ(0, deletes);
+
+ // If we never invoke the Callback, it retains ownership and deletes.
+ unused_callback.Reset();
+ EXPECT_EQ(1, deletes);
+
+ // Tests the Passed() function's support for rvalues.
+ deletes = 0;
+ DeleteCounter* counter = new DeleteCounter(&deletes);
+ Callback<scoped_ptr<DeleteCounter>(void)> callback =
+ Bind(&PassThru<scoped_ptr<DeleteCounter> >,
+ Passed(scoped_ptr<DeleteCounter>(counter)));
+ EXPECT_FALSE(ptr.get());
+ EXPECT_EQ(0, deletes);
+
+ // Check that ownership can be transferred back out.
+ scoped_ptr<DeleteCounter> result = callback.Run();
+ ASSERT_EQ(counter, result.get());
+ EXPECT_EQ(0, deletes);
+
+ // Resetting does not delete since ownership was transferred.
+ callback.Reset();
+ EXPECT_EQ(0, deletes);
+
+ // Ensure that we actually did get ownership.
+ result.reset();
+ EXPECT_EQ(1, deletes);
+
+ // Test unbound argument forwarding.
+ Callback<scoped_ptr<DeleteCounter>(scoped_ptr<DeleteCounter>)> cb_unbound =
+ Bind(&PassThru<scoped_ptr<DeleteCounter> >);
+ ptr.reset(new DeleteCounter(&deletes));
+ cb_unbound.Run(ptr.Pass());
+}
+
+// Argument Copy-constructor usage for non-reference parameters.
+// - Bound arguments are only copied once.
+// - Forwarded arguments are only copied once.
+// - Forwarded arguments with coercions are only copied twice (once for the
+// coercion, and one for the final dispatch).
+TEST_F(BindTest, ArgumentCopies) {
+ int copies = 0;
+ int assigns = 0;
+
+ CopyCounter counter(&copies, &assigns);
+
+ Callback<void(void)> copy_cb =
+ Bind(&VoidPolymorphic1<CopyCounter>, counter);
+ EXPECT_GE(1, copies);
+ EXPECT_EQ(0, assigns);
+
+ copies = 0;
+ assigns = 0;
+ Callback<void(CopyCounter)> forward_cb =
+ Bind(&VoidPolymorphic1<CopyCounter>);
+ forward_cb.Run(counter);
+ EXPECT_GE(1, copies);
+ EXPECT_EQ(0, assigns);
+
+ copies = 0;
+ assigns = 0;
+ DerivedCopyCounter dervied(&copies, &assigns);
+ Callback<void(CopyCounter)> coerce_cb =
+ Bind(&VoidPolymorphic1<CopyCounter>);
+ coerce_cb.Run(dervied);
+ EXPECT_GE(2, copies);
+ EXPECT_EQ(0, assigns);
+}
+
+// Callback construction and assignment tests.
+// - Construction from an InvokerStorageHolder should not cause ref/deref.
+// - Assignment from other callback should only cause one ref
+//
+// TODO(ajwong): Is there actually a way to test this?
+
+#if defined(OS_WIN)
+int __fastcall FastCallFunc(int n) {
+ return n;
+}
+
+int __stdcall StdCallFunc(int n) {
+ return n;
+}
+
+// Windows specific calling convention support.
+// - Can bind a __fastcall function.
+// - Can bind a __stdcall function.
+TEST_F(BindTest, WindowsCallingConventions) {
+ Callback<int(void)> fastcall_cb = Bind(&FastCallFunc, 1);
+ EXPECT_EQ(1, fastcall_cb.Run());
+
+ Callback<int(void)> stdcall_cb = Bind(&StdCallFunc, 2);
+ EXPECT_EQ(2, stdcall_cb.Run());
+}
+#endif
+
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST
+
+// Test null callbacks cause a DCHECK.
+TEST(BindDeathTest, NullCallback) {
+ base::Callback<void(int)> null_cb;
+ ASSERT_TRUE(null_cb.is_null());
+ EXPECT_DEATH(base::Bind(null_cb, 42), "");
+}
+
+#endif // (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) &&
+ // GTEST_HAS_DEATH_TEST
+
+} // namespace
+} // namespace base
diff --git a/src/base/bind_unittest.nc b/src/base/bind_unittest.nc
new file mode 100644
index 0000000..069092c
--- /dev/null
+++ b/src/base/bind_unittest.nc
@@ -0,0 +1,203 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/callback.h"
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+
+namespace base {
+
+// Do not put everything inside an anonymous namespace. If you do, many of the
+// helper function declarations will generate unused definition warnings unless
+// unused definition warnings.
+
+static const int kParentValue = 1;
+static const int kChildValue = 2;
+
+class NoRef {
+ public:
+ void VoidMethod0() {}
+ void VoidConstMethod0() const {}
+ int IntMethod0() { return 1; }
+};
+
+class HasRef : public NoRef, public base::RefCounted<HasRef> {
+};
+
+class Parent {
+ public:
+ void AddRef(void) const {}
+ void Release(void) const {}
+ virtual void VirtualSet() { value = kParentValue; }
+ void NonVirtualSet() { value = kParentValue; }
+ int value;
+};
+
+class Child : public Parent {
+ public:
+ virtual void VirtualSet() { value = kChildValue; }
+ void NonVirtualSet() { value = kChildValue; }
+};
+
+class NoRefParent {
+ public:
+ virtual void VirtualSet() { value = kParentValue; }
+ void NonVirtualSet() { value = kParentValue; }
+ int value;
+};
+
+class NoRefChild : public NoRefParent {
+ virtual void VirtualSet() { value = kChildValue; }
+ void NonVirtualSet() { value = kChildValue; }
+};
+
+template <typename T>
+T PolymorphicIdentity(T t) {
+ return t;
+}
+
+int UnwrapParentRef(Parent& p) {
+ return p.value;
+}
+
+template <typename T>
+void VoidPolymorphic1(T t) {
+}
+
+#if defined(NCTEST_METHOD_ON_CONST_OBJECT) // [r"invalid conversion from 'const base::NoRef\*' to 'base::NoRef\*'"]
+
+// Method bound to const-object.
+//
+// Only const methods should be allowed to work with const objects.
+void WontCompile() {
+ HasRef has_ref;
+ const HasRef* const_has_ref_ptr_ = &has_ref;
+ Callback<void(void)> method_to_const_cb =
+ Bind(&HasRef::VoidMethod0, const_has_ref_ptr_);
+ method_to_const_cb.Run();
+}
+
+#elif defined(NCTEST_METHOD_BIND_NEEDS_REFCOUNTED_OBJECT) // [r"has no member named 'AddRef'"]
+
+// Method bound to non-refcounted object.
+//
+// We require refcounts unless you have Unretained().
+void WontCompile() {
+ NoRef no_ref;
+ Callback<void(void)> no_ref_cb =
+ Bind(&NoRef::VoidMethod0, &no_ref);
+ no_ref_cb.Run();
+}
+
+#elif defined(NCTEST_CONST_METHOD_NEEDS_REFCOUNTED_OBJECT) // [r"has no member named 'AddRef'"]
+
+// Const Method bound to non-refcounted object.
+//
+// We require refcounts unless you have Unretained().
+void WontCompile() {
+ NoRef no_ref;
+ Callback<void(void)> no_ref_const_cb =
+ Bind(&NoRef::VoidConstMethod0, &no_ref);
+ no_ref_const_cb.Run();
+}
+
+#elif defined(NCTEST_CONST_POINTER) // [r"invalid conversion from 'const base::NoRef\*' to 'base::NoRef\*'"]
+
+// Const argument used with non-const pointer parameter of same type.
+//
+// This is just a const-correctness check.
+void WontCompile() {
+ const NoRef* const_no_ref_ptr;
+ Callback<NoRef*(void)> pointer_same_cb =
+ Bind(&PolymorphicIdentity<NoRef*>, const_no_ref_ptr);
+ pointer_same_cb.Run();
+}
+
+#elif defined(NCTEST_CONST_POINTER_SUBTYPE) // [r"'const base::NoRefParent\*' to 'base::NoRefParent\*'"]
+
+// Const argument used with non-const pointer parameter of super type.
+//
+// This is just a const-correctness check.
+void WontCompile() {
+ const NoRefChild* const_child_ptr;
+ Callback<NoRefParent*(void)> pointer_super_cb =
+ Bind(&PolymorphicIdentity<NoRefParent*>, const_child_ptr);
+ pointer_super_cb.Run();
+}
+
+#elif defined(DISABLED_NCTEST_DISALLOW_NON_CONST_REF_PARAM) // [r"badstring"]
+// I think there's a type safety promotion issue here where we can pass a const
+// ref to a non const-ref function, or vice versa accidentally. Or we make a
+// copy accidentally. Check.
+
+// Functions with reference parameters, unsupported.
+//
+// First, non-const reference parameters are disallowed by the Google
+// style guide. Second, since we are doing argument forwarding it becomes
+// very tricky to avoid copies, maintain const correctness, and not
+// accidentally have the function be modifying a temporary, or a copy.
+void WontCompile() {
+ Parent p;
+ Callback<int(Parent&)> ref_arg_cb = Bind(&UnwrapParentRef);
+ ref_arg_cb.Run(p);
+}
+
+#elif defined(NCTEST_DISALLOW_BIND_TO_NON_CONST_REF_PARAM) // [r"creating array with negative size"]
+
+// Binding functions with reference parameters, unsupported.
+//
+// See comment in NCTEST_DISALLOW_NON_CONST_REF_PARAM
+void WontCompile() {
+ Parent p;
+ Callback<int(void)> ref_cb = Bind(&UnwrapParentRef, p);
+ ref_cb.Run();
+}
+
+#elif defined(NCTEST_NO_IMPLICIT_ARRAY_PTR_CONVERSION) // [r"creating array with negative size"]
+
+// A method should not be bindable with an array of objects.
+//
+// This is likely not wanted behavior. We specifically check for it though
+// because it is possible, depending on how you implement prebinding, to
+// implicitly convert an array type to a pointer type.
+void WontCompile() {
+ HasRef p[10];
+ Callback<void(void)> method_bound_to_array_cb =
+ Bind(&HasRef::VoidMethod0, p);
+ method_bound_to_array_cb.Run();
+}
+
+#elif defined(NCTEST_NO_RAW_PTR_FOR_REFCOUNTED_TYPES) // [r"creating array with negative size"]
+
+// Refcounted types should not be bound as a raw pointer.
+void WontCompile() {
+ HasRef for_raw_ptr;
+ int a;
+ Callback<void(void)> ref_count_as_raw_ptr_a =
+ Bind(&VoidPolymorphic1<int*>, &a);
+ Callback<void(void)> ref_count_as_raw_ptr =
+ Bind(&VoidPolymorphic1<HasRef*>, &for_raw_ptr);
+}
+
+#elif defined(NCTEST_WEAKPTR_BIND_MUST_RETURN_VOID) // [r"creating array with negative size"]
+
+// WeakPtrs cannot be bound to methods with return types.
+void WontCompile() {
+ NoRef no_ref;
+ WeakPtrFactory<NoRef> weak_factory(&no_ref);
+ Callback<int(void)> weak_ptr_with_non_void_return_type =
+ Bind(&NoRef::IntMethod0, weak_factory.GetWeakPtr());
+ weak_ptr_with_non_void_return_type.Run();
+}
+
+#elif defined(NCTEST_DISALLOW_ASSIGN_DIFFERINT_TYPES) // [r"invalid conversion from"]
+
+// Bind result cannot be assigned to Callbacks with a mismatching type.
+void WontCompile() {
+ Closure callback_mismatches_bind_type = Bind(&VoidPolymorphic1<int>);
+}
+
+#endif
+
+} // namespace base
diff --git a/src/base/bits.h b/src/base/bits.h
new file mode 100644
index 0000000..b2209e8
--- /dev/null
+++ b/src/base/bits.h
@@ -0,0 +1,47 @@
+// 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 defines some bit utilities.
+
+#ifndef BASE_BITS_H_
+#define BASE_BITS_H_
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace base {
+namespace bits {
+
+// Returns the integer i such as 2^i <= n < 2^(i+1)
+inline int Log2Floor(uint32 n) {
+ if (n == 0)
+ return -1;
+ int log = 0;
+ uint32 value = n;
+ for (int i = 4; i >= 0; --i) {
+ int shift = (1 << i);
+ uint32 x = value >> shift;
+ if (x != 0) {
+ value = x;
+ log += shift;
+ }
+ }
+ DCHECK_EQ(value, 1u);
+ return log;
+}
+
+// Returns the integer i such as 2^(i-1) < n <= 2^i
+inline int Log2Ceiling(uint32 n) {
+ if (n == 0) {
+ return -1;
+ } else {
+ // Log2Floor returns -1 for 0, so the following works correctly for n=1.
+ return 1 + Log2Floor(n - 1);
+ }
+}
+
+} // namespace bits
+} // namespace base
+
+#endif // BASE_BITS_H_
diff --git a/src/base/bits_unittest.cc b/src/base/bits_unittest.cc
new file mode 100644
index 0000000..e913d6a
--- /dev/null
+++ b/src/base/bits_unittest.cc
@@ -0,0 +1,48 @@
+// 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 contains the unit tests for the bit utilities.
+
+#include "base/bits.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace bits {
+
+TEST(BitsTest, Log2Floor) {
+ EXPECT_EQ(-1, Log2Floor(0));
+ EXPECT_EQ(0, Log2Floor(1));
+ EXPECT_EQ(1, Log2Floor(2));
+ EXPECT_EQ(1, Log2Floor(3));
+ EXPECT_EQ(2, Log2Floor(4));
+ for (int i = 3; i < 31; ++i) {
+ unsigned int value = 1U << i;
+ EXPECT_EQ(i, Log2Floor(value));
+ EXPECT_EQ(i, Log2Floor(value + 1));
+ EXPECT_EQ(i, Log2Floor(value + 2));
+ EXPECT_EQ(i - 1, Log2Floor(value - 1));
+ EXPECT_EQ(i - 1, Log2Floor(value - 2));
+ }
+ EXPECT_EQ(31, Log2Floor(0xffffffffU));
+}
+
+TEST(BitsTest, Log2Ceiling) {
+ EXPECT_EQ(-1, Log2Ceiling(0));
+ EXPECT_EQ(0, Log2Ceiling(1));
+ EXPECT_EQ(1, Log2Ceiling(2));
+ EXPECT_EQ(2, Log2Ceiling(3));
+ EXPECT_EQ(2, Log2Ceiling(4));
+ for (int i = 3; i < 31; ++i) {
+ unsigned int value = 1U << i;
+ EXPECT_EQ(i, Log2Ceiling(value));
+ EXPECT_EQ(i + 1, Log2Ceiling(value + 1));
+ EXPECT_EQ(i + 1, Log2Ceiling(value + 2));
+ EXPECT_EQ(i, Log2Ceiling(value - 1));
+ EXPECT_EQ(i, Log2Ceiling(value - 2));
+ }
+ EXPECT_EQ(32, Log2Ceiling(0xffffffffU));
+}
+
+} // namespace bits
+} // namespace base
diff --git a/src/base/build_time.cc b/src/base/build_time.cc
new file mode 100644
index 0000000..bb2274b
--- /dev/null
+++ b/src/base/build_time.cc
@@ -0,0 +1,25 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/build_time.h"
+
+#include "base/logging.h"
+#include "base/time.h"
+
+namespace base {
+
+Time GetBuildTime() {
+ Time integral_build_time;
+ // The format of __DATE__ and __TIME__ is specified by the ANSI C Standard,
+ // section 6.8.8.
+ //
+ // __DATE__ is exactly "Mmm DD YYYY".
+ // __TIME__ is exactly "hh:mm:ss".
+ const char kDateTime[] = __DATE__ " " __TIME__ " PST";
+ bool result = Time::FromString(kDateTime, &integral_build_time);
+ DCHECK(result);
+ return integral_build_time;
+}
+
+} // namespace base
diff --git a/src/base/build_time.h b/src/base/build_time.h
new file mode 100644
index 0000000..e7bbb39
--- /dev/null
+++ b/src/base/build_time.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_BUILD_TIME_
+#define BASE_BUILD_TIME_
+
+#include "base/base_export.h"
+#include "base/time.h"
+
+namespace base {
+
+// GetBuildTime returns the time at which the current binary was built.
+//
+// This uses the __DATE__ and __TIME__ macros, which don't trigger a rebuild
+// when they change. However, official builds will always be rebuilt from
+// scratch.
+//
+// Also, since __TIME__ doesn't include a timezone, this value should only be
+// considered accurate to a day.
+Time BASE_EXPORT GetBuildTime();
+
+} // namespace base
+
+#endif // BASE_BUILD_TIME_
diff --git a/src/base/build_time_unittest.cc b/src/base/build_time_unittest.cc
new file mode 100644
index 0000000..399a53f
--- /dev/null
+++ b/src/base/build_time_unittest.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/build_time.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(BuildTime, DateLooksValid) {
+ char build_date[] = __DATE__;
+
+ EXPECT_EQ(11u, strlen(build_date));
+ EXPECT_EQ(' ', build_date[3]);
+ EXPECT_EQ(' ', build_date[6]);
+}
+
+TEST(BuildTime, TimeLooksValid) {
+ char build_time[] = __TIME__;
+
+ EXPECT_EQ(8u, strlen(build_time));
+ EXPECT_EQ(':', build_time[2]);
+ EXPECT_EQ(':', build_time[5]);
+}
+
+TEST(BuildTime, DoesntCrash) {
+ // Since __DATE__ isn't updated unless one does a clobber build, we can't
+ // really test the value returned by it, except to check that it doesn't
+ // crash.
+ base::GetBuildTime();
+}
diff --git a/src/base/callback.h b/src/base/callback.h
new file mode 100644
index 0000000..18c1c6c
--- /dev/null
+++ b/src/base/callback.h
@@ -0,0 +1,893 @@
+// This file was GENERATED by command:
+// pump.py callback.h.pump
+// DO NOT EDIT BY HAND!!!
+
+
+// Copyright (c) 2012 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_CALLBACK_H_
+#define BASE_CALLBACK_H_
+
+#include "base/callback_forward.h"
+#include "base/callback_internal.h"
+#include "base/template_util.h"
+
+// NOTE: Header files that do not require the full definition of Callback or
+// Closure should #include "base/callback_forward.h" instead of this file.
+
+// -----------------------------------------------------------------------------
+// Introduction
+// -----------------------------------------------------------------------------
+//
+// The templated Callback class is a generalized function object. Together
+// with the Bind() function in bind.h, they provide a type-safe method for
+// performing partial application of functions.
+//
+// Partial application (or "currying") is the process of binding a subset of
+// a function's arguments to produce another function that takes fewer
+// arguments. This can be used to pass around a unit of delayed execution,
+// much like lexical closures are used in other languages. For example, it
+// is used in Chromium code to schedule tasks on different MessageLoops.
+//
+// A callback with no unbound input parameters (base::Callback<void(void)>)
+// is called a base::Closure. Note that this is NOT the same as what other
+// languages refer to as a closure -- it does not retain a reference to its
+// enclosing environment.
+//
+// MEMORY MANAGEMENT AND PASSING
+//
+// The Callback objects themselves should be passed by const-reference, and
+// stored by copy. They internally store their state via a refcounted class
+// and thus do not need to be deleted.
+//
+// The reason to pass via a const-reference is to avoid unnecessary
+// AddRef/Release pairs to the internal state.
+//
+//
+// -----------------------------------------------------------------------------
+// Quick reference for basic stuff
+// -----------------------------------------------------------------------------
+//
+// BINDING A BARE FUNCTION
+//
+// int Return5() { return 5; }
+// base::Callback<int(void)> func_cb = base::Bind(&Return5);
+// LOG(INFO) << func_cb.Run(); // Prints 5.
+//
+// BINDING A CLASS METHOD
+//
+// The first argument to bind is the member function to call, the second is
+// the object on which to call it.
+//
+// class Ref : public base::RefCountedThreadSafe<Ref> {
+// public:
+// int Foo() { return 3; }
+// void PrintBye() { LOG(INFO) << "bye."; }
+// };
+// scoped_refptr<Ref> ref = new Ref();
+// base::Callback<void(void)> ref_cb = base::Bind(&Ref::Foo, ref);
+// LOG(INFO) << ref_cb.Run(); // Prints out 3.
+//
+// By default the object must support RefCounted or you will get a compiler
+// error. If you're passing between threads, be sure it's
+// RefCountedThreadSafe! See "Advanced binding of member functions" below if
+// you don't want to use reference counting.
+//
+// RUNNING A CALLBACK
+//
+// Callbacks can be run with their "Run" method, which has the same
+// signature as the template argument to the callback.
+//
+// void DoSomething(const base::Callback<void(int, std::string)>& callback) {
+// callback.Run(5, "hello");
+// }
+//
+// Callbacks can be run more than once (they don't get deleted or marked when
+// run). However, this precludes using base::Passed (see below).
+//
+// void DoSomething(const base::Callback<double(double)>& callback) {
+// double myresult = callback.Run(3.14159);
+// myresult += callback.Run(2.71828);
+// }
+//
+// PASSING UNBOUND INPUT PARAMETERS
+//
+// Unbound parameters are specified at the time a callback is Run(). They are
+// specified in the Callback template type:
+//
+// void MyFunc(int i, const std::string& str) {}
+// base::Callback<void(int, const std::string&)> cb = base::Bind(&MyFunc);
+// cb.Run(23, "hello, world");
+//
+// PASSING BOUND INPUT PARAMETERS
+//
+// Bound parameters are specified when you create thee callback as arguments
+// to Bind(). They will be passed to the function and the Run()ner of the
+// callback doesn't see those values or even know that the function it's
+// calling.
+//
+// void MyFunc(int i, const std::string& str) {}
+// base::Callback<void(void)> cb = base::Bind(&MyFunc, 23, "hello world");
+// cb.Run();
+//
+// A callback with no unbound input parameters (base::Callback<void(void)>)
+// is called a base::Closure. So we could have also written:
+//
+// base::Closure cb = base::Bind(&MyFunc, 23, "hello world");
+//
+// When calling member functions, bound parameters just go after the object
+// pointer.
+//
+// base::Closure cb = base::Bind(&MyClass::MyFunc, this, 23, "hello world");
+//
+// PARTIAL BINDING OF PARAMETERS
+//
+// You can specify some parameters when you create the callback, and specify
+// the rest when you execute the callback.
+//
+// void MyFunc(int i, const std::string& str) {}
+// base::Callback<void(const std::string&)> cb = base::Bind(&MyFunc, 23);
+// cb.Run("hello world");
+//
+// When calling a function bound parameters are first, followed by unbound
+// parameters.
+//
+//
+// -----------------------------------------------------------------------------
+// Quick reference for advanced binding
+// -----------------------------------------------------------------------------
+//
+// BINDING A CLASS METHOD WITH WEAK POINTERS
+//
+// base::Bind(&MyClass::Foo, GetWeakPtr());
+//
+// The callback will not be issued if the object is destroyed at the time
+// it's issued. DANGER: weak pointers are not threadsafe, so don't use this
+// when passing between threads!
+//
+// BINDING A CLASS METHOD WITH MANUAL LIFETIME MANAGEMENT
+//
+// base::Bind(&MyClass::Foo, base::Unretained(this));
+//
+// This disables all lifetime management on the object. You're responsible
+// for making sure the object is alive at the time of the call. You break it,
+// you own it!
+//
+// BINDING A CLASS METHOD AND HAVING THE CALLBACK OWN THE CLASS
+//
+// MyClass* myclass = new MyClass;
+// base::Bind(&MyClass::Foo, base::Owned(myclass));
+//
+// The object will be deleted when the callback is destroyed, even if it's
+// not run (like if you post a task during shutdown). Potentially useful for
+// "fire and forget" cases.
+//
+// IGNORING RETURN VALUES
+//
+// Sometimes you want to call a function that returns a value in a callback
+// that doesn't expect a return value.
+//
+// int DoSomething(int arg) { cout << arg << endl; }
+// base::Callback<void<int>) cb =
+// base::Bind(base::IgnoreResult(&DoSomething));
+//
+//
+// -----------------------------------------------------------------------------
+// Quick reference for binding parameters to Bind()
+// -----------------------------------------------------------------------------
+//
+// Bound parameters are specified as arguments to Bind() and are passed to the
+// function. A callback with no parameters or no unbound parameters is called a
+// Closure (base::Callback<void(void)> and base::Closure are the same thing).
+//
+// PASSING PARAMETERS OWNED BY THE CALLBACK
+//
+// void Foo(int* arg) { cout << *arg << endl; }
+// int* pn = new int(1);
+// base::Closure foo_callback = base::Bind(&foo, base::Owned(pn));
+//
+// The parameter will be deleted when the callback is destroyed, even if it's
+// not run (like if you post a task during shutdown).
+//
+// PASSING PARAMETERS AS A scoped_ptr
+//
+// void TakesOwnership(scoped_ptr<Foo> arg) {}
+// scoped_ptr<Foo> f(new Foo);
+// // f becomes null during the following call.
+// base::Closure cb = base::Bind(&TakesOwnership, base::Passed(&f));
+//
+// Ownership of the parameter will be with the callback until the it is run,
+// when ownership is passed to the callback function. This means the callback
+// can only be run once. If the callback is never run, it will delete the
+// object when it's destroyed.
+//
+// PASSING PARAMETERS AS A scoped_refptr
+//
+// void TakesOneRef(scoped_refptr<Foo> arg) {}
+// scoped_refptr<Foo> f(new Foo)
+// base::Closure cb = base::Bind(&TakesOneRef, f);
+//
+// This should "just work." The closure will take a reference as long as it
+// is alive, and another reference will be taken for the called function.
+//
+// PASSING PARAMETERS BY REFERENCE
+//
+// void foo(int arg) { cout << arg << endl }
+// int n = 1;
+// base::Closure has_ref = base::Bind(&foo, base::ConstRef(n));
+// n = 2;
+// has_ref.Run(); // Prints "2"
+//
+// Normally parameters are copied in the closure. DANGER: ConstRef stores a
+// const reference instead, referencing the original parameter. This means
+// that you must ensure the object outlives the callback!
+//
+//
+// -----------------------------------------------------------------------------
+// Implementation notes
+// -----------------------------------------------------------------------------
+//
+// WHERE IS THIS DESIGN FROM:
+//
+// The design Callback and Bind is heavily influenced by C++'s
+// tr1::function/tr1::bind, and by the "Google Callback" system used inside
+// Google.
+//
+//
+// HOW THE IMPLEMENTATION WORKS:
+//
+// There are three main components to the system:
+// 1) The Callback classes.
+// 2) The Bind() functions.
+// 3) The arguments wrappers (e.g., Unretained() and ConstRef()).
+//
+// The Callback classes represent a generic function pointer. Internally,
+// it stores a refcounted piece of state that represents the target function
+// and all its bound parameters. Each Callback specialization has a templated
+// constructor that takes an BindState<>*. In the context of the constructor,
+// the static type of this BindState<> pointer uniquely identifies the
+// function it is representing, all its bound parameters, and a Run() method
+// that is capable of invoking the target.
+//
+// Callback's constructor takes the BindState<>* that has the full static type
+// and erases the target function type as well as the types of the bound
+// parameters. It does this by storing a pointer to the specific Run()
+// function, and upcasting the state of BindState<>* to a
+// BindStateBase*. This is safe as long as this BindStateBase pointer
+// is only used with the stored Run() pointer.
+//
+// To BindState<> objects are created inside the Bind() functions.
+// These functions, along with a set of internal templates, are responsible for
+//
+// - Unwrapping the function signature into return type, and parameters
+// - Determining the number of parameters that are bound
+// - Creating the BindState storing the bound parameters
+// - Performing compile-time asserts to avoid error-prone behavior
+// - Returning an Callback<> with an arity matching the number of unbound
+// parameters and that knows the correct refcounting semantics for the
+// target object if we are binding a method.
+//
+// The Bind functions do the above using type-inference, and template
+// specializations.
+//
+// By default Bind() will store copies of all bound parameters, and attempt
+// to refcount a target object if the function being bound is a class method.
+// These copies are created even if the function takes parameters as const
+// references. (Binding to non-const references is forbidden, see bind.h.)
+//
+// To change this behavior, we introduce a set of argument wrappers
+// (e.g., Unretained(), and ConstRef()). These are simple container templates
+// that are passed by value, and wrap a pointer to argument. See the
+// file-level comment in base/bind_helpers.h for more info.
+//
+// These types are passed to the Unwrap() functions, and the MaybeRefcount()
+// functions respectively to modify the behavior of Bind(). The Unwrap()
+// and MaybeRefcount() functions change behavior by doing partial
+// specialization based on whether or not a parameter is a wrapper type.
+//
+// ConstRef() is similar to tr1::cref. Unretained() is specific to Chromium.
+//
+//
+// WHY NOT TR1 FUNCTION/BIND?
+//
+// Direct use of tr1::function and tr1::bind was considered, but ultimately
+// rejected because of the number of copy constructors invocations involved
+// in the binding of arguments during construction, and the forwarding of
+// arguments during invocation. These copies will no longer be an issue in
+// C++0x because C++0x will support rvalue reference allowing for the compiler
+// to avoid these copies. However, waiting for C++0x is not an option.
+//
+// Measured with valgrind on gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5), the
+// tr1::bind call itself will invoke a non-trivial copy constructor three times
+// for each bound parameter. Also, each when passing a tr1::function, each
+// bound argument will be copied again.
+//
+// In addition to the copies taken at binding and invocation, copying a
+// tr1::function causes a copy to be made of all the bound parameters and
+// state.
+//
+// Furthermore, in Chromium, it is desirable for the Callback to take a
+// reference on a target object when representing a class method call. This
+// is not supported by tr1.
+//
+// Lastly, tr1::function and tr1::bind has a more general and flexible API.
+// This includes things like argument reordering by use of
+// tr1::bind::placeholder, support for non-const reference parameters, and some
+// limited amount of subtyping of the tr1::function object (e.g.,
+// tr1::function<int(int)> is convertible to tr1::function<void(int)>).
+//
+// These are not features that are required in Chromium. Some of them, such as
+// allowing for reference parameters, and subtyping of functions, may actually
+// become a source of errors. Removing support for these features actually
+// allows for a simpler implementation, and a terser Currying API.
+//
+//
+// WHY NOT GOOGLE CALLBACKS?
+//
+// The Google callback system also does not support refcounting. Furthermore,
+// its implementation has a number of strange edge cases with respect to type
+// conversion of its arguments. In particular, the argument's constness must
+// at times match exactly the function signature, or the type-inference might
+// break. Given the above, writing a custom solution was easier.
+//
+//
+// MISSING FUNCTIONALITY
+// - Invoking the return of Bind. Bind(&foo).Run() does not work;
+// - Binding arrays to functions that take a non-const pointer.
+// Example:
+// void Foo(const char* ptr);
+// void Bar(char* ptr);
+// Bind(&Foo, "test");
+// Bind(&Bar, "test"); // This fails because ptr is not const.
+
+namespace base {
+
+// First, we forward declare the Callback class template. This informs the
+// compiler that the template only has 1 type parameter which is the function
+// signature that the Callback is representing.
+//
+// After this, create template specializations for 0-7 parameters. Note that
+// even though the template typelist grows, the specialization still
+// only has one type: the function signature.
+//
+// If you are thinking of forward declaring Callback in your own header file,
+// please include "base/callback_forward.h" instead.
+template <typename Sig>
+class Callback;
+
+namespace internal {
+template <typename Runnable, typename RunType, typename BoundArgsType>
+struct BindState;
+} // namespace internal
+
+template <typename R>
+class Callback<R(void)> : public internal::CallbackBase {
+ public:
+ typedef R(RunType)();
+
+ Callback() : CallbackBase(NULL) { }
+
+ // Note that this constructor CANNOT be explicit, and that Bind() CANNOT
+ // return the exact Callback<> type. See base/bind.h for details.
+ template <typename Runnable, typename BindRunType, typename BoundArgsType>
+ Callback(internal::BindState<Runnable, BindRunType,
+ BoundArgsType>* bind_state)
+ : CallbackBase(bind_state) {
+
+ // Force the assignment to a local variable of PolymorphicInvoke
+ // so the compiler will typecheck that the passed in Run() method has
+ // the correct type.
+ PolymorphicInvoke invoke_func =
+ &internal::BindState<Runnable, BindRunType, BoundArgsType>
+ ::InvokerType::Run;
+#if defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4191) // Calling this function through the result
+ // pointer may cause your program to fail.
+#endif
+ polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func);
+#if defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+ }
+
+ bool Equals(const Callback& other) const {
+ return CallbackBase::Equals(other);
+ }
+
+ R Run() const {
+#if defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4191) // Calling this function through the result
+ // pointer may cause your program to fail.
+#endif
+ PolymorphicInvoke f =
+ reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
+#if defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+
+ return f(bind_state_.get());
+ }
+
+ private:
+ typedef R(*PolymorphicInvoke)(
+ internal::BindStateBase*);
+
+};
+
+template <typename R, typename A1>
+class Callback<R(A1)> : public internal::CallbackBase {
+ public:
+ typedef R(RunType)(A1);
+
+ Callback() : CallbackBase(NULL) { }
+
+ // Note that this constructor CANNOT be explicit, and that Bind() CANNOT
+ // return the exact Callback<> type. See base/bind.h for details.
+ template <typename Runnable, typename BindRunType, typename BoundArgsType>
+ Callback(internal::BindState<Runnable, BindRunType,
+ BoundArgsType>* bind_state)
+ : CallbackBase(bind_state) {
+
+ // Force the assignment to a local variable of PolymorphicInvoke
+ // so the compiler will typecheck that the passed in Run() method has
+ // the correct type.
+ PolymorphicInvoke invoke_func =
+ &internal::BindState<Runnable, BindRunType, BoundArgsType>
+ ::InvokerType::Run;
+#if defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4191) // Calling this function through the result
+ // pointer may cause your program to fail.
+#endif
+ polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func);
+#if defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+ }
+
+ bool Equals(const Callback& other) const {
+ return CallbackBase::Equals(other);
+ }
+
+ R Run(typename internal::CallbackParamTraits<A1>::ForwardType a1) const {
+#if defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4191) // Calling this function through the result
+ // pointer may cause your program to fail.
+#endif
+ PolymorphicInvoke f =
+ reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
+#if defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+
+ return f(bind_state_.get(), internal::CallbackForward(a1));
+ }
+
+ private:
+ typedef R(*PolymorphicInvoke)(
+ internal::BindStateBase*,
+ typename internal::CallbackParamTraits<A1>::ForwardType);
+
+};
+
+template <typename R, typename A1, typename A2>
+class Callback<R(A1, A2)> : public internal::CallbackBase {
+ public:
+ typedef R(RunType)(A1, A2);
+
+ Callback() : CallbackBase(NULL) { }
+
+ // Note that this constructor CANNOT be explicit, and that Bind() CANNOT
+ // return the exact Callback<> type. See base/bind.h for details.
+ template <typename Runnable, typename BindRunType, typename BoundArgsType>
+ Callback(internal::BindState<Runnable, BindRunType,
+ BoundArgsType>* bind_state)
+ : CallbackBase(bind_state) {
+
+ // Force the assignment to a local variable of PolymorphicInvoke
+ // so the compiler will typecheck that the passed in Run() method has
+ // the correct type.
+ PolymorphicInvoke invoke_func =
+ &internal::BindState<Runnable, BindRunType, BoundArgsType>
+ ::InvokerType::Run;
+#if defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4191) // Calling this function through the result
+ // pointer may cause your program to fail.
+#endif
+ polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func);
+#if defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+ }
+
+ bool Equals(const Callback& other) const {
+ return CallbackBase::Equals(other);
+ }
+
+ R Run(typename internal::CallbackParamTraits<A1>::ForwardType a1,
+ typename internal::CallbackParamTraits<A2>::ForwardType a2) const {
+#if defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4191) // Calling this function through the result
+ // pointer may cause your program to fail.
+#endif
+ PolymorphicInvoke f =
+ reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
+#if defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+
+ return f(bind_state_.get(), internal::CallbackForward(a1),
+ internal::CallbackForward(a2));
+ }
+
+ private:
+ typedef R(*PolymorphicInvoke)(
+ internal::BindStateBase*,
+ typename internal::CallbackParamTraits<A1>::ForwardType,
+ typename internal::CallbackParamTraits<A2>::ForwardType);
+
+};
+
+template <typename R, typename A1, typename A2, typename A3>
+class Callback<R(A1, A2, A3)> : public internal::CallbackBase {
+ public:
+ typedef R(RunType)(A1, A2, A3);
+
+ Callback() : CallbackBase(NULL) { }
+
+ // Note that this constructor CANNOT be explicit, and that Bind() CANNOT
+ // return the exact Callback<> type. See base/bind.h for details.
+ template <typename Runnable, typename BindRunType, typename BoundArgsType>
+ Callback(internal::BindState<Runnable, BindRunType,
+ BoundArgsType>* bind_state)
+ : CallbackBase(bind_state) {
+
+ // Force the assignment to a local variable of PolymorphicInvoke
+ // so the compiler will typecheck that the passed in Run() method has
+ // the correct type.
+ PolymorphicInvoke invoke_func =
+ &internal::BindState<Runnable, BindRunType, BoundArgsType>
+ ::InvokerType::Run;
+#if defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4191) // Calling this function through the result
+ // pointer may cause your program to fail.
+#endif
+ polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func);
+#if defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+ }
+
+ bool Equals(const Callback& other) const {
+ return CallbackBase::Equals(other);
+ }
+
+ R Run(typename internal::CallbackParamTraits<A1>::ForwardType a1,
+ typename internal::CallbackParamTraits<A2>::ForwardType a2,
+ typename internal::CallbackParamTraits<A3>::ForwardType a3) const {
+#if defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4191) // Calling this function through the result
+ // pointer may cause your program to fail.
+#endif
+ PolymorphicInvoke f =
+ reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
+#if defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+
+ return f(bind_state_.get(), internal::CallbackForward(a1),
+ internal::CallbackForward(a2),
+ internal::CallbackForward(a3));
+ }
+
+ private:
+ typedef R(*PolymorphicInvoke)(
+ internal::BindStateBase*,
+ typename internal::CallbackParamTraits<A1>::ForwardType,
+ typename internal::CallbackParamTraits<A2>::ForwardType,
+ typename internal::CallbackParamTraits<A3>::ForwardType);
+
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+class Callback<R(A1, A2, A3, A4)> : public internal::CallbackBase {
+ public:
+ typedef R(RunType)(A1, A2, A3, A4);
+
+ Callback() : CallbackBase(NULL) { }
+
+ // Note that this constructor CANNOT be explicit, and that Bind() CANNOT
+ // return the exact Callback<> type. See base/bind.h for details.
+ template <typename Runnable, typename BindRunType, typename BoundArgsType>
+ Callback(internal::BindState<Runnable, BindRunType,
+ BoundArgsType>* bind_state)
+ : CallbackBase(bind_state) {
+
+ // Force the assignment to a local variable of PolymorphicInvoke
+ // so the compiler will typecheck that the passed in Run() method has
+ // the correct type.
+ PolymorphicInvoke invoke_func =
+ &internal::BindState<Runnable, BindRunType, BoundArgsType>
+ ::InvokerType::Run;
+#if defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4191) // Calling this function through the result
+ // pointer may cause your program to fail.
+#endif
+ polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func);
+#if defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+ }
+
+ bool Equals(const Callback& other) const {
+ return CallbackBase::Equals(other);
+ }
+
+ R Run(typename internal::CallbackParamTraits<A1>::ForwardType a1,
+ typename internal::CallbackParamTraits<A2>::ForwardType a2,
+ typename internal::CallbackParamTraits<A3>::ForwardType a3,
+ typename internal::CallbackParamTraits<A4>::ForwardType a4) const {
+#if defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4191) // Calling this function through the result
+ // pointer may cause your program to fail.
+#endif
+ PolymorphicInvoke f =
+ reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
+#if defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+
+ return f(bind_state_.get(), internal::CallbackForward(a1),
+ internal::CallbackForward(a2),
+ internal::CallbackForward(a3),
+ internal::CallbackForward(a4));
+ }
+
+ private:
+ typedef R(*PolymorphicInvoke)(
+ internal::BindStateBase*,
+ typename internal::CallbackParamTraits<A1>::ForwardType,
+ typename internal::CallbackParamTraits<A2>::ForwardType,
+ typename internal::CallbackParamTraits<A3>::ForwardType,
+ typename internal::CallbackParamTraits<A4>::ForwardType);
+
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5>
+class Callback<R(A1, A2, A3, A4, A5)> : public internal::CallbackBase {
+ public:
+ typedef R(RunType)(A1, A2, A3, A4, A5);
+
+ Callback() : CallbackBase(NULL) { }
+
+ // Note that this constructor CANNOT be explicit, and that Bind() CANNOT
+ // return the exact Callback<> type. See base/bind.h for details.
+ template <typename Runnable, typename BindRunType, typename BoundArgsType>
+ Callback(internal::BindState<Runnable, BindRunType,
+ BoundArgsType>* bind_state)
+ : CallbackBase(bind_state) {
+
+ // Force the assignment to a local variable of PolymorphicInvoke
+ // so the compiler will typecheck that the passed in Run() method has
+ // the correct type.
+ PolymorphicInvoke invoke_func =
+ &internal::BindState<Runnable, BindRunType, BoundArgsType>
+ ::InvokerType::Run;
+#if defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4191) // Calling this function through the result
+ // pointer may cause your program to fail.
+#endif
+ polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func);
+#if defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+ }
+
+ bool Equals(const Callback& other) const {
+ return CallbackBase::Equals(other);
+ }
+
+ R Run(typename internal::CallbackParamTraits<A1>::ForwardType a1,
+ typename internal::CallbackParamTraits<A2>::ForwardType a2,
+ typename internal::CallbackParamTraits<A3>::ForwardType a3,
+ typename internal::CallbackParamTraits<A4>::ForwardType a4,
+ typename internal::CallbackParamTraits<A5>::ForwardType a5) const {
+#if defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4191) // Calling this function through the result
+ // pointer may cause your program to fail.
+#endif
+ PolymorphicInvoke f =
+ reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
+#if defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+
+ return f(bind_state_.get(), internal::CallbackForward(a1),
+ internal::CallbackForward(a2),
+ internal::CallbackForward(a3),
+ internal::CallbackForward(a4),
+ internal::CallbackForward(a5));
+ }
+
+ private:
+ typedef R(*PolymorphicInvoke)(
+ internal::BindStateBase*,
+ typename internal::CallbackParamTraits<A1>::ForwardType,
+ typename internal::CallbackParamTraits<A2>::ForwardType,
+ typename internal::CallbackParamTraits<A3>::ForwardType,
+ typename internal::CallbackParamTraits<A4>::ForwardType,
+ typename internal::CallbackParamTraits<A5>::ForwardType);
+
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6>
+class Callback<R(A1, A2, A3, A4, A5, A6)> : public internal::CallbackBase {
+ public:
+ typedef R(RunType)(A1, A2, A3, A4, A5, A6);
+
+ Callback() : CallbackBase(NULL) { }
+
+ // Note that this constructor CANNOT be explicit, and that Bind() CANNOT
+ // return the exact Callback<> type. See base/bind.h for details.
+ template <typename Runnable, typename BindRunType, typename BoundArgsType>
+ Callback(internal::BindState<Runnable, BindRunType,
+ BoundArgsType>* bind_state)
+ : CallbackBase(bind_state) {
+
+ // Force the assignment to a local variable of PolymorphicInvoke
+ // so the compiler will typecheck that the passed in Run() method has
+ // the correct type.
+ PolymorphicInvoke invoke_func =
+ &internal::BindState<Runnable, BindRunType, BoundArgsType>
+ ::InvokerType::Run;
+#if defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4191) // Calling this function through the result
+ // pointer may cause your program to fail.
+#endif
+ polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func);
+#if defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+ }
+
+ bool Equals(const Callback& other) const {
+ return CallbackBase::Equals(other);
+ }
+
+ R Run(typename internal::CallbackParamTraits<A1>::ForwardType a1,
+ typename internal::CallbackParamTraits<A2>::ForwardType a2,
+ typename internal::CallbackParamTraits<A3>::ForwardType a3,
+ typename internal::CallbackParamTraits<A4>::ForwardType a4,
+ typename internal::CallbackParamTraits<A5>::ForwardType a5,
+ typename internal::CallbackParamTraits<A6>::ForwardType a6) const {
+#if defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4191) // Calling this function through the result
+ // pointer may cause your program to fail.
+#endif
+ PolymorphicInvoke f =
+ reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
+#if defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+
+ return f(bind_state_.get(), internal::CallbackForward(a1),
+ internal::CallbackForward(a2),
+ internal::CallbackForward(a3),
+ internal::CallbackForward(a4),
+ internal::CallbackForward(a5),
+ internal::CallbackForward(a6));
+ }
+
+ private:
+ typedef R(*PolymorphicInvoke)(
+ internal::BindStateBase*,
+ typename internal::CallbackParamTraits<A1>::ForwardType,
+ typename internal::CallbackParamTraits<A2>::ForwardType,
+ typename internal::CallbackParamTraits<A3>::ForwardType,
+ typename internal::CallbackParamTraits<A4>::ForwardType,
+ typename internal::CallbackParamTraits<A5>::ForwardType,
+ typename internal::CallbackParamTraits<A6>::ForwardType);
+
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+ typename A5, typename A6, typename A7>
+class Callback<R(A1, A2, A3, A4, A5, A6, A7)> : public internal::CallbackBase {
+ public:
+ typedef R(RunType)(A1, A2, A3, A4, A5, A6, A7);
+
+ Callback() : CallbackBase(NULL) { }
+
+ // Note that this constructor CANNOT be explicit, and that Bind() CANNOT
+ // return the exact Callback<> type. See base/bind.h for details.
+ template <typename Runnable, typename BindRunType, typename BoundArgsType>
+ Callback(internal::BindState<Runnable, BindRunType,
+ BoundArgsType>* bind_state)
+ : CallbackBase(bind_state) {
+
+ // Force the assignment to a local variable of PolymorphicInvoke
+ // so the compiler will typecheck that the passed in Run() method has
+ // the correct type.
+ PolymorphicInvoke invoke_func =
+ &internal::BindState<Runnable, BindRunType, BoundArgsType>
+ ::InvokerType::Run;
+#if defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4191) // Calling this function through the result
+ // pointer may cause your program to fail.
+#endif
+ polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func);
+#if defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+ }
+
+ bool Equals(const Callback& other) const {
+ return CallbackBase::Equals(other);
+ }
+
+ R Run(typename internal::CallbackParamTraits<A1>::ForwardType a1,
+ typename internal::CallbackParamTraits<A2>::ForwardType a2,
+ typename internal::CallbackParamTraits<A3>::ForwardType a3,
+ typename internal::CallbackParamTraits<A4>::ForwardType a4,
+ typename internal::CallbackParamTraits<A5>::ForwardType a5,
+ typename internal::CallbackParamTraits<A6>::ForwardType a6,
+ typename internal::CallbackParamTraits<A7>::ForwardType a7) const {
+#if defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4191) // Calling this function through the result
+ // pointer may cause your program to fail.
+#endif
+ PolymorphicInvoke f =
+ reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
+#if defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+
+ return f(bind_state_.get(), internal::CallbackForward(a1),
+ internal::CallbackForward(a2),
+ internal::CallbackForward(a3),
+ internal::CallbackForward(a4),
+ internal::CallbackForward(a5),
+ internal::CallbackForward(a6),
+ internal::CallbackForward(a7));
+ }
+
+ private:
+ typedef R(*PolymorphicInvoke)(
+ internal::BindStateBase*,
+ typename internal::CallbackParamTraits<A1>::ForwardType,
+ typename internal::CallbackParamTraits<A2>::ForwardType,
+ typename internal::CallbackParamTraits<A3>::ForwardType,
+ typename internal::CallbackParamTraits<A4>::ForwardType,
+ typename internal::CallbackParamTraits<A5>::ForwardType,
+ typename internal::CallbackParamTraits<A6>::ForwardType,
+ typename internal::CallbackParamTraits<A7>::ForwardType);
+
+};
+
+
+// Syntactic sugar to make Callbacks<void(void)> easier to declare since it
+// will be used in a lot of APIs with delayed execution.
+typedef Callback<void(void)> Closure;
+
+} // namespace base
+
+#endif // BASE_CALLBACK_H
diff --git a/src/base/callback.h.pump b/src/base/callback.h.pump
new file mode 100644
index 0000000..b6a0aee
--- /dev/null
+++ b/src/base/callback.h.pump
@@ -0,0 +1,452 @@
+$$ This is a pump file for generating file templates. Pump is a python
+$$ script that is part of the Google Test suite of utilities. Description
+$$ can be found here:
+$$
+$$ http://code.google.com/p/googletest/wiki/PumpManual
+$$
+
+$$ See comment for MAX_ARITY in base/bind.h.pump.
+$var MAX_ARITY = 7
+
+// Copyright (c) 2012 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_CALLBACK_H_
+#define BASE_CALLBACK_H_
+
+#include "base/callback_forward.h"
+#include "base/callback_internal.h"
+#include "base/template_util.h"
+
+// NOTE: Header files that do not require the full definition of Callback or
+// Closure should #include "base/callback_forward.h" instead of this file.
+
+// -----------------------------------------------------------------------------
+// Introduction
+// -----------------------------------------------------------------------------
+//
+// The templated Callback class is a generalized function object. Together
+// with the Bind() function in bind.h, they provide a type-safe method for
+// performing partial application of functions.
+//
+// Partial application (or "currying") is the process of binding a subset of
+// a function's arguments to produce another function that takes fewer
+// arguments. This can be used to pass around a unit of delayed execution,
+// much like lexical closures are used in other languages. For example, it
+// is used in Chromium code to schedule tasks on different MessageLoops.
+//
+// A callback with no unbound input parameters (base::Callback<void(void)>)
+// is called a base::Closure. Note that this is NOT the same as what other
+// languages refer to as a closure -- it does not retain a reference to its
+// enclosing environment.
+//
+// MEMORY MANAGEMENT AND PASSING
+//
+// The Callback objects themselves should be passed by const-reference, and
+// stored by copy. They internally store their state via a refcounted class
+// and thus do not need to be deleted.
+//
+// The reason to pass via a const-reference is to avoid unnecessary
+// AddRef/Release pairs to the internal state.
+//
+//
+// -----------------------------------------------------------------------------
+// Quick reference for basic stuff
+// -----------------------------------------------------------------------------
+//
+// BINDING A BARE FUNCTION
+//
+// int Return5() { return 5; }
+// base::Callback<int(void)> func_cb = base::Bind(&Return5);
+// LOG(INFO) << func_cb.Run(); // Prints 5.
+//
+// BINDING A CLASS METHOD
+//
+// The first argument to bind is the member function to call, the second is
+// the object on which to call it.
+//
+// class Ref : public base::RefCountedThreadSafe<Ref> {
+// public:
+// int Foo() { return 3; }
+// void PrintBye() { LOG(INFO) << "bye."; }
+// };
+// scoped_refptr<Ref> ref = new Ref();
+// base::Callback<void(void)> ref_cb = base::Bind(&Ref::Foo, ref);
+// LOG(INFO) << ref_cb.Run(); // Prints out 3.
+//
+// By default the object must support RefCounted or you will get a compiler
+// error. If you're passing between threads, be sure it's
+// RefCountedThreadSafe! See "Advanced binding of member functions" below if
+// you don't want to use reference counting.
+//
+// RUNNING A CALLBACK
+//
+// Callbacks can be run with their "Run" method, which has the same
+// signature as the template argument to the callback.
+//
+// void DoSomething(const base::Callback<void(int, std::string)>& callback) {
+// callback.Run(5, "hello");
+// }
+//
+// Callbacks can be run more than once (they don't get deleted or marked when
+// run). However, this precludes using base::Passed (see below).
+//
+// void DoSomething(const base::Callback<double(double)>& callback) {
+// double myresult = callback.Run(3.14159);
+// myresult += callback.Run(2.71828);
+// }
+//
+// PASSING UNBOUND INPUT PARAMETERS
+//
+// Unbound parameters are specified at the time a callback is Run(). They are
+// specified in the Callback template type:
+//
+// void MyFunc(int i, const std::string& str) {}
+// base::Callback<void(int, const std::string&)> cb = base::Bind(&MyFunc);
+// cb.Run(23, "hello, world");
+//
+// PASSING BOUND INPUT PARAMETERS
+//
+// Bound parameters are specified when you create thee callback as arguments
+// to Bind(). They will be passed to the function and the Run()ner of the
+// callback doesn't see those values or even know that the function it's
+// calling.
+//
+// void MyFunc(int i, const std::string& str) {}
+// base::Callback<void(void)> cb = base::Bind(&MyFunc, 23, "hello world");
+// cb.Run();
+//
+// A callback with no unbound input parameters (base::Callback<void(void)>)
+// is called a base::Closure. So we could have also written:
+//
+// base::Closure cb = base::Bind(&MyFunc, 23, "hello world");
+//
+// When calling member functions, bound parameters just go after the object
+// pointer.
+//
+// base::Closure cb = base::Bind(&MyClass::MyFunc, this, 23, "hello world");
+//
+// PARTIAL BINDING OF PARAMETERS
+//
+// You can specify some parameters when you create the callback, and specify
+// the rest when you execute the callback.
+//
+// void MyFunc(int i, const std::string& str) {}
+// base::Callback<void(const std::string&)> cb = base::Bind(&MyFunc, 23);
+// cb.Run("hello world");
+//
+// When calling a function bound parameters are first, followed by unbound
+// parameters.
+//
+//
+// -----------------------------------------------------------------------------
+// Quick reference for advanced binding
+// -----------------------------------------------------------------------------
+//
+// BINDING A CLASS METHOD WITH WEAK POINTERS
+//
+// base::Bind(&MyClass::Foo, GetWeakPtr());
+//
+// The callback will not be issued if the object is destroyed at the time
+// it's issued. DANGER: weak pointers are not threadsafe, so don't use this
+// when passing between threads!
+//
+// BINDING A CLASS METHOD WITH MANUAL LIFETIME MANAGEMENT
+//
+// base::Bind(&MyClass::Foo, base::Unretained(this));
+//
+// This disables all lifetime management on the object. You're responsible
+// for making sure the object is alive at the time of the call. You break it,
+// you own it!
+//
+// BINDING A CLASS METHOD AND HAVING THE CALLBACK OWN THE CLASS
+//
+// MyClass* myclass = new MyClass;
+// base::Bind(&MyClass::Foo, base::Owned(myclass));
+//
+// The object will be deleted when the callback is destroyed, even if it's
+// not run (like if you post a task during shutdown). Potentially useful for
+// "fire and forget" cases.
+//
+// IGNORING RETURN VALUES
+//
+// Sometimes you want to call a function that returns a value in a callback
+// that doesn't expect a return value.
+//
+// int DoSomething(int arg) { cout << arg << endl; }
+// base::Callback<void<int>) cb =
+// base::Bind(base::IgnoreResult(&DoSomething));
+//
+//
+// -----------------------------------------------------------------------------
+// Quick reference for binding parameters to Bind()
+// -----------------------------------------------------------------------------
+//
+// Bound parameters are specified as arguments to Bind() and are passed to the
+// function. A callback with no parameters or no unbound parameters is called a
+// Closure (base::Callback<void(void)> and base::Closure are the same thing).
+//
+// PASSING PARAMETERS OWNED BY THE CALLBACK
+//
+// void Foo(int* arg) { cout << *arg << endl; }
+// int* pn = new int(1);
+// base::Closure foo_callback = base::Bind(&foo, base::Owned(pn));
+//
+// The parameter will be deleted when the callback is destroyed, even if it's
+// not run (like if you post a task during shutdown).
+//
+// PASSING PARAMETERS AS A scoped_ptr
+//
+// void TakesOwnership(scoped_ptr<Foo> arg) {}
+// scoped_ptr<Foo> f(new Foo);
+// // f becomes null during the following call.
+// base::Closure cb = base::Bind(&TakesOwnership, base::Passed(&f));
+//
+// Ownership of the parameter will be with the callback until the it is run,
+// when ownership is passed to the callback function. This means the callback
+// can only be run once. If the callback is never run, it will delete the
+// object when it's destroyed.
+//
+// PASSING PARAMETERS AS A scoped_refptr
+//
+// void TakesOneRef(scoped_refptr<Foo> arg) {}
+// scoped_refptr<Foo> f(new Foo)
+// base::Closure cb = base::Bind(&TakesOneRef, f);
+//
+// This should "just work." The closure will take a reference as long as it
+// is alive, and another reference will be taken for the called function.
+//
+// PASSING PARAMETERS BY REFERENCE
+//
+// void foo(int arg) { cout << arg << endl }
+// int n = 1;
+// base::Closure has_ref = base::Bind(&foo, base::ConstRef(n));
+// n = 2;
+// has_ref.Run(); // Prints "2"
+//
+// Normally parameters are copied in the closure. DANGER: ConstRef stores a
+// const reference instead, referencing the original parameter. This means
+// that you must ensure the object outlives the callback!
+//
+//
+// -----------------------------------------------------------------------------
+// Implementation notes
+// -----------------------------------------------------------------------------
+//
+// WHERE IS THIS DESIGN FROM:
+//
+// The design Callback and Bind is heavily influenced by C++'s
+// tr1::function/tr1::bind, and by the "Google Callback" system used inside
+// Google.
+//
+//
+// HOW THE IMPLEMENTATION WORKS:
+//
+// There are three main components to the system:
+// 1) The Callback classes.
+// 2) The Bind() functions.
+// 3) The arguments wrappers (e.g., Unretained() and ConstRef()).
+//
+// The Callback classes represent a generic function pointer. Internally,
+// it stores a refcounted piece of state that represents the target function
+// and all its bound parameters. Each Callback specialization has a templated
+// constructor that takes an BindState<>*. In the context of the constructor,
+// the static type of this BindState<> pointer uniquely identifies the
+// function it is representing, all its bound parameters, and a Run() method
+// that is capable of invoking the target.
+//
+// Callback's constructor takes the BindState<>* that has the full static type
+// and erases the target function type as well as the types of the bound
+// parameters. It does this by storing a pointer to the specific Run()
+// function, and upcasting the state of BindState<>* to a
+// BindStateBase*. This is safe as long as this BindStateBase pointer
+// is only used with the stored Run() pointer.
+//
+// To BindState<> objects are created inside the Bind() functions.
+// These functions, along with a set of internal templates, are responsible for
+//
+// - Unwrapping the function signature into return type, and parameters
+// - Determining the number of parameters that are bound
+// - Creating the BindState storing the bound parameters
+// - Performing compile-time asserts to avoid error-prone behavior
+// - Returning an Callback<> with an arity matching the number of unbound
+// parameters and that knows the correct refcounting semantics for the
+// target object if we are binding a method.
+//
+// The Bind functions do the above using type-inference, and template
+// specializations.
+//
+// By default Bind() will store copies of all bound parameters, and attempt
+// to refcount a target object if the function being bound is a class method.
+// These copies are created even if the function takes parameters as const
+// references. (Binding to non-const references is forbidden, see bind.h.)
+//
+// To change this behavior, we introduce a set of argument wrappers
+// (e.g., Unretained(), and ConstRef()). These are simple container templates
+// that are passed by value, and wrap a pointer to argument. See the
+// file-level comment in base/bind_helpers.h for more info.
+//
+// These types are passed to the Unwrap() functions, and the MaybeRefcount()
+// functions respectively to modify the behavior of Bind(). The Unwrap()
+// and MaybeRefcount() functions change behavior by doing partial
+// specialization based on whether or not a parameter is a wrapper type.
+//
+// ConstRef() is similar to tr1::cref. Unretained() is specific to Chromium.
+//
+//
+// WHY NOT TR1 FUNCTION/BIND?
+//
+// Direct use of tr1::function and tr1::bind was considered, but ultimately
+// rejected because of the number of copy constructors invocations involved
+// in the binding of arguments during construction, and the forwarding of
+// arguments during invocation. These copies will no longer be an issue in
+// C++0x because C++0x will support rvalue reference allowing for the compiler
+// to avoid these copies. However, waiting for C++0x is not an option.
+//
+// Measured with valgrind on gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5), the
+// tr1::bind call itself will invoke a non-trivial copy constructor three times
+// for each bound parameter. Also, each when passing a tr1::function, each
+// bound argument will be copied again.
+//
+// In addition to the copies taken at binding and invocation, copying a
+// tr1::function causes a copy to be made of all the bound parameters and
+// state.
+//
+// Furthermore, in Chromium, it is desirable for the Callback to take a
+// reference on a target object when representing a class method call. This
+// is not supported by tr1.
+//
+// Lastly, tr1::function and tr1::bind has a more general and flexible API.
+// This includes things like argument reordering by use of
+// tr1::bind::placeholder, support for non-const reference parameters, and some
+// limited amount of subtyping of the tr1::function object (e.g.,
+// tr1::function<int(int)> is convertible to tr1::function<void(int)>).
+//
+// These are not features that are required in Chromium. Some of them, such as
+// allowing for reference parameters, and subtyping of functions, may actually
+// become a source of errors. Removing support for these features actually
+// allows for a simpler implementation, and a terser Currying API.
+//
+//
+// WHY NOT GOOGLE CALLBACKS?
+//
+// The Google callback system also does not support refcounting. Furthermore,
+// its implementation has a number of strange edge cases with respect to type
+// conversion of its arguments. In particular, the argument's constness must
+// at times match exactly the function signature, or the type-inference might
+// break. Given the above, writing a custom solution was easier.
+//
+//
+// MISSING FUNCTIONALITY
+// - Invoking the return of Bind. Bind(&foo).Run() does not work;
+// - Binding arrays to functions that take a non-const pointer.
+// Example:
+// void Foo(const char* ptr);
+// void Bar(char* ptr);
+// Bind(&Foo, "test");
+// Bind(&Bar, "test"); // This fails because ptr is not const.
+
+namespace base {
+
+// First, we forward declare the Callback class template. This informs the
+// compiler that the template only has 1 type parameter which is the function
+// signature that the Callback is representing.
+//
+// After this, create template specializations for 0-$(MAX_ARITY) parameters. Note that
+// even though the template typelist grows, the specialization still
+// only has one type: the function signature.
+//
+// If you are thinking of forward declaring Callback in your own header file,
+// please include "base/callback_forward.h" instead.
+template <typename Sig>
+class Callback;
+
+namespace internal {
+template <typename Runnable, typename RunType, typename BoundArgsType>
+struct BindState;
+} // namespace internal
+
+
+$range ARITY 0..MAX_ARITY
+$for ARITY [[
+$range ARG 1..ARITY
+
+$if ARITY == 0 [[
+template <typename R>
+class Callback<R(void)> : public internal::CallbackBase {
+]] $else [[
+template <typename R, $for ARG , [[typename A$(ARG)]]>
+class Callback<R($for ARG , [[A$(ARG)]])> : public internal::CallbackBase {
+]]
+
+ public:
+ typedef R(RunType)($for ARG , [[A$(ARG)]]);
+
+ Callback() : CallbackBase(NULL) { }
+
+ // Note that this constructor CANNOT be explicit, and that Bind() CANNOT
+ // return the exact Callback<> type. See base/bind.h for details.
+ template <typename Runnable, typename BindRunType, typename BoundArgsType>
+ Callback(internal::BindState<Runnable, BindRunType,
+ BoundArgsType>* bind_state)
+ : CallbackBase(bind_state) {
+
+ // Force the assignment to a local variable of PolymorphicInvoke
+ // so the compiler will typecheck that the passed in Run() method has
+ // the correct type.
+ PolymorphicInvoke invoke_func =
+ &internal::BindState<Runnable, BindRunType, BoundArgsType>
+ ::InvokerType::Run;
+#if defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4191) // Calling this function through the result
+ // pointer may cause your program to fail.
+#endif
+ polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func);
+#if defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+ }
+
+ bool Equals(const Callback& other) const {
+ return CallbackBase::Equals(other);
+ }
+
+ R Run($for ARG ,
+ [[typename internal::CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) const {
+#if defined(COBALT_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4191) // Calling this function through the result
+ // pointer may cause your program to fail.
+#endif
+ PolymorphicInvoke f =
+ reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
+#if defined(COBALT_WIN)
+#pragma warning(pop)
+#endif
+
+ return f(bind_state_.get()[[]]
+$if ARITY != 0 [[, ]]
+$for ARG ,
+ [[internal::CallbackForward(a$(ARG))]]);
+ }
+
+ private:
+ typedef R(*PolymorphicInvoke)(
+ internal::BindStateBase*[[]]
+$if ARITY != 0 [[, ]]
+$for ARG , [[typename internal::CallbackParamTraits<A$(ARG)>::ForwardType]]);
+
+};
+
+
+]] $$ for ARITY
+
+// Syntactic sugar to make Callbacks<void(void)> easier to declare since it
+// will be used in a lot of APIs with delayed execution.
+typedef Callback<void(void)> Closure;
+
+} // namespace base
+
+#endif // BASE_CALLBACK_H
diff --git a/src/base/callback_forward.h b/src/base/callback_forward.h
new file mode 100644
index 0000000..7983248
--- /dev/null
+++ b/src/base/callback_forward.h
@@ -0,0 +1,17 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_CALLBACK_FORWARD_H_
+#define BASE_CALLBACK_FORWARD_H_
+
+namespace base {
+
+template <typename Sig>
+class Callback;
+
+typedef Callback<void(void)> Closure;
+
+} // namespace base
+
+#endif // BASE_CALLBACK_FORWARD_H
diff --git a/src/base/callback_helpers.h b/src/base/callback_helpers.h
new file mode 100644
index 0000000..52cb71b
--- /dev/null
+++ b/src/base/callback_helpers.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 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 defines helpful methods for dealing with Callbacks. Because Callbacks
+// are implemented using templates, with a class per callback signature, adding
+// methods to Callback<> itself is unattractive (lots of extra code gets
+// generated). Instead, consider adding methods here.
+//
+// ResetAndReturn(&cb) is like cb.Reset() but allows executing a callback (via a
+// copy) after the original callback is Reset(). This can be handy if Run()
+// reads/writes the variable holding the Callback.
+
+#ifndef BASE_CALLBACK_HELPERS_H_
+#define BASE_CALLBACK_HELPERS_H_
+
+#include "base/callback.h"
+
+namespace base {
+
+template <typename Sig>
+base::Callback<Sig> ResetAndReturn(base::Callback<Sig>* cb) {
+ base::Callback<Sig> ret(*cb);
+ cb->Reset();
+ return ret;
+}
+
+} // namespace base
+
+#endif // BASE_CALLBACK_HELPERS_H_
diff --git a/src/base/callback_internal.cc b/src/base/callback_internal.cc
new file mode 100644
index 0000000..2dde402
--- /dev/null
+++ b/src/base/callback_internal.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 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/callback_internal.h"
+
+#include "base/logging.h"
+
+namespace base {
+namespace internal {
+
+bool CallbackBase::is_null() const {
+ return bind_state_.get() == NULL;
+}
+
+void CallbackBase::Reset() {
+ polymorphic_invoke_ = NULL;
+ // NULL the bind_state_ last, since it may be holding the last ref to whatever
+ // object owns us, and we may be deleted after that.
+ bind_state_ = NULL;
+}
+
+bool CallbackBase::Equals(const CallbackBase& other) const {
+ return bind_state_.get() == other.bind_state_.get() &&
+ polymorphic_invoke_ == other.polymorphic_invoke_;
+}
+
+CallbackBase::CallbackBase(BindStateBase* bind_state)
+ : bind_state_(bind_state),
+ polymorphic_invoke_(NULL) {
+ DCHECK(!bind_state_ || bind_state_->HasOneRef());
+}
+
+CallbackBase::~CallbackBase() {
+}
+
+} // namespace internal
+} // namespace base
diff --git a/src/base/callback_internal.h b/src/base/callback_internal.h
new file mode 100644
index 0000000..2f834c3
--- /dev/null
+++ b/src/base/callback_internal.h
@@ -0,0 +1,193 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains utility functions and classes that help the
+// implementation, and management of the Callback objects.
+
+#ifndef BASE_CALLBACK_INTERNAL_H_
+#define BASE_CALLBACK_INTERNAL_H_
+
+#include <stddef.h>
+
+#include "base/base_export.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+
+template <typename T>
+class ScopedVector;
+
+namespace base {
+namespace internal {
+
+// BindStateBase is used to provide an opaque handle that the Callback
+// class can use to represent a function object with bound arguments. It
+// behaves as an existential type that is used by a corresponding
+// DoInvoke function to perform the function execution. This allows
+// us to shield the Callback class from the types of the bound argument via
+// "type erasure."
+class BindStateBase : public RefCountedThreadSafe<BindStateBase> {
+ protected:
+ friend class RefCountedThreadSafe<BindStateBase>;
+ virtual ~BindStateBase() {}
+};
+
+// Holds the Callback methods that don't require specialization to reduce
+// template bloat.
+class BASE_EXPORT CallbackBase {
+ public:
+ // Returns true if Callback is null (doesn't refer to anything).
+ bool is_null() const;
+
+ // Returns the Callback into an uninitialized state.
+ void Reset();
+
+ protected:
+ // In C++, it is safe to cast function pointers to function pointers of
+ // another type. It is not okay to use void*. We create a InvokeFuncStorage
+ // that that can store our function pointer, and then cast it back to
+ // the original type on usage.
+ typedef void(*InvokeFuncStorage)(void);
+
+ // Returns true if this callback equals |other|. |other| may be null.
+ bool Equals(const CallbackBase& other) const;
+
+ // Allow initializing of |bind_state_| via the constructor to avoid default
+ // initialization of the scoped_refptr. We do not also initialize
+ // |polymorphic_invoke_| here because doing a normal assignment in the
+ // derived Callback templates makes for much nicer compiler errors.
+ explicit CallbackBase(BindStateBase* bind_state);
+
+ // Force the destructor to be instantiated inside this translation unit so
+ // that our subclasses will not get inlined versions. Avoids more template
+ // bloat.
+ ~CallbackBase();
+
+ scoped_refptr<BindStateBase> bind_state_;
+ InvokeFuncStorage polymorphic_invoke_;
+};
+
+// This is a typetraits object that's used to take an argument type, and
+// extract a suitable type for storing and forwarding arguments.
+//
+// In particular, it strips off references, and converts arrays to
+// pointers for storage; and it avoids accidentally trying to create a
+// "reference of a reference" if the argument is a reference type.
+//
+// This array type becomes an issue for storage because we are passing bound
+// parameters by const reference. In this case, we end up passing an actual
+// array type in the initializer list which C++ does not allow. This will
+// break passing of C-string literals.
+template <typename T>
+struct CallbackParamTraits {
+ typedef const T& ForwardType;
+ typedef T StorageType;
+};
+
+// The Storage should almost be impossible to trigger unless someone manually
+// specifies type of the bind parameters. However, in case they do,
+// this will guard against us accidentally storing a reference parameter.
+//
+// The ForwardType should only be used for unbound arguments.
+template <typename T>
+struct CallbackParamTraits<T&> {
+ typedef T& ForwardType;
+ typedef T StorageType;
+};
+
+// Note that for array types, we implicitly add a const in the conversion. This
+// means that it is not possible to bind array arguments to functions that take
+// a non-const pointer. Trying to specialize the template based on a "const
+// T[n]" does not seem to match correctly, so we are stuck with this
+// restriction.
+template <typename T, size_t n>
+struct CallbackParamTraits<T[n]> {
+ typedef const T* ForwardType;
+ typedef const T* StorageType;
+};
+
+// See comment for CallbackParamTraits<T[n]>.
+template <typename T>
+struct CallbackParamTraits<T[]> {
+ typedef const T* ForwardType;
+ typedef const T* StorageType;
+};
+
+// Parameter traits for movable-but-not-copyable scopers.
+//
+// Callback<>/Bind() understands movable-but-not-copyable semantics where
+// the type cannot be copied but can still have its state destructively
+// transferred (aka. moved) to another instance of the same type by calling a
+// helper function. When used with Bind(), this signifies transferal of the
+// object's state to the target function.
+//
+// For these types, the ForwardType must not be a const reference, or a
+// reference. A const reference is inappropriate, and would break const
+// correctness, because we are implementing a destructive move. A non-const
+// reference cannot be used with temporaries which means the result of a
+// function or a cast would not be usable with Callback<> or Bind().
+//
+// TODO(ajwong): We might be able to use SFINAE to search for the existence of
+// a Pass() function in the type and avoid the whitelist in CallbackParamTraits
+// and CallbackForward.
+template <typename T>
+struct CallbackParamTraits<scoped_ptr<T> > {
+ typedef scoped_ptr<T> ForwardType;
+ typedef scoped_ptr<T> StorageType;
+};
+
+template <typename T>
+struct CallbackParamTraits<scoped_array<T> > {
+ typedef scoped_array<T> ForwardType;
+ typedef scoped_array<T> StorageType;
+};
+
+template <typename T, typename R>
+struct CallbackParamTraits<scoped_ptr_malloc<T, R> > {
+ typedef scoped_ptr_malloc<T, R> ForwardType;
+ typedef scoped_ptr_malloc<T, R> StorageType;
+};
+
+template <typename T>
+struct CallbackParamTraits<ScopedVector<T> > {
+ typedef ScopedVector<T> ForwardType;
+ typedef ScopedVector<T> StorageType;
+};
+
+// CallbackForward() is a very limited simulation of C++11's std::forward()
+// used by the Callback/Bind system for a set of movable-but-not-copyable
+// types. It is needed because forwarding a movable-but-not-copyable
+// argument to another function requires us to invoke the proper move
+// operator to create a rvalue version of the type. The supported types are
+// whitelisted below as overloads of the CallbackForward() function. The
+// default template compiles out to be a no-op.
+//
+// In C++11, std::forward would replace all uses of this function. However, it
+// is impossible to implement a general std::forward with C++11 due to a lack
+// of rvalue references.
+//
+// In addition to Callback/Bind, this is used by PostTaskAndReplyWithResult to
+// simulate std::forward() and forward the result of one Callback as a
+// parameter to another callback. This is to support Callbacks that return
+// the movable-but-not-copyable types whitelisted above.
+template <typename T>
+T& CallbackForward(T& t) { return t; }
+
+template <typename T>
+scoped_ptr<T> CallbackForward(scoped_ptr<T>& p) { return p.Pass(); }
+
+template <typename T>
+scoped_array<T> CallbackForward(scoped_array<T>& p) { return p.Pass(); }
+
+template <typename T, typename R>
+scoped_ptr_malloc<T, R> CallbackForward(scoped_ptr_malloc<T, R>& p) {
+ return p.Pass();
+}
+
+template <typename T>
+ScopedVector<T> CallbackForward(ScopedVector<T>& p) { return p.Pass(); }
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_CALLBACK_INTERNAL_H_
diff --git a/src/base/callback_unittest.cc b/src/base/callback_unittest.cc
new file mode 100644
index 0000000..2c2bef1
--- /dev/null
+++ b/src/base/callback_unittest.cc
@@ -0,0 +1,181 @@
+// Copyright (c) 2012 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/bind.h"
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/callback_internal.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+struct FakeInvoker {
+ typedef void(RunType)(internal::BindStateBase*);
+ static void Run(internal::BindStateBase*) {
+ }
+};
+
+} // namespace
+
+namespace internal {
+template <typename Runnable, typename RunType, typename BoundArgsType>
+struct BindState;
+
+// White-box testpoints to inject into a Callback<> object for checking
+// comparators and emptiness APIs. Use a BindState that is specialized
+// based on a type we declared in the anonymous namespace above to remove any
+// chance of colliding with another instantiation and breaking the
+// one-definition-rule.
+template <>
+struct BindState<void(void), void(void), void(FakeInvoker)>
+ : public BindStateBase {
+ public:
+ typedef FakeInvoker InvokerType;
+};
+
+template <>
+struct BindState<void(void), void(void),
+ void(FakeInvoker, FakeInvoker)>
+ : public BindStateBase {
+ public:
+ typedef FakeInvoker InvokerType;
+};
+} // namespace internal
+
+namespace {
+
+typedef internal::BindState<void(void), void(void), void(FakeInvoker)>
+ FakeBindState1;
+typedef internal::BindState<void(void), void(void),
+ void(FakeInvoker, FakeInvoker)>
+ FakeBindState2;
+
+class CallbackTest : public ::testing::Test {
+ public:
+ CallbackTest()
+ : callback_a_(new FakeBindState1()),
+ callback_b_(new FakeBindState2()) {
+ }
+
+ virtual ~CallbackTest() {
+ }
+
+ protected:
+ Callback<void(void)> callback_a_;
+ const Callback<void(void)> callback_b_; // Ensure APIs work with const.
+ Callback<void(void)> null_callback_;
+};
+
+// Ensure we can create unbound callbacks. We need this to be able to store
+// them in class members that can be initialized later.
+TEST_F(CallbackTest, DefaultConstruction) {
+ Callback<void(void)> c0;
+ Callback<void(int)> c1;
+ Callback<void(int,int)> c2;
+ Callback<void(int,int,int)> c3;
+ Callback<void(int,int,int,int)> c4;
+ Callback<void(int,int,int,int,int)> c5;
+ Callback<void(int,int,int,int,int,int)> c6;
+
+ EXPECT_TRUE(c0.is_null());
+ EXPECT_TRUE(c1.is_null());
+ EXPECT_TRUE(c2.is_null());
+ EXPECT_TRUE(c3.is_null());
+ EXPECT_TRUE(c4.is_null());
+ EXPECT_TRUE(c5.is_null());
+ EXPECT_TRUE(c6.is_null());
+}
+
+TEST_F(CallbackTest, IsNull) {
+ EXPECT_TRUE(null_callback_.is_null());
+ EXPECT_FALSE(callback_a_.is_null());
+ EXPECT_FALSE(callback_b_.is_null());
+}
+
+TEST_F(CallbackTest, Equals) {
+ EXPECT_TRUE(callback_a_.Equals(callback_a_));
+ EXPECT_FALSE(callback_a_.Equals(callback_b_));
+ EXPECT_FALSE(callback_b_.Equals(callback_a_));
+
+ // We should compare based on instance, not type.
+ Callback<void(void)> callback_c(new FakeBindState1());
+ Callback<void(void)> callback_a2 = callback_a_;
+ EXPECT_TRUE(callback_a_.Equals(callback_a2));
+ EXPECT_FALSE(callback_a_.Equals(callback_c));
+
+ // Empty, however, is always equal to empty.
+ Callback<void(void)> empty2;
+ EXPECT_TRUE(null_callback_.Equals(empty2));
+}
+
+TEST_F(CallbackTest, Reset) {
+ // Resetting should bring us back to empty.
+ ASSERT_FALSE(callback_a_.is_null());
+ ASSERT_FALSE(callback_a_.Equals(null_callback_));
+
+ callback_a_.Reset();
+
+ EXPECT_TRUE(callback_a_.is_null());
+ EXPECT_TRUE(callback_a_.Equals(null_callback_));
+}
+
+struct TestForReentrancy {
+ TestForReentrancy()
+ : cb_already_run(false),
+ cb(Bind(&TestForReentrancy::AssertCBIsNull, Unretained(this))) {
+ }
+ void AssertCBIsNull() {
+ ASSERT_TRUE(cb.is_null());
+ cb_already_run = true;
+ }
+ bool cb_already_run;
+ Closure cb;
+};
+
+TEST_F(CallbackTest, ResetAndReturn) {
+ TestForReentrancy tfr;
+ ASSERT_FALSE(tfr.cb.is_null());
+ ASSERT_FALSE(tfr.cb_already_run);
+ ResetAndReturn(&tfr.cb).Run();
+ ASSERT_TRUE(tfr.cb.is_null());
+ ASSERT_TRUE(tfr.cb_already_run);
+}
+
+class CallbackOwner : public base::RefCounted<CallbackOwner> {
+ public:
+ CallbackOwner(bool* deleted) {
+ callback_ = Bind(&CallbackOwner::Unused, this);
+ deleted_ = deleted;
+ }
+ void Reset() {
+ callback_.Reset();
+ // We are deleted here if no-one else had a ref to us.
+ }
+
+ private:
+ friend class base::RefCounted<CallbackOwner>;
+ virtual ~CallbackOwner() {
+ *deleted_ = true;
+ }
+ void Unused() {
+ FAIL() << "Should never be called";
+ }
+
+ Closure callback_;
+ bool* deleted_;
+};
+
+TEST_F(CallbackTest, CallbackHasLastRefOnContainingObject) {
+ bool deleted = false;
+ CallbackOwner* owner = new CallbackOwner(&deleted);
+ owner->Reset();
+ ASSERT_TRUE(deleted);
+}
+
+} // namespace
+} // namespace base
diff --git a/src/base/callback_unittest.nc b/src/base/callback_unittest.nc
new file mode 100644
index 0000000..9bddd1f
--- /dev/null
+++ b/src/base/callback_unittest.nc
@@ -0,0 +1,50 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/callback.h"
+
+namespace base {
+
+class Parent {
+};
+
+class Child : Parent {
+};
+
+#if defined(NCTEST_EQUALS_REQUIRES_SAMETYPE) // [r"no matching function for call to 'base::Callback<void\(\)>::Equals\(base::Callback<int\(\)>&\)'"]
+
+// Attempting to call comparison function on two callbacks of different type.
+//
+// This should be a compile time failure because each callback type should be
+// considered distinct.
+void WontCompile() {
+ Closure c1;
+ Callback<int(void)> c2;
+ c1.Equals(c2);
+}
+
+#elif defined(NCTEST_CONSTRUCTION_FROM_SUBTYPE) // [r"conversion from 'base::Callback<base::Parent\(\)>' to non-scalar type 'base::Callback<base::Child\(\)>'"]
+
+// Construction of Callback<A> from Callback<B> if A is supertype of B.
+//
+// While this is technically safe, most people aren't used to it when coding
+// C++ so if this is happening, it is almost certainly an error.
+void WontCompile() {
+ Callback<Parent(void)> cb_a;
+ Callback<Child(void)> cb_b = cb_a;
+}
+
+#elif defined(NCTEST_ASSIGNMENT_FROM_SUBTYPE) // [r"no match for 'operator=' in 'cb_a = cb_b'"]
+
+// Assignment of Callback<A> from Callback<B> if A is supertype of B.
+// See explanation for NCTEST_CONSTRUCTION_FROM_SUBTYPE
+void WontCompile() {
+ Callback<Parent(void)> cb_a;
+ Callback<Child(void)> cb_b;
+ cb_a = cb_b;
+}
+
+#endif
+
+} // namespace base
diff --git a/src/base/cancelable_callback.h b/src/base/cancelable_callback.h
new file mode 100644
index 0000000..b781677
--- /dev/null
+++ b/src/base/cancelable_callback.h
@@ -0,0 +1,202 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// CancelableCallback is a wrapper around base::Callback that allows
+// cancellation of a callback. CancelableCallback takes a reference on the
+// wrapped callback until this object is destroyed or Reset()/Cancel() are
+// called.
+//
+// NOTE:
+//
+// Calling CancellableCallback::Cancel() brings the object back to its natural,
+// default-constructed state, i.e., CancellableCallback::callback() will return
+// a null callback.
+//
+// THREAD-SAFETY:
+//
+// CancelableCallback objects must be created on, posted to, cancelled on, and
+// destroyed on the same thread.
+//
+//
+// EXAMPLE USAGE:
+//
+// In the following example, the test is verifying that RunIntensiveTest()
+// Quit()s the message loop within 4 seconds. The cancelable callback is posted
+// to the message loop, the intensive test runs, the message loop is run,
+// then the callback is cancelled.
+//
+// void TimeoutCallback(const std::string& timeout_message) {
+// FAIL() << timeout_message;
+// MessageLoop::current()->Quit();
+// }
+//
+// CancelableClosure timeout(base::Bind(&TimeoutCallback, "Test timed out."));
+// MessageLoop::current()->PostDelayedTask(FROM_HERE, timeout.callback(),
+// 4000) // 4 seconds to run.
+// RunIntensiveTest();
+// MessageLoop::current()->Run();
+// timeout.Cancel(); // Hopefully this is hit before the timeout callback runs.
+//
+
+#ifndef BASE_CANCELABLE_CALLBACK_H_
+#define BASE_CANCELABLE_CALLBACK_H_
+
+#include "base/base_export.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/callback_internal.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/memory/weak_ptr.h"
+
+namespace base {
+
+template <typename Sig>
+class CancelableCallback;
+
+template <>
+class CancelableCallback<void(void)> {
+ public:
+ CancelableCallback() : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {}
+
+ // |callback| must not be null.
+ explicit CancelableCallback(const base::Callback<void(void)>& callback)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
+ callback_(callback) {
+ DCHECK(!callback.is_null());
+ InitializeForwarder();
+ }
+
+ ~CancelableCallback() {}
+
+ // Cancels and drops the reference to the wrapped callback.
+ void Cancel() {
+ weak_factory_.InvalidateWeakPtrs();
+ forwarder_.Reset();
+ callback_.Reset();
+ }
+
+ // Returns true if the wrapped callback has been cancelled.
+ bool IsCancelled() const {
+ return callback_.is_null();
+ }
+
+ // Sets |callback| as the closure that may be cancelled. |callback| may not
+ // be null. Outstanding and any previously wrapped callbacks are cancelled.
+ void Reset(const base::Callback<void(void)>& callback) {
+ DCHECK(!callback.is_null());
+
+ // Outstanding tasks (e.g., posted to a message loop) must not be called.
+ Cancel();
+
+ // |forwarder_| is no longer valid after Cancel(), so re-bind.
+ InitializeForwarder();
+
+ callback_ = callback;
+ }
+
+ // Returns a callback that can be disabled by calling Cancel().
+ const base::Callback<void(void)>& callback() const {
+ return forwarder_;
+ }
+
+ private:
+ void Forward() {
+ callback_.Run();
+ }
+
+ // Helper method to bind |forwarder_| using a weak pointer from
+ // |weak_factory_|.
+ void InitializeForwarder() {
+ forwarder_ = base::Bind(&CancelableCallback<void(void)>::Forward,
+ weak_factory_.GetWeakPtr());
+ }
+
+ // Used to ensure Forward() is not run when this object is destroyed.
+ base::WeakPtrFactory<CancelableCallback<void(void)> > weak_factory_;
+
+ // The wrapper closure.
+ base::Callback<void(void)> forwarder_;
+
+ // The stored closure that may be cancelled.
+ base::Callback<void(void)> callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(CancelableCallback);
+};
+
+template <typename A1>
+class CancelableCallback<void(A1)> {
+ public:
+ CancelableCallback() : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {}
+
+ // |callback| must not be null.
+ explicit CancelableCallback(const base::Callback<void(A1)>& callback)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
+ callback_(callback) {
+ DCHECK(!callback.is_null());
+ InitializeForwarder();
+ }
+
+ ~CancelableCallback() {}
+
+ // Cancels and drops the reference to the wrapped callback.
+ void Cancel() {
+ weak_factory_.InvalidateWeakPtrs();
+ forwarder_.Reset();
+ callback_.Reset();
+ }
+
+ // Returns true if the wrapped callback has been cancelled.
+ bool IsCancelled() const {
+ return callback_.is_null();
+ }
+
+ // Sets |callback| as the closure that may be cancelled. |callback| may not
+ // be null. Outstanding and any previously wrapped callbacks are cancelled.
+ void Reset(const base::Callback<void(A1)>& callback) {
+ DCHECK(!callback.is_null());
+
+ // Outstanding tasks (e.g., posted to a message loop) must not be called.
+ Cancel();
+
+ // |forwarder_| is no longer valid after Cancel(), so re-bind.
+ InitializeForwarder();
+
+ callback_ = callback;
+ }
+
+ // Returns a callback that can be disabled by calling Cancel().
+ const base::Callback<void(A1)>& callback() const {
+ return forwarder_;
+ }
+
+ private:
+ void Forward(A1 a1) const {
+ callback_.Run(a1);
+ }
+
+ // Helper method to bind |forwarder_| using a weak pointer from
+ // |weak_factory_|.
+ void InitializeForwarder() {
+ forwarder_ = base::Bind(&CancelableCallback<void(A1)>::Forward,
+ weak_factory_.GetWeakPtr());
+ }
+
+ // Used to ensure Forward() is not run when this object is destroyed.
+ base::WeakPtrFactory<CancelableCallback<void(A1)> > weak_factory_;
+
+ // The wrapper closure.
+ base::Callback<void(A1)> forwarder_;
+
+ // The stored closure that may be cancelled.
+ base::Callback<void(A1)> callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(CancelableCallback);
+};
+
+typedef CancelableCallback<void(void)> CancelableClosure;
+
+} // namespace base
+
+#endif // BASE_CANCELABLE_CALLBACK_H_
diff --git a/src/base/cancelable_callback_unittest.cc b/src/base/cancelable_callback_unittest.cc
new file mode 100644
index 0000000..51a2a09
--- /dev/null
+++ b/src/base/cancelable_callback_unittest.cc
@@ -0,0 +1,185 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/cancelable_callback.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+class TestRefCounted : public RefCountedThreadSafe<TestRefCounted> {
+ private:
+ friend class RefCountedThreadSafe<TestRefCounted>;
+ ~TestRefCounted() {};
+};
+
+void Increment(int* count) { (*count)++; }
+void IncrementBy(int* count, int n) { (*count) += n; }
+void RefCountedParam(const scoped_refptr<TestRefCounted>& ref_counted) {}
+
+// Cancel().
+// - Callback can be run multiple times.
+// - After Cancel(), Run() completes but has no effect.
+TEST(CancelableCallbackTest, Cancel) {
+ int count = 0;
+ CancelableClosure cancelable(
+ base::Bind(&Increment, base::Unretained(&count)));
+
+ base::Closure callback = cancelable.callback();
+ callback.Run();
+ EXPECT_EQ(1, count);
+
+ callback.Run();
+ EXPECT_EQ(2, count);
+
+ cancelable.Cancel();
+ callback.Run();
+ EXPECT_EQ(2, count);
+}
+
+// Cancel() called multiple times.
+// - Cancel() cancels all copies of the wrapped callback.
+// - Calling Cancel() more than once has no effect.
+// - After Cancel(), callback() returns a null callback.
+TEST(CancelableCallbackTest, MultipleCancel) {
+ int count = 0;
+ CancelableClosure cancelable(
+ base::Bind(&Increment, base::Unretained(&count)));
+
+ base::Closure callback1 = cancelable.callback();
+ base::Closure callback2 = cancelable.callback();
+ cancelable.Cancel();
+
+ callback1.Run();
+ EXPECT_EQ(0, count);
+
+ callback2.Run();
+ EXPECT_EQ(0, count);
+
+ // Calling Cancel() again has no effect.
+ cancelable.Cancel();
+
+ // callback() of a cancelled callback is null.
+ base::Closure callback3 = cancelable.callback();
+ EXPECT_TRUE(callback3.is_null());
+}
+
+// CancelableCallback destroyed before callback is run.
+// - Destruction of CancelableCallback cancels outstanding callbacks.
+TEST(CancelableCallbackTest, CallbackCanceledOnDestruction) {
+ int count = 0;
+ base::Closure callback;
+
+ {
+ CancelableClosure cancelable(
+ base::Bind(&Increment, base::Unretained(&count)));
+
+ callback = cancelable.callback();
+ callback.Run();
+ EXPECT_EQ(1, count);
+ }
+
+ callback.Run();
+ EXPECT_EQ(1, count);
+}
+
+// Cancel() called on bound closure with a RefCounted parameter.
+// - Cancel drops wrapped callback (and, implicitly, its bound arguments).
+TEST(CancelableCallbackTest, CancelDropsCallback) {
+ scoped_refptr<TestRefCounted> ref_counted = new TestRefCounted;
+ EXPECT_TRUE(ref_counted->HasOneRef());
+
+ CancelableClosure cancelable(base::Bind(RefCountedParam, ref_counted));
+ EXPECT_FALSE(cancelable.IsCancelled());
+ EXPECT_TRUE(ref_counted.get());
+ EXPECT_FALSE(ref_counted->HasOneRef());
+
+ // There is only one reference to |ref_counted| after the Cancel().
+ cancelable.Cancel();
+ EXPECT_TRUE(cancelable.IsCancelled());
+ EXPECT_TRUE(ref_counted.get());
+ EXPECT_TRUE(ref_counted->HasOneRef());
+}
+
+// Reset().
+// - Reset() replaces the existing wrapped callback with a new callback.
+// - Reset() deactivates outstanding callbacks.
+TEST(CancelableCallbackTest, Reset) {
+ int count = 0;
+ CancelableClosure cancelable(
+ base::Bind(&Increment, base::Unretained(&count)));
+
+ base::Closure callback = cancelable.callback();
+ callback.Run();
+ EXPECT_EQ(1, count);
+
+ callback.Run();
+ EXPECT_EQ(2, count);
+
+ cancelable.Reset(
+ base::Bind(&IncrementBy, base::Unretained(&count), 3));
+ EXPECT_FALSE(cancelable.IsCancelled());
+
+ // The stale copy of the cancelable callback is non-null.
+ ASSERT_FALSE(callback.is_null());
+
+ // The stale copy of the cancelable callback is no longer active.
+ callback.Run();
+ EXPECT_EQ(2, count);
+
+ base::Closure callback2 = cancelable.callback();
+ ASSERT_FALSE(callback2.is_null());
+
+ callback2.Run();
+ EXPECT_EQ(5, count);
+}
+
+// IsCanceled().
+// - Cancel() transforms the CancelableCallback into a cancelled state.
+TEST(CancelableCallbackTest, IsNull) {
+ CancelableClosure cancelable;
+ EXPECT_TRUE(cancelable.IsCancelled());
+
+ int count = 0;
+ cancelable.Reset(base::Bind(&Increment,
+ base::Unretained(&count)));
+ EXPECT_FALSE(cancelable.IsCancelled());
+
+ cancelable.Cancel();
+ EXPECT_TRUE(cancelable.IsCancelled());
+}
+
+// CancelableCallback posted to a MessageLoop with PostTask.
+// - Callbacks posted to a MessageLoop can be cancelled.
+TEST(CancelableCallbackTest, PostTask) {
+ MessageLoop loop(MessageLoop::TYPE_DEFAULT);
+
+ int count = 0;
+ CancelableClosure cancelable(base::Bind(&Increment,
+ base::Unretained(&count)));
+
+ MessageLoop::current()->PostTask(FROM_HERE, cancelable.callback());
+ MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure());
+ MessageLoop::current()->Run();
+
+ EXPECT_EQ(1, count);
+
+ MessageLoop::current()->PostTask(FROM_HERE, cancelable.callback());
+ MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure());
+
+ // Cancel before running the message loop.
+ cancelable.Cancel();
+ MessageLoop::current()->Run();
+
+ // Callback never ran due to cancellation; count is the same.
+ EXPECT_EQ(1, count);
+}
+
+} // namespace
+} // namespace base
diff --git a/src/base/check_example.cc b/src/base/check_example.cc
new file mode 100644
index 0000000..4b3f428
--- /dev/null
+++ b/src/base/check_example.cc
@@ -0,0 +1,25 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is meant for analyzing the code generated by the CHECK
+// macros in a small executable file that's easy to disassemble.
+
+#include "base/logging.h"
+
+// An official build shouldn't generate code to print out messages for
+// the CHECK* macros, nor should it have the strings in the
+// executable.
+
+void DoCheck(bool b) {
+ CHECK(b) << "DoCheck " << b;
+}
+
+void DoCheckEq(int x, int y) {
+ CHECK_EQ(x, y);
+}
+
+int main(int argc, const char* argv[]) {
+ DoCheck(argc > 1);
+ DoCheckEq(argc, 1);
+}
diff --git a/src/base/chromeos/chromeos_version.cc b/src/base/chromeos/chromeos_version.cc
new file mode 100644
index 0000000..4a70cd5
--- /dev/null
+++ b/src/base/chromeos/chromeos_version.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 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/chromeos/chromeos_version.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "base/logging.h"
+
+namespace base {
+namespace chromeos {
+
+bool IsRunningOnChromeOS() {
+ // Check if the user name is chronos. Note that we don't go with
+ // getuid() + getpwuid_r() as it may end up reading /etc/passwd, which
+ // can be expensive.
+ const char* user = getenv("USER");
+ return user && strcmp(user, "chronos") == 0;
+}
+
+} // namespace chromeos
+} // namespace base
diff --git a/src/base/chromeos/chromeos_version.h b/src/base/chromeos/chromeos_version.h
new file mode 100644
index 0000000..25acd43
--- /dev/null
+++ b/src/base/chromeos/chromeos_version.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 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_CHROMEOS_CHROMEOS_VERSION_H_
+#define BASE_CHROMEOS_CHROMEOS_VERSION_H_
+
+#include "base/base_export.h"
+
+namespace base {
+namespace chromeos {
+
+// Returns true if the browser is running on Chrome OS.
+// Useful for implementing stubs for Linux desktop.
+BASE_EXPORT bool IsRunningOnChromeOS();
+
+} // namespace chromeos
+} // namespace base
+
+#endif // BASE_CHROMEOS_CHROMEOS_VERSION_H_
diff --git a/src/base/circular_buffer_shell.cc b/src/base/circular_buffer_shell.cc
new file mode 100644
index 0000000..a41d89a
--- /dev/null
+++ b/src/base/circular_buffer_shell.cc
@@ -0,0 +1,251 @@
+// Copyright (c) 2014 Google Inc. 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/circular_buffer_shell.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "build/build_config.h"
+
+#if defined(OS_STARBOARD)
+#include "starboard/memory.h"
+#define malloc SbMemoryAllocate
+#define realloc SbMemoryReallocate
+#define free SbMemoryFree
+#endif
+
+static inline void* add_to_pointer(void* pointer, size_t amount) {
+ return static_cast<uint8_t*>(pointer) + amount;
+}
+
+static inline const void* add_to_pointer(const void* pointer, size_t amount) {
+ return static_cast<const uint8_t*>(pointer) + amount;
+}
+
+namespace base {
+
+CircularBufferShell::CircularBufferShell(
+ size_t max_capacity,
+ ReserveType reserve_type /*= kDoNotReserve*/)
+ : max_capacity_(max_capacity),
+ buffer_(NULL),
+ capacity_(0),
+ length_(0),
+ read_position_(0) {
+ if (reserve_type == kReserve) {
+ IncreaseCapacityTo(max_capacity_);
+ }
+}
+
+CircularBufferShell::~CircularBufferShell() {
+ Clear();
+}
+
+void CircularBufferShell::Clear() {
+ base::AutoLock l(lock_);
+ if (buffer_ != NULL) {
+ free(buffer_);
+ buffer_ = NULL;
+ }
+
+ capacity_ = 0;
+ length_ = 0;
+ read_position_ = 0;
+}
+
+void CircularBufferShell::Read(void* destination,
+ size_t length,
+ size_t* bytes_read) {
+ base::AutoLock l(lock_);
+ DCHECK(destination != NULL || length == 0);
+ if (destination == NULL)
+ length = 0;
+
+ ReadAndAdvanceUnchecked(destination, length, bytes_read);
+}
+
+void CircularBufferShell::Peek(void* destination,
+ size_t length,
+ size_t source_offset,
+ size_t* bytes_peeked) const {
+ base::AutoLock l(lock_);
+ DCHECK(destination != NULL || length == 0);
+ if (destination == NULL)
+ length = 0;
+
+ ReadUnchecked(destination, length, source_offset, bytes_peeked);
+}
+
+void CircularBufferShell::Skip(size_t length, size_t* bytes_skipped) {
+ base::AutoLock l(lock_);
+ ReadAndAdvanceUnchecked(NULL, length, bytes_skipped);
+}
+
+bool CircularBufferShell::Write(const void* source,
+ size_t length,
+ size_t* bytes_written) {
+ base::AutoLock l(lock_);
+ DCHECK(source != NULL || length == 0);
+ if (source == NULL)
+ length = 0;
+
+ if (!EnsureCapacityToWrite(length)) {
+ return false;
+ }
+
+ size_t produced = 0;
+ while (true) {
+ size_t remaining = length - produced;
+
+ // In this pass, write up to the contiguous space left.
+ size_t to_write = std::min(remaining, capacity_ - GetWritePosition());
+ if (to_write == 0)
+ break;
+
+ // Copy this segment and do the accounting.
+ void* destination = GetWritePointer();
+ const void* src = add_to_pointer(source, produced);
+ memcpy(destination, src, to_write);
+ length_ += to_write;
+ produced += to_write;
+ }
+
+ if (bytes_written)
+ *bytes_written = produced;
+ return true;
+}
+
+size_t CircularBufferShell::GetLength() const {
+ base::AutoLock l(lock_);
+ return length_;
+}
+
+void CircularBufferShell::ReadUnchecked(void* destination,
+ size_t destination_length,
+ size_t source_offset,
+ size_t* bytes_read) const {
+ DCHECK(destination != NULL || bytes_read != NULL);
+
+ size_t dummy = 0;
+ if (!bytes_read) {
+ bytes_read = &dummy;
+ }
+
+ // Return immediately if the CircularBuffer is empty or if |source_offset| is
+ // greater or equal than |length_|.
+ if (capacity_ == 0 || source_offset >= length_) {
+ *bytes_read = 0;
+ return;
+ }
+
+ size_t consumed = 0;
+ size_t source_length = length_ - source_offset;
+ size_t read_position = (read_position_ + source_offset) % capacity_;
+
+ while (true) {
+ size_t remaining = std::min(source_length, destination_length - consumed);
+
+ // In this pass, read the remaining data that is contiguous.
+ size_t to_read = std::min(remaining, capacity_ - read_position);
+ if (to_read == 0)
+ break;
+
+ // Copy this segment and do the accounting.
+ const void* source = add_to_pointer(buffer_, read_position);
+ if (destination) {
+ void* dest = add_to_pointer(destination, consumed);
+ memcpy(dest, source, to_read);
+ }
+ source_length -= to_read;
+ read_position = (read_position + to_read) % capacity_;
+ consumed += to_read;
+ }
+
+ *bytes_read = consumed;
+}
+
+void CircularBufferShell::ReadAndAdvanceUnchecked(void* destination,
+ size_t destination_length,
+ size_t* bytes_read) {
+ size_t dummy = 0;
+ if (!bytes_read) {
+ bytes_read = &dummy;
+ }
+
+ // Return immediately if the CircularBuffer is empty.
+ if (capacity_ == 0) {
+ *bytes_read = 0;
+ return;
+ }
+
+ ReadUnchecked(destination, destination_length, 0, bytes_read);
+ length_ -= *bytes_read;
+ read_position_ = (read_position_ + *bytes_read) % capacity_;
+}
+
+void* CircularBufferShell::GetWritePointer() const {
+ return add_to_pointer(buffer_, GetWritePosition());
+}
+
+size_t CircularBufferShell::GetWritePosition() const {
+ return (read_position_ + length_) % capacity_;
+}
+
+bool CircularBufferShell::EnsureCapacityToWrite(size_t length) {
+ if (capacity_ - length_ < length) {
+ size_t capacity = std::max(2 * capacity_, length_ + length);
+ if (capacity > max_capacity_)
+ capacity = max_capacity_;
+
+ // New capacity still won't be enough.
+ if (capacity - length_ < length) {
+ return false;
+ }
+
+ return IncreaseCapacityTo(capacity);
+ }
+
+ return true;
+}
+
+bool CircularBufferShell::IncreaseCapacityTo(size_t capacity) {
+ if (capacity <= capacity_) {
+ return true;
+ }
+
+ // If the data isn't wrapped, we can just use realloc.
+ if (buffer_ != NULL && read_position_ + length_ <= capacity_) {
+ void* result = realloc(buffer_, capacity);
+ if (result == NULL) {
+ return false;
+ }
+ capacity_ = capacity;
+ buffer_ = result;
+ return true;
+ }
+
+ void* buffer = malloc(capacity);
+ if (buffer == NULL) {
+ return false;
+ }
+
+ // Read does everything we want, but it will trounce length_.
+ size_t length = length_;
+
+ // Copy the data over to the new buffer.
+ ReadUnchecked(buffer, length_, 0, NULL);
+
+ // Adjust the accounting.
+ length_ = length;
+ read_position_ = 0;
+ capacity_ = capacity;
+ free(buffer_);
+ buffer_ = buffer;
+ return true;
+}
+
+} // namespace base
diff --git a/src/base/circular_buffer_shell.h b/src/base/circular_buffer_shell.h
new file mode 100644
index 0000000..4350af1
--- /dev/null
+++ b/src/base/circular_buffer_shell.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2014 Google Inc. 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_CIRCULAR_BUFFER_SHELL_H_
+#define BASE_CIRCULAR_BUFFER_SHELL_H_
+
+#include "base/base_export.h"
+#include "base/synchronization/lock.h"
+
+namespace base {
+
+// A thread-safe circular buffer implementation.
+// TODO: Returns the size in Read(), Peek(), and Skip() as a return
+// value.
+class BASE_EXPORT CircularBufferShell {
+ public:
+ enum ReserveType { kDoNotReserve, kReserve };
+
+ CircularBufferShell(size_t max_capacity,
+ ReserveType reserve_type = kDoNotReserve);
+ ~CircularBufferShell();
+
+ // Clears out all data in the buffer, releasing any allocated memory.
+ // Idempotent.
+ void Clear();
+
+ // Reads the requested amount of data into the given buffer, writing the
+ // number of bytes actually read into the bytes_read paramter. If there is
+ // less data then requested, then the remaining data will be consumed.
+ void Read(void* destination, size_t length, size_t* bytes_read);
+
+ // It works the same as Read() except:
+ // 1. It doesn't modify the buffer in any way.
+ // 2. It allows the caller to specify an offset inside the buffer where the
+ // peek starts.
+ void Peek(void* destination,
+ size_t length,
+ size_t source_offset,
+ size_t* bytes_peeked) const;
+
+ // Advance the buffer cursor without reading any data.
+ void Skip(size_t length, size_t* bytes_skipped);
+
+ // Writes the given data into the circular buffer. Returns false if the buffer
+ // could not be expanded to hold the new data. If returning false,
+ // bytes_written will not be set, and the buffer will remain unchanged.
+ // TODO: Remove bytes_written. Because Write returns false when
+ // the buffer cannot hold all data, bytes_written isn't useful here unless we
+ // allow partial write.
+ bool Write(const void* source, size_t length, size_t* bytes_written);
+
+ // Returns the length of the data left in the buffer to read.
+ size_t GetLength() const;
+
+ private:
+ // Ensures that there is enough capacity to write length bytes to the
+ // buffer. Returns false if it was unable to ensure that capacity due to an
+ // allocation error, or if it would surpass the configured maximum capacity.
+ bool EnsureCapacityToWrite(size_t length);
+
+ // Increases the capacity to the given absolute size in bytes. Returns false
+ // if there was an allocation error, or it would surpass the configured
+ // maximum capacity.
+ bool IncreaseCapacityTo(size_t capacity);
+
+ // Private workhorse for Read without the parameter validation or locking.
+ // When |destination| is NULL, it purely calculates the the bytes that would
+ // have been read.
+ // Note that the function doesn't advance the read cursor or modify the
+ // length. It is caller's responsibility to adjust |read_position_| and
+ // |length_| according to the return value, which is the actual number of
+ // bytes read.
+ void ReadUnchecked(void* destination,
+ size_t destination_length,
+ size_t source_offset,
+ size_t* bytes_read) const;
+
+ // The same the as above functions except that it also advance the
+ // |read_position_| and adjust the |length_| accordingly.
+ void ReadAndAdvanceUnchecked(void* destination,
+ size_t destination_length,
+ size_t* bytes_read);
+
+ // Gets a pointer to the current write position.
+ void* GetWritePointer() const;
+
+ // Gets the current write position.
+ size_t GetWritePosition() const;
+
+ const size_t max_capacity_;
+ void* buffer_;
+ size_t capacity_;
+ size_t length_;
+ size_t read_position_;
+ mutable base::Lock lock_;
+};
+
+} // namespace base
+
+#endif // BASE_CIRCULAR_BUFFER_SHELL_H_
diff --git a/src/base/circular_buffer_shell_unittest.cc b/src/base/circular_buffer_shell_unittest.cc
new file mode 100644
index 0000000..0d4aa1c
--- /dev/null
+++ b/src/base/circular_buffer_shell_unittest.cc
@@ -0,0 +1,454 @@
+/*
+ * Copyright 2014 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/circular_buffer_shell.h"
+
+#include <string.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// 100 characters, repeating every 16 characters.
+const char kTestData[] =
+ "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"
+ "01234567890ABCDEF0123456789ABCDEF0123";
+
+// 100 characters, repeating every 17 characters.
+#define UNSET_DATA \
+ "GHIJKLMNOPQRSTUVWGHIJKLMNOPQRSTUVWGHIJKLMNOPQRSTUVW" \
+ "GHIJKLMNOPQRSTUVWGHIJKLMNOPQRSTUVWGHIJKLMNOPQRSTU"
+
+const char kUnsetData[] = UNSET_DATA;
+const size_t kUnsetSize = 1024;
+
+// Like memcmp, but reports which index and values failed.
+void IsSame(const char* expected, const char* actual, size_t length) {
+ for (size_t i = 0; i < length; ++i) {
+ if (expected[i] != actual[i]) {
+ EXPECT_EQ(expected[i], actual[i]) << "at " << i;
+ return;
+ }
+ }
+}
+
+size_t read_pos = 0;
+size_t write_pos = 0;
+
+// If the test uses testWrite and TestRead, then it needs to call ClearPos to
+// avoid contamination from previous tests.
+void ClearPos() {
+ read_pos = 0;
+ write_pos = 0;
+}
+
+void TestWrite(base::CircularBufferShell* circular_buffer, size_t to_write) {
+ size_t before_length = circular_buffer->GetLength();
+ size_t bytes_written = kUnsetSize;
+ bool result =
+ circular_buffer->Write(kTestData + write_pos, to_write, &bytes_written);
+ EXPECT_EQ(true, result);
+ EXPECT_EQ(to_write, bytes_written);
+ EXPECT_EQ(before_length + to_write, circular_buffer->GetLength());
+ write_pos += to_write;
+}
+
+void TestRead(base::CircularBufferShell* circular_buffer, size_t to_read) {
+ size_t before_length = circular_buffer->GetLength();
+ char data[] = UNSET_DATA UNSET_DATA;
+ char* buffer = data + strlen(kUnsetData);
+ size_t bytes_read = kUnsetSize;
+ circular_buffer->Read(buffer, to_read, &bytes_read);
+ EXPECT_EQ(to_read, bytes_read);
+ EXPECT_EQ(before_length - to_read, circular_buffer->GetLength());
+ IsSame(kTestData + read_pos, buffer, to_read);
+ IsSame(kUnsetData + to_read, buffer + to_read, to_read);
+ IsSame(kUnsetData + strlen(kUnsetData) - to_read,
+ buffer - to_read, to_read);
+ read_pos += to_read;
+}
+
+} // namespace
+
+// --- Sunny Day Tests ---
+
+TEST(CircularBufferShellTest, Construct) {
+ ClearPos();
+ scoped_ptr<base::CircularBufferShell> circular_buffer(
+ new base::CircularBufferShell(20));
+}
+
+TEST(CircularBufferShellTest, SimpleWriteAndRead) {
+ ClearPos();
+ scoped_ptr<base::CircularBufferShell> circular_buffer(
+ new base::CircularBufferShell(20));
+
+ TestWrite(circular_buffer.get(), 15);
+ TestRead(circular_buffer.get(), 5);
+ TestRead(circular_buffer.get(), 4);
+ TestRead(circular_buffer.get(), 3);
+ TestRead(circular_buffer.get(), 2);
+ TestRead(circular_buffer.get(), 1);
+}
+
+TEST(CircularBufferShellTest, ReadWriteOverBoundary) {
+ ClearPos();
+ scoped_ptr<base::CircularBufferShell> circular_buffer(
+ new base::CircularBufferShell(20));
+
+ // Fill the buffer.
+ TestWrite(circular_buffer.get(), 20);
+
+ // Read half the data from the front.
+ TestRead(circular_buffer.get(), 10);
+
+ // Fill the back half, making the data wrap around the end.
+ TestWrite(circular_buffer.get(), 10);
+
+ // Read the whole thing, which should require two memcpys.
+ TestRead(circular_buffer.get(), 20);
+
+ // Fill the buffer, again around the end, should require two memcpys.
+ TestWrite(circular_buffer.get(), 20);
+
+ // Read the buffer to verify, should again require two memcpys.
+ TestRead(circular_buffer.get(), 20);
+}
+
+TEST(CircularBufferShellTest, ExpandWhileNotWrapped) {
+ ClearPos();
+ scoped_ptr<base::CircularBufferShell> circular_buffer(
+ new base::CircularBufferShell(20));
+
+ // Set the size with the first write.
+ TestWrite(circular_buffer.get(), 5);
+
+ // Expand with the second write.
+ TestWrite(circular_buffer.get(), 5);
+
+ // Read to verify the data is intact
+ TestRead(circular_buffer.get(), 10);
+}
+
+TEST(CircularBufferShellTest, ExpandWhileNotWrapped2) {
+ ClearPos();
+ scoped_ptr<base::CircularBufferShell> circular_buffer(
+ new base::CircularBufferShell(20));
+
+ // Set the size with the first write.
+ TestWrite(circular_buffer.get(), 5);
+
+ // Read a couple out so that the data doesn't start at the beginning of the
+ // buffer.
+ TestRead(circular_buffer.get(), 2);
+
+ // Expand with the second write.
+ TestWrite(circular_buffer.get(), 5);
+
+ // Read to verify the data is intact
+ TestRead(circular_buffer.get(), 8);
+}
+
+TEST(CircularBufferShellTest, ExpandWhileWrapped) {
+ ClearPos();
+ scoped_ptr<base::CircularBufferShell> circular_buffer(
+ new base::CircularBufferShell(20));
+
+ // Set the size with the first write.
+ TestWrite(circular_buffer.get(), 10);
+
+ // Read front half.
+ TestRead(circular_buffer.get(), 5);
+
+ // Wrap with second write
+ TestWrite(circular_buffer.get(), 5);
+
+ // Write again to expand while wrapped.
+ TestWrite(circular_buffer.get(), 5);
+
+ // Read to verify the data is intact
+ TestRead(circular_buffer.get(), 15);
+}
+
+// --- Rainy Day Tests ---
+
+TEST(CircularBufferShellTest, WriteTooMuch) {
+ ClearPos();
+ scoped_ptr<base::CircularBufferShell> circular_buffer(
+ new base::CircularBufferShell(20));
+
+ {
+ size_t bytes_written = kUnsetSize;
+ bool result = circular_buffer->Write(kTestData, 25, &bytes_written);
+ EXPECT_EQ(false, result);
+ EXPECT_EQ(kUnsetSize, bytes_written);
+ EXPECT_EQ(0, circular_buffer->GetLength());
+ }
+}
+
+TEST(CircularBufferShellTest, ReadEmpty) {
+ ClearPos();
+ scoped_ptr<base::CircularBufferShell> circular_buffer(
+ new base::CircularBufferShell(20));
+
+ EXPECT_EQ(circular_buffer->GetLength(), 0);
+
+ {
+ char buffer[] = UNSET_DATA;
+ size_t bytes_read = kUnsetSize;
+ circular_buffer->Read(buffer, 0, &bytes_read);
+ EXPECT_EQ(0, bytes_read);
+ EXPECT_EQ(0, circular_buffer->GetLength());
+ IsSame(kUnsetData, buffer, 10);
+ }
+
+ {
+ char buffer[] = UNSET_DATA;
+ size_t bytes_read = kUnsetSize;
+ circular_buffer->Read(buffer, 10, &bytes_read);
+ EXPECT_EQ(0, bytes_read);
+ EXPECT_EQ(0, circular_buffer->GetLength());
+ IsSame(kUnsetData, buffer, 10);
+ }
+}
+
+TEST(CircularBufferShellTest, ReadToNull) {
+ ClearPos();
+ scoped_ptr<base::CircularBufferShell> circular_buffer(
+ new base::CircularBufferShell(20));
+
+ {
+ size_t bytes_read = kUnsetSize;
+ circular_buffer->Read(NULL, 0, &bytes_read);
+ EXPECT_EQ(0, bytes_read);
+ }
+}
+
+TEST(CircularBufferShellTest, Peek) {
+ const size_t kMaxCapacity = 20;
+ base::CircularBufferShell circular_buffer(kMaxCapacity);
+ size_t bytes_peeked;
+ size_t peek_offset = 0;
+
+ circular_buffer.Write(kTestData, kMaxCapacity, NULL);
+
+ // Peek with offset 0.
+ {
+ char destination[] = UNSET_DATA;
+ circular_buffer.Peek(destination + 9, 10, peek_offset, &bytes_peeked);
+
+ EXPECT_EQ(10, bytes_peeked);
+ IsSame(UNSET_DATA, destination, 9);
+ IsSame(UNSET_DATA + 9 + bytes_peeked,
+ destination + 9 + bytes_peeked,
+ sizeof(UNSET_DATA) - 9 - bytes_peeked);
+ IsSame(kTestData, destination + 9, 10);
+ peek_offset += bytes_peeked;
+ }
+
+ // Peek with non-zero offset.
+ {
+ char destination[] = UNSET_DATA;
+ circular_buffer.Peek(destination + 9, 7, peek_offset, &bytes_peeked);
+
+ EXPECT_EQ(7, bytes_peeked);
+ IsSame(UNSET_DATA, destination, 9);
+ IsSame(UNSET_DATA + 9 + bytes_peeked,
+ destination + 9 + bytes_peeked,
+ sizeof(UNSET_DATA) - 9 - bytes_peeked);
+ IsSame(kTestData + peek_offset, destination + 9, bytes_peeked);
+ peek_offset += bytes_peeked;
+ }
+
+ // Peek more data than available.
+ {
+ char destination[] = UNSET_DATA;
+ circular_buffer.Peek(destination + 9, 7, peek_offset, &bytes_peeked);
+
+ EXPECT_EQ(3, bytes_peeked);
+ IsSame(UNSET_DATA, destination, 9);
+ IsSame(UNSET_DATA + 9 + bytes_peeked,
+ destination + 9 + bytes_peeked,
+ sizeof(UNSET_DATA) - 9 - bytes_peeked);
+ IsSame(kTestData + peek_offset, destination + 9, bytes_peeked);
+ peek_offset += bytes_peeked;
+ }
+
+ // Peek an empty buffer.
+ {
+ char destination[] = UNSET_DATA;
+ circular_buffer.Peek(destination + 9, 7, peek_offset, &bytes_peeked);
+
+ IsSame(UNSET_DATA, destination, sizeof(destination));
+ EXPECT_EQ(0, bytes_peeked);
+ // Verify that we are actually peeking instead of reading.
+ EXPECT_EQ(kMaxCapacity, circular_buffer.GetLength());
+ }
+}
+
+TEST(CircularBufferShellTest, Skip) {
+ const size_t kMaxCapacity = 20;
+ base::CircularBufferShell circular_buffer(kMaxCapacity);
+ char destination[] = UNSET_DATA UNSET_DATA;
+ size_t bytes;
+
+ circular_buffer.Write(kTestData, kMaxCapacity, NULL);
+ circular_buffer.Skip(10, &bytes);
+ EXPECT_EQ(10, bytes);
+ EXPECT_EQ(kMaxCapacity - 10, circular_buffer.GetLength());
+
+ circular_buffer.Read(destination, kMaxCapacity, &bytes);
+
+ EXPECT_EQ(kMaxCapacity - 10, bytes);
+ IsSame(kTestData + 10, destination, bytes);
+}
+
+TEST(CircularBufferShellTest, PeekWrapped) {
+ const size_t kMaxCapacity = 20;
+ base::CircularBufferShell circular_buffer(kMaxCapacity);
+ char destination[] = UNSET_DATA;
+ size_t bytes_peeked;
+
+ circular_buffer.Write(kTestData, kMaxCapacity, NULL);
+
+ // Skip 10 bytes to free some space.
+ circular_buffer.Skip(10, &bytes_peeked);
+
+ // Fill the free space with new data.
+ circular_buffer.Write(kTestData + kMaxCapacity, 10, NULL);
+ EXPECT_EQ(kMaxCapacity, circular_buffer.GetLength());
+
+ // Peek with a non-zero offset.
+ circular_buffer.Peek(destination, kMaxCapacity, 5, &bytes_peeked);
+
+ EXPECT_EQ(kMaxCapacity - 5, bytes_peeked);
+ IsSame(kTestData + 15, destination, bytes_peeked);
+}
+
+TEST(CircularBufferShellTest, SkipWrapped) {
+ const size_t kMaxCapacity = 20;
+ base::CircularBufferShell circular_buffer(kMaxCapacity);
+ char destination[] = UNSET_DATA;
+ size_t bytes;
+
+ circular_buffer.Write(kTestData, kMaxCapacity, NULL);
+ circular_buffer.Skip(10, &bytes);
+
+ circular_buffer.Write(kTestData + kMaxCapacity, 10, NULL);
+ EXPECT_EQ(kMaxCapacity, circular_buffer.GetLength());
+
+ circular_buffer.Skip(kMaxCapacity - 5, NULL);
+ EXPECT_EQ(5, circular_buffer.GetLength());
+
+ circular_buffer.Read(destination, 5, &bytes);
+ EXPECT_EQ(5, bytes);
+ IsSame(kTestData + kMaxCapacity + 5, destination, 5);
+}
+
+// --- Legacy Tests ---
+
+TEST(CircularBufferShellTest, Basic) {
+ const int max_buffer_length = 10;
+ const int kReadSize1 = 4;
+ const int kReadSize2 = 2;
+ const int kReadSize3 = 4;
+ const int kReadSize4 = 6;
+
+ // Create a Circular Buffer.
+ scoped_ptr<base::CircularBufferShell> circular_buffer(
+ new base::CircularBufferShell(max_buffer_length));
+ ASSERT_TRUE(circular_buffer);
+ EXPECT_EQ(circular_buffer->GetLength(), 0);
+
+ char read_buffer[20];
+ size_t bytes_read = 0;
+ size_t bytes_written = 0;
+ // Read 4 bytes, got read_pos 0, write_pos 0
+ circular_buffer->Read(read_buffer, kReadSize1, &bytes_read);
+ EXPECT_EQ(bytes_read, 0);
+
+ // Write 5 bytes, got read_pos 0, write_pos 5
+ const char write_buffer[] = "hello";
+ circular_buffer->Write(write_buffer, strlen(write_buffer), &bytes_written);
+ EXPECT_EQ(bytes_written, strlen(write_buffer));
+ EXPECT_EQ(circular_buffer->GetLength(), strlen(write_buffer));
+
+ // Write 1 byte, increased buffer size to 10, read_pos 0, write_pos 6
+ const char write_buffer2[] = " ";
+ circular_buffer->Write(write_buffer2, strlen(write_buffer2), &bytes_written);
+ EXPECT_EQ(bytes_written, strlen(write_buffer2));
+ EXPECT_EQ(circular_buffer->GetLength(),
+ strlen(write_buffer) + strlen(write_buffer2));
+
+ // Read 2 bytes, got read_pos 2, write_pos 6
+ circular_buffer->Read(read_buffer, kReadSize2, &bytes_read);
+ EXPECT_EQ(0, memcmp(read_buffer, "he", kReadSize2));
+ EXPECT_EQ(bytes_read, kReadSize2);
+ EXPECT_EQ(circular_buffer->GetLength(),
+ strlen(write_buffer) + strlen(write_buffer2) - kReadSize2);
+
+ // Write 6 bytes, got read_pos 2, write_pos 2, full of data
+ const char write_buffer3[] = "world!";
+ circular_buffer->Write(write_buffer3, strlen(write_buffer3), &bytes_written);
+ EXPECT_EQ(bytes_written, strlen(write_buffer3));
+ EXPECT_EQ(circular_buffer->GetLength(),
+ strlen(write_buffer) + strlen(write_buffer2) +
+ strlen(write_buffer3) - kReadSize2);
+
+ // Read 4 bytes, got read_pos 6, write_pos 2
+ circular_buffer->Read(read_buffer, kReadSize3, &bytes_read);
+ EXPECT_EQ(bytes_read, kReadSize3);
+ EXPECT_EQ(0, memcmp(read_buffer, "llo ", kReadSize3));
+ EXPECT_EQ(circular_buffer->GetLength(),
+ strlen(write_buffer) + strlen(write_buffer2) +
+ strlen(write_buffer3) - kReadSize2 - kReadSize3);
+
+ // Read 6 bytes, got read_pos 2, write_pos 2, empty
+ circular_buffer->Read(read_buffer, kReadSize4, &bytes_read);
+ EXPECT_EQ(bytes_read, kReadSize4);
+ EXPECT_EQ(0, memcmp(read_buffer, "world!", kReadSize4));
+ EXPECT_EQ(circular_buffer->GetLength(), 0);
+}
+
+TEST(CircularBufferShellTest, CycleReadWrite) {
+ const int max_buffer_length = 5000;
+ // Create a Circular Buffer.
+ scoped_ptr<base::CircularBufferShell> circular_buffer(
+ new base::CircularBufferShell(max_buffer_length));
+ ASSERT_TRUE(circular_buffer);
+ EXPECT_EQ(circular_buffer->GetLength(), 0);
+
+ size_t bytes_written = 0;
+ size_t bytes_read = 0;
+ char write_buffer[500];
+ char read_buffer[2000];
+
+ for (int i = 0; i < 50; ++i) {
+ circular_buffer->Write(write_buffer, sizeof(write_buffer), &bytes_written);
+ EXPECT_EQ(bytes_written, sizeof(write_buffer));
+ EXPECT_EQ(circular_buffer->GetLength(), sizeof(write_buffer));
+
+ circular_buffer->Write(write_buffer, sizeof(write_buffer), &bytes_written);
+ EXPECT_EQ(bytes_written, sizeof(write_buffer));
+ EXPECT_EQ(circular_buffer->GetLength(),
+ sizeof(write_buffer) + sizeof(write_buffer));
+
+ circular_buffer->Read(read_buffer, sizeof(read_buffer), &bytes_read);
+ EXPECT_EQ(bytes_read, sizeof(write_buffer) + sizeof(write_buffer));
+ EXPECT_EQ(circular_buffer->GetLength(), 0);
+ }
+}
diff --git a/src/base/command_line.cc b/src/base/command_line.cc
new file mode 100644
index 0000000..66fe11a
--- /dev/null
+++ b/src/base/command_line.cc
@@ -0,0 +1,416 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+
+#include <algorithm>
+#include <ostream>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/string_split.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include <shellapi.h>
+#endif
+
+CommandLine* CommandLine::current_process_commandline_ = NULL;
+
+namespace {
+const CommandLine::CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--");
+const CommandLine::CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("=");
+// Since we use a lazy match, make sure that longer versions (like "--") are
+// listed before shorter versions (like "-") of similar prefixes.
+#if defined(OS_WIN)
+const CommandLine::CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"};
+#elif defined(OS_POSIX) || defined(OS_STARBOARD)
+// Unixes don't use slash as a switch.
+const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"};
+#endif
+
+size_t GetSwitchPrefixLength(const CommandLine::StringType& string) {
+ for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) {
+ CommandLine::StringType prefix(kSwitchPrefixes[i]);
+ if (string.compare(0, prefix.length(), prefix) == 0)
+ return prefix.length();
+ }
+ return 0;
+}
+
+// Fills in |switch_string| and |switch_value| if |string| is a switch.
+// This will preserve the input switch prefix in the output |switch_string|.
+bool IsSwitch(const CommandLine::StringType& string,
+ CommandLine::StringType* switch_string,
+ CommandLine::StringType* switch_value) {
+ switch_string->clear();
+ switch_value->clear();
+ size_t prefix_length = GetSwitchPrefixLength(string);
+ if (prefix_length == 0 || prefix_length == string.length())
+ return false;
+
+ const size_t equals_position = string.find(kSwitchValueSeparator);
+ *switch_string = string.substr(0, equals_position);
+ if (equals_position != CommandLine::StringType::npos)
+ *switch_value = string.substr(equals_position + 1);
+ return true;
+}
+
+// Append switches and arguments, keeping switches before arguments.
+void AppendSwitchesAndArguments(CommandLine& command_line,
+ const CommandLine::StringVector& argv) {
+ bool parse_switches = true;
+ for (size_t i = 1; i < argv.size(); ++i) {
+ CommandLine::StringType arg = argv[i];
+ TrimWhitespace(arg, TRIM_ALL, &arg);
+
+ CommandLine::StringType switch_string;
+ CommandLine::StringType switch_value;
+ parse_switches &= (arg != kSwitchTerminator);
+ if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) {
+#if defined(OS_WIN)
+ command_line.AppendSwitchNative(WideToASCII(switch_string), switch_value);
+#elif defined(OS_POSIX) || defined(OS_STARBOARD)
+ command_line.AppendSwitchNative(switch_string, switch_value);
+#endif
+ } else {
+ command_line.AppendArgNative(arg);
+ }
+ }
+}
+
+// Lowercase switches for backwards compatiblity *on Windows*.
+std::string LowerASCIIOnWindows(const std::string& string) {
+#if defined(OS_WIN)
+ return StringToLowerASCII(string);
+#elif defined(OS_POSIX) || defined(OS_STARBOARD)
+ return string;
+#endif
+}
+
+
+#if defined(OS_WIN)
+// Quote a string as necessary for CommandLineToArgvW compatiblity *on Windows*.
+std::wstring QuoteForCommandLineToArgvW(const std::wstring& arg) {
+ // We follow the quoting rules of CommandLineToArgvW.
+ // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
+ if (arg.find_first_of(L" \\\"") == std::wstring::npos) {
+ // No quoting necessary.
+ return arg;
+ }
+
+ std::wstring out;
+ out.push_back(L'"');
+ for (size_t i = 0; i < arg.size(); ++i) {
+ if (arg[i] == '\\') {
+ // Find the extent of this run of backslashes.
+ size_t start = i, end = start + 1;
+ for (; end < arg.size() && arg[end] == '\\'; ++end)
+ /* empty */;
+ size_t backslash_count = end - start;
+
+ // Backslashes are escapes only if the run is followed by a double quote.
+ // Since we also will end the string with a double quote, we escape for
+ // either a double quote or the end of the string.
+ if (end == arg.size() || arg[end] == '"') {
+ // To quote, we need to output 2x as many backslashes.
+ backslash_count *= 2;
+ }
+ for (size_t j = 0; j < backslash_count; ++j)
+ out.push_back('\\');
+
+ // Advance i to one before the end to balance i++ in loop.
+ i = end - 1;
+ } else if (arg[i] == '"') {
+ out.push_back('\\');
+ out.push_back('"');
+ } else {
+ out.push_back(arg[i]);
+ }
+ }
+ out.push_back('"');
+
+ return out;
+}
+#endif
+
+} // namespace
+
+CommandLine::CommandLine(NoProgram no_program)
+ : argv_(1),
+ begin_args_(1) {
+}
+
+CommandLine::CommandLine(const FilePath& program)
+ : argv_(1),
+ begin_args_(1) {
+ SetProgram(program);
+}
+
+CommandLine::CommandLine(int argc, const CommandLine::CharType* const* argv)
+ : argv_(1),
+ begin_args_(1) {
+ InitFromArgv(argc, argv);
+}
+
+CommandLine::CommandLine(const StringVector& argv)
+ : argv_(1),
+ begin_args_(1) {
+ InitFromArgv(argv);
+}
+
+CommandLine::~CommandLine() {
+}
+
+// static
+bool CommandLine::Init(int argc, const char* const* argv) {
+ if (current_process_commandline_) {
+ // If this is intentional, Reset() must be called first. If we are using
+ // the shared build mode, we have to share a single object across multiple
+ // shared libraries.
+ return false;
+ }
+
+ current_process_commandline_ = new CommandLine(NO_PROGRAM);
+#if defined(OS_WIN)
+ current_process_commandline_->ParseFromString(::GetCommandLineW());
+#elif defined(OS_POSIX) || defined(OS_STARBOARD)
+ current_process_commandline_->InitFromArgv(argc, argv);
+#endif
+
+ return true;
+}
+
+// static
+void CommandLine::Reset() {
+ DCHECK(current_process_commandline_);
+ delete current_process_commandline_;
+ current_process_commandline_ = NULL;
+}
+
+// static
+CommandLine* CommandLine::ForCurrentProcess() {
+ DCHECK(current_process_commandline_);
+ return current_process_commandline_;
+}
+
+#if defined(OS_WIN)
+// static
+CommandLine CommandLine::FromString(const std::wstring& command_line) {
+ CommandLine cmd(NO_PROGRAM);
+ cmd.ParseFromString(command_line);
+ return cmd;
+}
+#endif
+
+void CommandLine::InitFromArgv(int argc,
+ const CommandLine::CharType* const* argv) {
+ StringVector new_argv;
+ for (int i = 0; i < argc; ++i)
+ new_argv.push_back(argv[i]);
+ InitFromArgv(new_argv);
+}
+
+void CommandLine::InitFromArgv(const StringVector& argv) {
+ argv_ = StringVector(1);
+ begin_args_ = 1;
+ SetProgram(argv.empty() ? FilePath() : FilePath(argv[0]));
+ AppendSwitchesAndArguments(*this, argv);
+}
+
+CommandLine::StringType CommandLine::GetCommandLineString() const {
+ StringType string(argv_[0]);
+#if defined(OS_WIN)
+ string = QuoteForCommandLineToArgvW(string);
+#endif
+ StringType params(GetArgumentsString());
+ if (!params.empty()) {
+ string.append(StringType(FILE_PATH_LITERAL(" ")));
+ string.append(params);
+ }
+ return string;
+}
+
+CommandLine::StringType CommandLine::GetArgumentsString() const {
+ StringType params;
+ // Append switches and arguments.
+ bool parse_switches = true;
+ for (size_t i = 1; i < argv_.size(); ++i) {
+ StringType arg = argv_[i];
+ StringType switch_string;
+ StringType switch_value;
+ parse_switches &= arg != kSwitchTerminator;
+ if (i > 1)
+ params.append(StringType(FILE_PATH_LITERAL(" ")));
+ if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) {
+ params.append(switch_string);
+ if (!switch_value.empty()) {
+#if defined(OS_WIN)
+ switch_value = QuoteForCommandLineToArgvW(switch_value);
+#endif
+ params.append(kSwitchValueSeparator + switch_value);
+ }
+ }
+ else {
+#if defined(OS_WIN)
+ arg = QuoteForCommandLineToArgvW(arg);
+#endif
+ params.append(arg);
+ }
+ }
+ return params;
+}
+
+FilePath CommandLine::GetProgram() const {
+ return FilePath(argv_[0]);
+}
+
+void CommandLine::SetProgram(const FilePath& program) {
+ TrimWhitespace(program.value(), TRIM_ALL, &argv_[0]);
+}
+
+bool CommandLine::HasSwitch(const std::string& switch_string) const {
+ return switches_.find(LowerASCIIOnWindows(switch_string)) != switches_.end();
+}
+
+std::string CommandLine::GetSwitchValueASCII(
+ const std::string& switch_string) const {
+ StringType value = GetSwitchValueNative(switch_string);
+ if (!IsStringASCII(value)) {
+ DLOG(WARNING) << "Value of switch (" << switch_string << ") must be ASCII.";
+ return std::string();
+ }
+#if defined(OS_WIN)
+ return WideToASCII(value);
+#else
+ return value;
+#endif
+}
+
+FilePath CommandLine::GetSwitchValuePath(
+ const std::string& switch_string) const {
+ return FilePath(GetSwitchValueNative(switch_string));
+}
+
+CommandLine::StringType CommandLine::GetSwitchValueNative(
+ const std::string& switch_string) const {
+ SwitchMap::const_iterator result = switches_.end();
+ result = switches_.find(LowerASCIIOnWindows(switch_string));
+ return result == switches_.end() ? StringType() : result->second;
+}
+
+void CommandLine::AppendSwitch(const std::string& switch_string) {
+ AppendSwitchNative(switch_string, StringType());
+}
+
+void CommandLine::AppendSwitchPath(const std::string& switch_string,
+ const FilePath& path) {
+ AppendSwitchNative(switch_string, path.value());
+}
+
+void CommandLine::AppendSwitchNative(const std::string& switch_string,
+ const CommandLine::StringType& value) {
+ std::string switch_key(LowerASCIIOnWindows(switch_string));
+#if defined(OS_WIN)
+ StringType combined_switch_string(ASCIIToWide(switch_key));
+#elif defined(OS_POSIX) || defined(OS_STARBOARD)
+ StringType combined_switch_string(switch_string);
+#endif
+ size_t prefix_length = GetSwitchPrefixLength(combined_switch_string);
+ switches_[switch_key.substr(prefix_length)] = value;
+ // Preserve existing switch prefixes in |argv_|; only append one if necessary.
+ if (prefix_length == 0)
+ combined_switch_string = kSwitchPrefixes[0] + combined_switch_string;
+ if (!value.empty())
+ combined_switch_string += kSwitchValueSeparator + value;
+ // Append the switch and update the switches/arguments divider |begin_args_|.
+ argv_.insert(argv_.begin() + begin_args_++, combined_switch_string);
+}
+
+void CommandLine::AppendSwitchASCII(const std::string& switch_string,
+ const std::string& value_string) {
+#if defined(OS_WIN)
+ AppendSwitchNative(switch_string, ASCIIToWide(value_string));
+#elif defined(OS_POSIX) || defined(OS_STARBOARD)
+ AppendSwitchNative(switch_string, value_string);
+#endif
+}
+
+void CommandLine::CopySwitchesFrom(const CommandLine& source,
+ const char* const switches[],
+ size_t count) {
+ for (size_t i = 0; i < count; ++i) {
+ if (source.HasSwitch(switches[i]))
+ AppendSwitchNative(switches[i], source.GetSwitchValueNative(switches[i]));
+ }
+}
+
+CommandLine::StringVector CommandLine::GetArgs() const {
+ // Gather all arguments after the last switch (may include kSwitchTerminator).
+ StringVector args(argv_.begin() + begin_args_, argv_.end());
+ // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?)
+ StringVector::iterator switch_terminator =
+ std::find(args.begin(), args.end(), kSwitchTerminator);
+ if (switch_terminator != args.end())
+ args.erase(switch_terminator);
+ return args;
+}
+
+void CommandLine::AppendArg(const std::string& value) {
+#if defined(OS_WIN)
+ DCHECK(IsStringUTF8(value));
+ AppendArgNative(UTF8ToWide(value));
+#elif defined(OS_POSIX) || defined(OS_STARBOARD)
+ AppendArgNative(value);
+#endif
+}
+
+void CommandLine::AppendArgPath(const FilePath& path) {
+ AppendArgNative(path.value());
+}
+
+void CommandLine::AppendArgNative(const CommandLine::StringType& value) {
+ argv_.push_back(value);
+}
+
+void CommandLine::AppendArguments(const CommandLine& other,
+ bool include_program) {
+ if (include_program)
+ SetProgram(other.GetProgram());
+ AppendSwitchesAndArguments(*this, other.argv());
+}
+
+void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) {
+ if (wrapper.empty())
+ return;
+ // 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.
+ StringVector wrapper_argv;
+ base::SplitString(wrapper, FILE_PATH_LITERAL(' '), &wrapper_argv);
+ // Prepend the wrapper and update the switches/arguments |begin_args_|.
+ argv_.insert(argv_.begin(), wrapper_argv.begin(), wrapper_argv.end());
+ begin_args_ += wrapper_argv.size();
+}
+
+#if defined(OS_WIN)
+void CommandLine::ParseFromString(const std::wstring& command_line) {
+ std::wstring command_line_string;
+ 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);
+
+ DPLOG_IF(FATAL, !args) << "CommandLineToArgvW failed on command line: "
+ << command_line;
+ InitFromArgv(num_args, args);
+ LocalFree(args);
+}
+#endif
diff --git a/src/base/command_line.h b/src/base/command_line.h
new file mode 100644
index 0000000..fcb7032
--- /dev/null
+++ b/src/base/command_line.h
@@ -0,0 +1,172 @@
+// Copyright (c) 2012 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.
+// Arguments with prefixes ('--', '-', and on Windows, '/') are switches.
+// Switches will precede all other arguments without switch prefixes.
+// Switches can optionally have values, delimited by '=', e.g., "-switch=value".
+// An argument of "--" will terminate switch parsing during initialization,
+// interpreting subsequent tokens as non-switch arguments, regardless of prefix.
+
+// 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().
+
+#ifndef BASE_COMMAND_LINE_H_
+#define BASE_COMMAND_LINE_H_
+
+#include <stddef.h>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "build/build_config.h"
+
+class FilePath;
+
+class BASE_EXPORT CommandLine {
+ public:
+#if defined(OS_WIN)
+ // The native command line string type.
+ typedef std::wstring StringType;
+#elif defined(OS_POSIX) || defined(OS_STARBOARD)
+ typedef std::string StringType;
+#endif
+
+ typedef StringType::value_type CharType;
+ typedef std::vector<StringType> StringVector;
+ typedef std::map<std::string, StringType> SwitchMap;
+
+ // A constructor for CommandLines that only carry switches and arguments.
+ enum NoProgram { NO_PROGRAM };
+ explicit CommandLine(NoProgram no_program);
+
+ // Construct a new command line with |program| as argv[0].
+ explicit CommandLine(const FilePath& program);
+
+ // Construct a new command line from an argument list.
+ CommandLine(int argc, const CharType* const* argv);
+ explicit CommandLine(const StringVector& argv);
+
+ ~CommandLine();
+
+ // 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. Returns false if initialization has
+ // already occurred, and true otherwise. Only the caller receiving a 'true'
+ // return value should take responsibility for calling Reset.
+ static bool 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, as in main(), Reset() is not necessary.
+ static void Reset();
+
+ // Get the singleton CommandLine representing the current process's
+ // command line. Note: returned value is mutable, but not thread safe;
+ // only mutate if you know what you're doing!
+ static CommandLine* ForCurrentProcess();
+
+#if defined(OS_WIN)
+ static CommandLine FromString(const std::wstring& command_line);
+#endif
+
+ // Initialize from an argv vector.
+ void InitFromArgv(int argc, const CharType* const* argv);
+ void InitFromArgv(const StringVector& argv);
+
+ // Constructs and returns the represented command line string.
+ // CAUTION! This should be avoided on POSIX because quoting behavior is
+ // unclear.
+ StringType GetCommandLineString() const;
+
+ // Constructs and returns the represented arguments string.
+ // CAUTION! This should be avoided on POSIX because quoting behavior is
+ // unclear.
+ StringType GetArgumentsString() const;
+
+ // Returns the original command line string as a vector of strings.
+ const StringVector& argv() const { return argv_; }
+
+ // Get and Set the program part of the command line string (the first item).
+ FilePath GetProgram() const;
+ void SetProgram(const FilePath& program);
+
+ // Returns true if this command line contains the given switch.
+ // (Switch names are case-insensitive).
+ bool HasSwitch(const std::string& 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::string GetSwitchValueASCII(const std::string& switch_string) const;
+ FilePath GetSwitchValuePath(const std::string& switch_string) const;
+ StringType GetSwitchValueNative(const std::string& switch_string) const;
+
+ // Get a copy of all switches, along with their values.
+ const SwitchMap& GetSwitches() const { return switches_; }
+
+ // Append a switch [with optional value] to the command line.
+ // Note: Switches will precede arguments regardless of appending order.
+ void AppendSwitch(const std::string& switch_string);
+ void AppendSwitchPath(const std::string& switch_string, const FilePath& path);
+ void AppendSwitchNative(const std::string& switch_string,
+ const StringType& value);
+ void AppendSwitchASCII(const std::string& switch_string,
+ const std::string& value);
+
+ // Copy a set of switches (and any values) from another command line.
+ // Commonly used when launching a subprocess.
+ void CopySwitchesFrom(const CommandLine& source,
+ const char* const switches[],
+ size_t count);
+
+ // Get the remaining arguments to the command.
+ StringVector GetArgs() const;
+
+ // Append an argument to the command line. Note that the argument is quoted
+ // properly such that it is interpreted as one argument to the target command.
+ // AppendArg is primarily for ASCII; non-ASCII input is interpreted as UTF-8.
+ // Note: Switches will precede arguments regardless of appending order.
+ void AppendArg(const std::string& value);
+ void AppendArgPath(const FilePath& value);
+ void AppendArgNative(const StringType& value);
+
+ // Append the switches and 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);
+
+ // Insert a command before the current command.
+ // Common for debuggers, like "valgrind" or "gdb --args".
+ void PrependWrapper(const StringType& wrapper);
+
+#if defined(OS_WIN)
+ // Initialize by parsing 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);
+#endif
+
+ private:
+ // Disallow default constructor; a program name must be explicitly specified.
+ CommandLine();
+ // Allow the copy constructor. A common pattern is to copy of the current
+ // process's command line and then add some flags to it. For example:
+ // CommandLine cl(*CommandLine::ForCurrentProcess());
+ // cl.AppendSwitch(...);
+
+ // The singleton CommandLine representing the current process's command line.
+ static CommandLine* current_process_commandline_;
+
+ // The argv array: { program, [(--|-|/)switch[=value]]*, [--], [argument]* }
+ StringVector argv_;
+
+ // Parsed-out switch keys and values.
+ SwitchMap switches_;
+
+ // The index after the program and switches, any arguments start here.
+ size_t begin_args_;
+};
+
+#endif // BASE_COMMAND_LINE_H_
diff --git a/src/base/command_line_unittest.cc b/src/base/command_line_unittest.cc
new file mode 100644
index 0000000..b9ee650
--- /dev/null
+++ b/src/base/command_line_unittest.cc
@@ -0,0 +1,358 @@
+// Copyright (c) 2012 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/basictypes.h"
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// To test Windows quoting behavior, we use a string that has some backslashes
+// and quotes.
+// Consider the command-line argument: q\"bs1\bs2\\bs3q\\\"
+// Here it is with C-style escapes.
+static const CommandLine::StringType kTrickyQuoted =
+ FILE_PATH_LITERAL("q\\\"bs1\\bs2\\\\bs3q\\\\\\\"");
+// It should be parsed by Windows as: q"bs1\bs2\\bs3q\"
+// Here that is with C-style escapes.
+static const CommandLine::StringType kTricky =
+ FILE_PATH_LITERAL("q\"bs1\\bs2\\\\bs3q\\\"");
+
+TEST(CommandLineTest, CommandLineConstructor) {
+ const CommandLine::CharType* argv[] = {
+ FILE_PATH_LITERAL("program"),
+ FILE_PATH_LITERAL("--foo="),
+ FILE_PATH_LITERAL("-bAr"),
+ FILE_PATH_LITERAL("-spaetzel=pierogi"),
+ FILE_PATH_LITERAL("-baz"),
+ FILE_PATH_LITERAL("flim"),
+ FILE_PATH_LITERAL("--other-switches=--dog=canine --cat=feline"),
+ FILE_PATH_LITERAL("-spaetzle=Crepe"),
+ FILE_PATH_LITERAL("-=loosevalue"),
+ FILE_PATH_LITERAL("-"),
+ FILE_PATH_LITERAL("FLAN"),
+ FILE_PATH_LITERAL("a"),
+ FILE_PATH_LITERAL("--input-translation=45--output-rotation"),
+ FILE_PATH_LITERAL("--"),
+ FILE_PATH_LITERAL("--"),
+ FILE_PATH_LITERAL("--not-a-switch"),
+ FILE_PATH_LITERAL("\"in the time of submarines...\""),
+ FILE_PATH_LITERAL("unquoted arg-with-space")};
+ CommandLine cl(arraysize(argv), argv);
+
+ EXPECT_FALSE(cl.GetCommandLineString().empty());
+ EXPECT_FALSE(cl.HasSwitch("cruller"));
+ EXPECT_FALSE(cl.HasSwitch("flim"));
+ EXPECT_FALSE(cl.HasSwitch("program"));
+ EXPECT_FALSE(cl.HasSwitch("dog"));
+ EXPECT_FALSE(cl.HasSwitch("cat"));
+ EXPECT_FALSE(cl.HasSwitch("output-rotation"));
+ EXPECT_FALSE(cl.HasSwitch("not-a-switch"));
+ EXPECT_FALSE(cl.HasSwitch("--"));
+
+ EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")).value(),
+ cl.GetProgram().value());
+
+ EXPECT_TRUE(cl.HasSwitch("foo"));
+ EXPECT_TRUE(cl.HasSwitch("bAr"));
+ EXPECT_TRUE(cl.HasSwitch("baz"));
+ EXPECT_TRUE(cl.HasSwitch("spaetzle"));
+#if defined(OS_WIN)
+ EXPECT_TRUE(cl.HasSwitch("SPAETZLE"));
+#endif
+ EXPECT_TRUE(cl.HasSwitch("other-switches"));
+ EXPECT_TRUE(cl.HasSwitch("input-translation"));
+
+ EXPECT_EQ("Crepe", cl.GetSwitchValueASCII("spaetzle"));
+ EXPECT_EQ("", cl.GetSwitchValueASCII("Foo"));
+ EXPECT_EQ("", cl.GetSwitchValueASCII("bar"));
+ EXPECT_EQ("", cl.GetSwitchValueASCII("cruller"));
+ EXPECT_EQ("--dog=canine --cat=feline", cl.GetSwitchValueASCII(
+ "other-switches"));
+ EXPECT_EQ("45--output-rotation", cl.GetSwitchValueASCII("input-translation"));
+
+ const CommandLine::StringVector& args = cl.GetArgs();
+ ASSERT_EQ(8U, args.size());
+
+ std::vector<CommandLine::StringType>::const_iterator iter = args.begin();
+ EXPECT_EQ(FILE_PATH_LITERAL("flim"), *iter);
+ ++iter;
+ EXPECT_EQ(FILE_PATH_LITERAL("-"), *iter);
+ ++iter;
+ EXPECT_EQ(FILE_PATH_LITERAL("FLAN"), *iter);
+ ++iter;
+ EXPECT_EQ(FILE_PATH_LITERAL("a"), *iter);
+ ++iter;
+ EXPECT_EQ(FILE_PATH_LITERAL("--"), *iter);
+ ++iter;
+ EXPECT_EQ(FILE_PATH_LITERAL("--not-a-switch"), *iter);
+ ++iter;
+ EXPECT_EQ(FILE_PATH_LITERAL("\"in the time of submarines...\""), *iter);
+ ++iter;
+ EXPECT_EQ(FILE_PATH_LITERAL("unquoted arg-with-space"), *iter);
+ ++iter;
+ EXPECT_TRUE(iter == args.end());
+}
+
+TEST(CommandLineTest, CommandLineFromString) {
+#if defined(OS_WIN)
+ CommandLine cl = CommandLine::FromString(
+ 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"--quotes=" + kTrickyQuoted + L" "
+ L"-- -- --not-a-switch "
+ L"\"in the time of submarines...\"");
+
+ EXPECT_FALSE(cl.GetCommandLineString().empty());
+ EXPECT_FALSE(cl.HasSwitch("cruller"));
+ EXPECT_FALSE(cl.HasSwitch("flim"));
+ EXPECT_FALSE(cl.HasSwitch("program"));
+ EXPECT_FALSE(cl.HasSwitch("dog"));
+ EXPECT_FALSE(cl.HasSwitch("cat"));
+ EXPECT_FALSE(cl.HasSwitch("output-rotation"));
+ EXPECT_FALSE(cl.HasSwitch("not-a-switch"));
+ EXPECT_FALSE(cl.HasSwitch("--"));
+
+ EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")).value(),
+ cl.GetProgram().value());
+
+ EXPECT_TRUE(cl.HasSwitch("foo"));
+ EXPECT_TRUE(cl.HasSwitch("bar"));
+ EXPECT_TRUE(cl.HasSwitch("baz"));
+ EXPECT_TRUE(cl.HasSwitch("spaetzle"));
+ EXPECT_TRUE(cl.HasSwitch("SPAETZLE"));
+ EXPECT_TRUE(cl.HasSwitch("other-switches"));
+ EXPECT_TRUE(cl.HasSwitch("input-translation"));
+ EXPECT_TRUE(cl.HasSwitch("quotes"));
+
+ EXPECT_EQ("Crepe", cl.GetSwitchValueASCII("spaetzle"));
+ EXPECT_EQ("", cl.GetSwitchValueASCII("Foo"));
+ EXPECT_EQ("", cl.GetSwitchValueASCII("bar"));
+ EXPECT_EQ("", cl.GetSwitchValueASCII("cruller"));
+ EXPECT_EQ("--dog=canine --cat=feline", cl.GetSwitchValueASCII(
+ "other-switches"));
+ EXPECT_EQ("45--output-rotation", cl.GetSwitchValueASCII("input-translation"));
+ EXPECT_EQ(kTricky, cl.GetSwitchValueNative("quotes"));
+
+ const CommandLine::StringVector& args = cl.GetArgs();
+ ASSERT_EQ(5U, args.size());
+
+ std::vector<CommandLine::StringType>::const_iterator iter = args.begin();
+ EXPECT_EQ(FILE_PATH_LITERAL("flim"), *iter);
+ ++iter;
+ EXPECT_EQ(FILE_PATH_LITERAL("FLAN"), *iter);
+ ++iter;
+ EXPECT_EQ(FILE_PATH_LITERAL("--"), *iter);
+ ++iter;
+ EXPECT_EQ(FILE_PATH_LITERAL("--not-a-switch"), *iter);
+ ++iter;
+ EXPECT_EQ(FILE_PATH_LITERAL("in the time of submarines..."), *iter);
+ ++iter;
+ EXPECT_TRUE(iter == args.end());
+
+ // Check that a generated string produces an equivalent command line.
+ CommandLine cl_duplicate = CommandLine::FromString(cl.GetCommandLineString());
+ EXPECT_EQ(cl.GetCommandLineString(), cl_duplicate.GetCommandLineString());
+#endif
+}
+
+// Tests behavior with an empty input string.
+TEST(CommandLineTest, EmptyString) {
+#if defined(OS_WIN)
+ CommandLine cl_from_string = CommandLine::FromString(L"");
+ EXPECT_TRUE(cl_from_string.GetCommandLineString().empty());
+ EXPECT_TRUE(cl_from_string.GetProgram().empty());
+ EXPECT_EQ(1U, cl_from_string.argv().size());
+ EXPECT_TRUE(cl_from_string.GetArgs().empty());
+#endif
+ CommandLine cl_from_argv(0, NULL);
+ EXPECT_TRUE(cl_from_argv.GetCommandLineString().empty());
+ EXPECT_TRUE(cl_from_argv.GetProgram().empty());
+ EXPECT_EQ(1U, cl_from_argv.argv().size());
+ EXPECT_TRUE(cl_from_argv.GetArgs().empty());
+}
+
+TEST(CommandLineTest, GetArgumentsString) {
+ static const FilePath::CharType kPath1[] =
+ FILE_PATH_LITERAL("C:\\Some File\\With Spaces.ggg");
+ static const FilePath::CharType kPath2[] =
+ FILE_PATH_LITERAL("C:\\no\\spaces.ggg");
+
+ static const char kFirstArgName[] = "first-arg";
+ static const char kSecondArgName[] = "arg2";
+ static const char kThirdArgName[] = "arg with space";
+ static const char kFourthArgName[] = "nospace";
+
+ CommandLine cl(CommandLine::NO_PROGRAM);
+ cl.AppendSwitchPath(kFirstArgName, FilePath(kPath1));
+ cl.AppendSwitchPath(kSecondArgName, FilePath(kPath2));
+ cl.AppendArg(kThirdArgName);
+ cl.AppendArg(kFourthArgName);
+
+#if defined(OS_WIN)
+ CommandLine::StringType expected_first_arg(UTF8ToUTF16(kFirstArgName));
+ CommandLine::StringType expected_second_arg(UTF8ToUTF16(kSecondArgName));
+ CommandLine::StringType expected_third_arg(UTF8ToUTF16(kThirdArgName));
+ CommandLine::StringType expected_fourth_arg(UTF8ToUTF16(kFourthArgName));
+#elif defined(OS_POSIX) || defined(OS_STARBOARD)
+ CommandLine::StringType expected_first_arg(kFirstArgName);
+ CommandLine::StringType expected_second_arg(kSecondArgName);
+ CommandLine::StringType expected_third_arg(kThirdArgName);
+ CommandLine::StringType expected_fourth_arg(kFourthArgName);
+#endif
+
+#if defined(OS_WIN)
+#define QUOTE_ON_WIN FILE_PATH_LITERAL("\"")
+#else
+#define QUOTE_ON_WIN FILE_PATH_LITERAL("")
+#endif // OS_WIN
+
+ CommandLine::StringType expected_str;
+ expected_str.append(FILE_PATH_LITERAL("--"))
+ .append(expected_first_arg)
+ .append(FILE_PATH_LITERAL("="))
+ .append(QUOTE_ON_WIN)
+ .append(kPath1)
+ .append(QUOTE_ON_WIN)
+ .append(FILE_PATH_LITERAL(" "))
+ .append(FILE_PATH_LITERAL("--"))
+ .append(expected_second_arg)
+ .append(FILE_PATH_LITERAL("="))
+ .append(QUOTE_ON_WIN)
+ .append(kPath2)
+ .append(QUOTE_ON_WIN)
+ .append(FILE_PATH_LITERAL(" "))
+ .append(QUOTE_ON_WIN)
+ .append(expected_third_arg)
+ .append(QUOTE_ON_WIN)
+ .append(FILE_PATH_LITERAL(" "))
+ .append(expected_fourth_arg);
+ EXPECT_EQ(expected_str, cl.GetArgumentsString());
+}
+
+// Test methods for appending switches to a command line.
+TEST(CommandLineTest, AppendSwitches) {
+ std::string switch1 = "switch1";
+ std::string switch2 = "switch2";
+ std::string value2 = "value";
+ std::string switch3 = "switch3";
+ std::string value3 = "a value with spaces";
+ std::string switch4 = "switch4";
+ std::string value4 = "\"a value with quotes\"";
+ std::string switch5 = "quotes";
+ CommandLine::StringType value5 = kTricky;
+
+ CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
+
+ cl.AppendSwitch(switch1);
+ cl.AppendSwitchASCII(switch2, value2);
+ cl.AppendSwitchASCII(switch3, value3);
+ cl.AppendSwitchASCII(switch4, value4);
+ cl.AppendSwitchNative(switch5, value5);
+
+ EXPECT_TRUE(cl.HasSwitch(switch1));
+ EXPECT_TRUE(cl.HasSwitch(switch2));
+ EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch2));
+ EXPECT_TRUE(cl.HasSwitch(switch3));
+ EXPECT_EQ(value3, cl.GetSwitchValueASCII(switch3));
+ EXPECT_TRUE(cl.HasSwitch(switch4));
+ EXPECT_EQ(value4, cl.GetSwitchValueASCII(switch4));
+ EXPECT_TRUE(cl.HasSwitch(switch5));
+ EXPECT_EQ(value5, cl.GetSwitchValueNative(switch5));
+
+#if defined(OS_WIN)
+ EXPECT_EQ(L"Program "
+ L"--switch1 "
+ L"--switch2=value "
+ L"--switch3=\"a value with spaces\" "
+ L"--switch4=\"\\\"a value with quotes\\\"\" "
+ L"--quotes=\"" + kTrickyQuoted + L"\"",
+ cl.GetCommandLineString());
+#endif
+}
+
+TEST(CommandLineTest, AppendSwitchesDashDash) {
+ const CommandLine::CharType* raw_argv[] = { FILE_PATH_LITERAL("prog"),
+ FILE_PATH_LITERAL("--"),
+ FILE_PATH_LITERAL("--arg1") };
+ CommandLine cl(arraysize(raw_argv), raw_argv);
+
+ cl.AppendSwitch("switch1");
+ cl.AppendSwitchASCII("switch2", "foo");
+
+ cl.AppendArg("--arg2");
+
+ EXPECT_EQ(FILE_PATH_LITERAL("prog --switch1 --switch2=foo -- --arg1 --arg2"),
+ cl.GetCommandLineString());
+ CommandLine::StringVector cl_argv = cl.argv();
+ EXPECT_EQ(FILE_PATH_LITERAL("prog"), cl_argv[0]);
+ EXPECT_EQ(FILE_PATH_LITERAL("--switch1"), cl_argv[1]);
+ EXPECT_EQ(FILE_PATH_LITERAL("--switch2=foo"), cl_argv[2]);
+ EXPECT_EQ(FILE_PATH_LITERAL("--"), cl_argv[3]);
+ EXPECT_EQ(FILE_PATH_LITERAL("--arg1"), cl_argv[4]);
+ EXPECT_EQ(FILE_PATH_LITERAL("--arg2"), cl_argv[5]);
+}
+
+// Tests that when AppendArguments is called that the program is set correctly
+// on the target CommandLine object and the switches from the source
+// CommandLine are added to the target.
+TEST(CommandLineTest, AppendArguments) {
+ CommandLine cl1(FilePath(FILE_PATH_LITERAL("Program")));
+ cl1.AppendSwitch("switch1");
+ cl1.AppendSwitchASCII("switch2", "foo");
+
+ CommandLine cl2(CommandLine::NO_PROGRAM);
+ cl2.AppendArguments(cl1, true);
+ EXPECT_EQ(cl1.GetProgram().value(), cl2.GetProgram().value());
+ EXPECT_EQ(cl1.GetCommandLineString(), cl2.GetCommandLineString());
+
+ CommandLine c1(FilePath(FILE_PATH_LITERAL("Program1")));
+ c1.AppendSwitch("switch1");
+ CommandLine c2(FilePath(FILE_PATH_LITERAL("Program2")));
+ c2.AppendSwitch("switch2");
+
+ c1.AppendArguments(c2, true);
+ EXPECT_EQ(c1.GetProgram().value(), c2.GetProgram().value());
+ EXPECT_TRUE(c1.HasSwitch("switch1"));
+ EXPECT_TRUE(c1.HasSwitch("switch2"));
+}
+
+#if defined(OS_WIN)
+// Make sure that the command line string program paths are quoted as necessary.
+// This only makes sense on Windows and the test is basically here to guard
+// against regressions.
+TEST(CommandLineTest, ProgramQuotes) {
+ // Check that quotes are not added for paths without spaces.
+ const FilePath kProgram(L"Program");
+ CommandLine cl_program(kProgram);
+ EXPECT_EQ(kProgram.value(), cl_program.GetProgram().value());
+ EXPECT_EQ(kProgram.value(), cl_program.GetCommandLineString());
+
+ const FilePath kProgramPath(L"Program Path");
+
+ // Check that quotes are not returned from GetProgram().
+ CommandLine cl_program_path(kProgramPath);
+ EXPECT_EQ(kProgramPath.value(), cl_program_path.GetProgram().value());
+
+ // Check that quotes are added to command line string paths containing spaces.
+ CommandLine::StringType cmd_string(cl_program_path.GetCommandLineString());
+ CommandLine::StringType program_string(cl_program_path.GetProgram().value());
+ EXPECT_EQ('"', cmd_string[0]);
+ EXPECT_EQ(program_string, cmd_string.substr(1, program_string.length()));
+ EXPECT_EQ('"', cmd_string[program_string.length() + 1]);
+}
+#endif
+
+// Calling Init multiple times should not modify the previous CommandLine.
+TEST(CommandLineTest, Init) {
+ CommandLine* initial = CommandLine::ForCurrentProcess();
+ EXPECT_FALSE(CommandLine::Init(0, NULL));
+ CommandLine* current = CommandLine::ForCurrentProcess();
+ EXPECT_EQ(initial, current);
+}
diff --git a/src/base/compiler_specific.h b/src/base/compiler_specific.h
new file mode 100644
index 0000000..789aa1e
--- /dev/null
+++ b/src/base/compiler_specific.h
@@ -0,0 +1,175 @@
+// Copyright (c) 2012 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()
+
+// Allows exporting a class that inherits from a non-exported base class.
+// This uses suppress instead of push/pop because the delimiter after the
+// declaration (either "," or "{") has to be placed before the pop macro.
+//
+// Example usage:
+// class EXPORT_API Foo : NON_EXPORTED_BASE(public Bar) {
+//
+// MSVC Compiler warning C4275:
+// non dll-interface class 'Bar' used as base for dll-interface class 'Foo'.
+// Note that this is intended to be used only when no access to the base class'
+// static data is done through derived classes or inline methods. For more info,
+// see http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx
+#define NON_EXPORTED_BASE(code) MSVC_SUPPRESS_WARNING(4275) \
+ code
+
+#if !defined(UNREFERENCED_PARAMETER)
+#define UNREFERENCED_PARAMETER(x) (x)
+#endif
+#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
+#define NON_EXPORTED_BASE(code) code
+#if !defined(UNREFERENCED_PARAMETER)
+#define UNREFERENCED_PARAMETER(x) \
+ do { \
+ (void)(x); \
+ } while (0)
+#endif
+#endif // COMPILER_MSVC
+
+
+// Annotate a variable indicating it's ok if the variable is not used.
+// (Typically used to silence a compiler warning when the assignment
+// is important for some other reason.)
+// Use like:
+// int x ALLOW_UNUSED = ...;
+#if defined(COMPILER_GCC)
+#define ALLOW_UNUSED __attribute__((unused))
+#else
+#define ALLOW_UNUSED
+#endif
+
+// Annotate a function indicating it should not be inlined.
+// Use like:
+// NOINLINE void DoStuff() { ... }
+#if defined(COMPILER_GCC)
+#define NOINLINE __attribute__((noinline))
+#elif defined(COMPILER_MSVC)
+#define NOINLINE __declspec(noinline)
+#else
+#define NOINLINE
+#endif
+
+// Specify memory alignment for structs, classes, etc.
+// Use like:
+// class ALIGNAS(16) MyClass { ... }
+// ALIGNAS(16) int array[4];
+#if defined(COMPILER_MSVC)
+#define ALIGNAS(byte_alignment) __declspec(align(byte_alignment))
+#elif defined(COMPILER_GCC)
+#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
+#endif
+
+// Return the byte alignment of the given type (available at compile time). Use
+// sizeof(type) prior to checking __alignof to workaround Visual C++ bug:
+// http://goo.gl/isH0C
+// Use like:
+// ALIGNOF(int32) // this would be 4
+#if defined(COMPILER_MSVC)
+#define ALIGNOF(type) (sizeof(type) - sizeof(type) + __alignof(type))
+#elif defined(COMPILER_GCC)
+#define ALIGNOF(type) __alignof__(type)
+#endif
+
+// Annotate a virtual method indicating it must be overriding a virtual
+// method in the parent class.
+// Use like:
+// virtual void foo() OVERRIDE;
+#if defined(COMPILER_MSVC) || defined(COMPILER_GCC)
+#define OVERRIDE override
+#else
+#define OVERRIDE
+#endif
+
+// Annotate a function indicating the caller must examine the return value.
+// Use like:
+// int foo() WARN_UNUSED_RESULT;
+// To explicitly ignore a result, see |ignore_result()| in <base/basictypes.h>.
+#if defined(COMPILER_GCC)
+#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+#define WARN_UNUSED_RESULT
+#endif
+
+// Tell the compiler a function is using a printf-style format string.
+// |format_param| is the one-based index of the format string parameter;
+// |dots_param| is the one-based index of the "..." parameter.
+// For v*printf functions (which take a va_list), pass 0 for dots_param.
+// (This is undocumented but matches what the system C headers do.)
+#if defined(COMPILER_GCC)
+#define PRINTF_FORMAT(format_param, dots_param) \
+ __attribute__((format(printf, format_param, dots_param)))
+#else
+#define PRINTF_FORMAT(format_param, dots_param)
+#endif
+
+// WPRINTF_FORMAT is the same, but for wide format strings.
+// This doesn't appear to yet be implemented in any compiler.
+// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38308 .
+#define WPRINTF_FORMAT(format_param, dots_param)
+// If available, it would look like:
+// __attribute__((format(wprintf, format_param, dots_param)))
+
+#endif // BASE_COMPILER_SPECIFIC_H_
diff --git a/src/base/containers/linked_hash_map.h b/src/base/containers/linked_hash_map.h
new file mode 100644
index 0000000..070e121
--- /dev/null
+++ b/src/base/containers/linked_hash_map.h
@@ -0,0 +1,241 @@
+// Copyright (c) 2013 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 simplistic insertion-ordered map. It behaves similarly to an STL
+// map, but only implements a small subset of the map's methods. Internally, we
+// just keep a map and a list going in parallel.
+//
+// This class provides no thread safety guarantees, beyond what you would
+// normally see with std::list.
+//
+// Iterators should be stable in the face of mutations, except for an
+// iterator pointing to an element that was just deleted.
+
+#ifndef BASE_CONTAINERS_LINKED_HASH_MAP_H_
+#define BASE_CONTAINERS_LINKED_HASH_MAP_H_
+
+#include <list>
+#include <utility>
+
+#include "base/hash_tables.h"
+#include "base/logging.h"
+
+namespace base {
+
+// This holds a list of pair<Key, Value> items. This list is what gets
+// traversed, and it's iterators from this list that we return from
+// begin/end/find.
+//
+// We also keep a map<Key, list::iterator> for find. Since std::list is a
+// doubly-linked list, the iterators should remain stable.
+template<class Key, class Value>
+class linked_hash_map {
+ private:
+ typedef std::list<std::pair<Key, Value> > ListType;
+ typedef base::hash_map<Key, typename ListType::iterator> MapType;
+
+ public:
+ typedef typename ListType::iterator iterator;
+ typedef typename ListType::reverse_iterator reverse_iterator;
+ typedef typename ListType::const_iterator const_iterator;
+ typedef typename ListType::const_reverse_iterator const_reverse_iterator;
+ typedef typename MapType::key_type key_type;
+ typedef typename ListType::value_type value_type;
+ typedef typename ListType::size_type size_type;
+
+ linked_hash_map() : map_(), list_() {
+ }
+
+ // Returns an iterator to the first (insertion-ordered) element. Like a map,
+ // this can be dereferenced to a pair<Key, Value>.
+ iterator begin() {
+ return list_.begin();
+ }
+ const_iterator begin() const {
+ return list_.begin();
+ }
+
+ // Returns an iterator beyond the last element.
+ iterator end() {
+ return list_.end();
+ }
+ const_iterator end() const {
+ return list_.end();
+ }
+
+ // Returns an iterator to the last (insertion-ordered) element. Like a map,
+ // this can be dereferenced to a pair<Key, Value>.
+ reverse_iterator rbegin() {
+ return list_.rbegin();
+ }
+ const_reverse_iterator rbegin() const {
+ return list_.rbegin();
+ }
+
+ // Returns an iterator beyond the first element.
+ reverse_iterator rend() {
+ return list_.rend();
+ }
+ const_reverse_iterator rend() const {
+ return list_.rend();
+ }
+
+ // Front and back accessors common to many stl containers.
+
+ // Returns the earliest-inserted element
+ const value_type& front() const {
+ return list_.front();
+ }
+
+ // Returns the earliest-inserted element.
+ value_type& front() {
+ return list_.front();
+ }
+
+ // Returns the most-recently-inserted element.
+ const value_type& back() const {
+ return list_.back();
+ }
+
+ // Returns the most-recently-inserted element.
+ value_type& back() {
+ return list_.back();
+ }
+
+ // Clears the map of all values.
+ void clear() {
+ map_.clear();
+ list_.clear();
+ }
+
+ // Returns true iff the map is empty.
+ bool empty() const {
+ return list_.empty();
+ }
+
+ // Erases values with the provided key. Returns the number of elements
+ // erased. In this implementation, this will be 0 or 1.
+ size_type erase(const Key& key) {
+ typename MapType::iterator found = map_.find(key);
+ if (found == map_.end()) return 0;
+
+ list_.erase(found->second);
+ map_.erase(found);
+
+ return 1;
+ }
+
+ // Erases the item that 'position' points to. Returns an iterator that points
+ // to the item that comes immediately after the deleted item in the list, or
+ // end().
+ // If the provided iterator is invalid or there is inconsistency between the
+ // map and list, a CHECK() error will occur.
+ iterator erase(iterator position) {
+ typename MapType::iterator found = map_.find(position->first);
+ CHECK(found->second == position)
+ << "Inconsisent iterator for map and list, or the iterator is invalid.";
+
+ map_.erase(found);
+ return list_.erase(position);
+ }
+
+ // Erases all the items in the range [first, last). Returns an iterator that
+ // points to the item that comes immediately after the last deleted item in
+ // the list, or end().
+ iterator erase(iterator first, iterator last) {
+ while (first != last && first != end()) {
+ first = erase(first);
+ }
+ return first;
+ }
+
+ // Finds the element with the given key. Returns an iterator to the
+ // value found, or to end() if the value was not found. Like a map, this
+ // iterator points to a pair<Key, Value>.
+ iterator find(const Key& key) {
+ typename MapType::iterator found = map_.find(key);
+ if (found == map_.end()) {
+ return end();
+ }
+ return found->second;
+ }
+
+ const_iterator find(const Key& key) const {
+ typename MapType::const_iterator found = map_.find(key);
+ if (found == map_.end()) {
+ return end();
+ }
+ return found->second;
+ }
+
+ // Returns the bounds of a range that includes all the elements in the
+ // container with a key that compares equal to x.
+ std::pair<iterator, iterator> equal_range(const key_type& key) {
+ std::pair<typename MapType::iterator, typename MapType::iterator> eq_range =
+ map_.equal_range(key);
+
+ return std::make_pair(eq_range.first->second, eq_range.second->second);
+ }
+
+ std::pair<const_iterator, const_iterator> equal_range(
+ const key_type& key) const {
+ std::pair<typename MapType::const_iterator,
+ typename MapType::const_iterator> eq_range =
+ map_.equal_range(key);
+ const const_iterator& start_iter = eq_range.first != map_.end() ?
+ eq_range.first->second : end();
+ const const_iterator& end_iter = eq_range.second != map_.end() ?
+ eq_range.second->second : end();
+
+ return std::make_pair(start_iter, end_iter);
+ }
+
+ // Returns the value mapped to key, or an inserted iterator to that position
+ // in the map.
+ Value& operator[](const key_type& key) {
+ return (*((this->insert(std::make_pair(key, Value()))).first)).second;
+ }
+
+ // Inserts an element into the map
+ std::pair<iterator, bool> insert(const std::pair<Key, Value>& pair) {
+ // First make sure the map doesn't have a key with this value. If it does,
+ // return a pair with an iterator to it, and false indicating that we
+ // didn't insert anything.
+ typename MapType::iterator found = map_.find(pair.first);
+ if (found != map_.end()) return std::make_pair(found->second, false);
+
+ // Otherwise, insert into the list first.
+ list_.push_back(pair);
+
+ // Obtain an iterator to the newly added element. We do -- instead of -
+ // since list::iterator doesn't implement operator-().
+ typename ListType::iterator last = list_.end();
+ --last;
+
+ CHECK(map_.insert(std::make_pair(pair.first, last)).second)
+ << "Map and list are inconsistent";
+
+ return std::make_pair(last, true);
+ }
+
+ size_type size() const {
+ return list_.size();
+ }
+
+ void swap(linked_hash_map& other) {
+ map_.swap(other.map_);
+ list_.swap(other.list_);
+ }
+
+ private:
+ // The map component, used for speedy lookups
+ MapType map_;
+
+ // The list component, used for maintaining insertion order
+ ListType list_;
+};
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_LINKED_HASH_MAP_H_
diff --git a/src/base/containers/linked_list.h b/src/base/containers/linked_list.h
new file mode 100644
index 0000000..25bbe76
--- /dev/null
+++ b/src/base/containers/linked_list.h
@@ -0,0 +1,164 @@
+// 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_CONTAINERS_LINKED_LIST_H_
+#define BASE_CONTAINERS_LINKED_LIST_H_
+
+// Simple LinkedList type. (See the Q&A section to understand how this
+// differs from std::list).
+//
+// To use, start by declaring the class which will be contained in the linked
+// list, as extending LinkNode (this gives it next/previous pointers).
+//
+// class MyNodeType : public LinkNode<MyNodeType> {
+// ...
+// };
+//
+// Next, to keep track of the list's head/tail, use a LinkedList instance:
+//
+// LinkedList<MyNodeType> list;
+//
+// To add elements to the list, use any of LinkedList::Append,
+// LinkNode::InsertBefore, or LinkNode::InsertAfter:
+//
+// LinkNode<MyNodeType>* n1 = ...;
+// LinkNode<MyNodeType>* n2 = ...;
+// LinkNode<MyNodeType>* n3 = ...;
+//
+// list.Append(n1);
+// list.Append(n3);
+// n3->InsertBefore(n3);
+//
+// Lastly, to iterate through the linked list forwards:
+//
+// for (LinkNode<MyNodeType>* node = list.head();
+// node != list.end();
+// node = node->next()) {
+// MyNodeType* value = node->value();
+// ...
+// }
+//
+// Or to iterate the linked list backwards:
+//
+// for (LinkNode<MyNodeType>* node = list.tail();
+// node != list.end();
+// node = node->previous()) {
+// MyNodeType* value = node->value();
+// ...
+// }
+//
+// Questions and Answers:
+//
+// Q. Should I use std::list or base::LinkedList?
+//
+// A. The main reason to use base::LinkedList over std::list is
+// performance. If you don't care about the performance differences
+// then use an STL container, as it makes for better code readability.
+//
+// Comparing the performance of base::LinkedList<T> to std::list<T*>:
+//
+// * Erasing an element of type T* from base::LinkedList<T> is
+// an O(1) operation. Whereas for std::list<T*> it is O(n).
+// That is because with std::list<T*> you must obtain an
+// iterator to the T* element before you can call erase(iterator).
+//
+// * Insertion operations with base::LinkedList<T> never require
+// heap allocations.
+//
+// Q. How does base::LinkedList implementation differ from std::list?
+//
+// A. Doubly-linked lists are made up of nodes that contain "next" and
+// "previous" pointers that reference other nodes in the list.
+//
+// With base::LinkedList<T>, the type being inserted already reserves
+// space for the "next" and "previous" pointers (base::LinkNode<T>*).
+// Whereas with std::list<T> the type can be anything, so the implementation
+// needs to glue on the "next" and "previous" pointers using
+// some internal node type.
+
+namespace base {
+
+template <typename T>
+class LinkNode {
+ public:
+ LinkNode() : previous_(0), next_(0) {}
+ LinkNode(LinkNode<T>* previous, LinkNode<T>* next)
+ : previous_(previous), next_(next) {}
+
+ // Insert |this| into the linked list, before |e|.
+ void InsertBefore(LinkNode<T>* e) {
+ this->next_ = e;
+ this->previous_ = e->previous_;
+ e->previous_->next_ = this;
+ e->previous_ = this;
+ }
+
+ // Insert |this| into the linked list, after |e|.
+ void InsertAfter(LinkNode<T>* e) {
+ this->next_ = e->next_;
+ this->previous_ = e;
+ e->next_->previous_ = this;
+ e->next_ = this;
+ }
+
+ // Remove |this| from the linked list.
+ void RemoveFromList() {
+ this->previous_->next_ = this->next_;
+ this->next_->previous_ = this->previous_;
+ }
+
+ LinkNode<T>* previous() const {
+ return previous_;
+ }
+
+ LinkNode<T>* next() const {
+ return next_;
+ }
+
+ // Cast from the node-type to the value type.
+ const T* value() const {
+ return static_cast<const T*>(this);
+ }
+
+ T* value() {
+ return static_cast<T*>(this);
+ }
+
+ private:
+ LinkNode<T>* previous_;
+ LinkNode<T>* next_;
+};
+
+template <typename T>
+class LinkedList {
+ public:
+ // The "root" node is self-referential, and forms the basis of a circular
+ // list (root_.next() will point back to the start of the list,
+ // and root_->previous() wraps around to the end of the list).
+ LinkedList() : root_(&root_, &root_) {}
+
+ // Appends |e| to the end of the linked list.
+ void Append(LinkNode<T>* e) {
+ e->InsertBefore(&root_);
+ }
+
+ LinkNode<T>* head() const {
+ return root_.next();
+ }
+
+ LinkNode<T>* tail() const {
+ return root_.previous();
+ }
+
+ const LinkNode<T>* end() const {
+ return &root_;
+ }
+
+ private:
+ LinkNode<T> root_;
+};
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_LINKED_LIST_H_
diff --git a/src/base/containers/linked_list_unittest.cc b/src/base/containers/linked_list_unittest.cc
new file mode 100644
index 0000000..801e302
--- /dev/null
+++ b/src/base/containers/linked_list_unittest.cc
@@ -0,0 +1,261 @@
+// 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/basictypes.h"
+#include "base/containers/linked_list.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+class Node : public LinkNode<Node> {
+ public:
+ explicit Node(int id) : id_(id) {}
+
+ int id() const { return id_; }
+
+ private:
+ int id_;
+};
+
+class MultipleInheritanceNodeBase {
+ public:
+ MultipleInheritanceNodeBase() : field_taking_up_space_(0) {}
+ int field_taking_up_space_;
+};
+
+class MultipleInheritanceNode : public MultipleInheritanceNodeBase,
+ public LinkNode<MultipleInheritanceNode> {
+ public:
+ MultipleInheritanceNode() {}
+};
+
+// Checks that when iterating |list| (either from head to tail, or from
+// tail to head, as determined by |forward|), we get back |node_ids|,
+// which is an array of size |num_nodes|.
+void ExpectListContentsForDirection(const LinkedList<Node>& list,
+ int num_nodes, const int* node_ids, bool forward) {
+ int i = 0;
+ for (const LinkNode<Node>* node = (forward ? list.head() : list.tail());
+ node != list.end();
+ node = (forward ? node->next() : node->previous())) {
+ ASSERT_LT(i, num_nodes);
+ int index_of_id = forward ? i : num_nodes - i - 1;
+ EXPECT_EQ(node_ids[index_of_id], node->value()->id());
+ ++i;
+ }
+ EXPECT_EQ(num_nodes, i);
+}
+
+void ExpectListContents(const LinkedList<Node>& list,
+ int num_nodes,
+ const int* node_ids) {
+ {
+ SCOPED_TRACE("Iterating forward (from head to tail)");
+ ExpectListContentsForDirection(list, num_nodes, node_ids, true);
+ }
+ {
+ SCOPED_TRACE("Iterating backward (from tail to head)");
+ ExpectListContentsForDirection(list, num_nodes, node_ids, false);
+ }
+}
+
+TEST(LinkedList, Empty) {
+ LinkedList<Node> list;
+ EXPECT_EQ(list.end(), list.head());
+ EXPECT_EQ(list.end(), list.tail());
+ ExpectListContents(list, 0, NULL);
+}
+
+TEST(LinkedList, Append) {
+ LinkedList<Node> list;
+ ExpectListContents(list, 0, NULL);
+
+ Node n1(1);
+ list.Append(&n1);
+
+ EXPECT_EQ(&n1, list.head());
+ EXPECT_EQ(&n1, list.tail());
+ {
+ const int expected[] = {1};
+ ExpectListContents(list, arraysize(expected), expected);
+ }
+
+ Node n2(2);
+ list.Append(&n2);
+
+ EXPECT_EQ(&n1, list.head());
+ EXPECT_EQ(&n2, list.tail());
+ {
+ const int expected[] = {1, 2};
+ ExpectListContents(list, arraysize(expected), expected);
+ }
+
+ Node n3(3);
+ list.Append(&n3);
+
+ EXPECT_EQ(&n1, list.head());
+ EXPECT_EQ(&n3, list.tail());
+ {
+ const int expected[] = {1, 2, 3};
+ ExpectListContents(list, arraysize(expected), expected);
+ }
+}
+
+TEST(LinkedList, RemoveFromList) {
+ LinkedList<Node> list;
+
+ Node n1(1);
+ Node n2(2);
+ Node n3(3);
+ Node n4(4);
+ Node n5(5);
+
+ list.Append(&n1);
+ list.Append(&n2);
+ list.Append(&n3);
+ list.Append(&n4);
+ list.Append(&n5);
+
+ EXPECT_EQ(&n1, list.head());
+ EXPECT_EQ(&n5, list.tail());
+ {
+ const int expected[] = {1, 2, 3, 4, 5};
+ ExpectListContents(list, arraysize(expected), expected);
+ }
+
+ // Remove from the middle.
+ n3.RemoveFromList();
+
+ EXPECT_EQ(&n1, list.head());
+ EXPECT_EQ(&n5, list.tail());
+ {
+ const int expected[] = {1, 2, 4, 5};
+ ExpectListContents(list, arraysize(expected), expected);
+ }
+
+ // Remove from the tail.
+ n5.RemoveFromList();
+
+ EXPECT_EQ(&n1, list.head());
+ EXPECT_EQ(&n4, list.tail());
+ {
+ const int expected[] = {1, 2, 4};
+ ExpectListContents(list, arraysize(expected), expected);
+ }
+
+ // Remove from the head.
+ n1.RemoveFromList();
+
+ EXPECT_EQ(&n2, list.head());
+ EXPECT_EQ(&n4, list.tail());
+ {
+ const int expected[] = {2, 4};
+ ExpectListContents(list, arraysize(expected), expected);
+ }
+
+ // Empty the list.
+ n2.RemoveFromList();
+ n4.RemoveFromList();
+
+ ExpectListContents(list, 0, NULL);
+ EXPECT_EQ(list.end(), list.head());
+ EXPECT_EQ(list.end(), list.tail());
+
+ // Fill the list once again.
+ list.Append(&n1);
+ list.Append(&n2);
+ list.Append(&n3);
+ list.Append(&n4);
+ list.Append(&n5);
+
+ EXPECT_EQ(&n1, list.head());
+ EXPECT_EQ(&n5, list.tail());
+ {
+ const int expected[] = {1, 2, 3, 4, 5};
+ ExpectListContents(list, arraysize(expected), expected);
+ }
+}
+
+TEST(LinkedList, InsertBefore) {
+ LinkedList<Node> list;
+
+ Node n1(1);
+ Node n2(2);
+ Node n3(3);
+ Node n4(4);
+
+ list.Append(&n1);
+ list.Append(&n2);
+
+ EXPECT_EQ(&n1, list.head());
+ EXPECT_EQ(&n2, list.tail());
+ {
+ const int expected[] = {1, 2};
+ ExpectListContents(list, arraysize(expected), expected);
+ }
+
+ n3.InsertBefore(&n2);
+
+ EXPECT_EQ(&n1, list.head());
+ EXPECT_EQ(&n2, list.tail());
+ {
+ const int expected[] = {1, 3, 2};
+ ExpectListContents(list, arraysize(expected), expected);
+ }
+
+ n4.InsertBefore(&n1);
+
+ EXPECT_EQ(&n4, list.head());
+ EXPECT_EQ(&n2, list.tail());
+ {
+ const int expected[] = {4, 1, 3, 2};
+ ExpectListContents(list, arraysize(expected), expected);
+ }
+}
+
+TEST(LinkedList, InsertAfter) {
+ LinkedList<Node> list;
+
+ Node n1(1);
+ Node n2(2);
+ Node n3(3);
+ Node n4(4);
+
+ list.Append(&n1);
+ list.Append(&n2);
+
+ EXPECT_EQ(&n1, list.head());
+ EXPECT_EQ(&n2, list.tail());
+ {
+ const int expected[] = {1, 2};
+ ExpectListContents(list, arraysize(expected), expected);
+ }
+
+ n3.InsertAfter(&n2);
+
+ EXPECT_EQ(&n1, list.head());
+ EXPECT_EQ(&n3, list.tail());
+ {
+ const int expected[] = {1, 2, 3};
+ ExpectListContents(list, arraysize(expected), expected);
+ }
+
+ n4.InsertAfter(&n1);
+
+ EXPECT_EQ(&n1, list.head());
+ EXPECT_EQ(&n3, list.tail());
+ {
+ const int expected[] = {1, 4, 2, 3};
+ ExpectListContents(list, arraysize(expected), expected);
+ }
+}
+
+TEST(LinkedList, MultipleInheritanceNode) {
+ MultipleInheritanceNode node;
+ EXPECT_EQ(&node, node.value());
+}
+
+} // namespace
+} // namespace base
diff --git a/src/base/containers/mru_cache.h b/src/base/containers/mru_cache.h
new file mode 100644
index 0000000..f9fb308
--- /dev/null
+++ b/src/base/containers/mru_cache.h
@@ -0,0 +1,305 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains a template for a Most Recently Used cache that allows
+// constant-time access to items using a key, but easy identification of the
+// least-recently-used items for removal. Each key can only be associated with
+// one payload item at a time.
+//
+// The key object will be stored twice, so it should support efficient copying.
+//
+// NOTE: While all operations are O(1), this code is written for
+// legibility rather than optimality. If future profiling identifies this as
+// a bottleneck, there is room for smaller values of 1 in the O(1). :]
+
+#ifndef BASE_CONTAINERS_MRU_CACHE_H_
+#define BASE_CONTAINERS_MRU_CACHE_H_
+
+#include <list>
+#include <map>
+#include <utility>
+
+#include "base/basictypes.h"
+#include "base/hash_tables.h"
+#include "base/logging.h"
+
+namespace base {
+
+// MRUCacheBase ----------------------------------------------------------------
+
+// This template is used to standardize map type containers that can be used
+// by MRUCacheBase. This level of indirection is necessary because of the way
+// that template template params and default template params interact.
+template <class KeyType, class ValueType>
+struct MRUCacheStandardMap {
+ typedef std::map<KeyType, ValueType> Type;
+};
+
+// Base class for the MRU cache specializations defined below.
+// The deletor will get called on all payloads that are being removed or
+// replaced.
+template <class KeyType, class PayloadType, class DeletorType,
+ template <typename, typename> class MapType = MRUCacheStandardMap>
+class MRUCacheBase {
+ public:
+ // The payload of the list. This maintains a copy of the key so we can
+ // efficiently delete things given an element of the list.
+ typedef std::pair<KeyType, PayloadType> value_type;
+
+ private:
+ typedef std::list<value_type> PayloadList;
+ typedef typename MapType<KeyType,
+ typename PayloadList::iterator>::Type KeyIndex;
+
+ public:
+ typedef typename PayloadList::size_type size_type;
+
+ typedef typename PayloadList::iterator iterator;
+ typedef typename PayloadList::const_iterator const_iterator;
+ typedef typename PayloadList::reverse_iterator reverse_iterator;
+ typedef typename PayloadList::const_reverse_iterator const_reverse_iterator;
+
+ enum { NO_AUTO_EVICT = 0 };
+
+ // The max_size is the size at which the cache will prune its members to when
+ // a new item is inserted. If the caller wants to manager this itself (for
+ // example, maybe it has special work to do when something is evicted), it
+ // can pass NO_AUTO_EVICT to not restrict the cache size.
+ explicit MRUCacheBase(size_type max_size) : max_size_(max_size) {
+ }
+
+ MRUCacheBase(size_type max_size, const DeletorType& deletor)
+ : max_size_(max_size), deletor_(deletor) {
+ }
+
+ virtual ~MRUCacheBase() {
+ iterator i = begin();
+ while (i != end())
+ i = Erase(i);
+ }
+
+ size_type max_size() const { return max_size_; }
+
+ // Inserts a payload item with the given key. If an existing item has
+ // the same key, it is removed prior to insertion. An iterator indicating the
+ // inserted item will be returned (this will always be the front of the list).
+ //
+ // The payload will be copied. In the case of an OwningMRUCache, this function
+ // will take ownership of the pointer.
+ iterator Put(const KeyType& key, const PayloadType& payload) {
+ // Remove any existing payload with that key.
+ typename KeyIndex::iterator index_iter = index_.find(key);
+ if (index_iter != index_.end()) {
+ // Erase the reference to it. This will call the deletor on the removed
+ // element. The index reference will be replaced in the code below.
+ Erase(index_iter->second);
+ } else if (max_size_ != NO_AUTO_EVICT) {
+ // New item is being inserted which might make it larger than the maximum
+ // size: kick the oldest thing out if necessary.
+ ShrinkToSize(max_size_ - 1);
+ }
+
+ ordering_.push_front(value_type(key, payload));
+ index_.insert(std::make_pair(key, ordering_.begin()));
+ return ordering_.begin();
+ }
+
+ // Retrieves the contents of the given key, or end() if not found. This method
+ // has the side effect of moving the requested item to the front of the
+ // recency list.
+ //
+ // TODO(brettw) We may want a const version of this function in the future.
+ iterator Get(const KeyType& key) {
+ typename KeyIndex::iterator index_iter = index_.find(key);
+ if (index_iter == index_.end())
+ return end();
+ typename PayloadList::iterator iter = index_iter->second;
+
+ // Move the touched item to the front of the recency ordering.
+ ordering_.splice(ordering_.begin(), ordering_, iter);
+ return ordering_.begin();
+ }
+
+ // Retrieves the payload associated with a given key and returns it via
+ // result without affecting the ordering (unlike Get).
+ //
+ // TODO(brettw) We may want a const version of this function in the future.
+ iterator Peek(const KeyType& key) {
+ typename KeyIndex::const_iterator index_iter = index_.find(key);
+ if (index_iter == index_.end())
+ return end();
+ return index_iter->second;
+ }
+
+ // Erases the item referenced by the given iterator. An iterator to the item
+ // following it will be returned. The iterator must be valid.
+ iterator Erase(iterator pos) {
+ deletor_(pos->second);
+ index_.erase(pos->first);
+ return ordering_.erase(pos);
+ }
+
+ // MRUCache entries are often processed in reverse order, so we add this
+ // convenience function (not typically defined by STL containers).
+ reverse_iterator Erase(reverse_iterator pos) {
+ // We have to actually give it the incremented iterator to delete, since
+ // the forward iterator that base() returns is actually one past the item
+ // being iterated over.
+ return reverse_iterator(Erase((++pos).base()));
+ }
+
+ // Shrinks the cache so it only holds |new_size| items. If |new_size| is
+ // bigger or equal to the current number of items, this will do nothing.
+ void ShrinkToSize(size_type new_size) {
+ for (size_type i = size(); i > new_size; i--)
+ Erase(rbegin());
+ }
+
+ // Deletes everything from the cache.
+ void Clear() {
+ for (typename PayloadList::iterator i(ordering_.begin());
+ i != ordering_.end(); ++i)
+ deletor_(i->second);
+ index_.clear();
+ ordering_.clear();
+ }
+
+ // Returns the number of elements in the cache.
+ size_type size() const {
+ // We don't use ordering_.size() for the return value because
+ // (as a linked list) it can be O(n).
+ DCHECK(index_.size() == ordering_.size());
+ return index_.size();
+ }
+
+ // Allows iteration over the list. Forward iteration starts with the most
+ // recent item and works backwards.
+ //
+ // Note that since these iterators are actually iterators over a list, you
+ // can keep them as you insert or delete things (as long as you don't delete
+ // the one you are pointing to) and they will still be valid.
+ iterator begin() { return ordering_.begin(); }
+ const_iterator begin() const { return ordering_.begin(); }
+ iterator end() { return ordering_.end(); }
+ const_iterator end() const { return ordering_.end(); }
+
+ reverse_iterator rbegin() { return ordering_.rbegin(); }
+ const_reverse_iterator rbegin() const { return ordering_.rbegin(); }
+ reverse_iterator rend() { return ordering_.rend(); }
+ const_reverse_iterator rend() const { return ordering_.rend(); }
+
+ bool empty() const { return ordering_.empty(); }
+
+ private:
+ PayloadList ordering_;
+ KeyIndex index_;
+
+ size_type max_size_;
+
+ DeletorType deletor_;
+
+ DISALLOW_COPY_AND_ASSIGN(MRUCacheBase);
+};
+
+// MRUCache --------------------------------------------------------------------
+
+// A functor that does nothing. Used by the MRUCache.
+template<class PayloadType>
+class MRUCacheNullDeletor {
+ public:
+ void operator()(PayloadType& /*payload*/) {
+ }
+};
+
+// A container that does not do anything to free its data. Use this when storing
+// value types (as opposed to pointers) in the list.
+template <class KeyType, class PayloadType>
+class MRUCache : public MRUCacheBase<KeyType,
+ PayloadType,
+ MRUCacheNullDeletor<PayloadType> > {
+ private:
+ typedef MRUCacheBase<KeyType, PayloadType,
+ MRUCacheNullDeletor<PayloadType> > ParentType;
+
+ public:
+ // See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT.
+ explicit MRUCache(typename ParentType::size_type max_size)
+ : ParentType(max_size) {
+ }
+ virtual ~MRUCache() {
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MRUCache);
+};
+
+// OwningMRUCache --------------------------------------------------------------
+
+template<class PayloadType>
+class MRUCachePointerDeletor {
+ public:
+ void operator()(PayloadType& payload) {
+ delete payload;
+ }
+};
+
+// A cache that owns the payload type, which must be a non-const pointer type.
+// The pointers will be deleted when they are removed, replaced, or when the
+// cache is destroyed.
+template <class KeyType, class PayloadType>
+class OwningMRUCache
+ : public MRUCacheBase<KeyType,
+ PayloadType,
+ MRUCachePointerDeletor<PayloadType> > {
+ private:
+ typedef MRUCacheBase<KeyType, PayloadType,
+ MRUCachePointerDeletor<PayloadType> > ParentType;
+
+ public:
+ // See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT.
+ explicit OwningMRUCache(typename ParentType::size_type max_size)
+ : ParentType(max_size) {
+ }
+ virtual ~OwningMRUCache() {
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(OwningMRUCache);
+};
+
+// HashingMRUCache ------------------------------------------------------------
+
+template <class KeyType, class ValueType>
+struct MRUCacheHashMap {
+ typedef base::hash_map<KeyType, ValueType> Type;
+};
+
+// This class is similar to MRUCache, except that it uses base::hash_map as
+// the map type instead of std::map. Note that your KeyType must be hashable
+// to use this cache.
+template <class KeyType, class PayloadType>
+class HashingMRUCache : public MRUCacheBase<KeyType,
+ PayloadType,
+ MRUCacheNullDeletor<PayloadType>,
+ MRUCacheHashMap> {
+ private:
+ typedef MRUCacheBase<KeyType, PayloadType,
+ MRUCacheNullDeletor<PayloadType>,
+ MRUCacheHashMap> ParentType;
+
+ public:
+ // See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT.
+ explicit HashingMRUCache(typename ParentType::size_type max_size)
+ : ParentType(max_size) {
+ }
+ virtual ~HashingMRUCache() {
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HashingMRUCache);
+};
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_MRU_CACHE_H_
diff --git a/src/base/containers/mru_cache_unittest.cc b/src/base/containers/mru_cache_unittest.cc
new file mode 100644
index 0000000..644f38a
--- /dev/null
+++ b/src/base/containers/mru_cache_unittest.cc
@@ -0,0 +1,271 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/containers/mru_cache.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+int cached_item_live_count = 0;
+
+struct CachedItem {
+ CachedItem() : value(0) {
+ cached_item_live_count++;
+ }
+
+ explicit CachedItem(int new_value) : value(new_value) {
+ cached_item_live_count++;
+ }
+
+ CachedItem(const CachedItem& other) : value(other.value) {
+ cached_item_live_count++;
+ }
+
+ ~CachedItem() {
+ cached_item_live_count--;
+ }
+
+ int value;
+};
+
+} // namespace
+
+TEST(MRUCacheTest, Basic) {
+ typedef base::MRUCache<int, CachedItem> Cache;
+ Cache cache(Cache::NO_AUTO_EVICT);
+
+ // Check failure conditions
+ {
+ CachedItem test_item;
+ EXPECT_TRUE(cache.Get(0) == cache.end());
+ EXPECT_TRUE(cache.Peek(0) == cache.end());
+ }
+
+ static const int kItem1Key = 5;
+ CachedItem item1(10);
+ Cache::iterator inserted_item = cache.Put(kItem1Key, item1);
+ EXPECT_EQ(1U, cache.size());
+
+ // Check that item1 was properly inserted.
+ {
+ Cache::iterator found = cache.Get(kItem1Key);
+ EXPECT_TRUE(inserted_item == cache.begin());
+ EXPECT_TRUE(found != cache.end());
+
+ found = cache.Peek(kItem1Key);
+ EXPECT_TRUE(found != cache.end());
+
+ EXPECT_EQ(kItem1Key, found->first);
+ EXPECT_EQ(item1.value, found->second.value);
+ }
+
+ static const int kItem2Key = 7;
+ CachedItem item2(12);
+ cache.Put(kItem2Key, item2);
+ EXPECT_EQ(2U, cache.size());
+
+ // Check that item1 is the oldest since item2 was added afterwards.
+ {
+ Cache::reverse_iterator oldest = cache.rbegin();
+ ASSERT_TRUE(oldest != cache.rend());
+ EXPECT_EQ(kItem1Key, oldest->first);
+ EXPECT_EQ(item1.value, oldest->second.value);
+ }
+
+ // Check that item1 is still accessible by key.
+ {
+ Cache::iterator test_item = cache.Get(kItem1Key);
+ ASSERT_TRUE(test_item != cache.end());
+ EXPECT_EQ(kItem1Key, test_item->first);
+ EXPECT_EQ(item1.value, test_item->second.value);
+ }
+
+ // Check that retrieving item1 pushed item2 to oldest.
+ {
+ Cache::reverse_iterator oldest = cache.rbegin();
+ ASSERT_TRUE(oldest != cache.rend());
+ EXPECT_EQ(kItem2Key, oldest->first);
+ EXPECT_EQ(item2.value, oldest->second.value);
+ }
+
+ // Remove the oldest item and check that item1 is now the only member.
+ {
+ Cache::reverse_iterator next = cache.Erase(cache.rbegin());
+
+ EXPECT_EQ(1U, cache.size());
+
+ EXPECT_TRUE(next == cache.rbegin());
+ EXPECT_EQ(kItem1Key, next->first);
+ EXPECT_EQ(item1.value, next->second.value);
+
+ cache.Erase(cache.begin());
+ EXPECT_EQ(0U, cache.size());
+ }
+
+ // Check that Clear() works properly.
+ cache.Put(kItem1Key, item1);
+ cache.Put(kItem2Key, item2);
+ EXPECT_EQ(2U, cache.size());
+ cache.Clear();
+ EXPECT_EQ(0U, cache.size());
+}
+
+TEST(MRUCacheTest, GetVsPeek) {
+ typedef base::MRUCache<int, CachedItem> Cache;
+ Cache cache(Cache::NO_AUTO_EVICT);
+
+ static const int kItem1Key = 1;
+ CachedItem item1(10);
+ cache.Put(kItem1Key, item1);
+
+ static const int kItem2Key = 2;
+ CachedItem item2(20);
+ cache.Put(kItem2Key, item2);
+
+ // This should do nothing since the size is bigger than the number of items.
+ cache.ShrinkToSize(100);
+
+ // Check that item1 starts out as oldest
+ {
+ Cache::reverse_iterator iter = cache.rbegin();
+ ASSERT_TRUE(iter != cache.rend());
+ EXPECT_EQ(kItem1Key, iter->first);
+ EXPECT_EQ(item1.value, iter->second.value);
+ }
+
+ // Check that Peek doesn't change ordering
+ {
+ Cache::iterator peekiter = cache.Peek(kItem1Key);
+ ASSERT_TRUE(peekiter != cache.end());
+
+ Cache::reverse_iterator iter = cache.rbegin();
+ ASSERT_TRUE(iter != cache.rend());
+ EXPECT_EQ(kItem1Key, iter->first);
+ EXPECT_EQ(item1.value, iter->second.value);
+ }
+}
+
+TEST(MRUCacheTest, KeyReplacement) {
+ typedef base::MRUCache<int, CachedItem> Cache;
+ Cache cache(Cache::NO_AUTO_EVICT);
+
+ static const int kItem1Key = 1;
+ CachedItem item1(10);
+ cache.Put(kItem1Key, item1);
+
+ static const int kItem2Key = 2;
+ CachedItem item2(20);
+ cache.Put(kItem2Key, item2);
+
+ static const int kItem3Key = 3;
+ CachedItem item3(30);
+ cache.Put(kItem3Key, item3);
+
+ static const int kItem4Key = 4;
+ CachedItem item4(40);
+ cache.Put(kItem4Key, item4);
+
+ CachedItem item5(50);
+ cache.Put(kItem3Key, item5);
+
+ EXPECT_EQ(4U, cache.size());
+ for (int i = 0; i < 3; ++i) {
+ Cache::reverse_iterator iter = cache.rbegin();
+ ASSERT_TRUE(iter != cache.rend());
+ }
+
+ // Make it so only the most important element is there.
+ cache.ShrinkToSize(1);
+
+ Cache::iterator iter = cache.begin();
+ EXPECT_EQ(kItem3Key, iter->first);
+ EXPECT_EQ(item5.value, iter->second.value);
+}
+
+// Make sure that the owning version release its pointers properly.
+TEST(MRUCacheTest, Owning) {
+ typedef base::OwningMRUCache<int, CachedItem*> Cache;
+ Cache cache(Cache::NO_AUTO_EVICT);
+
+ int initial_count = cached_item_live_count;
+
+ // First insert and item and then overwrite it.
+ static const int kItem1Key = 1;
+ cache.Put(kItem1Key, new CachedItem(20));
+ cache.Put(kItem1Key, new CachedItem(22));
+
+ // There should still be one item, and one extra live item.
+ Cache::iterator iter = cache.Get(kItem1Key);
+ EXPECT_EQ(1U, cache.size());
+ EXPECT_TRUE(iter != cache.end());
+ EXPECT_EQ(initial_count + 1, cached_item_live_count);
+
+ // Now remove it.
+ cache.Erase(cache.begin());
+ EXPECT_EQ(initial_count, cached_item_live_count);
+
+ // Now try another cache that goes out of scope to make sure its pointers
+ // go away.
+ {
+ Cache cache2(Cache::NO_AUTO_EVICT);
+ cache2.Put(1, new CachedItem(20));
+ cache2.Put(2, new CachedItem(20));
+ }
+
+ // There should be no objects leaked.
+ EXPECT_EQ(initial_count, cached_item_live_count);
+
+ // Check that Clear() also frees things correctly.
+ {
+ Cache cache2(Cache::NO_AUTO_EVICT);
+ cache2.Put(1, new CachedItem(20));
+ cache2.Put(2, new CachedItem(20));
+ EXPECT_EQ(initial_count + 2, cached_item_live_count);
+ cache2.Clear();
+ EXPECT_EQ(initial_count, cached_item_live_count);
+ }
+}
+
+TEST(MRUCacheTest, AutoEvict) {
+ typedef base::OwningMRUCache<int, CachedItem*> Cache;
+ static const Cache::size_type kMaxSize = 3;
+
+ int initial_count = cached_item_live_count;
+
+ {
+ Cache cache(kMaxSize);
+
+ static const int kItem1Key = 1, kItem2Key = 2, kItem3Key = 3, kItem4Key = 4;
+ cache.Put(kItem1Key, new CachedItem(20));
+ cache.Put(kItem2Key, new CachedItem(21));
+ cache.Put(kItem3Key, new CachedItem(22));
+ cache.Put(kItem4Key, new CachedItem(23));
+
+ // The cache should only have kMaxSize items in it even though we inserted
+ // more.
+ EXPECT_EQ(kMaxSize, cache.size());
+ }
+
+ // There should be no objects leaked.
+ EXPECT_EQ(initial_count, cached_item_live_count);
+}
+
+TEST(MRUCacheTest, HashingMRUCache) {
+ // Very simple test to make sure that the hashing cache works correctly.
+ typedef base::HashingMRUCache<std::string, CachedItem> Cache;
+ Cache cache(Cache::NO_AUTO_EVICT);
+
+ CachedItem one(1);
+ cache.Put("First", one);
+
+ CachedItem two(2);
+ cache.Put("Second", two);
+
+ EXPECT_EQ(one.value, cache.Get("First")->second.value);
+ EXPECT_EQ(two.value, cache.Get("Second")->second.value);
+ cache.ShrinkToSize(1);
+ EXPECT_EQ(two.value, cache.Get("Second")->second.value);
+ EXPECT_TRUE(cache.Get("First") == cache.end());
+}
diff --git a/src/base/containers/small_map.h b/src/base/containers/small_map.h
new file mode 100644
index 0000000..e00e012
--- /dev/null
+++ b/src/base/containers/small_map.h
@@ -0,0 +1,677 @@
+// Copyright (c) 2012 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_CONTAINERS_SMALL_MAP_H_
+#define BASE_CONTAINERS_SMALL_MAP_H_
+
+#include <map>
+#include <string>
+#include <utility>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/hash_tables.h"
+#include "base/logging.h"
+#include "base/memory/manual_constructor.h"
+
+// Disable warning about conversion from '__int64' to 'int'. This value is based
+// on the number of elements in the array, and the program would crash from
+// running out of memory long before the max int value was reached.
+MSVC_PUSH_DISABLE_WARNING(4244)
+// Disable warning about calling an uninitialized object's methods. In this
+// case, the uninitialized object is ManualConstructor and its method is
+// aware that it is uninitialized.
+MSVC_PUSH_DISABLE_WARNING(6001)
+// Disable warning about reading invalid data from the array. The array offset
+// index is determined by the array_iter, which will not go past the end of
+// the array.
+MSVC_PUSH_DISABLE_WARNING(6385)
+
+namespace base {
+
+// An STL-like associative container which starts out backed by a simple
+// array but switches to some other container type if it grows beyond a
+// fixed size.
+//
+// WHAT TYPE OF MAP SHOULD YOU USE?
+// --------------------------------
+//
+// - std::map should be the default if you're not sure, since it's the most
+// difficult to mess up. Generally this is backed by a red-black tree. It
+// will generate a lot of code (if you use a common key type like int or
+// string the linker will probably emiminate the duplicates). It will
+// do heap allocations for each element.
+//
+// - If you only ever keep a couple of items and have very simple usage,
+// consider whether a using a vector and brute-force searching it will be
+// the most efficient. It's not a lot of generated code (less then a
+// red-black tree if your key is "weird" and not eliminated as duplicate of
+// something else) and will probably be faster and do fewer heap allocations
+// than std::map if you have just a couple of items.
+//
+// - base::hash_map should be used if you need O(1) lookups. It may waste
+// space in the hash table, and it can be easy to write correct-looking
+// code with the default hash function being wrong or poorly-behaving.
+//
+// - SmallMap combines the performance benefits of the brute-force-searched
+// vector for small cases (no extra heap allocations), but can efficiently
+// fall back if you end up adding many items. It will generate more code
+// than std::map (at least 160 bytes for operator[]) which is bad if you
+// have a "weird" key where map functions can't be
+// duplicate-code-eliminated. If you have a one-off key and aren't in
+// performance-critical code, this bloat may negate some of the benefits and
+// you should consider on of the other options.
+//
+// SmallMap will pick up the comparator from the underlying map type. In
+// std::map (and in MSVC additionally hash_map) only a "less" operator is
+// defined, which requires us to do two comparisons per element when doing the
+// brute-force search in the simple array.
+//
+// We define default overrides for the common map types to avoid this
+// double-compare, but you should be aware of this if you use your own
+// operator< for your map and supply yor own version of == to the SmallMap.
+// You can use regular operator== by just doing:
+//
+// base::SmallMap<std::map<MyKey, MyValue>, 4, std::equal_to<KyKey> >
+//
+//
+// USAGE
+// -----
+//
+// NormalMap: The map type to fall back to. This also defines the key
+// and value types for the SmallMap.
+// kArraySize: The size of the initial array of results. This will be
+// allocated with the SmallMap object rather than separately on
+// the heap. Once the map grows beyond this size, the map type
+// will be used instead.
+// EqualKey: A functor which tests two keys for equality. If the wrapped
+// map type has a "key_equal" member (hash_map does), then that will
+// be used by default. If the wrapped map type has a strict weak
+// ordering "key_compare" (std::map does), that will be used to
+// implement equality by default.
+// MapInit: A functor that takes a ManualConstructor<NormalMap>* and uses it to
+// initialize the map. This functor will be called at most once per
+// SmallMap, when the map exceeds the threshold of kArraySize and we
+// are about to copy values from the array to the map. The functor
+// *must* call one of the Init() methods provided by
+// ManualConstructor, since after it runs we assume that the NormalMap
+// has been initialized.
+//
+// example:
+// base::SmallMap< std::map<string, int> > days;
+// days["sunday" ] = 0;
+// days["monday" ] = 1;
+// days["tuesday" ] = 2;
+// days["wednesday"] = 3;
+// days["thursday" ] = 4;
+// days["friday" ] = 5;
+// days["saturday" ] = 6;
+//
+// You should assume that SmallMap might invalidate all the iterators
+// on any call to erase(), insert() and operator[].
+
+namespace internal {
+
+template <typename NormalMap>
+class SmallMapDefaultInit {
+ public:
+ void operator()(ManualConstructor<NormalMap>* map) const {
+ map->Init();
+ }
+};
+
+// has_key_equal<M>::value is true iff there exists a type M::key_equal. This is
+// used to dispatch to one of the select_equal_key<> metafunctions below.
+template <typename M>
+struct has_key_equal {
+ typedef char sml; // "small" is sometimes #defined so we use an abbreviation.
+ typedef struct { char dummy[2]; } big;
+ // Two functions, one accepts types that have a key_equal member, and one that
+ // accepts anything. They each return a value of a different size, so we can
+ // determine at compile-time which function would have been called.
+ template <typename U> static big test(typename U::key_equal*);
+ template <typename> static sml test(...);
+ // Determines if M::key_equal exists by looking at the size of the return
+ // type of the compiler-chosen test() function.
+ static const bool value = (sizeof(test<M>(0)) == sizeof(big));
+};
+template <typename M> const bool has_key_equal<M>::value;
+
+// Base template used for map types that do NOT have an M::key_equal member,
+// e.g., std::map<>. These maps have a strict weak ordering comparator rather
+// than an equality functor, so equality will be implemented in terms of that
+// comparator.
+//
+// There's a partial specialization of this template below for map types that do
+// have an M::key_equal member.
+template <typename M, bool has_key_equal_value>
+struct select_equal_key {
+ struct equal_key {
+ bool operator()(const typename M::key_type& left,
+ const typename M::key_type& right) {
+ // Implements equality in terms of a strict weak ordering comparator.
+ typename M::key_compare comp;
+ return !comp(left, right) && !comp(right, left);
+ }
+ };
+};
+
+// Provide overrides to use operator== for key compare for the "normal" map and
+// hash map types. If you override the default comparator or allocator for a
+// map or hash_map, or use another type of map, this won't get used.
+//
+// If we switch to using std::unordered_map for base::hash_map, then the
+// hash_map specialization can be removed.
+template <typename KeyType, typename ValueType>
+struct select_equal_key< std::map<KeyType, ValueType>, false> {
+ struct equal_key {
+ bool operator()(const KeyType& left, const KeyType& right) {
+ return left == right;
+ }
+ };
+};
+template <typename KeyType, typename ValueType>
+struct select_equal_key< base::hash_map<KeyType, ValueType>, false> {
+ struct equal_key {
+ bool operator()(const KeyType& left, const KeyType& right) {
+ return left == right;
+ }
+ };
+};
+
+// Partial template specialization handles case where M::key_equal exists, e.g.,
+// hash_map<>.
+template <typename M>
+struct select_equal_key<M, true> {
+ typedef typename M::key_equal equal_key;
+};
+
+} // namespace internal
+
+template <typename NormalMap,
+ int kArraySize = 4,
+ typename EqualKey =
+ typename internal::select_equal_key<
+ NormalMap,
+ internal::has_key_equal<NormalMap>::value>::equal_key,
+ typename MapInit = internal::SmallMapDefaultInit<NormalMap> >
+class SmallMap {
+ // We cannot rely on the compiler to reject array of size 0. In
+ // particular, gcc 2.95.3 does it but later versions allow 0-length
+ // arrays. Therefore, we explicitly reject non-positive kArraySize
+ // here.
+ COMPILE_ASSERT(kArraySize > 0, default_initial_size_should_be_positive);
+
+ public:
+ typedef typename NormalMap::key_type key_type;
+ typedef typename NormalMap::mapped_type data_type;
+ typedef typename NormalMap::mapped_type mapped_type;
+ typedef typename NormalMap::value_type value_type;
+ typedef EqualKey key_equal;
+
+ SmallMap() : size_(0), functor_(MapInit()) {}
+
+ explicit SmallMap(const MapInit& functor) : size_(0), functor_(functor) {}
+
+ // Allow copy-constructor and assignment, since STL allows them too.
+ SmallMap(const SmallMap& src) {
+ // size_ and functor_ are initted in InitFrom()
+ InitFrom(src);
+ }
+ void operator=(const SmallMap& src) {
+ if (&src == this) return;
+
+ // This is not optimal. If src and dest are both using the small
+ // array, we could skip the teardown and reconstruct. One problem
+ // to be resolved is that the value_type itself is pair<const K,
+ // V>, and const K is not assignable.
+ Destroy();
+ InitFrom(src);
+ }
+ ~SmallMap() {
+ Destroy();
+ }
+
+ class const_iterator;
+
+ class iterator {
+ public:
+ typedef typename NormalMap::iterator::iterator_category iterator_category;
+ typedef typename NormalMap::iterator::value_type value_type;
+ typedef typename NormalMap::iterator::difference_type difference_type;
+ typedef typename NormalMap::iterator::pointer pointer;
+ typedef typename NormalMap::iterator::reference reference;
+
+ inline iterator(): array_iter_(NULL) {}
+
+ inline iterator& operator++() {
+ if (array_iter_ != NULL) {
+ ++array_iter_;
+ } else {
+ ++hash_iter_;
+ }
+ return *this;
+ }
+ inline iterator operator++(int /*unused*/) {
+ iterator result(*this);
+ ++(*this);
+ return result;
+ }
+ inline iterator& operator--() {
+ if (array_iter_ != NULL) {
+ --array_iter_;
+ } else {
+ --hash_iter_;
+ }
+ return *this;
+ }
+ inline iterator operator--(int /*unused*/) {
+ iterator result(*this);
+ --(*this);
+ return result;
+ }
+ inline value_type* operator->() const {
+ if (array_iter_ != NULL) {
+ return array_iter_->get();
+ } else {
+ return hash_iter_.operator->();
+ }
+ }
+
+ inline value_type& operator*() const {
+ if (array_iter_ != NULL) {
+ return *array_iter_->get();
+ } else {
+ return *hash_iter_;
+ }
+ }
+
+ inline bool operator==(const iterator& other) const {
+ if (array_iter_ != NULL) {
+ return array_iter_ == other.array_iter_;
+ } else {
+ return other.array_iter_ == NULL && hash_iter_ == other.hash_iter_;
+ }
+ }
+
+ inline bool operator!=(const iterator& other) const {
+ return !(*this == other);
+ }
+
+ bool operator==(const const_iterator& other) const;
+ bool operator!=(const const_iterator& other) const;
+
+ private:
+ friend class SmallMap;
+ friend class const_iterator;
+ inline explicit iterator(ManualConstructor<value_type>* init)
+ : array_iter_(init) {}
+ inline explicit iterator(const typename NormalMap::iterator& init)
+ : array_iter_(NULL), hash_iter_(init) {}
+
+ ManualConstructor<value_type>* array_iter_;
+ typename NormalMap::iterator hash_iter_;
+ };
+
+ class const_iterator {
+ public:
+ typedef typename NormalMap::const_iterator::iterator_category
+ iterator_category;
+ typedef typename NormalMap::const_iterator::value_type value_type;
+ typedef typename NormalMap::const_iterator::difference_type difference_type;
+ typedef typename NormalMap::const_iterator::pointer pointer;
+ typedef typename NormalMap::const_iterator::reference reference;
+
+ inline const_iterator(): array_iter_(NULL) {}
+ // Non-explicit ctor lets us convert regular iterators to const iterators
+ inline const_iterator(const iterator& other)
+ : array_iter_(other.array_iter_), hash_iter_(other.hash_iter_) {}
+
+ inline const_iterator& operator++() {
+ if (array_iter_ != NULL) {
+ ++array_iter_;
+ } else {
+ ++hash_iter_;
+ }
+ return *this;
+ }
+ inline const_iterator operator++(int /*unused*/) {
+ const_iterator result(*this);
+ ++(*this);
+ return result;
+ }
+
+ inline const_iterator& operator--() {
+ if (array_iter_ != NULL) {
+ --array_iter_;
+ } else {
+ --hash_iter_;
+ }
+ return *this;
+ }
+ inline const_iterator operator--(int /*unused*/) {
+ const_iterator result(*this);
+ --(*this);
+ return result;
+ }
+
+ inline const value_type* operator->() const {
+ if (array_iter_ != NULL) {
+ return array_iter_->get();
+ } else {
+ return hash_iter_.operator->();
+ }
+ }
+
+ inline const value_type& operator*() const {
+ if (array_iter_ != NULL) {
+ return *array_iter_->get();
+ } else {
+ return *hash_iter_;
+ }
+ }
+
+ inline bool operator==(const const_iterator& other) const {
+ if (array_iter_ != NULL) {
+ return array_iter_ == other.array_iter_;
+ } else {
+ return other.array_iter_ == NULL && hash_iter_ == other.hash_iter_;
+ }
+ }
+
+ inline bool operator!=(const const_iterator& other) const {
+ return !(*this == other);
+ }
+
+ private:
+ friend class SmallMap;
+ inline explicit const_iterator(
+ const ManualConstructor<value_type>* init)
+ : array_iter_(init) {}
+ inline explicit const_iterator(
+ const typename NormalMap::const_iterator& init)
+ : array_iter_(NULL), hash_iter_(init) {}
+
+ const ManualConstructor<value_type>* array_iter_;
+ typename NormalMap::const_iterator hash_iter_;
+ };
+
+ iterator find(const key_type& key) {
+ key_equal compare;
+ if (size_ >= 0) {
+ for (int i = 0; i < size_; i++) {
+ if (compare(array_[i]->first, key)) {
+ return iterator(array_ + i);
+ }
+ }
+ return iterator(array_ + size_);
+ } else {
+ return iterator(map()->find(key));
+ }
+ }
+
+ const_iterator find(const key_type& key) const {
+ key_equal compare;
+ if (size_ >= 0) {
+ for (int i = 0; i < size_; i++) {
+ if (compare(array_[i]->first, key)) {
+ return const_iterator(array_ + i);
+ }
+ }
+ return const_iterator(array_ + size_);
+ } else {
+ return const_iterator(map()->find(key));
+ }
+ }
+
+ // Invalidates iterators.
+ data_type& operator[](const key_type& key) {
+ key_equal compare;
+
+ if (size_ >= 0) {
+ // operator[] searches backwards, favoring recently-added
+ // elements.
+ for (int i = size_-1; i >= 0; --i) {
+ if (compare(array_[i]->first, key)) {
+ return array_[i]->second;
+ }
+ }
+ if (size_ == kArraySize) {
+ ConvertToRealMap();
+ return (*map_)[key];
+ } else {
+ array_[size_].Init(key, data_type());
+ return array_[size_++]->second;
+ }
+ } else {
+ return (*map_)[key];
+ }
+ }
+
+ // Invalidates iterators.
+ std::pair<iterator, bool> insert(const value_type& x) {
+ key_equal compare;
+
+ if (size_ >= 0) {
+ for (int i = 0; i < size_; i++) {
+ if (compare(array_[i]->first, x.first)) {
+ return std::make_pair(iterator(array_ + i), false);
+ }
+ }
+ if (size_ == kArraySize) {
+ ConvertToRealMap(); // Invalidates all iterators!
+ std::pair<typename NormalMap::iterator, bool> ret = map_->insert(x);
+ return std::make_pair(iterator(ret.first), ret.second);
+ } else {
+ array_[size_].Init(x);
+ return std::make_pair(iterator(array_ + size_++), true);
+ }
+ } else {
+ std::pair<typename NormalMap::iterator, bool> ret = map_->insert(x);
+ return std::make_pair(iterator(ret.first), ret.second);
+ }
+ }
+
+ // Invalidates iterators.
+ template <class InputIterator>
+ void insert(InputIterator f, InputIterator l) {
+ while (f != l) {
+ insert(*f);
+ ++f;
+ }
+ }
+
+ iterator begin() {
+ if (size_ >= 0) {
+ return iterator(array_);
+ } else {
+ return iterator(map_->begin());
+ }
+ }
+ const_iterator begin() const {
+ if (size_ >= 0) {
+ return const_iterator(array_);
+ } else {
+ return const_iterator(map_->begin());
+ }
+ }
+
+ iterator end() {
+ if (size_ >= 0) {
+ return iterator(array_ + size_);
+ } else {
+ return iterator(map_->end());
+ }
+ }
+ const_iterator end() const {
+ if (size_ >= 0) {
+ return const_iterator(array_ + size_);
+ } else {
+ return const_iterator(map_->end());
+ }
+ }
+
+ void clear() {
+ if (size_ >= 0) {
+ for (int i = 0; i < size_; i++) {
+ array_[i].Destroy();
+ }
+ } else {
+ map_.Destroy();
+ }
+ size_ = 0;
+ }
+
+ // Invalidates iterators.
+ void erase(const iterator& position) {
+ if (size_ >= 0) {
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wshorten-64-to-32"
+#endif
+ int i = position.array_iter_ - array_;
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+ array_[i].Destroy();
+ --size_;
+ if (i != size_) {
+ array_[i].Init(*array_[size_]);
+ array_[size_].Destroy();
+ }
+ } else {
+ map_->erase(position.hash_iter_);
+ }
+ }
+
+ size_t erase(const key_type& key) {
+ iterator iter = find(key);
+ if (iter == end()) return 0u;
+ erase(iter);
+ return 1u;
+ }
+
+ size_t count(const key_type& key) const {
+ return (find(key) == end()) ? 0 : 1;
+ }
+
+ size_t size() const {
+ if (size_ >= 0) {
+ return static_cast<size_t>(size_);
+ } else {
+ return map_->size();
+ }
+ }
+
+ bool empty() const {
+ if (size_ >= 0) {
+ return (size_ == 0);
+ } else {
+ return map_->empty();
+ }
+ }
+
+ // Returns true if we have fallen back to using the underlying map
+ // representation.
+ bool UsingFullMap() const {
+ return size_ < 0;
+ }
+
+ inline NormalMap* map() {
+ CHECK(UsingFullMap());
+ return map_.get();
+ }
+ inline const NormalMap* map() const {
+ CHECK(UsingFullMap());
+ return map_.get();
+ }
+
+ private:
+ int size_; // negative = using hash_map
+
+ MapInit functor_;
+
+ // We want to call constructors and destructors manually, but we don't
+ // want to allocate and deallocate the memory used for them separately.
+ // So, we use this crazy ManualConstructor class.
+ //
+ // Since array_ and map_ are mutually exclusive, we'll put them in a
+ // union, too. We add in a dummy_ value which quiets MSVC from otherwise
+ // giving an erroneous "union member has copy constructor" error message
+ // (C2621). This dummy member has to come before array_ to quiet the
+ // compiler.
+ //
+ // TODO(brettw) remove this and use C++11 unions when we require C++11.
+ union {
+ ManualConstructor<value_type> dummy_;
+ ManualConstructor<value_type> array_[kArraySize];
+ ManualConstructor<NormalMap> map_;
+ };
+
+ void ConvertToRealMap() {
+ // Move the current elements into a temporary array.
+ ManualConstructor<value_type> temp_array[kArraySize];
+
+ for (int i = 0; i < kArraySize; i++) {
+ temp_array[i].Init(*array_[i]);
+ array_[i].Destroy();
+ }
+
+ // Initialize the map.
+ size_ = -1;
+ functor_(&map_);
+
+ // Insert elements into it.
+ for (int i = 0; i < kArraySize; i++) {
+ map_->insert(*temp_array[i]);
+ temp_array[i].Destroy();
+ }
+ }
+
+ // Helpers for constructors and destructors.
+ void InitFrom(const SmallMap& src) {
+ functor_ = src.functor_;
+ size_ = src.size_;
+ if (src.size_ >= 0) {
+ for (int i = 0; i < size_; i++) {
+ array_[i].Init(*src.array_[i]);
+ }
+ } else {
+ functor_(&map_);
+ (*map_.get()) = (*src.map_.get());
+ }
+ }
+ void Destroy() {
+ if (size_ >= 0) {
+ for (int i = 0; i < size_; i++) {
+ array_[i].Destroy();
+ }
+ } else {
+ map_.Destroy();
+ }
+ }
+};
+
+template <typename NormalMap, int kArraySize, typename EqualKey,
+ typename Functor>
+inline bool SmallMap<NormalMap, kArraySize, EqualKey,
+ Functor>::iterator::operator==(
+ const const_iterator& other) const {
+ return other == *this;
+}
+template <typename NormalMap, int kArraySize, typename EqualKey,
+ typename Functor>
+inline bool SmallMap<NormalMap, kArraySize, EqualKey,
+ Functor>::iterator::operator!=(
+ const const_iterator& other) const {
+ return other != *this;
+}
+
+} // namespace base
+
+MSVC_POP_WARNING()
+MSVC_POP_WARNING()
+MSVC_POP_WARNING()
+
+#endif // BASE_CONTAINERS_SMALL_MAP_H_
diff --git a/src/base/containers/small_map_unittest.cc b/src/base/containers/small_map_unittest.cc
new file mode 100644
index 0000000..d1c8680
--- /dev/null
+++ b/src/base/containers/small_map_unittest.cc
@@ -0,0 +1,491 @@
+// Copyright (c) 2012 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/containers/small_map.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <functional>
+#include <map>
+
+#include "base/logging.h"
+#include "base/hash_tables.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(SmallMap, General) {
+ SmallMap<hash_map<int, int> > m;
+
+ EXPECT_TRUE(m.empty());
+
+ m[0] = 5;
+
+ EXPECT_FALSE(m.empty());
+ EXPECT_EQ(m.size(), 1u);
+
+ m[9] = 2;
+
+ EXPECT_FALSE(m.empty());
+ EXPECT_EQ(m.size(), 2u);
+
+ EXPECT_EQ(m[9], 2);
+ EXPECT_EQ(m[0], 5);
+ EXPECT_FALSE(m.UsingFullMap());
+
+ SmallMap<hash_map<int, int> >::iterator iter(m.begin());
+ ASSERT_TRUE(iter != m.end());
+ EXPECT_EQ(iter->first, 0);
+ EXPECT_EQ(iter->second, 5);
+ ++iter;
+ ASSERT_TRUE(iter != m.end());
+ EXPECT_EQ((*iter).first, 9);
+ EXPECT_EQ((*iter).second, 2);
+ ++iter;
+ EXPECT_TRUE(iter == m.end());
+
+ m[8] = 23;
+ m[1234] = 90;
+ m[-5] = 6;
+
+ EXPECT_EQ(m[ 9], 2);
+ EXPECT_EQ(m[ 0], 5);
+ EXPECT_EQ(m[1234], 90);
+ EXPECT_EQ(m[ 8], 23);
+ EXPECT_EQ(m[ -5], 6);
+ EXPECT_EQ(m.size(), 5u);
+ EXPECT_FALSE(m.empty());
+ EXPECT_TRUE(m.UsingFullMap());
+
+ iter = m.begin();
+ for (int i = 0; i < 5; i++) {
+ EXPECT_TRUE(iter != m.end());
+ ++iter;
+ }
+ EXPECT_TRUE(iter == m.end());
+
+ const SmallMap<hash_map<int, int> >& ref = m;
+ EXPECT_TRUE(ref.find(1234) != m.end());
+ EXPECT_TRUE(ref.find(5678) == m.end());
+}
+
+TEST(SmallMap, PostFixIteratorIncrement) {
+ SmallMap<hash_map<int, int> > m;
+ m[0] = 5;
+ m[2] = 3;
+
+ {
+ SmallMap<hash_map<int, int> >::iterator iter(m.begin());
+ SmallMap<hash_map<int, int> >::iterator last(iter++);
+ ++last;
+ EXPECT_TRUE(last == iter);
+ }
+
+ {
+ SmallMap<hash_map<int, int> >::const_iterator iter(m.begin());
+ SmallMap<hash_map<int, int> >::const_iterator last(iter++);
+ ++last;
+ EXPECT_TRUE(last == iter);
+ }
+}
+
+// Based on the General testcase.
+TEST(SmallMap, CopyConstructor) {
+ SmallMap<hash_map<int, int> > src;
+
+ {
+ SmallMap<hash_map<int, int> > m(src);
+ EXPECT_TRUE(m.empty());
+ }
+
+ src[0] = 5;
+
+ {
+ SmallMap<hash_map<int, int> > m(src);
+ EXPECT_FALSE(m.empty());
+ EXPECT_EQ(m.size(), 1u);
+ }
+
+ src[9] = 2;
+
+ {
+ SmallMap<hash_map<int, int> > m(src);
+ EXPECT_FALSE(m.empty());
+ EXPECT_EQ(m.size(), 2u);
+
+ EXPECT_EQ(m[9], 2);
+ EXPECT_EQ(m[0], 5);
+ EXPECT_FALSE(m.UsingFullMap());
+ }
+
+ src[8] = 23;
+ src[1234] = 90;
+ src[-5] = 6;
+
+ {
+ SmallMap<hash_map<int, int> > m(src);
+ EXPECT_EQ(m[ 9], 2);
+ EXPECT_EQ(m[ 0], 5);
+ EXPECT_EQ(m[1234], 90);
+ EXPECT_EQ(m[ 8], 23);
+ EXPECT_EQ(m[ -5], 6);
+ EXPECT_EQ(m.size(), 5u);
+ EXPECT_FALSE(m.empty());
+ EXPECT_TRUE(m.UsingFullMap());
+ }
+}
+
+template<class inner>
+static void SmallMapToMap(SmallMap<inner> const& src, inner* dest) {
+ typename SmallMap<inner>::const_iterator it;
+ for (it = src.begin(); it != src.end(); ++it) {
+ dest->insert(std::make_pair(it->first, it->second));
+ }
+}
+
+template<class inner>
+static bool SmallMapEqual(SmallMap<inner> const& a,
+ SmallMap<inner> const& b) {
+ inner ia, ib;
+ SmallMapToMap(a, &ia);
+ SmallMapToMap(b, &ib);
+
+ // On most systems we can use operator== here, but under some lesser STL
+ // implementations it doesn't seem to work. So we manually compare.
+ if (ia.size() != ib.size())
+ return false;
+ for (typename inner::iterator ia_it = ia.begin(), ib_it = ib.begin();
+ ia_it != ia.end(); ++ia_it, ++ib_it) {
+ if (*ia_it != *ib_it)
+ return false;
+ }
+ return true;
+}
+
+TEST(SmallMap, AssignmentOperator) {
+ SmallMap<hash_map<int, int> > src_small;
+ SmallMap<hash_map<int, int> > src_large;
+
+ src_small[1] = 20;
+ src_small[2] = 21;
+ src_small[3] = 22;
+ EXPECT_FALSE(src_small.UsingFullMap());
+
+ src_large[1] = 20;
+ src_large[2] = 21;
+ src_large[3] = 22;
+ src_large[5] = 23;
+ src_large[6] = 24;
+ src_large[7] = 25;
+ EXPECT_TRUE(src_large.UsingFullMap());
+
+ // Assignments to empty.
+ SmallMap<hash_map<int, int> > dest_small;
+ dest_small = src_small;
+ EXPECT_TRUE(SmallMapEqual(dest_small, src_small));
+ EXPECT_EQ(dest_small.UsingFullMap(),
+ src_small.UsingFullMap());
+
+ SmallMap<hash_map<int, int> > dest_large;
+ dest_large = src_large;
+ EXPECT_TRUE(SmallMapEqual(dest_large, src_large));
+ EXPECT_EQ(dest_large.UsingFullMap(),
+ src_large.UsingFullMap());
+
+ // Assignments which assign from full to small, and vice versa.
+ dest_small = src_large;
+ EXPECT_TRUE(SmallMapEqual(dest_small, src_large));
+ EXPECT_EQ(dest_small.UsingFullMap(),
+ src_large.UsingFullMap());
+
+ dest_large = src_small;
+ EXPECT_TRUE(SmallMapEqual(dest_large, src_small));
+ EXPECT_EQ(dest_large.UsingFullMap(),
+ src_small.UsingFullMap());
+
+ // Double check that SmallMapEqual works:
+ dest_large[42] = 666;
+ EXPECT_FALSE(SmallMapEqual(dest_large, src_small));
+}
+
+TEST(SmallMap, Insert) {
+ SmallMap<hash_map<int, int> > sm;
+
+ // loop through the transition from small map to map.
+ for (int i = 1; i <= 10; ++i) {
+ VLOG(1) << "Iteration " << i;
+ // insert an element
+ std::pair<SmallMap<hash_map<int, int> >::iterator,
+ bool> ret;
+ ret = sm.insert(std::make_pair(i, 100*i));
+ EXPECT_TRUE(ret.second);
+ EXPECT_TRUE(ret.first == sm.find(i));
+ EXPECT_EQ(ret.first->first, i);
+ EXPECT_EQ(ret.first->second, 100*i);
+
+ // try to insert it again with different value, fails, but we still get an
+ // iterator back with the original value.
+ ret = sm.insert(std::make_pair(i, -i));
+ EXPECT_FALSE(ret.second);
+ EXPECT_TRUE(ret.first == sm.find(i));
+ EXPECT_EQ(ret.first->first, i);
+ EXPECT_EQ(ret.first->second, 100*i);
+
+ // check the state of the map.
+ for (int j = 1; j <= i; ++j) {
+ SmallMap<hash_map<int, int> >::iterator it = sm.find(j);
+ EXPECT_TRUE(it != sm.end());
+ EXPECT_EQ(it->first, j);
+ EXPECT_EQ(it->second, j * 100);
+ }
+ EXPECT_EQ(sm.size(), static_cast<size_t>(i));
+ EXPECT_FALSE(sm.empty());
+ }
+}
+
+TEST(SmallMap, InsertRange) {
+ // loop through the transition from small map to map.
+ for (int elements = 0; elements <= 10; ++elements) {
+ VLOG(1) << "Elements " << elements;
+ hash_map<int, int> normal_map;
+ for (int i = 1; i <= elements; ++i) {
+ normal_map.insert(std::make_pair(i, 100*i));
+ }
+
+ SmallMap<hash_map<int, int> > sm;
+ sm.insert(normal_map.begin(), normal_map.end());
+ EXPECT_EQ(normal_map.size(), sm.size());
+ for (int i = 1; i <= elements; ++i) {
+ VLOG(1) << "Iteration " << i;
+ EXPECT_TRUE(sm.find(i) != sm.end());
+ EXPECT_EQ(sm.find(i)->first, i);
+ EXPECT_EQ(sm.find(i)->second, 100*i);
+ }
+ }
+}
+
+TEST(SmallMap, Erase) {
+ SmallMap<hash_map<std::string, int> > m;
+ SmallMap<hash_map<std::string, int> >::iterator iter;
+
+ m["monday"] = 1;
+ m["tuesday"] = 2;
+ m["wednesday"] = 3;
+
+ EXPECT_EQ(m["monday" ], 1);
+ EXPECT_EQ(m["tuesday" ], 2);
+ EXPECT_EQ(m["wednesday"], 3);
+ EXPECT_EQ(m.count("tuesday"), 1u);
+ EXPECT_FALSE(m.UsingFullMap());
+
+ iter = m.begin();
+ ASSERT_TRUE(iter != m.end());
+ EXPECT_EQ(iter->first, "monday");
+ EXPECT_EQ(iter->second, 1);
+ ++iter;
+ ASSERT_TRUE(iter != m.end());
+ EXPECT_EQ(iter->first, "tuesday");
+ EXPECT_EQ(iter->second, 2);
+ ++iter;
+ ASSERT_TRUE(iter != m.end());
+ EXPECT_EQ(iter->first, "wednesday");
+ EXPECT_EQ(iter->second, 3);
+ ++iter;
+ EXPECT_TRUE(iter == m.end());
+
+ EXPECT_EQ(m.erase("tuesday"), 1u);
+
+ EXPECT_EQ(m["monday" ], 1);
+ EXPECT_EQ(m["wednesday"], 3);
+ EXPECT_EQ(m.count("tuesday"), 0u);
+ EXPECT_EQ(m.erase("tuesday"), 0u);
+
+ iter = m.begin();
+ ASSERT_TRUE(iter != m.end());
+ EXPECT_EQ(iter->first, "monday");
+ EXPECT_EQ(iter->second, 1);
+ ++iter;
+ ASSERT_TRUE(iter != m.end());
+ EXPECT_EQ(iter->first, "wednesday");
+ EXPECT_EQ(iter->second, 3);
+ ++iter;
+ EXPECT_TRUE(iter == m.end());
+
+ m["thursday"] = 4;
+ m["friday"] = 5;
+ EXPECT_EQ(m.size(), 4u);
+ EXPECT_FALSE(m.empty());
+ EXPECT_FALSE(m.UsingFullMap());
+
+ m["saturday"] = 6;
+ EXPECT_TRUE(m.UsingFullMap());
+
+ EXPECT_EQ(m.count("friday"), 1u);
+ EXPECT_EQ(m.erase("friday"), 1u);
+ EXPECT_TRUE(m.UsingFullMap());
+ EXPECT_EQ(m.count("friday"), 0u);
+ EXPECT_EQ(m.erase("friday"), 0u);
+
+ EXPECT_EQ(m.size(), 4u);
+ EXPECT_FALSE(m.empty());
+ EXPECT_EQ(m.erase("monday"), 1u);
+ EXPECT_EQ(m.size(), 3u);
+ EXPECT_FALSE(m.empty());
+
+ m.clear();
+ EXPECT_FALSE(m.UsingFullMap());
+ EXPECT_EQ(m.size(), 0u);
+ EXPECT_TRUE(m.empty());
+}
+
+TEST(SmallMap, NonHashMap) {
+ SmallMap<std::map<int, int>, 4, std::equal_to<int> > m;
+ EXPECT_TRUE(m.empty());
+
+ m[9] = 2;
+ m[0] = 5;
+
+ EXPECT_EQ(m[9], 2);
+ EXPECT_EQ(m[0], 5);
+ EXPECT_EQ(m.size(), 2u);
+ EXPECT_FALSE(m.empty());
+ EXPECT_FALSE(m.UsingFullMap());
+
+ SmallMap<std::map<int, int>, 4, std::equal_to<int> >::iterator iter(
+ m.begin());
+ ASSERT_TRUE(iter != m.end());
+ EXPECT_EQ(iter->first, 9);
+ EXPECT_EQ(iter->second, 2);
+ ++iter;
+ ASSERT_TRUE(iter != m.end());
+ EXPECT_EQ(iter->first, 0);
+ EXPECT_EQ(iter->second, 5);
+ ++iter;
+ EXPECT_TRUE(iter == m.end());
+ --iter;
+ ASSERT_TRUE(iter != m.end());
+ EXPECT_EQ(iter->first, 0);
+ EXPECT_EQ(iter->second, 5);
+
+ m[8] = 23;
+ m[1234] = 90;
+ m[-5] = 6;
+
+ EXPECT_EQ(m[ 9], 2);
+ EXPECT_EQ(m[ 0], 5);
+ EXPECT_EQ(m[1234], 90);
+ EXPECT_EQ(m[ 8], 23);
+ EXPECT_EQ(m[ -5], 6);
+ EXPECT_EQ(m.size(), 5u);
+ EXPECT_FALSE(m.empty());
+ EXPECT_TRUE(m.UsingFullMap());
+
+ iter = m.begin();
+ ASSERT_TRUE(iter != m.end());
+ EXPECT_EQ(iter->first, -5);
+ EXPECT_EQ(iter->second, 6);
+ ++iter;
+ ASSERT_TRUE(iter != m.end());
+ EXPECT_EQ(iter->first, 0);
+ EXPECT_EQ(iter->second, 5);
+ ++iter;
+ ASSERT_TRUE(iter != m.end());
+ EXPECT_EQ(iter->first, 8);
+ EXPECT_EQ(iter->second, 23);
+ ++iter;
+ ASSERT_TRUE(iter != m.end());
+ EXPECT_EQ(iter->first, 9);
+ EXPECT_EQ(iter->second, 2);
+ ++iter;
+ ASSERT_TRUE(iter != m.end());
+ EXPECT_EQ(iter->first, 1234);
+ EXPECT_EQ(iter->second, 90);
+ ++iter;
+ EXPECT_TRUE(iter == m.end());
+ --iter;
+ ASSERT_TRUE(iter != m.end());
+ EXPECT_EQ(iter->first, 1234);
+ EXPECT_EQ(iter->second, 90);
+}
+
+TEST(SmallMap, DefaultEqualKeyWorks) {
+ // If these tests compile, they pass. The EXPECT calls are only there to avoid
+ // unused variable warnings.
+ SmallMap<hash_map<int, int> > hm;
+ EXPECT_EQ(0u, hm.size());
+ SmallMap<std::map<int, int> > m;
+ EXPECT_EQ(0u, m.size());
+}
+
+namespace {
+
+class hash_map_add_item : public hash_map<int, int> {
+ public:
+ hash_map_add_item() : hash_map<int, int>() {}
+ hash_map_add_item(const std::pair<int, int>& item) : hash_map<int, int>() {
+ insert(item);
+ }
+};
+
+void InitMap(ManualConstructor<hash_map_add_item>* map_ctor) {
+ map_ctor->Init(std::make_pair(0, 0));
+}
+
+class hash_map_add_item_initializer {
+ public:
+ explicit hash_map_add_item_initializer(int item_to_add)
+ : item_(item_to_add) {}
+ hash_map_add_item_initializer()
+ : item_(0) {}
+ void operator()(ManualConstructor<hash_map_add_item>* map_ctor) const {
+ map_ctor->Init(std::make_pair(item_, item_));
+ }
+
+ int item_;
+};
+
+} // anonymous namespace
+
+TEST(SmallMap, SubclassInitializationWithFunctionPointer) {
+ SmallMap<hash_map_add_item, 4, std::equal_to<int>,
+ void (&)(ManualConstructor<hash_map_add_item>*)> m(InitMap);
+
+ EXPECT_TRUE(m.empty());
+
+ m[1] = 1;
+ m[2] = 2;
+ m[3] = 3;
+ m[4] = 4;
+
+ EXPECT_EQ(4u, m.size());
+ EXPECT_EQ(0u, m.count(0));
+
+ m[5] = 5;
+ EXPECT_EQ(6u, m.size());
+ // Our function adds an extra item when we convert to a map.
+ EXPECT_EQ(1u, m.count(0));
+}
+
+TEST(SmallMap, SubclassInitializationWithFunctionObject) {
+ SmallMap<hash_map_add_item, 4, std::equal_to<int>,
+ hash_map_add_item_initializer> m(hash_map_add_item_initializer(-1));
+
+ EXPECT_TRUE(m.empty());
+
+ m[1] = 1;
+ m[2] = 2;
+ m[3] = 3;
+ m[4] = 4;
+
+ EXPECT_EQ(4u, m.size());
+ EXPECT_EQ(0u, m.count(-1));
+
+ m[5] = 5;
+ EXPECT_EQ(6u, m.size());
+ // Our functor adds an extra item when we convert to a map.
+ EXPECT_EQ(1u, m.count(-1));
+}
+
+} // namespace base
diff --git a/src/base/containers/stack_container.h b/src/base/containers/stack_container.h
new file mode 100644
index 0000000..b3e508b
--- /dev/null
+++ b/src/base/containers/stack_container.h
@@ -0,0 +1,258 @@
+// Copyright (c) 2012 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_CONTAINERS_STACK_CONTAINER_H_
+#define BASE_CONTAINERS_STACK_CONTAINER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+#include "base/memory/aligned_memory.h"
+#include "base/string16.h"
+
+namespace base {
+
+// This allocator can be used with STL containers to provide a stack buffer
+// from which to allocate memory and overflows onto the heap. This stack buffer
+// would be allocated on the stack and allows us to avoid heap operations in
+// some situations.
+//
+// STL likes to make copies of allocators, so the allocator itself can't hold
+// the data. Instead, we make the creator responsible for creating a
+// StackAllocator::Source which contains the data. Copying the allocator
+// merely copies the pointer to this shared source, so all allocators created
+// based on our allocator will share the same stack buffer.
+//
+// This stack buffer implementation is very simple. The first allocation that
+// fits in the stack buffer will use the stack buffer. Any subsequent
+// allocations will not use the stack buffer, even if there is unused room.
+// This makes it appropriate for array-like containers, but the caller should
+// be sure to reserve() in the container up to the stack buffer size. Otherwise
+// the container will allocate a small array which will "use up" the stack
+// buffer.
+template<typename T, size_t stack_capacity>
+class StackAllocator : public std::allocator<T> {
+ public:
+ typedef typename std::allocator<T>::pointer pointer;
+ typedef typename std::allocator<T>::size_type size_type;
+
+ // Backing store for the allocator. The container owner is responsible for
+ // maintaining this for as long as any containers using this allocator are
+ // live.
+ struct Source {
+ Source() : used_stack_buffer_(false) {
+ }
+
+ // Casts the buffer in its right type.
+ T* stack_buffer() { return stack_buffer_.template data_as<T>(); }
+ const T* stack_buffer() const {
+ return stack_buffer_.template data_as<T>();
+ }
+
+ // The buffer itself. It is not of type T because we don't want the
+ // constructors and destructors to be automatically called. Define a POD
+ // buffer of the right size instead.
+ base::AlignedMemory<sizeof(T[stack_capacity]), ALIGNOF(T)> stack_buffer_;
+#if defined(OS_ANDROID)
+ COMPILE_ASSERT(ALIGNOF(T) <= 16, crbug_115612);
+#endif
+
+ // Set when the stack buffer is used for an allocation. We do not track
+ // how much of the buffer is used, only that somebody is using it.
+ bool used_stack_buffer_;
+ };
+
+ // Used by containers when they want to refer to an allocator of type U.
+ template<typename U>
+ struct rebind {
+ typedef StackAllocator<U, stack_capacity> other;
+ };
+
+ // For the straight up copy c-tor, we can share storage.
+ StackAllocator(const StackAllocator<T, stack_capacity>& rhs)
+ : std::allocator<T>(), source_(rhs.source_) {
+ }
+
+ // ISO C++ requires the following constructor to be defined,
+ // and std::vector in VC++2008SP1 Release fails with an error
+ // in the class _Container_base_aux_alloc_real (from <xutility>)
+ // if the constructor does not exist.
+ // For this constructor, we cannot share storage; there's
+ // no guarantee that the Source buffer of Ts is large enough
+ // for Us.
+ // TODO: If we were fancy pants, perhaps we could share storage
+ // iff sizeof(T) == sizeof(U).
+ template<typename U, size_t other_capacity>
+ StackAllocator(const StackAllocator<U, other_capacity>& other)
+ : source_(NULL) {
+ }
+
+ explicit StackAllocator(Source* source) : source_(source) {
+ }
+
+ // Actually do the allocation. Use the stack buffer if nobody has used it yet
+ // and the size requested fits. Otherwise, fall through to the standard
+ // allocator.
+ pointer allocate(size_type n, void* hint = 0) {
+ if (source_ != NULL && !source_->used_stack_buffer_
+ && n <= stack_capacity) {
+ source_->used_stack_buffer_ = true;
+ return source_->stack_buffer();
+ } else {
+ return std::allocator<T>::allocate(n, hint);
+ }
+ }
+
+ // Free: when trying to free the stack buffer, just mark it as free. For
+ // non-stack-buffer pointers, just fall though to the standard allocator.
+ void deallocate(pointer p, size_type n) {
+ if (source_ != NULL && p == source_->stack_buffer())
+ source_->used_stack_buffer_ = false;
+ else
+ std::allocator<T>::deallocate(p, n);
+ }
+
+ private:
+ Source* source_;
+};
+
+// A wrapper around STL containers that maintains a stack-sized buffer that the
+// initial capacity of the vector is based on. Growing the container beyond the
+// stack capacity will transparently overflow onto the heap. The container must
+// support reserve().
+//
+// WATCH OUT: the ContainerType MUST use the proper StackAllocator for this
+// type. This object is really intended to be used only internally. You'll want
+// to use the wrappers below for different types.
+template<typename TContainerType, int stack_capacity>
+class StackContainer {
+ public:
+ typedef TContainerType ContainerType;
+ typedef typename ContainerType::value_type ContainedType;
+ typedef StackAllocator<ContainedType, stack_capacity> Allocator;
+
+ // Allocator must be constructed before the container!
+ StackContainer() : allocator_(&stack_data_), container_(allocator_) {
+ // Make the container use the stack allocation by reserving our buffer size
+ // before doing anything else.
+ container_.reserve(stack_capacity);
+ }
+
+ // Getters for the actual container.
+ //
+ // Danger: any copies of this made using the copy constructor must have
+ // shorter lifetimes than the source. The copy will share the same allocator
+ // and therefore the same stack buffer as the original. Use std::copy to
+ // copy into a "real" container for longer-lived objects.
+ ContainerType& container() { return container_; }
+ const ContainerType& container() const { return container_; }
+
+ // Support operator-> to get to the container. This allows nicer syntax like:
+ // StackContainer<...> foo;
+ // std::sort(foo->begin(), foo->end());
+ ContainerType* operator->() { return &container_; }
+ const ContainerType* operator->() const { return &container_; }
+
+#ifdef UNIT_TEST
+ // Retrieves the stack source so that that unit tests can verify that the
+ // buffer is being used properly.
+ const typename Allocator::Source& stack_data() const {
+ return stack_data_;
+ }
+#endif
+
+ protected:
+ typename Allocator::Source stack_data_;
+ Allocator allocator_;
+ ContainerType container_;
+
+ DISALLOW_COPY_AND_ASSIGN(StackContainer);
+};
+
+// StackString -----------------------------------------------------------------
+
+template<size_t stack_capacity>
+class StackString : public StackContainer<
+ std::basic_string<char,
+ std::char_traits<char>,
+ StackAllocator<char, stack_capacity> >,
+ stack_capacity> {
+ public:
+ StackString() : StackContainer<
+ std::basic_string<char,
+ std::char_traits<char>,
+ StackAllocator<char, stack_capacity> >,
+ stack_capacity>() {
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StackString);
+};
+
+// StackStrin16 ----------------------------------------------------------------
+
+template<size_t stack_capacity>
+class StackString16 : public StackContainer<
+ std::basic_string<char16,
+ base::string16_char_traits,
+ StackAllocator<char16, stack_capacity> >,
+ stack_capacity> {
+ public:
+ StackString16() : StackContainer<
+ std::basic_string<char16,
+ base::string16_char_traits,
+ StackAllocator<char16, stack_capacity> >,
+ stack_capacity>() {
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StackString16);
+};
+
+// StackVector -----------------------------------------------------------------
+
+// Example:
+// StackVector<int, 16> foo;
+// foo->push_back(22); // we have overloaded operator->
+// foo[0] = 10; // as well as operator[]
+template<typename T, size_t stack_capacity>
+class StackVector : public StackContainer<
+ std::vector<T, StackAllocator<T, stack_capacity> >,
+ stack_capacity> {
+ public:
+ StackVector() : StackContainer<
+ std::vector<T, StackAllocator<T, stack_capacity> >,
+ stack_capacity>() {
+ }
+
+ // We need to put this in STL containers sometimes, which requires a copy
+ // constructor. We can't call the regular copy constructor because that will
+ // take the stack buffer from the original. Here, we create an empty object
+ // and make a stack buffer of its own.
+ StackVector(const StackVector<T, stack_capacity>& other)
+ : StackContainer<
+ std::vector<T, StackAllocator<T, stack_capacity> >,
+ stack_capacity>() {
+ this->container().assign(other->begin(), other->end());
+ }
+
+ StackVector<T, stack_capacity>& operator=(
+ const StackVector<T, stack_capacity>& other) {
+ this->container().assign(other->begin(), other->end());
+ return *this;
+ }
+
+ // Vectors are commonly indexed, which isn't very convenient even with
+ // operator-> (using "->at()" does exception stuff we don't want).
+ T& operator[](size_t i) { return this->container().operator[](i); }
+ const T& operator[](size_t i) const {
+ return this->container().operator[](i);
+ }
+};
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_STACK_CONTAINER_H_
diff --git a/src/base/containers/stack_container_unittest.cc b/src/base/containers/stack_container_unittest.cc
new file mode 100644
index 0000000..3a1b3ed
--- /dev/null
+++ b/src/base/containers/stack_container_unittest.cc
@@ -0,0 +1,142 @@
+// Copyright (c) 2012 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/containers/stack_container.h"
+
+#include <algorithm>
+
+#include "base/memory/aligned_memory.h"
+#include "base/memory/ref_counted.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class Dummy : public base::RefCounted<Dummy> {
+ public:
+ explicit Dummy(int* alive) : alive_(alive) {
+ ++*alive_;
+ }
+
+ private:
+ friend class base::RefCounted<Dummy>;
+
+ ~Dummy() {
+ --*alive_;
+ }
+
+ int* const alive_;
+};
+
+} // namespace
+
+TEST(StackContainer, Vector) {
+ const int stack_size = 3;
+ StackVector<int, stack_size> vect;
+ const int* stack_buffer = &vect.stack_data().stack_buffer()[0];
+
+ // The initial |stack_size| elements should appear in the stack buffer.
+ EXPECT_EQ(static_cast<size_t>(stack_size), vect.container().capacity());
+ for (int i = 0; i < stack_size; i++) {
+ vect.container().push_back(i);
+ EXPECT_EQ(stack_buffer, &vect.container()[0]);
+ EXPECT_TRUE(vect.stack_data().used_stack_buffer_);
+ }
+
+ // Adding more elements should push the array onto the heap.
+ for (int i = 0; i < stack_size; i++) {
+ vect.container().push_back(i + stack_size);
+ EXPECT_NE(stack_buffer, &vect.container()[0]);
+ EXPECT_FALSE(vect.stack_data().used_stack_buffer_);
+ }
+
+ // The array should still be in order.
+ for (int i = 0; i < stack_size * 2; i++)
+ EXPECT_EQ(i, vect.container()[i]);
+
+ // Resize to smaller. Our STL implementation won't reallocate in this case,
+ // otherwise it might use our stack buffer. We reserve right after the resize
+ // to guarantee it isn't using the stack buffer, even though it doesn't have
+ // much data.
+ vect.container().resize(stack_size);
+ vect.container().reserve(stack_size * 2);
+ EXPECT_FALSE(vect.stack_data().used_stack_buffer_);
+
+ // Copying the small vector to another should use the same allocator and use
+ // the now-unused stack buffer. GENERALLY CALLERS SHOULD NOT DO THIS since
+ // they have to get the template types just right and it can cause errors.
+ std::vector<int, StackAllocator<int, stack_size> > other(vect.container());
+ EXPECT_EQ(stack_buffer, &other.front());
+ EXPECT_TRUE(vect.stack_data().used_stack_buffer_);
+ for (int i = 0; i < stack_size; i++)
+ EXPECT_EQ(i, other[i]);
+}
+
+TEST(StackContainer, VectorDoubleDelete) {
+ // Regression testing for double-delete.
+ typedef StackVector<scoped_refptr<Dummy>, 2> Vector;
+ typedef Vector::ContainerType Container;
+ Vector vect;
+
+ int alive = 0;
+ scoped_refptr<Dummy> dummy(new Dummy(&alive));
+ EXPECT_EQ(alive, 1);
+
+ vect->push_back(dummy);
+ EXPECT_EQ(alive, 1);
+
+ Dummy* dummy_unref = dummy.get();
+ dummy = NULL;
+ EXPECT_EQ(alive, 1);
+
+ Container::iterator itr = std::find(vect->begin(), vect->end(), dummy_unref);
+ EXPECT_EQ(itr->get(), dummy_unref);
+ vect->erase(itr);
+ EXPECT_EQ(alive, 0);
+
+ // Shouldn't crash at exit.
+}
+
+namespace {
+
+template <size_t alignment>
+class AlignedData {
+ public:
+ AlignedData() { memset(data_.void_data(), 0, alignment); }
+ ~AlignedData() {}
+ base::AlignedMemory<alignment, alignment> data_;
+};
+
+} // anonymous namespace
+
+#define EXPECT_ALIGNED(ptr, align) \
+ EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(ptr) & (align - 1))
+
+TEST(StackContainer, BufferAlignment) {
+ StackVector<wchar_t, 16> text;
+ text->push_back(L'A');
+ EXPECT_ALIGNED(&text[0], ALIGNOF(wchar_t));
+
+ StackVector<double, 1> doubles;
+ doubles->push_back(0.0);
+ EXPECT_ALIGNED(&doubles[0], ALIGNOF(double));
+
+ StackVector<AlignedData<16>, 1> aligned16;
+ aligned16->push_back(AlignedData<16>());
+ EXPECT_ALIGNED(&aligned16[0], 16);
+
+#if !defined(OS_ANDROID)
+ // It seems that android doesn't respect greater than 16 byte alignment for
+ // non-POD data on the stack, even though ALIGNOF(aligned256) == 256.
+ StackVector<AlignedData<256>, 1> aligned256;
+ aligned256->push_back(AlignedData<256>());
+ EXPECT_ALIGNED(&aligned256[0], 256);
+#endif
+}
+
+template class StackVector<int, 2>;
+template class StackVector<scoped_refptr<Dummy>, 2>;
+
+} // namespace base
diff --git a/src/base/cpu.cc b/src/base/cpu.cc
new file mode 100644
index 0000000..cf4f2f1
--- /dev/null
+++ b/src/base/cpu.cc
@@ -0,0 +1,140 @@
+// Copyright (c) 2012 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 <string.h>
+
+#include <algorithm>
+
+#include "build/build_config.h"
+
+#if defined(ARCH_CPU_X86_FAMILY)
+#if defined(_MSC_VER)
+#include <intrin.h>
+#endif
+#endif
+
+namespace base {
+
+CPU::CPU()
+ : type_(0),
+ family_(0),
+ model_(0),
+ stepping_(0),
+ ext_model_(0),
+ ext_family_(0),
+ has_mmx_(false),
+ has_sse_(false),
+ has_sse2_(false),
+ has_sse3_(false),
+ has_ssse3_(false),
+ has_sse41_(false),
+ has_sse42_(false),
+ cpu_vendor_("unknown") {
+ Initialize();
+}
+
+#if defined(ARCH_CPU_X86_FAMILY)
+#ifndef _MSC_VER
+
+#if defined(__pic__) && defined(__i386__)
+
+void __cpuid(int cpu_info[4], int info_type) {
+ __asm__ volatile (
+ "mov %%ebx, %%edi\n"
+ "cpuid\n"
+ "xchg %%edi, %%ebx\n"
+ : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
+ : "a"(info_type)
+ );
+}
+
+void __cpuidex(int cpu_info[4], int info_type, int info_index) {
+ __asm__ volatile (
+ "mov %%ebx, %%edi\n"
+ "cpuid\n"
+ "xchg %%edi, %%ebx\n"
+ : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
+ : "a"(info_type), "c"(info_index)
+ );
+}
+
+#else
+
+void __cpuid(int cpu_info[4], int info_type) {
+ __asm__ volatile (
+ "cpuid \n\t"
+ : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
+ : "a"(info_type)
+ );
+}
+
+void __cpuidex(int cpu_info[4], int info_type, int info_index) {
+ __asm__ volatile (
+ "cpuid \n\t"
+ : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
+ : "a"(info_type), "c"(info_index)
+ );
+}
+
+#endif
+#endif // _MSC_VER
+#endif // ARCH_CPU_X86_FAMILY
+
+void CPU::Initialize() {
+#if defined(ARCH_CPU_X86_FAMILY)
+ int cpu_info[4] = {-1};
+ char cpu_string[48];
+
+ // __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. The human readable order is CPUInfo[1] |
+ // CPUInfo[3] | CPUInfo[2]. CPUInfo[2] and CPUInfo[3] are swapped
+ // before using memcpy to copy these three array elements to cpu_string.
+ __cpuid(cpu_info, 0);
+ int num_ids = cpu_info[0];
+ std::swap(cpu_info[2], cpu_info[3]);
+ memcpy(cpu_string, &cpu_info[1], 3 * sizeof(cpu_info[1]));
+ cpu_vendor_.assign(cpu_string, 3 * sizeof(cpu_info[1]));
+
+ // Interpret CPU feature information.
+ if (num_ids > 0) {
+ __cpuid(cpu_info, 1);
+ stepping_ = cpu_info[0] & 0xf;
+ model_ = ((cpu_info[0] >> 4) & 0xf) + ((cpu_info[0] >> 12) & 0xf0);
+ 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;
+ has_mmx_ = (cpu_info[3] & 0x00800000) != 0;
+ has_sse_ = (cpu_info[3] & 0x02000000) != 0;
+ has_sse2_ = (cpu_info[3] & 0x04000000) != 0;
+ has_sse3_ = (cpu_info[2] & 0x00000001) != 0;
+ has_ssse3_ = (cpu_info[2] & 0x00000200) != 0;
+ has_sse41_ = (cpu_info[2] & 0x00080000) != 0;
+ has_sse42_ = (cpu_info[2] & 0x00100000) != 0;
+ }
+
+ // Get the brand string of the cpu.
+ __cpuid(cpu_info, 0x80000000);
+ const int parameter_end = 0x80000004;
+
+ if (cpu_info[0] >= parameter_end) {
+ char* cpu_string_ptr = cpu_string;
+
+ for (int parameter = 0x80000002; parameter <= parameter_end &&
+ cpu_string_ptr < &cpu_string[sizeof(cpu_string)]; parameter++) {
+ __cpuid(cpu_info, parameter);
+ memcpy(cpu_string_ptr, cpu_info, sizeof(cpu_info));
+ cpu_string_ptr += sizeof(cpu_info);
+ }
+ cpu_brand_.assign(cpu_string, cpu_string_ptr - cpu_string);
+ }
+#endif
+}
+
+} // namespace base
diff --git a/src/base/cpu.h b/src/base/cpu.h
new file mode 100644
index 0000000..957b1a5
--- /dev/null
+++ b/src/base/cpu.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 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>
+
+#include "base/base_export.h"
+
+namespace base {
+
+// Query information about the processor.
+class BASE_EXPORT 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_; }
+ bool has_mmx() const { return has_mmx_; }
+ bool has_sse() const { return has_sse_; }
+ bool has_sse2() const { return has_sse2_; }
+ bool has_sse3() const { return has_sse3_; }
+ bool has_ssse3() const { return has_ssse3_; }
+ bool has_sse41() const { return has_sse41_; }
+ bool has_sse42() const { return has_sse42_; }
+ const std::string& cpu_brand() const { return cpu_brand_; }
+
+ 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_;
+ bool has_mmx_;
+ bool has_sse_;
+ bool has_sse2_;
+ bool has_sse3_;
+ bool has_ssse3_;
+ bool has_sse41_;
+ bool has_sse42_;
+ std::string cpu_vendor_;
+ std::string cpu_brand_;
+};
+
+} // namespace base
+
+#endif // BASE_CPU_H_
diff --git a/src/base/cpu_unittest.cc b/src/base/cpu_unittest.cc
new file mode 100644
index 0000000..d059dee
--- /dev/null
+++ b/src/base/cpu_unittest.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2012 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 "build/build_config.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Tests whether we can run extended instructions represented by the CPU
+// information. This test actually executes some extended instructions (such as
+// MMX, SSE, etc.) supported by the CPU and sees we can run them without
+// "undefined instruction" exceptions. That is, this test succeeds when this
+// test finishes without a crash.
+TEST(CPU, RunExtendedInstructions) {
+#if defined(ARCH_CPU_X86_FAMILY)
+ // Retrieve the CPU information.
+ base::CPU cpu;
+
+#if defined(OS_WIN)
+ ASSERT_TRUE(cpu.has_mmx());
+
+ // Execute an MMX instruction.
+ __asm emms;
+
+ if (cpu.has_sse()) {
+ // Execute an SSE instruction.
+ __asm xorps xmm0, xmm0;
+ }
+
+ if (cpu.has_sse2()) {
+ // Execute an SSE 2 instruction.
+ __asm psrldq xmm0, 0;
+ }
+
+ if (cpu.has_sse3()) {
+ // Execute an SSE 3 instruction.
+ __asm addsubpd xmm0, xmm0;
+ }
+
+ if (cpu.has_ssse3()) {
+ // Execute a Supplimental SSE 3 instruction.
+ __asm psignb xmm0, xmm0;
+ }
+
+ if (cpu.has_sse41()) {
+ // Execute an SSE 4.1 instruction.
+ __asm pmuldq xmm0, xmm0;
+ }
+
+ if (cpu.has_sse42()) {
+ // Execute an SSE 4.2 instruction.
+ __asm crc32 eax, eax;
+ }
+#elif defined(OS_POSIX) && defined(__x86_64__)
+ ASSERT_TRUE(cpu.has_mmx());
+
+ // Execute an MMX instruction.
+ __asm__ __volatile__("emms\n" : : : "mm0");
+
+ if (cpu.has_sse()) {
+ // Execute an SSE instruction.
+ __asm__ __volatile__("xorps %%xmm0, %%xmm0\n" : : : "xmm0");
+ }
+
+ if (cpu.has_sse2()) {
+ // Execute an SSE 2 instruction.
+ __asm__ __volatile__("psrldq $0, %%xmm0\n" : : : "xmm0");
+ }
+
+ if (cpu.has_sse3()) {
+ // Execute an SSE 3 instruction.
+ __asm__ __volatile__("addsubpd %%xmm0, %%xmm0\n" : : : "xmm0");
+ }
+
+ if (cpu.has_ssse3()) {
+ // Execute a Supplimental SSE 3 instruction.
+ __asm__ __volatile__("psignb %%xmm0, %%xmm0\n" : : : "xmm0");
+ }
+
+ if (cpu.has_sse41()) {
+ // Execute an SSE 4.1 instruction.
+ __asm__ __volatile__("pmuldq %%xmm0, %%xmm0\n" : : : "xmm0");
+ }
+
+ if (cpu.has_sse42()) {
+ // Execute an SSE 4.2 instruction.
+ __asm__ __volatile__("crc32 %%eax, %%eax\n" : : : "eax");
+ }
+#endif
+#endif
+}
diff --git a/src/base/critical_closure.h b/src/base/critical_closure.h
new file mode 100644
index 0000000..ca51ed5
--- /dev/null
+++ b/src/base/critical_closure.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 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_CRITICAL_CLOSURE_H_
+#define BASE_CRITICAL_CLOSURE_H_
+
+#include "base/callback.h"
+
+namespace base {
+
+// Returns a closure that will continue to run for a period of time when the
+// application goes to the background if possible on platforms where
+// applications don't execute while backgrounded, otherwise the original task is
+// returned.
+//
+// Example:
+// file_message_loop_proxy_->PostTask(
+// FROM_HERE,
+// MakeCriticalClosure(base::Bind(&WriteToDiskTask, path_, data)));
+//
+// Note new closures might be posted in this closure. If the new closures need
+// background running time, |MakeCriticalClosure| should be applied on them
+// before posting.
+#if defined(OS_IOS)
+base::Closure MakeCriticalClosure(const base::Closure& closure);
+#else
+inline base::Closure MakeCriticalClosure(const base::Closure& closure) {
+ // No-op for platforms where the application does not need to acquire
+ // background time for closures to finish when it goes into the background.
+ return closure;
+}
+#endif // !defined(OS_IOS)
+
+} // namespace base
+
+#endif // BASE_CRITICAL_CLOSURE_H_
diff --git a/src/base/critical_closure_ios.mm b/src/base/critical_closure_ios.mm
new file mode 100644
index 0000000..156612b
--- /dev/null
+++ b/src/base/critical_closure_ios.mm
@@ -0,0 +1,52 @@
+// Copyright (c) 2012 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/critical_closure.h"
+
+#import <UIKit/UIKit.h>
+
+#include "base/bind.h"
+#include "base/ios/scoped_critical_action.h"
+#include "base/memory/ref_counted.h"
+
+namespace {
+
+// This class wraps a closure so it can continue to run for a period of time
+// when the application goes to the background by using
+// |base::ios::ScopedCriticalAction|.
+class CriticalClosure : public base::RefCountedThreadSafe<CriticalClosure> {
+ public:
+ explicit CriticalClosure(base::Closure* closure) : closure_(closure) {
+ background_scope_.reset(new base::ios::ScopedCriticalAction());
+ }
+
+ void Run() {
+ closure_->Run();
+
+ background_scope_.reset();
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<CriticalClosure>;
+
+ virtual ~CriticalClosure() {}
+
+ scoped_ptr<base::Closure> closure_;
+ scoped_ptr<base::ios::ScopedCriticalAction> background_scope_;
+
+ DISALLOW_COPY_AND_ASSIGN(CriticalClosure);
+};
+
+} // namespace
+
+namespace base {
+
+base::Closure MakeCriticalClosure(const base::Closure& closure) {
+ DCHECK([[UIDevice currentDevice] isMultitaskingSupported]);
+ scoped_refptr<CriticalClosure> critical_closure(
+ new CriticalClosure(new base::Closure(closure)));
+ return base::Bind(&CriticalClosure::Run, critical_closure.get());
+}
+
+} // namespace base
diff --git a/src/base/data/file_util_unittest/binary_file.bin b/src/base/data/file_util_unittest/binary_file.bin
new file mode 100644
index 0000000..f53cc82
--- /dev/null
+++ b/src/base/data/file_util_unittest/binary_file.bin
Binary files differ
diff --git a/src/base/data/file_util_unittest/binary_file_diff.bin b/src/base/data/file_util_unittest/binary_file_diff.bin
new file mode 100644
index 0000000..103b26d
--- /dev/null
+++ b/src/base/data/file_util_unittest/binary_file_diff.bin
Binary files differ
diff --git a/src/base/data/file_util_unittest/binary_file_same.bin b/src/base/data/file_util_unittest/binary_file_same.bin
new file mode 100644
index 0000000..f53cc82
--- /dev/null
+++ b/src/base/data/file_util_unittest/binary_file_same.bin
Binary files differ
diff --git a/src/base/data/file_util_unittest/blank_line.txt b/src/base/data/file_util_unittest/blank_line.txt
new file mode 100644
index 0000000..8892069
--- /dev/null
+++ b/src/base/data/file_util_unittest/blank_line.txt
@@ -0,0 +1,3 @@
+The next line is blank.
+
+But this one isn't.
diff --git a/src/base/data/file_util_unittest/blank_line_crlf.txt b/src/base/data/file_util_unittest/blank_line_crlf.txt
new file mode 100644
index 0000000..3aefe52
--- /dev/null
+++ b/src/base/data/file_util_unittest/blank_line_crlf.txt
@@ -0,0 +1,3 @@
+The next line is blank.
+
+But this one isn't.
diff --git a/src/base/data/file_util_unittest/crlf.txt b/src/base/data/file_util_unittest/crlf.txt
new file mode 100644
index 0000000..0e62728
--- /dev/null
+++ b/src/base/data/file_util_unittest/crlf.txt
@@ -0,0 +1 @@
+This file is the same.
diff --git a/src/base/data/file_util_unittest/different.txt b/src/base/data/file_util_unittest/different.txt
new file mode 100644
index 0000000..5b9f9c4
--- /dev/null
+++ b/src/base/data/file_util_unittest/different.txt
@@ -0,0 +1 @@
+This file is different.
diff --git a/src/base/data/file_util_unittest/different_first.txt b/src/base/data/file_util_unittest/different_first.txt
new file mode 100644
index 0000000..8661d66
--- /dev/null
+++ b/src/base/data/file_util_unittest/different_first.txt
@@ -0,0 +1 @@
+this file is the same.
diff --git a/src/base/data/file_util_unittest/different_last.txt b/src/base/data/file_util_unittest/different_last.txt
new file mode 100644
index 0000000..e8b3e5a
--- /dev/null
+++ b/src/base/data/file_util_unittest/different_last.txt
@@ -0,0 +1 @@
+This file is the same.
\ No newline at end of file
diff --git a/src/base/data/file_util_unittest/empty1.txt b/src/base/data/file_util_unittest/empty1.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/base/data/file_util_unittest/empty1.txt
diff --git a/src/base/data/file_util_unittest/empty2.txt b/src/base/data/file_util_unittest/empty2.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/base/data/file_util_unittest/empty2.txt
diff --git a/src/base/data/file_util_unittest/first1.txt b/src/base/data/file_util_unittest/first1.txt
new file mode 100644
index 0000000..2c6e300
--- /dev/null
+++ b/src/base/data/file_util_unittest/first1.txt
@@ -0,0 +1,2 @@
+The first line is the same.
+The second line is different.
diff --git a/src/base/data/file_util_unittest/first2.txt b/src/base/data/file_util_unittest/first2.txt
new file mode 100644
index 0000000..e39b5ec
--- /dev/null
+++ b/src/base/data/file_util_unittest/first2.txt
@@ -0,0 +1,2 @@
+The first line is the same.
+The second line is not.
diff --git a/src/base/data/file_util_unittest/original.txt b/src/base/data/file_util_unittest/original.txt
new file mode 100644
index 0000000..4422f57
--- /dev/null
+++ b/src/base/data/file_util_unittest/original.txt
@@ -0,0 +1 @@
+This file is the same.
diff --git a/src/base/data/file_util_unittest/same.txt b/src/base/data/file_util_unittest/same.txt
new file mode 100644
index 0000000..4422f57
--- /dev/null
+++ b/src/base/data/file_util_unittest/same.txt
@@ -0,0 +1 @@
+This file is the same.
diff --git a/src/base/data/file_util_unittest/same_length.txt b/src/base/data/file_util_unittest/same_length.txt
new file mode 100644
index 0000000..157405c
--- /dev/null
+++ b/src/base/data/file_util_unittest/same_length.txt
@@ -0,0 +1 @@
+This file is not same.
diff --git a/src/base/data/file_util_unittest/shortened.txt b/src/base/data/file_util_unittest/shortened.txt
new file mode 100644
index 0000000..2bee82c
--- /dev/null
+++ b/src/base/data/file_util_unittest/shortened.txt
@@ -0,0 +1 @@
+This file is the
\ No newline at end of file
diff --git a/src/base/data/file_version_info_unittest/FileVersionInfoTest1.dll b/src/base/data/file_version_info_unittest/FileVersionInfoTest1.dll
new file mode 100755
index 0000000..bdf8dc0
--- /dev/null
+++ b/src/base/data/file_version_info_unittest/FileVersionInfoTest1.dll
Binary files differ
diff --git a/src/base/data/file_version_info_unittest/FileVersionInfoTest2.dll b/src/base/data/file_version_info_unittest/FileVersionInfoTest2.dll
new file mode 100755
index 0000000..51e7966
--- /dev/null
+++ b/src/base/data/file_version_info_unittest/FileVersionInfoTest2.dll
Binary files differ
diff --git a/src/base/data/json/bom_feff.json b/src/base/data/json/bom_feff.json
new file mode 100644
index 0000000..b05ae50
--- /dev/null
+++ b/src/base/data/json/bom_feff.json
@@ -0,0 +1,10 @@
+{
+ "appName": {
+ "message": "Gmail",
+ "description": "App name."
+ },
+ "appDesc": {
+ "message": "بريد إلكتروني يوفر إمكانية البحث مع مقدار أقل من الرسائل غير المرغوب فيها.",
+ "description":"App description."
+ }
+}
\ No newline at end of file
diff --git a/src/base/debug/alias.cc b/src/base/debug/alias.cc
new file mode 100644
index 0000000..6b0caaa
--- /dev/null
+++ b/src/base/debug/alias.cc
@@ -0,0 +1,23 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/debug/alias.h"
+#include "build/build_config.h"
+
+namespace base {
+namespace debug {
+
+#if defined(COMPILER_MSVC)
+#pragma optimize("", off)
+#endif
+
+void Alias(const void* var) {
+}
+
+#if defined(COMPILER_MSVC)
+#pragma optimize("", on)
+#endif
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/alias.h b/src/base/debug/alias.h
new file mode 100644
index 0000000..3b2ab64
--- /dev/null
+++ b/src/base/debug/alias.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_DEBUG_ALIAS_H_
+#define BASE_DEBUG_ALIAS_H_
+
+#include "base/base_export.h"
+
+namespace base {
+namespace debug {
+
+// Make the optimizer think that var is aliased. This is to prevent it from
+// optimizing out variables that that would not otherwise be live at the point
+// of a potential crash.
+void BASE_EXPORT Alias(const void* var);
+
+} // namespace debug
+} // namespace base
+
+#endif // BASE_DEBUG_ALIAS_H_
diff --git a/src/base/debug/debug_on_start_win.cc b/src/base/debug/debug_on_start_win.cc
new file mode 100644
index 0000000..6ca88dd
--- /dev/null
+++ b/src/base/debug/debug_on_start_win.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/debug/debug_on_start_win.h"
+
+#include <windows.h>
+
+#include "base/base_switches.h"
+#include "base/basictypes.h"
+#include "base/debug/debugger.h"
+
+namespace base {
+namespace debug {
+
+// 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 char* argument_c) {
+ wchar_t argument[50] = {};
+ for (int i = 0; argument_c[i]; ++i)
+ argument[i] = argument_c[i];
+
+ 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.
+ base::debug::SpawnDebuggerOnProcess(GetCurrentProcessId());
+
+ // Wait for a debugger to come take us.
+ base::debug::WaitForDebugger(60, false);
+ } else if (FindArgument(GetCommandLine(), switches::kWaitForDebugger)) {
+ // Wait for a debugger to come take us.
+ base::debug::WaitForDebugger(60, true);
+ }
+ return 0;
+}
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/debug_on_start_win.h b/src/base/debug/debug_on_start_win.h
new file mode 100644
index 0000000..edcaa0a
--- /dev/null
+++ b/src/base/debug/debug_on_start_win.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// 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_DEBUG_ON_START_WIN_H_
+#define BASE_DEBUG_DEBUG_ON_START_WIN_H_
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+
+// This only works on Windows. It's legal to include on other platforms, but
+// will be a NOP.
+#if defined(OS_WIN)
+
+#ifndef DECLSPEC_SELECTANY
+#define DECLSPEC_SELECTANY __declspec(selectany)
+#endif
+
+namespace base {
+namespace debug {
+
+// There is no way for this code, as currently implemented, to work across DLLs.
+// TODO(rvargas): It looks like we really don't use this code, at least not for
+// Chrome. Figure out if it's really worth implementing something simpler.
+#if !defined(COMPONENT_BUILD)
+
+// 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 char* 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(COMPONENT_BUILD)
+
+} // namespace debug
+} // namespace base
+
+#endif // defined(OS_WIN)
+
+#endif // BASE_DEBUG_DEBUG_ON_START_WIN_H_
diff --git a/src/base/debug/debugger.cc b/src/base/debug/debugger.cc
new file mode 100644
index 0000000..a297ed1
--- /dev/null
+++ b/src/base/debug/debugger.cc
@@ -0,0 +1,47 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/debug/debugger.h"
+#include "base/logging.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+namespace debug {
+
+static bool is_debug_ui_suppressed = false;
+
+bool WaitForDebugger(int wait_seconds, bool silent) {
+#if defined(OS_ANDROID)
+ // The pid from which we know which process to attach to are not output by
+ // android ddms, so we have to print it out explicitly.
+ DLOG(INFO) << "DebugUtil::WaitForDebugger(pid=" << static_cast<int>(getpid())
+ << ")";
+#endif
+ for (int i = 0; i < wait_seconds * 10; ++i) {
+ if (BeingDebugged()) {
+ if (!silent) {
+#if defined(__LB_ANDROID__)
+ // Hack around a race condition where the debugger is still attaching
+ // and isn't ready to catch the trap thrown by BreakDebugger yet.
+ PlatformThread::Sleep(TimeDelta::FromSeconds(5));
+#endif
+ BreakDebugger();
+ }
+ return true;
+ }
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+ }
+ return false;
+}
+
+void SetSuppressDebugUI(bool suppress) {
+ is_debug_ui_suppressed = suppress;
+}
+
+bool IsDebugUISuppressed() {
+ return is_debug_ui_suppressed;
+}
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/debugger.h b/src/base/debug/debugger.h
new file mode 100644
index 0000000..4f368d9
--- /dev/null
+++ b/src/base/debug/debugger.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// 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_DEBUGGER_H
+#define BASE_DEBUG_DEBUGGER_H
+
+#include "base/base_export.h"
+
+namespace base {
+namespace debug {
+
+// Starts the registered system-wide JIT debugger to attach it to specified
+// process.
+BASE_EXPORT 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.
+BASE_EXPORT bool WaitForDebugger(int wait_seconds, bool silent);
+
+// Returns true if the given process is being run 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.
+BASE_EXPORT bool BeingDebugged();
+
+// Break into the debugger, assumes a debugger is present.
+BASE_EXPORT void BreakDebugger();
+
+// Used in test code, this controls whether showing dialogs and breaking into
+// the debugger is suppressed for debug errors, even in debug mode (normally
+// release mode doesn't do this stuff -- this is controlled separately).
+// Normally UI is not suppressed. This is normally used when running automated
+// tests where we want a crash rather than a dialog or a debugger.
+BASE_EXPORT void SetSuppressDebugUI(bool suppress);
+BASE_EXPORT bool IsDebugUISuppressed();
+
+} // namespace debug
+} // namespace base
+
+#endif // BASE_DEBUG_DEBUGGER_H
diff --git a/src/base/debug/debugger_posix.cc b/src/base/debug/debugger_posix.cc
new file mode 100644
index 0000000..edfbbe1
--- /dev/null
+++ b/src/base/debug/debugger_posix.cc
@@ -0,0 +1,273 @@
+// Copyright (c) 2012 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/debugger.h"
+#include "build/build_config.h"
+
+#if defined(__LB_SHELL__)
+#include "lb_platform.h"
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#if !defined(OS_ANDROID) && !defined(OS_NACL)
+#include <execinfo.h>
+#endif
+
+#if defined(__GLIBCXX__)
+#include <cxxabi.h>
+#endif
+
+#if defined(OS_MACOSX)
+#include <AvailabilityMacros.h>
+#endif
+
+#if defined(OS_MACOSX) || defined(OS_BSD)
+#include <sys/sysctl.h>
+#endif
+
+#if defined(OS_FREEBSD)
+#include <sys/user.h>
+#endif
+
+#include <ostream>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/safe_strerror_posix.h"
+#include "base/string_piece.h"
+#include "base/stringprintf.h"
+
+#if defined(USE_SYMBOLIZE)
+#include "base/third_party/symbolize/symbolize.h"
+#endif
+
+#if defined(OS_ANDROID)
+#include "base/threading/platform_thread.h"
+#endif
+
+namespace base {
+namespace debug {
+
+bool SpawnDebuggerOnProcess(unsigned process_id) {
+#if OS_ANDROID || OS_NACL || defined(__LB_SHELL__)
+ NOTIMPLEMENTED();
+ return false;
+#else
+ const std::string debug_cmd =
+ StringPrintf("xterm -e 'gdb --pid=%u' &", process_id);
+ LOG(WARNING) << "Starting debugger on pid " << process_id
+ << " with command `" << debug_cmd << "`";
+ int ret = system(debug_cmd.c_str());
+ if (ret == -1)
+ return false;
+ return true;
+#endif
+}
+
+#if defined(OS_MACOSX) || defined(OS_BSD)
+
+// Based on Apple's recommended method as described in
+// http://developer.apple.com/qa/qa2004/qa1361.html
+bool BeingDebugged() {
+ // NOTE: This code MUST be async-signal safe (it's used by in-process
+ // stack dumping signal handler). NO malloc or stdio is allowed here.
+ //
+ // While some code used below may be async-signal unsafe, note how
+ // the result is cached (see |is_set| and |being_debugged| static variables
+ // right below). If this code is properly warmed-up early
+ // in the start-up process, it should be safe to use later.
+
+ // 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()
+#if defined(OS_OPENBSD)
+ , sizeof(struct kinfo_proc),
+ 0
+#endif
+ };
+
+ // 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);
+
+#if defined(OS_OPENBSD)
+ if (sysctl(mib, arraysize(mib), NULL, &info_size, NULL, 0) < 0)
+ return -1;
+
+ mib[5] = (info_size / sizeof(struct kinfo_proc));
+#endif
+
+ int sysctl_result = sysctl(mib, arraysize(mib), &info, &info_size, NULL, 0);
+ DCHECK_EQ(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;
+#if defined(OS_FREEBSD)
+ being_debugged = (info.ki_flag & P_TRACED) != 0;
+#elif defined(OS_BSD)
+ being_debugged = (info.p_flag & P_TRACED) != 0;
+#else
+ being_debugged = (info.kp_proc.p_flag & P_TRACED) != 0;
+#endif
+ return being_debugged;
+}
+
+#elif defined(OS_LINUX) || defined(OS_ANDROID)
+
+// 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 BeingDebugged() {
+ // NOTE: This code MUST be async-signal safe (it's used by in-process
+ // stack dumping signal handler). NO malloc or stdio is allowed here.
+
+ 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)));
+ if (HANDLE_EINTR(close(status_fd)) < 0)
+ return false;
+
+ 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';
+}
+
+#elif defined(__LB_SHELL__)
+
+bool BeingDebugged() {
+ return LB::Platform::BeingDebugged();
+}
+
+#else
+
+bool BeingDebugged() {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+#endif
+
+// We want to break into the debugger in Debug mode, and cause a crash dump in
+// Release mode. Breakpad behaves as follows:
+//
+// +-------+-----------------+-----------------+
+// | OS | Dump on SIGTRAP | Dump on SIGABRT |
+// +-------+-----------------+-----------------+
+// | Linux | N | Y |
+// | Mac | Y | N |
+// +-------+-----------------+-----------------+
+//
+// Thus we do the following:
+// Linux: Debug mode, send SIGTRAP; Release mode, send SIGABRT.
+// Mac: Always send SIGTRAP.
+
+#if defined(__LB_SHELL__)
+#define DEBUG_BREAK() LB::Platform::DEBUG_BREAK()
+#elif defined(NDEBUG) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
+#define DEBUG_BREAK() abort()
+#elif defined(OS_NACL)
+// The NaCl verifier doesn't let use use int3. For now, we call abort(). We
+// should ask for advice from some NaCl experts about the optimum thing here.
+// http://code.google.com/p/nativeclient/issues/detail?id=645
+#define DEBUG_BREAK() abort()
+
+#elif defined(ARCH_CPU_ARM_FAMILY)
+#if defined(OS_ANDROID)
+// Though Android has a "helpful" process called debuggerd to catch native
+// signals on the general assumption that they are fatal errors. The bkpt
+// instruction appears to cause SIGBUS which is trapped by debuggerd, and
+// we've had great difficulty continuing in a debugger once we stop from
+// SIG triggered by native code.
+//
+// Use GDB to set |go| to 1 to resume execution.
+#define DEBUG_BREAK() do { \
+ if (!BeingDebugged()) { \
+ abort(); \
+ } else { \
+ volatile int go = 0; \
+ while (!go) { \
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); \
+ } \
+ } \
+} while (0)
+#else
+// ARM && !ANDROID
+#define DEBUG_BREAK() asm("bkpt 0")
+#endif
+#elif defined(ARCH_CPU_MIPS_FAMILY)
+#define DEBUG_BREAK() asm("break 2")
+#else
+#define DEBUG_BREAK() asm("int3")
+#endif
+
+void BreakDebugger() {
+ // NOTE: This code MUST be async-signal safe (it's used by in-process
+ // stack dumping signal handler). NO malloc or stdio is allowed here.
+
+ DEBUG_BREAK();
+#if (defined(OS_ANDROID) && !defined(OFFICIAL_BUILD)) || \
+ (defined(__LB_ANDROID__) && !defined(__LB_SHELL__FOR_RELEASE__))
+ // For Android development we always build release (debug builds are
+ // unmanageably large), so the unofficial build is used for debugging. It is
+ // helpful to be able to insert BreakDebugger() statements in the source,
+ // attach the debugger, inspect the state of the program and then resume it by
+ // setting the 'go' variable above.
+#elif defined(NDEBUG)
+ // Terminate the program after signaling the debug break.
+ _exit(1);
+#endif
+}
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/debugger_shell.cc b/src/base/debug/debugger_shell.cc
new file mode 100644
index 0000000..855777b
--- /dev/null
+++ b/src/base/debug/debugger_shell.cc
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2012 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// We use the same code as posix version
+#if !defined(COBALT_WIN)
+#include "debugger_posix.cc"
+#endif
diff --git a/src/base/debug/debugger_starboard.cc b/src/base/debug/debugger_starboard.cc
new file mode 100644
index 0000000..ee9e762
--- /dev/null
+++ b/src/base/debug/debugger_starboard.cc
@@ -0,0 +1,41 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/debug/stack_trace.h"
+
+#include "base/logging.h"
+#include "starboard/system.h"
+
+namespace base {
+namespace debug {
+
+bool SpawnDebuggerOnProcess(unsigned process_id) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool BeingDebugged() {
+#if defined(__LB_SHELL__FOR_RELEASE__)
+ return false;
+#else
+ return SbSystemIsDebuggerAttached();
+#endif
+}
+
+void BreakDebugger() {
+ SbSystemBreakIntoDebugger();
+}
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/debugger_win.cc b/src/base/debug/debugger_win.cc
new file mode 100644
index 0000000..b13dbfd
--- /dev/null
+++ b/src/base/debug/debugger_win.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/debug/debugger.h"
+
+#include <windows.h>
+#include <dbghelp.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace base {
+namespace debug {
+
+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;
+}
+
+} // namespace
+
+// Note: Does not use the CRT.
+bool 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;
+}
+
+bool BeingDebugged() {
+ return ::IsDebuggerPresent() != 0;
+}
+
+void BreakDebugger() {
+ if (IsDebugUISuppressed())
+ _exit(1);
+ __debugbreak();
+#if defined(NDEBUG)
+ _exit(1);
+#endif
+}
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/leak_annotations.h b/src/base/debug/leak_annotations.h
new file mode 100644
index 0000000..97be127
--- /dev/null
+++ b/src/base/debug/leak_annotations.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_DEBUG_LEAK_ANNOTATIONS_H_
+#define BASE_DEBUG_LEAK_ANNOTATIONS_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_NACL) && \
+ defined(USE_HEAPCHECKER)
+
+#include "third_party/tcmalloc/chromium/src/gperftools/heap-checker.h"
+
+// Annotate a program scope as having memory leaks. Tcmalloc's heap leak
+// checker will ignore them. Note that these annotations may mask real bugs
+// and should not be used in the production code.
+#define ANNOTATE_SCOPED_MEMORY_LEAK \
+ HeapLeakChecker::Disabler heap_leak_checker_disabler
+
+// Annotate an object pointer as referencing a leaky object. This object and all
+// the heap objects referenced by it will be ignored by the heap checker.
+//
+// X should be referencing an active allocated object. If it is not, the
+// annotation will be ignored.
+// No object should be annotated with ANNOTATE_SCOPED_MEMORY_LEAK twice.
+// Once an object is annotated with ANNOTATE_SCOPED_MEMORY_LEAK, it cannot be
+// deleted.
+#define ANNOTATE_LEAKING_OBJECT_PTR(X) \
+ HeapLeakChecker::IgnoreObject(X)
+
+#elif defined(ADDRESS_SANITIZER)
+#include <sanitizer/lsan_interface.h>
+
+class ScopedLeakSanitizerDisabler {
+ public:
+ ScopedLeakSanitizerDisabler() { __lsan_disable(); }
+ ~ScopedLeakSanitizerDisabler() { __lsan_enable(); }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedLeakSanitizerDisabler);
+};
+
+#define ANNOTATE_SCOPED_MEMORY_LEAK \
+ ScopedLeakSanitizerDisabler leak_sanitizer_disabler; static_cast<void>(0)
+
+#define ANNOTATE_LEAKING_OBJECT_PTR(X) __lsan_ignore_object(X);
+
+#else
+
+// If tcmalloc is not used, the annotations should be no-ops.
+#define ANNOTATE_SCOPED_MEMORY_LEAK ((void)0)
+#define ANNOTATE_LEAKING_OBJECT_PTR(X) ((void)0)
+
+#endif
+
+#endif // BASE_DEBUG_LEAK_ANNOTATIONS_H_
diff --git a/src/base/debug/leak_tracker.h b/src/base/debug/leak_tracker.h
new file mode 100644
index 0000000..93cea39
--- /dev/null
+++ b/src/base/debug/leak_tracker.h
@@ -0,0 +1,136 @@
+// Copyright (c) 2012 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_DEBUG_LEAK_TRACKER_H_
+#define BASE_DEBUG_LEAK_TRACKER_H_
+
+// Only enable leak tracking in debug builds.
+#ifndef NDEBUG
+#define ENABLE_LEAK_TRACKER
+#endif
+
+#ifdef ENABLE_LEAK_TRACKER
+#include "base/containers/linked_list.h"
+#include "base/debug/stack_trace.h"
+#include "base/logging.h"
+#endif // ENABLE_LEAK_TRACKER
+
+// LeakTracker is a helper to verify that all instances of a class
+// have been destroyed.
+//
+// It is particularly useful for classes that are bound to a single thread --
+// before destroying that thread, one can check that there are no remaining
+// instances of that class.
+//
+// For example, to enable leak tracking for class net::URLRequest, start by
+// adding a member variable of type LeakTracker<net::URLRequest>.
+//
+// class URLRequest {
+// ...
+// private:
+// base::LeakTracker<URLRequest> leak_tracker_;
+// };
+//
+//
+// Next, when we believe all instances of net::URLRequest have been deleted:
+//
+// LeakTracker<net::URLRequest>::CheckForLeaks();
+//
+// Should the check fail (because there are live instances of net::URLRequest),
+// then the allocation callstack for each leaked instances is dumped to
+// the error log.
+//
+// If ENABLE_LEAK_TRACKER is not defined, then the check has no effect.
+
+namespace base {
+namespace debug {
+
+#ifndef ENABLE_LEAK_TRACKER
+
+// If leak tracking is disabled, do nothing.
+template<typename T>
+class LeakTracker {
+ public:
+ ~LeakTracker() {}
+ static void CheckForLeaks() {}
+ static int NumLiveInstances() { return -1; }
+};
+
+#else
+
+// If leak tracking is enabled we track where the object was allocated from.
+
+template<typename T>
+class LeakTracker : public LinkNode<LeakTracker<T> > {
+ public:
+ LeakTracker() {
+ instances()->Append(this);
+ }
+
+ ~LeakTracker() {
+ this->RemoveFromList();
+ }
+
+ static void CheckForLeaks() {
+ // Walk the allocation list and print each entry it contains.
+ size_t count = 0;
+
+ // Copy the first 3 leak allocation callstacks onto the stack.
+ // This way if we hit the CHECK() in a release build, the leak
+ // information will be available in mini-dump.
+ const size_t kMaxStackTracesToCopyOntoStack = 3;
+ StackTrace stacktraces[kMaxStackTracesToCopyOntoStack];
+
+ for (LinkNode<LeakTracker<T> >* node = instances()->head();
+ node != instances()->end();
+ node = node->next()) {
+ StackTrace& allocation_stack = node->value()->allocation_stack_;
+
+ if (count < kMaxStackTracesToCopyOntoStack)
+ stacktraces[count] = allocation_stack;
+
+ ++count;
+ if (LOG_IS_ON(ERROR)) {
+ LOG_STREAM(ERROR) << "Leaked " << node << " which was allocated by:";
+ allocation_stack.OutputToStream(&LOG_STREAM(ERROR));
+ }
+ }
+
+ CHECK_EQ(0u, count);
+
+ // Hack to keep |stacktraces| and |count| alive (so compiler
+ // doesn't optimize it out, and it will appear in mini-dumps).
+ if (count == 0x1234) {
+ for (size_t i = 0; i < kMaxStackTracesToCopyOntoStack; ++i)
+ stacktraces[i].PrintBacktrace();
+ }
+ }
+
+ static int NumLiveInstances() {
+ // Walk the allocation list and count how many entries it has.
+ int count = 0;
+ for (LinkNode<LeakTracker<T> >* node = instances()->head();
+ node != instances()->end();
+ node = node->next()) {
+ ++count;
+ }
+ return count;
+ }
+
+ private:
+ // Each specialization of LeakTracker gets its own static storage.
+ static LinkedList<LeakTracker<T> >* instances() {
+ static LinkedList<LeakTracker<T> > list;
+ return &list;
+ }
+
+ StackTrace allocation_stack_;
+};
+
+#endif // ENABLE_LEAK_TRACKER
+
+} // namespace debug
+} // namespace base
+
+#endif // BASE_DEBUG_LEAK_TRACKER_H_
diff --git a/src/base/debug/leak_tracker_unittest.cc b/src/base/debug/leak_tracker_unittest.cc
new file mode 100644
index 0000000..99df4c1
--- /dev/null
+++ b/src/base/debug/leak_tracker_unittest.cc
@@ -0,0 +1,113 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/debug/leak_tracker.h"
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace debug {
+
+namespace {
+
+class ClassA {
+ private:
+ LeakTracker<ClassA> leak_tracker_;
+};
+
+class ClassB {
+ private:
+ LeakTracker<ClassB> leak_tracker_;
+};
+
+#ifndef ENABLE_LEAK_TRACKER
+
+// If leak tracking is disabled, we should do nothing.
+TEST(LeakTrackerTest, NotEnabled) {
+ EXPECT_EQ(-1, LeakTracker<ClassA>::NumLiveInstances());
+ EXPECT_EQ(-1, LeakTracker<ClassB>::NumLiveInstances());
+
+ // Use scoped_ptr so compiler doesn't complain about unused variables.
+ scoped_ptr<ClassA> a1(new ClassA);
+ scoped_ptr<ClassB> b1(new ClassB);
+ scoped_ptr<ClassB> b2(new ClassB);
+
+ EXPECT_EQ(-1, LeakTracker<ClassA>::NumLiveInstances());
+ EXPECT_EQ(-1, LeakTracker<ClassB>::NumLiveInstances());
+}
+
+#else
+
+TEST(LeakTrackerTest, Basic) {
+ {
+ ClassA a1;
+
+ EXPECT_EQ(1, LeakTracker<ClassA>::NumLiveInstances());
+ EXPECT_EQ(0, LeakTracker<ClassB>::NumLiveInstances());
+
+ ClassB b1;
+ ClassB b2;
+
+ EXPECT_EQ(1, LeakTracker<ClassA>::NumLiveInstances());
+ EXPECT_EQ(2, LeakTracker<ClassB>::NumLiveInstances());
+
+ scoped_ptr<ClassA> a2(new ClassA);
+
+ EXPECT_EQ(2, LeakTracker<ClassA>::NumLiveInstances());
+ EXPECT_EQ(2, LeakTracker<ClassB>::NumLiveInstances());
+
+ a2.reset();
+
+ EXPECT_EQ(1, LeakTracker<ClassA>::NumLiveInstances());
+ EXPECT_EQ(2, LeakTracker<ClassB>::NumLiveInstances());
+ }
+
+ EXPECT_EQ(0, LeakTracker<ClassA>::NumLiveInstances());
+ EXPECT_EQ(0, LeakTracker<ClassB>::NumLiveInstances());
+}
+
+// Try some orderings of create/remove to hit different cases in the linked-list
+// assembly.
+TEST(LeakTrackerTest, LinkedList) {
+ EXPECT_EQ(0, LeakTracker<ClassB>::NumLiveInstances());
+
+ scoped_ptr<ClassA> a1(new ClassA);
+ scoped_ptr<ClassA> a2(new ClassA);
+ scoped_ptr<ClassA> a3(new ClassA);
+ scoped_ptr<ClassA> a4(new ClassA);
+
+ EXPECT_EQ(4, LeakTracker<ClassA>::NumLiveInstances());
+
+ // Remove the head of the list (a1).
+ a1.reset();
+ EXPECT_EQ(3, LeakTracker<ClassA>::NumLiveInstances());
+
+ // Remove the tail of the list (a4).
+ a4.reset();
+ EXPECT_EQ(2, LeakTracker<ClassA>::NumLiveInstances());
+
+ // Append to the new tail of the list (a3).
+ scoped_ptr<ClassA> a5(new ClassA);
+ EXPECT_EQ(3, LeakTracker<ClassA>::NumLiveInstances());
+
+ a2.reset();
+ a3.reset();
+
+ EXPECT_EQ(1, LeakTracker<ClassA>::NumLiveInstances());
+
+ a5.reset();
+ EXPECT_EQ(0, LeakTracker<ClassA>::NumLiveInstances());
+}
+
+TEST(LeakTrackerTest, NoOpCheckForLeaks) {
+ // There are no live instances of ClassA, so this should do nothing.
+ LeakTracker<ClassA>::CheckForLeaks();
+}
+
+#endif // ENABLE_LEAK_TRACKER
+
+} // namespace
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/profiler.cc b/src/base/debug/profiler.cc
new file mode 100644
index 0000000..de79124
--- /dev/null
+++ b/src/base/debug/profiler.cc
@@ -0,0 +1,177 @@
+// Copyright (c) 2012 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/profiler.h"
+
+#include <string>
+
+#include "base/process_util.h"
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+
+#if defined(OS_WIN)
+#include "base/win/pe_image.h"
+#endif // defined(OS_WIN)
+
+#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC)
+#include "third_party/tcmalloc/chromium/src/gperftools/profiler.h"
+#endif
+
+namespace base {
+namespace debug {
+
+#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC)
+
+static int profile_count = 0;
+
+void StartProfiling(const std::string& name) {
+ ++profile_count;
+ std::string full_name(name);
+ std::string pid = StringPrintf("%d", GetCurrentProcId());
+ std::string count = StringPrintf("%d", profile_count);
+ ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid);
+ ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count);
+ ProfilerStart(full_name.c_str());
+}
+
+void StopProfiling() {
+ ProfilerFlush();
+ ProfilerStop();
+}
+
+void FlushProfiling() {
+ ProfilerFlush();
+}
+
+bool BeingProfiled() {
+ return ProfilingIsEnabledForAllThreads();
+}
+
+void RestartProfilingAfterFork() {
+ ProfilerRegisterThread();
+}
+
+#else
+
+void StartProfiling(const std::string& name) {
+}
+
+void StopProfiling() {
+}
+
+void FlushProfiling() {
+}
+
+bool BeingProfiled() {
+ return false;
+}
+
+void RestartProfilingAfterFork() {
+}
+
+#endif
+
+#if !defined(OS_WIN)
+
+bool IsBinaryInstrumented() {
+ return false;
+}
+
+ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
+ return NULL;
+}
+
+#else // defined(OS_WIN)
+
+// http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
+extern "C" IMAGE_DOS_HEADER __ImageBase;
+
+bool IsBinaryInstrumented() {
+ enum InstrumentationCheckState {
+ UNINITIALIZED,
+ INSTRUMENTED_IMAGE,
+ NON_INSTRUMENTED_IMAGE,
+ };
+
+ static InstrumentationCheckState state = UNINITIALIZED;
+
+ if (state == UNINITIALIZED) {
+ HMODULE this_module = reinterpret_cast<HMODULE>(&__ImageBase);
+ base::win::PEImage image(this_module);
+
+ // Check to be sure our image is structured as we'd expect.
+ DCHECK(image.VerifyMagic());
+
+ // Syzygy-instrumented binaries contain a PE image section named ".thunks",
+ // and all Syzygy-modified binaries contain the ".syzygy" image section.
+ // This is a very fast check, as it only looks at the image header.
+ if ((image.GetImageSectionHeaderByName(".thunks") != NULL) &&
+ (image.GetImageSectionHeaderByName(".syzygy") != NULL)) {
+ state = INSTRUMENTED_IMAGE;
+ } else {
+ state = NON_INSTRUMENTED_IMAGE;
+ }
+ }
+ DCHECK(state != UNINITIALIZED);
+
+ return state == INSTRUMENTED_IMAGE;
+}
+
+// Callback function to PEImage::EnumImportChunks.
+static bool FindResolutionFunctionInImports(
+ const base::win::PEImage &image, const char* module_name,
+ PIMAGE_THUNK_DATA unused_name_table, PIMAGE_THUNK_DATA import_address_table,
+ PVOID cookie) {
+ // Our import address table contains pointers to the functions we import
+ // at this point. Let's retrieve the first such function and use it to
+ // find the module this import was resolved to by the loader.
+ const wchar_t* function_in_module =
+ reinterpret_cast<const wchar_t*>(import_address_table->u1.Function);
+
+ // Retrieve the module by a function in the module.
+ const DWORD kFlags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
+ HMODULE module = NULL;
+ if (!::GetModuleHandleEx(kFlags, function_in_module, &module)) {
+ // This can happen if someone IAT patches us to a thunk.
+ return true;
+ }
+
+ // See whether this module exports the function we're looking for.
+ ReturnAddressLocationResolver exported_func =
+ reinterpret_cast<ReturnAddressLocationResolver>(
+ ::GetProcAddress(module, "ResolveReturnAddressLocation"));
+
+ if (exported_func != NULL) {
+ ReturnAddressLocationResolver* resolver_func =
+ reinterpret_cast<ReturnAddressLocationResolver*>(cookie);
+ DCHECK(resolver_func != NULL);
+ DCHECK(*resolver_func == NULL);
+
+ // We found it, return the function and terminate the enumeration.
+ *resolver_func = exported_func;
+ return false;
+ }
+
+ // Keep going.
+ return true;
+}
+
+ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
+ if (!IsBinaryInstrumented())
+ return NULL;
+
+ HMODULE this_module = reinterpret_cast<HMODULE>(&__ImageBase);
+ base::win::PEImage image(this_module);
+
+ ReturnAddressLocationResolver resolver_func = NULL;
+ image.EnumImportChunks(FindResolutionFunctionInImports, &resolver_func);
+
+ return resolver_func;
+}
+
+#endif // defined(OS_WIN)
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/profiler.h b/src/base/debug/profiler.h
new file mode 100644
index 0000000..d703876
--- /dev/null
+++ b/src/base/debug/profiler.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 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_DEBUG_PROFILER_H
+#define BASE_DEBUG_PROFILER_H
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+// The Profiler functions allow usage of the underlying sampling based
+// profiler. If the application has not been built with the necessary
+// flags (-DENABLE_PROFILING and not -DNO_TCMALLOC) then these functions
+// are noops.
+namespace base {
+namespace debug {
+
+// Start profiling with the supplied name.
+// {pid} will be replaced by the process' pid and {count} will be replaced
+// by the count of the profile run (starts at 1 with each process).
+BASE_EXPORT void StartProfiling(const std::string& name);
+
+// Stop profiling and write out data.
+BASE_EXPORT void StopProfiling();
+
+// Force data to be written to file.
+BASE_EXPORT void FlushProfiling();
+
+// Returns true if process is being profiled.
+BASE_EXPORT bool BeingProfiled();
+
+// Reset profiling after a fork, which disables timers.
+BASE_EXPORT void RestartProfilingAfterFork();
+
+// Returns true iff this executable is instrumented with the Syzygy profiler.
+BASE_EXPORT bool IsBinaryInstrumented();
+
+// There's a class of profilers that use "return address swizzling" to get a
+// hook on function exits. This class of profilers uses some form of entry hook,
+// like e.g. binary instrumentation, or a compiler flag, that calls a hook each
+// time a function is invoked. The hook then switches the return address on the
+// stack for the address of an exit hook function, and pushes the original
+// return address to a shadow stack of some type. When in due course the CPU
+// executes a return to the exit hook, the exit hook will do whatever work it
+// does on function exit, then arrange to return to the original return address.
+// This class of profiler does not play well with programs that look at the
+// return address, as does e.g. V8. V8 uses the return address to certain
+// runtime functions to find the JIT code that called it, and from there finds
+// the V8 data structures associated to the JS function involved.
+// A return address resolution function is used to fix this. It allows such
+// programs to resolve a location on stack where a return address originally
+// resided, to the shadow stack location where the profiler stashed it.
+typedef uintptr_t (*ReturnAddressLocationResolver)(
+ uintptr_t return_addr_location);
+
+// If this binary is instrumented and the instrumentation supplies a return
+// address resolution function, finds and returns the address resolution
+// function. Otherwise returns NULL.
+BASE_EXPORT ReturnAddressLocationResolver
+ GetProfilerReturnAddrResolutionFunc();
+
+} // namespace debug
+} // namespace base
+
+#endif // BASE_DEBUG_DEBUGGER_H
diff --git a/src/base/debug/stack_trace.cc b/src/base/debug/stack_trace.cc
new file mode 100644
index 0000000..6fab183
--- /dev/null
+++ b/src/base/debug/stack_trace.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 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/stack_trace.h"
+
+#include "base/basictypes.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <sstream>
+
+namespace base {
+namespace debug {
+
+StackTrace::StackTrace(const void* const* trace, size_t count) {
+ count = std::min(count, arraysize(trace_));
+ if (count)
+ memcpy(trace_, trace, count * sizeof(trace_[0]));
+ count_ = count;
+}
+
+StackTrace::~StackTrace() {
+}
+
+const void *const *StackTrace::Addresses(size_t* count) const {
+ *count = count_;
+ if (count_)
+ return trace_;
+ return NULL;
+}
+
+std::string StackTrace::ToString() const {
+ std::stringstream stream;
+ OutputToStream(&stream);
+ return stream.str();
+}
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/stack_trace.h b/src/base/debug/stack_trace.h
new file mode 100644
index 0000000..be3395d
--- /dev/null
+++ b/src/base/debug/stack_trace.h
@@ -0,0 +1,97 @@
+// Copyright (c) 2012 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_DEBUG_STACK_TRACE_H_
+#define BASE_DEBUG_STACK_TRACE_H_
+
+#include <iosfwd>
+#include <string>
+
+#include "base/base_export.h"
+#include "build/build_config.h"
+
+#if defined(OS_POSIX)
+#include <unistd.h>
+#endif
+
+#if defined(OS_WIN) || defined(COMPILER_MSVC)
+struct _EXCEPTION_POINTERS;
+#endif
+
+namespace base {
+namespace debug {
+
+// Enables stack dump to console output on exception and signals.
+// When enabled, the process will quit immediately. This is meant to be used in
+// unit_tests only! This is not thread-safe: only call from main thread.
+BASE_EXPORT bool EnableInProcessStackDumping();
+
+// 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 BASE_EXPORT StackTrace {
+ public:
+ // Creates a stacktrace from the current location.
+ StackTrace();
+
+ // Creates a stacktrace from an existing array of instruction
+ // pointers (such as returned by Addresses()). |count| will be
+ // trimmed to |kMaxTraces|.
+ StackTrace(const void* const* trace, size_t count);
+
+#if defined(OS_WIN) || defined(COMPILER_MSVC)
+ // Creates a stacktrace for an exception.
+ // Note: this function will throw an import not found (StackWalk64) exception
+ // on system without dbghelp 5.1.
+ StackTrace(_EXCEPTION_POINTERS* exception_pointers);
+#endif
+
+ // Copying and assignment are allowed with the default functions.
+
+ ~StackTrace();
+
+ // Gets an array of instruction pointer values. |*count| will be set to the
+ // number of elements in the returned array.
+ const void* const* Addresses(size_t* count) const;
+
+ // Prints a backtrace to stderr
+ void PrintBacktrace() const;
+
+ // Resolves backtrace to symbols and write to stream.
+ void OutputToStream(std::ostream* os) const;
+
+ // Resolves backtrace to symbols and returns as string.
+ std::string ToString() const;
+
+ private:
+ // From http://msdn.microsoft.com/en-us/library/bb204633.aspx,
+ // the sum of FramesToSkip and FramesToCapture must be less than 63,
+ // so set it to 62. Even if on POSIX it could be a larger value, it usually
+ // doesn't give much more information.
+ static const int kMaxTraces = 62;
+
+ void* trace_[kMaxTraces];
+
+ // The number of valid frames in |trace_|.
+ size_t count_;
+};
+
+namespace internal {
+
+#if defined(OS_POSIX) && !defined(OS_ANDROID)
+// POSIX doesn't define any async-signal safe function for converting
+// an integer to ASCII. We'll have to define our own version.
+// itoa_r() converts a (signed) integer to ASCII. It returns "buf", if the
+// conversion was successful or NULL otherwise. It never writes more than "sz"
+// bytes. Output will be truncated as needed, and a NUL character is always
+// appended.
+BASE_EXPORT char *itoa_r(intptr_t i, char *buf, size_t sz, int base);
+#endif // defined(OS_POSIX) && !defined(OS_ANDROID)
+
+} // namespace internal
+
+} // namespace debug
+} // namespace base
+
+#endif // BASE_DEBUG_STACK_TRACE_H_
diff --git a/src/base/debug/stack_trace_android.cc b/src/base/debug/stack_trace_android.cc
new file mode 100644
index 0000000..cc03d60
--- /dev/null
+++ b/src/base/debug/stack_trace_android.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 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/stack_trace.h"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+
+namespace base {
+namespace debug {
+
+bool EnableInProcessStackDumping() {
+ // When running in an application, our code typically expects SIGPIPE
+ // to be ignored. Therefore, when testing that same code, it should run
+ // with SIGPIPE ignored as well.
+ // TODO(phajdan.jr): De-duplicate this SIGPIPE code.
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = SIG_IGN;
+ sigemptyset(&action.sa_mask);
+ return (sigaction(SIGPIPE, &action, NULL) == 0);
+}
+
+StackTrace::StackTrace() {
+}
+
+// Sends fake SIGSTKFLT signals to let the Android linker and debuggerd dump
+// stack. See inlined comments and Android bionic/linker/debugger.c and
+// system/core/debuggerd/debuggerd.c for details.
+void StackTrace::PrintBacktrace() const {
+ // Get the current handler of SIGSTKFLT for later use.
+ sighandler_t sig_handler = signal(SIGSTKFLT, SIG_DFL);
+ signal(SIGSTKFLT, sig_handler);
+
+ // The Android linker will handle this signal and send a stack dumping request
+ // to debuggerd which will ptrace_attach this process. Before returning from
+ // the signal handler, the linker sets the signal handler to SIG_IGN.
+ kill(gettid(), SIGSTKFLT);
+
+ // Because debuggerd will wait for the process to be stopped by the actual
+ // signal in crashing scenarios, signal is sent again to met the expectation.
+ // Debuggerd will dump stack into the system log and /data/tombstones/ files.
+ // NOTE: If this process runs in the interactive shell, it will be put
+ // in the background. To resume it in the foreground, use 'fg' command.
+ kill(gettid(), SIGSTKFLT);
+
+ // Restore the signal handler so that this method can work the next time.
+ signal(SIGSTKFLT, sig_handler);
+}
+
+void StackTrace::OutputToStream(std::ostream* os) const {
+ NOTIMPLEMENTED();
+}
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/stack_trace_ios.mm b/src/base/debug/stack_trace_ios.mm
new file mode 100644
index 0000000..ab0abc4
--- /dev/null
+++ b/src/base/debug/stack_trace_ios.mm
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 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/process_util.h"
+
+#import <Foundation/Foundation.h>
+#include <mach/task.h>
+#include <stdio.h>
+
+#include "base/logging.h"
+
+// This is just enough of a shim to let the support needed by test_support
+// link.
+
+namespace base {
+namespace debug {
+
+namespace {
+
+void StackDumpSignalHandler(int signal) {
+ // TODO(phajdan.jr): Fix async-signal unsafety.
+ LOG(ERROR) << "Received signal " << signal;
+ NSArray *stack_symbols = [NSThread callStackSymbols];
+ for (NSString* stack_symbol in stack_symbols) {
+ fprintf(stderr, "\t%s\n", [stack_symbol UTF8String]);
+ }
+ _exit(1);
+}
+
+} // namespace
+
+// TODO(phajdan.jr): Deduplicate, see copy in stack_trace_posix.cc.
+bool EnableInProcessStackDumping() {
+ // When running in an application, our code typically expects SIGPIPE
+ // to be ignored. Therefore, when testing that same code, it should run
+ // with SIGPIPE ignored as well.
+ struct sigaction action;
+ action.sa_handler = SIG_IGN;
+ action.sa_flags = 0;
+ sigemptyset(&action.sa_mask);
+ bool success = (sigaction(SIGPIPE, &action, NULL) == 0);
+
+ success &= (signal(SIGILL, &StackDumpSignalHandler) != SIG_ERR);
+ success &= (signal(SIGABRT, &StackDumpSignalHandler) != SIG_ERR);
+ success &= (signal(SIGFPE, &StackDumpSignalHandler) != SIG_ERR);
+ success &= (signal(SIGBUS, &StackDumpSignalHandler) != SIG_ERR);
+ success &= (signal(SIGSEGV, &StackDumpSignalHandler) != SIG_ERR);
+ success &= (signal(SIGSYS, &StackDumpSignalHandler) != SIG_ERR);
+
+ return success;
+}
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/stack_trace_posix.cc b/src/base/debug/stack_trace_posix.cc
new file mode 100644
index 0000000..fd0bb34
--- /dev/null
+++ b/src/base/debug/stack_trace_posix.cc
@@ -0,0 +1,425 @@
+// Copyright (c) 2012 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/stack_trace.h"
+
+#include <errno.h>
+#include <execinfo.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <ostream>
+
+#if defined(__GLIBCXX__)
+#include <cxxabi.h>
+#endif
+
+#if defined(OS_MACOSX)
+#include <AvailabilityMacros.h>
+#endif
+
+#include "base/basictypes.h"
+#include "base/debug/debugger.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/string_number_conversions.h"
+
+#if defined(USE_SYMBOLIZE)
+#include "base/third_party/symbolize/symbolize.h"
+#endif
+
+namespace base {
+namespace debug {
+
+namespace {
+
+#if defined(__LB_SHELL__)
+typedef int sig_atomic_t;
+#endif
+
+volatile sig_atomic_t in_signal_handler = 0;
+
+// The prefix used for mangled symbols, per the Itanium C++ ABI:
+// http://www.codesourcery.com/cxx-abi/abi.html#mangling
+const char kMangledSymbolPrefix[] = "_Z";
+
+// Characters that can be used for symbols, generated by Ruby:
+// (('a'..'z').to_a+('A'..'Z').to_a+('0'..'9').to_a + ['_']).join
+const char kSymbolCharacters[] =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
+
+#if !defined(USE_SYMBOLIZE)
+// Demangles C++ symbols in the given text. Example:
+//
+// "out/Debug/base_unittests(_ZN10StackTraceC1Ev+0x20) [0x817778c]"
+// =>
+// "out/Debug/base_unittests(StackTrace::StackTrace()+0x20) [0x817778c]"
+void DemangleSymbols(std::string* text) {
+ // Note: code in this function is NOT async-signal safe (std::string uses
+ // malloc internally).
+
+#if defined(__GLIBCXX__)
+
+ std::string::size_type search_from = 0;
+ while (search_from < text->size()) {
+ // Look for the start of a mangled symbol, from search_from.
+ std::string::size_type mangled_start =
+ text->find(kMangledSymbolPrefix, search_from);
+ if (mangled_start == std::string::npos) {
+ break; // Mangled symbol not found.
+ }
+
+ // Look for the end of the mangled symbol.
+ std::string::size_type mangled_end =
+ text->find_first_not_of(kSymbolCharacters, mangled_start);
+ if (mangled_end == std::string::npos) {
+ mangled_end = text->size();
+ }
+ std::string mangled_symbol =
+ text->substr(mangled_start, mangled_end - mangled_start);
+
+ // Try to demangle the mangled symbol candidate.
+ int status = 0;
+ scoped_ptr_malloc<char> demangled_symbol(
+ abi::__cxa_demangle(mangled_symbol.c_str(), NULL, 0, &status));
+ if (status == 0) { // Demangling is successful.
+ // Remove the mangled symbol.
+ text->erase(mangled_start, mangled_end - mangled_start);
+ // Insert the demangled symbol.
+ text->insert(mangled_start, demangled_symbol.get());
+ // Next time, we'll start right after the demangled symbol we inserted.
+ search_from = mangled_start + strlen(demangled_symbol.get());
+ } else {
+ // Failed to demangle. Retry after the "_Z" we just found.
+ search_from = mangled_start + 2;
+ }
+ }
+
+#endif // defined(__GLIBCXX__)
+}
+#endif // !defined(USE_SYMBOLIZE)
+
+class BacktraceOutputHandler {
+ public:
+ virtual void HandleOutput(const char* output) = 0;
+
+ protected:
+ virtual ~BacktraceOutputHandler() {}
+};
+
+void OutputPointer(void* pointer, BacktraceOutputHandler* handler) {
+ char buf[1024] = { '\0' };
+ handler->HandleOutput(" [0x");
+ internal::itoa_r(reinterpret_cast<intptr_t>(pointer), buf, sizeof(buf), 16);
+ handler->HandleOutput(buf);
+ handler->HandleOutput("]");
+}
+
+void ProcessBacktrace(void *const *trace,
+ int size,
+ BacktraceOutputHandler* handler) {
+ // NOTE: This code MUST be async-signal safe (it's used by in-process
+ // stack dumping signal handler). NO malloc or stdio is allowed here.
+
+#if defined(USE_SYMBOLIZE)
+ for (int i = 0; i < size; ++i) {
+ handler->HandleOutput("\t");
+
+ char buf[1024] = { '\0' };
+
+ // Subtract by one as return address of function may be in the next
+ // function when a function is annotated as noreturn.
+ void* address = static_cast<char*>(trace[i]) - 1;
+ if (google::Symbolize(address, buf, sizeof(buf)))
+ handler->HandleOutput(buf);
+ else
+ handler->HandleOutput("<unknown>");
+
+ OutputPointer(trace[i], handler);
+ handler->HandleOutput("\n");
+ }
+#else
+ bool printed = false;
+
+ // Below part is async-signal unsafe (uses malloc), so execute it only
+ // when we are not executing the signal handler.
+ if (in_signal_handler == 0) {
+#if defined(__LB_PS3__)
+ std::string symbols;
+ bool success = ResolveSymbolsFromHostPS3(trace, size, &symbols);
+ if (success && symbols.length() > 0) {
+ handler->HandleOutput(symbols.c_str());
+ printed = true;
+ }
+#else
+ scoped_ptr_malloc<char*> trace_symbols(backtrace_symbols(trace, size));
+ if (trace_symbols.get()) {
+ for (int i = 0; i < size; ++i) {
+ std::string trace_symbol = trace_symbols.get()[i];
+ DemangleSymbols(&trace_symbol);
+ handler->HandleOutput(trace_symbol.c_str());
+ handler->HandleOutput("\n");
+ }
+
+ printed = true;
+ }
+#endif
+ }
+
+ if (!printed) {
+ for (int i = 0; i < size; ++i) {
+ OutputPointer(trace[i], handler);
+ handler->HandleOutput("\n");
+ }
+ }
+#endif // defined(USE_SYMBOLIZE)
+}
+
+#if !defined(__LB_SHELL__)
+void StackDumpSignalHandler(int signal, siginfo_t* info, ucontext_t* context) {
+ // NOTE: This code MUST be async-signal safe.
+ // NO malloc or stdio is allowed here.
+
+ // Record the fact that we are in the signal handler now, so that the rest
+ // of StackTrace can behave in an async-signal-safe manner.
+ in_signal_handler = 1;
+
+ if (BeingDebugged())
+ BreakDebugger();
+
+ char buf[1024] = "Received signal ";
+ size_t buf_len = strlen(buf);
+ internal::itoa_r(signal, buf + buf_len, sizeof(buf) - buf_len, 10);
+ RAW_LOG(ERROR, buf);
+
+ debug::StackTrace().PrintBacktrace();
+
+ // TODO(shess): Port to Linux.
+#if defined(OS_MACOSX)
+ // TODO(shess): Port to 64-bit.
+#if ARCH_CPU_X86_FAMILY && ARCH_CPU_32_BITS
+ size_t len;
+
+ // NOTE: Even |snprintf()| is not on the approved list for signal
+ // handlers, but buffered I/O is definitely not on the list due to
+ // potential for |malloc()|.
+ len = static_cast<size_t>(
+ snprintf(buf, sizeof(buf),
+ "ax: %x, bx: %x, cx: %x, dx: %x\n",
+ context->uc_mcontext->__ss.__eax,
+ context->uc_mcontext->__ss.__ebx,
+ context->uc_mcontext->__ss.__ecx,
+ context->uc_mcontext->__ss.__edx));
+ write(STDERR_FILENO, buf, std::min(len, sizeof(buf) - 1));
+
+ len = static_cast<size_t>(
+ snprintf(buf, sizeof(buf),
+ "di: %x, si: %x, bp: %x, sp: %x, ss: %x, flags: %x\n",
+ context->uc_mcontext->__ss.__edi,
+ context->uc_mcontext->__ss.__esi,
+ context->uc_mcontext->__ss.__ebp,
+ context->uc_mcontext->__ss.__esp,
+ context->uc_mcontext->__ss.__ss,
+ context->uc_mcontext->__ss.__eflags));
+ write(STDERR_FILENO, buf, std::min(len, sizeof(buf) - 1));
+
+ len = static_cast<size_t>(
+ snprintf(buf, sizeof(buf),
+ "ip: %x, cs: %x, ds: %x, es: %x, fs: %x, gs: %x\n",
+ context->uc_mcontext->__ss.__eip,
+ context->uc_mcontext->__ss.__cs,
+ context->uc_mcontext->__ss.__ds,
+ context->uc_mcontext->__ss.__es,
+ context->uc_mcontext->__ss.__fs,
+ context->uc_mcontext->__ss.__gs));
+ write(STDERR_FILENO, buf, std::min(len, sizeof(buf) - 1));
+#endif // ARCH_CPU_32_BITS
+#endif // defined(OS_MACOSX)
+ _exit(1);
+}
+#endif
+
+class PrintBacktraceOutputHandler : public BacktraceOutputHandler {
+ public:
+ PrintBacktraceOutputHandler() {}
+
+ virtual void HandleOutput(const char* output) {
+ // NOTE: This code MUST be async-signal safe (it's used by in-process
+ // stack dumping signal handler). NO malloc or stdio is allowed here.
+ ignore_result(HANDLE_EINTR(write(STDERR_FILENO, output, strlen(output))));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrintBacktraceOutputHandler);
+};
+
+class StreamBacktraceOutputHandler : public BacktraceOutputHandler {
+ public:
+ StreamBacktraceOutputHandler(std::ostream* os) : os_(os) {
+ }
+
+ virtual void HandleOutput(const char* output) {
+ (*os_) << output;
+ }
+
+ private:
+ std::ostream* os_;
+
+ DISALLOW_COPY_AND_ASSIGN(StreamBacktraceOutputHandler);
+};
+
+void WarmUpBacktrace() {
+ // Warm up stack trace infrastructure. It turns out that on the first
+ // call glibc initializes some internal data structures using pthread_once,
+ // and even backtrace() can call malloc(), leading to hangs.
+ //
+ // Example stack trace snippet (with tcmalloc):
+ //
+ // #8 0x0000000000a173b5 in tc_malloc
+ // at ./third_party/tcmalloc/chromium/src/debugallocation.cc:1161
+ // #9 0x00007ffff7de7900 in _dl_map_object_deps at dl-deps.c:517
+ // #10 0x00007ffff7ded8a9 in dl_open_worker at dl-open.c:262
+ // #11 0x00007ffff7de9176 in _dl_catch_error at dl-error.c:178
+ // #12 0x00007ffff7ded31a in _dl_open (file=0x7ffff625e298 "libgcc_s.so.1")
+ // at dl-open.c:639
+ // #13 0x00007ffff6215602 in do_dlopen at dl-libc.c:89
+ // #14 0x00007ffff7de9176 in _dl_catch_error at dl-error.c:178
+ // #15 0x00007ffff62156c4 in dlerror_run at dl-libc.c:48
+ // #16 __GI___libc_dlopen_mode at dl-libc.c:165
+ // #17 0x00007ffff61ef8f5 in init
+ // at ../sysdeps/x86_64/../ia64/backtrace.c:53
+ // #18 0x00007ffff6aad400 in pthread_once
+ // at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_once.S:104
+ // #19 0x00007ffff61efa14 in __GI___backtrace
+ // at ../sysdeps/x86_64/../ia64/backtrace.c:104
+ // #20 0x0000000000752a54 in base::debug::StackTrace::StackTrace
+ // at base/debug/stack_trace_posix.cc:175
+ // #21 0x00000000007a4ae5 in
+ // base::(anonymous namespace)::StackDumpSignalHandler
+ // at base/process_util_posix.cc:172
+ // #22 <signal handler called>
+ StackTrace stack_trace;
+}
+
+} // namespace
+
+#if !defined(OS_IOS) && !defined(__LB_SHELL__)
+bool EnableInProcessStackDumping() {
+ // When running in an application, our code typically expects SIGPIPE
+ // to be ignored. Therefore, when testing that same code, it should run
+ // with SIGPIPE ignored as well.
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = SIG_IGN;
+ sigemptyset(&action.sa_mask);
+ bool success = (sigaction(SIGPIPE, &action, NULL) == 0);
+
+ // Avoid hangs during backtrace initialization, see above.
+ WarmUpBacktrace();
+
+ sig_t handler = reinterpret_cast<sig_t>(&StackDumpSignalHandler);
+ success &= (signal(SIGILL, handler) != SIG_ERR);
+ success &= (signal(SIGABRT, handler) != SIG_ERR);
+ success &= (signal(SIGFPE, handler) != SIG_ERR);
+ success &= (signal(SIGBUS, handler) != SIG_ERR);
+ success &= (signal(SIGSEGV, handler) != SIG_ERR);
+ success &= (signal(SIGSYS, handler) != SIG_ERR);
+
+ return success;
+}
+#endif // !defined(OS_IOS)
+
+StackTrace::StackTrace() {
+ // NOTE: This code MUST be async-signal safe (it's used by in-process
+ // stack dumping signal handler). NO malloc or stdio is allowed here.
+
+ // Though the backtrace API man page does not list any possible negative
+ // return values, we take no chance.
+ count_ = std::max(backtrace(trace_, arraysize(trace_)), 0);
+}
+
+void StackTrace::PrintBacktrace() const {
+ // NOTE: This code MUST be async-signal safe (it's used by in-process
+ // stack dumping signal handler). NO malloc or stdio is allowed here.
+
+ PrintBacktraceOutputHandler handler;
+ ProcessBacktrace(trace_, count_, &handler);
+}
+
+void StackTrace::OutputToStream(std::ostream* os) const {
+ StreamBacktraceOutputHandler handler(os);
+ ProcessBacktrace(trace_, count_, &handler);
+}
+
+namespace internal {
+
+// NOTE: code from sandbox/linux/seccomp-bpf/demo.cc.
+char *itoa_r(intptr_t i, char *buf, size_t sz, int base) {
+ // Make sure we can write at least one NUL byte.
+ size_t n = 1;
+ if (n > sz)
+ return NULL;
+
+ if (base < 2 || base > 16) {
+ buf[0] = '\000';
+ return NULL;
+ }
+
+ char *start = buf;
+
+ uintptr_t j = i;
+
+ // Handle negative numbers (only for base 10).
+ if (i < 0 && base == 10) {
+ j = -i;
+
+ // Make sure we can write the '-' character.
+ if (++n > sz) {
+ buf[0] = '\000';
+ return NULL;
+ }
+ *start++ = '-';
+ }
+
+ // Loop until we have converted the entire number. Output at least one
+ // character (i.e. '0').
+ char *ptr = start;
+ do {
+ // Make sure there is still enough space left in our output buffer.
+ if (++n > sz) {
+ buf[0] = '\000';
+ return NULL;
+ }
+
+ // Output the next digit.
+ *ptr++ = "0123456789abcdef"[j % base];
+ j /= base;
+ } while (j);
+
+ // Terminate the output with a NUL character.
+ *ptr = '\000';
+
+ // Conversion to ASCII actually resulted in the digits being in reverse
+ // order. We can't easily generate them in forward order, as we can't tell
+ // the number of characters needed until we are done converting.
+ // So, now, we reverse the string (except for the possible "-" sign).
+ while (--ptr > start) {
+ char ch = *ptr;
+ *ptr = *start;
+ *start++ = ch;
+ }
+ return buf;
+}
+
+} // namespace internal
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/stack_trace_shell.cc b/src/base/debug/stack_trace_shell.cc
new file mode 100644
index 0000000..8c42ae6
--- /dev/null
+++ b/src/base/debug/stack_trace_shell.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2012 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.
+
+#if defined(__LB_XB1__) && !defined(__LB_SHELL__FOR_RELEASE__)
+
+// Stack tracing is using DbgHelp which doesn't support the TV_APP partition
+// at the moment. To make it work, we have to trick the headers into thinking
+// that they are compiled for a desktop application. This is the reason
+// why the functionality will only be enabled in internal builds.
+
+#if !defined(COBALT_WIN)
+#pragma push_macro("WINAPI_PARTITION_DESKTOP")
+#undef WINAPI_PARTITION_DESKTOP
+#define WINAPI_PARTITION_DESKTOP 1
+#endif
+
+#include "stack_trace_win.cc"
+
+#if !defined(COBALT_WIN)
+#pragma pop_macro("WINAPI_PARTITION_DESKTOP")
+#endif
+
+// EnableInProcessStackDumping is implemented in stack_trace_xb1.cc
+
+#elif !defined(__LB_ANDROID__)
+// stack_trace_android.cc is already compiled via base.gypi
+
+#if defined(__LB_PS3__)
+#include "stack_trace_ps3.cc"
+#endif
+#include "stack_trace_posix.cc"
+
+namespace base {
+namespace debug {
+
+// But re-implement this:
+bool EnableInProcessStackDumping() {
+ // We need this to return true to run unit tests
+ return true;
+}
+
+} // namespace debug
+} // namespace base
+
+#endif
diff --git a/src/base/debug/stack_trace_starboard.cc b/src/base/debug/stack_trace_starboard.cc
new file mode 100644
index 0000000..163105c
--- /dev/null
+++ b/src/base/debug/stack_trace_starboard.cc
@@ -0,0 +1,185 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/debug/stack_trace.h"
+
+#include <ostream>
+
+#include "base/basictypes.h"
+#include "starboard/log.h"
+#include "starboard/system.h"
+
+namespace base {
+namespace debug {
+
+namespace {
+
+class BacktraceOutputHandler {
+ public:
+ virtual void HandleOutput(const char* output) = 0;
+
+ protected:
+ virtual ~BacktraceOutputHandler() {}
+};
+
+class PrintBacktraceOutputHandler : public BacktraceOutputHandler {
+ public:
+ PrintBacktraceOutputHandler() {}
+
+ virtual void HandleOutput(const char* output) {
+ // NOTE: This code MUST be async-signal safe (it's used by in-process
+ // stack dumping signal handler). NO malloc or stdio is allowed here.
+ SbLogRaw(output);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrintBacktraceOutputHandler);
+};
+
+class StreamBacktraceOutputHandler : public BacktraceOutputHandler {
+ public:
+ StreamBacktraceOutputHandler(std::ostream* os) : os_(os) {}
+
+ virtual void HandleOutput(const char* output) { (*os_) << output; }
+
+ private:
+ std::ostream* os_;
+
+ DISALLOW_COPY_AND_ASSIGN(StreamBacktraceOutputHandler);
+};
+
+// NOTE: code from sandbox/linux/seccomp-bpf/demo.cc.
+char* itoa_r(intptr_t i, char* buf, size_t sz, int base) {
+ // Make sure we can write at least one NUL byte.
+ size_t n = 1;
+ if (n > sz)
+ return NULL;
+
+ if (base < 2 || base > 16) {
+ buf[0] = '\000';
+ return NULL;
+ }
+
+ char* start = buf;
+
+ uintptr_t j = i;
+
+ // Handle negative numbers (only for base 10).
+ if (i < 0 && base == 10) {
+ j = -i;
+
+ // Make sure we can write the '-' character.
+ if (++n > sz) {
+ buf[0] = '\000';
+ return NULL;
+ }
+ *start++ = '-';
+ }
+
+ // Loop until we have converted the entire number. Output at least one
+ // character (i.e. '0').
+ char* ptr = start;
+ do {
+ // Make sure there is still enough space left in our output buffer.
+ if (++n > sz) {
+ buf[0] = '\000';
+ return NULL;
+ }
+
+ // Output the next digit.
+ *ptr++ = "0123456789abcdef"[j % base];
+ j /= base;
+ } while (j);
+
+ // Terminate the output with a NUL character.
+ *ptr = '\000';
+
+ // Conversion to ASCII actually resulted in the digits being in reverse
+ // order. We can't easily generate them in forward order, as we can't tell
+ // the number of characters needed until we are done converting.
+ // So, now, we reverse the string (except for the possible "-" sign).
+ while (--ptr > start) {
+ char ch = *ptr;
+ *ptr = *start;
+ *start++ = ch;
+ }
+ return buf;
+}
+
+void OutputPointer(void* pointer, BacktraceOutputHandler* handler) {
+ char buf[1024] = {'\0'};
+ handler->HandleOutput(" [0x");
+ itoa_r(reinterpret_cast<intptr_t>(pointer), buf, sizeof(buf), 16);
+ handler->HandleOutput(buf);
+ handler->HandleOutput("]");
+}
+
+void ProcessBacktrace(void* const* trace,
+ int size,
+ BacktraceOutputHandler* handler) {
+ for (int i = 0; i < size; ++i) {
+ handler->HandleOutput("\t");
+
+ char buf[1024] = {'\0'};
+
+ // Subtract by one as return address of function may be in the next
+ // function when a function is annotated as noreturn.
+ void* address = static_cast<char*>(trace[i]) - 1;
+ if (SbSystemSymbolize(address, buf, sizeof(buf))) {
+ handler->HandleOutput(buf);
+ } else {
+ handler->HandleOutput("<unknown>");
+ }
+
+ OutputPointer(trace[i], handler);
+ handler->HandleOutput("\n");
+ }
+}
+
+} // namespace
+
+StackTrace::StackTrace() {
+ // NOTE: This code MUST be async-signal safe (it's used by in-process
+ // stack dumping signal handler). NO malloc or stdio is allowed here.
+
+ // Though the SbSystemGetStack API documentation does not specify any possible
+ // negative return values, we take no chance.
+ count_ = std::max(SbSystemGetStack(trace_, arraysize(trace_)), 0);
+ if (count_ < 1) {
+ return;
+ }
+
+ // We can remove this call from the stack trace, since we know it is always
+ // going to be in it.
+ for (int i = 1; i < count_; ++i) {
+ trace_[i - 1] = trace_[i];
+ }
+}
+
+void StackTrace::PrintBacktrace() const {
+ PrintBacktraceOutputHandler handler;
+ ProcessBacktrace(trace_, count_, &handler);
+}
+
+void StackTrace::OutputToStream(std::ostream* out_stream) const {
+ StreamBacktraceOutputHandler handler(out_stream);
+ ProcessBacktrace(trace_, count_, &handler);
+}
+
+bool EnableInProcessStackDumping() {
+ return true;
+}
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/stack_trace_unittest.cc b/src/base/debug/stack_trace_unittest.cc
new file mode 100644
index 0000000..21205ca
--- /dev/null
+++ b/src/base/debug/stack_trace_unittest.cc
@@ -0,0 +1,211 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <limits>
+#include <sstream>
+#include <string>
+
+#include "base/debug/stack_trace.h"
+#include "base/logging.h"
+#include "base/process_util.h"
+#include "base/test/test_timeouts.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(__LB_SHELL__)
+#include "base/test/multiprocess_test.h"
+#endif
+
+namespace base {
+namespace debug {
+
+#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(__LB_SHELL__)
+typedef MultiProcessTest StackTraceTest;
+#else
+typedef testing::Test StackTraceTest;
+#endif
+
+// Note: On Linux, this test currently only fully works on Debug builds.
+// See comments in the #ifdef soup if you intend to change this.
+#if defined(OS_WIN)
+// Always fails on Windows: crbug.com/32070
+#define MAYBE_OutputToStream DISABLED_OutputToStream
+#else
+#define MAYBE_OutputToStream OutputToStream
+#endif
+TEST_F(StackTraceTest, MAYBE_OutputToStream) {
+ StackTrace trace;
+
+ // Dump the trace into a string.
+ std::ostringstream os;
+ trace.OutputToStream(&os);
+ std::string backtrace_message = os.str();
+
+ // ToString() should produce the same output.
+ EXPECT_EQ(backtrace_message, trace.ToString());
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && NDEBUG
+ // Stack traces require an extra data table that bloats our binaries,
+ // so they're turned off for release builds. We stop the test here,
+ // at least letting us verify that the calls don't crash.
+ return;
+#endif // defined(OS_POSIX) && !defined(OS_MACOSX) && NDEBUG
+
+ size_t frames_found = 0;
+ trace.Addresses(&frames_found);
+ ASSERT_GE(frames_found, 5u) <<
+ "No stack frames found. Skipping rest of test.";
+
+ // Check if the output has symbol initialization warning. If it does, fail.
+ ASSERT_EQ(backtrace_message.find("Dumping unresolved backtrace"),
+ std::string::npos) <<
+ "Unable to resolve symbols. Skipping rest of test.";
+
+#if defined(OS_MACOSX)
+#if 0
+ // Disabled due to -fvisibility=hidden in build config.
+
+ // Symbol resolution via the backtrace_symbol function does not work well
+ // in OS X.
+ // 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;
+
+#endif
+#elif defined(USE_SYMBOLIZE)
+ // This branch is for gcc-compiled code, but not Mac due to the
+ // above #if.
+ // Expect a demangled symbol.
+ EXPECT_TRUE(backtrace_message.find("testing::Test::Run()") !=
+ std::string::npos)
+ << "Expected a demangled symbol in backtrace:\n"
+ << backtrace_message;
+
+#elif 0
+ // This is the fall-through case; it used to cover Windows.
+ // But it's disabled because of varying buildbot configs;
+ // some lack symbols.
+
+ // 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)
+}
+
+// The test is used for manual testing, e.g., to see the raw output.
+TEST_F(StackTraceTest, DebugOutputToStream) {
+ StackTrace trace;
+ std::ostringstream os;
+ trace.OutputToStream(&os);
+ VLOG(1) << os.str();
+}
+
+// The test is used for manual testing, e.g., to see the raw output.
+TEST_F(StackTraceTest, DebugPrintBacktrace) {
+ StackTrace().PrintBacktrace();
+}
+
+#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(__LB_ANDROID__)
+#if !defined(OS_IOS) && !defined(__LB_SHELL__)
+MULTIPROCESS_TEST_MAIN(MismatchedMallocChildProcess) {
+ char* pointer = new char[10];
+ delete pointer;
+ return 2;
+}
+
+// Regression test for StackDumpingSignalHandler async-signal unsafety.
+// Combined with tcmalloc's debugallocation, that signal handler
+// and e.g. mismatched new[]/delete would cause a hang because
+// of re-entering malloc.
+TEST_F(StackTraceTest, AsyncSignalUnsafeSignalHandlerHang) {
+ ProcessHandle child = this->SpawnChild("MismatchedMallocChildProcess", false);
+ ASSERT_NE(kNullProcessHandle, child);
+ ASSERT_TRUE(WaitForSingleProcess(child, TestTimeouts::action_timeout()));
+}
+#endif // !defined(OS_IOS) &&!defined(__LB_SHELL__)
+
+namespace {
+
+std::string itoa_r_wrapper(intptr_t i, size_t sz, int base) {
+ char buffer[1024];
+ CHECK_LE(sz, sizeof(buffer));
+
+ char* result = internal::itoa_r(i, buffer, sz, base);
+ EXPECT_TRUE(result);
+ return std::string(buffer);
+}
+
+} // namespace
+
+TEST_F(StackTraceTest, itoa_r) {
+ EXPECT_EQ("0", itoa_r_wrapper(0, 128, 10));
+ EXPECT_EQ("-1", itoa_r_wrapper(-1, 128, 10));
+
+ // Test edge cases.
+ if (sizeof(intptr_t) == 4) {
+ EXPECT_EQ("ffffffff", itoa_r_wrapper(-1, 128, 16));
+ EXPECT_EQ("-2147483648",
+ itoa_r_wrapper(std::numeric_limits<intptr_t>::min(), 128, 10));
+ EXPECT_EQ("2147483647",
+ itoa_r_wrapper(std::numeric_limits<intptr_t>::max(), 128, 10));
+
+ EXPECT_EQ("80000000",
+ itoa_r_wrapper(std::numeric_limits<intptr_t>::min(), 128, 16));
+ EXPECT_EQ("7fffffff",
+ itoa_r_wrapper(std::numeric_limits<intptr_t>::max(), 128, 16));
+ } else if (sizeof(intptr_t) == 8) {
+ EXPECT_EQ("ffffffffffffffff", itoa_r_wrapper(-1, 128, 16));
+ EXPECT_EQ("-9223372036854775808",
+ itoa_r_wrapper(std::numeric_limits<intptr_t>::min(), 128, 10));
+ EXPECT_EQ("9223372036854775807",
+ itoa_r_wrapper(std::numeric_limits<intptr_t>::max(), 128, 10));
+
+ EXPECT_EQ("8000000000000000",
+ itoa_r_wrapper(std::numeric_limits<intptr_t>::min(), 128, 16));
+ EXPECT_EQ("7fffffffffffffff",
+ itoa_r_wrapper(std::numeric_limits<intptr_t>::max(), 128, 16));
+ } else {
+ ADD_FAILURE() << "Missing test case for your size of intptr_t ("
+ << sizeof(intptr_t) << ")";
+ }
+
+ // Test hex output.
+ EXPECT_EQ("688", itoa_r_wrapper(0x688, 128, 16));
+ EXPECT_EQ("deadbeef", itoa_r_wrapper(0xdeadbeef, 128, 16));
+
+ // Check that itoa_r respects passed buffer size limit.
+ char buffer[1024];
+ EXPECT_TRUE(internal::itoa_r(0xdeadbeef, buffer, 10, 16));
+ EXPECT_TRUE(internal::itoa_r(0xdeadbeef, buffer, 9, 16));
+ EXPECT_FALSE(internal::itoa_r(0xdeadbeef, buffer, 8, 16));
+ EXPECT_FALSE(internal::itoa_r(0xdeadbeef, buffer, 7, 16));
+}
+#endif // defined(OS_POSIX) && !defined(OS_ANDROID)
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/stack_trace_win.cc b/src/base/debug/stack_trace_win.cc
new file mode 100644
index 0000000..d26b492
--- /dev/null
+++ b/src/base/debug/stack_trace_win.cc
@@ -0,0 +1,245 @@
+// Copyright (c) 2012 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/stack_trace.h"
+
+#include <windows.h>
+#include <dbghelp.h>
+
+#include <iostream>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/process_util.h"
+#include "base/synchronization/lock.h"
+
+namespace base {
+namespace debug {
+
+namespace {
+
+#if !defined(COBALT_WIN)
+// Previous unhandled filter. Will be called if not NULL when we intercept an
+// exception. Only used in unit tests.
+LPTOP_LEVEL_EXCEPTION_FILTER g_previous_filter = NULL;
+
+// Prints the exception call stack.
+// This is the unit tests exception filter.
+long WINAPI StackDumpExceptionFilter(EXCEPTION_POINTERS* info) {
+ debug::StackTrace(info).PrintBacktrace();
+ if (g_previous_filter)
+ return g_previous_filter(info);
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+#endif // !defined(COBALT_WIN)
+
+// 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 adversely 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* GetInstance() {
+ // We use a leaky singleton because code may call this during process
+ // termination.
+ return
+ Singleton<SymbolContext, LeakySingletonTraits<SymbolContext> >::get();
+ }
+
+ // Returns the error code of a failed initialization.
+ DWORD init_error() const {
+ return init_error_;
+ }
+
+ // 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 void* const* trace,
+ size_t count,
+ std::ostream* os) {
+ base::AutoLock lock(lock_);
+
+ for (size_t i = 0; (i < count) && 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)];
+ memset(buffer, 0, sizeof(buffer));
+
+ // 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 - 1;
+ BOOL has_symbol = SymFromAddr(GetCurrentProcess(), 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(GetCurrentProcess(), 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 information, add a spacer.
+ (*os) << "(No symbol) [0x" << trace[i] << "]";
+ }
+ if (has_line) {
+ (*os) << " (" << line.FileName << ":" << line.LineNumber << ")";
+ }
+ (*os) << "\n";
+ }
+ }
+
+ private:
+ friend struct DefaultSingletonTraits<SymbolContext>;
+
+ SymbolContext() : init_error_(ERROR_SUCCESS) {
+ // Initializes the symbols for the process.
+ // 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(GetCurrentProcess(), NULL, TRUE)) {
+ init_error_ = ERROR_SUCCESS;
+ } else {
+ init_error_ = GetLastError();
+ // TODO(awong): Handle error: SymInitialize can fail with
+ // ERROR_INVALID_PARAMETER.
+ // When it fails, we should not call debugbreak since it kills the current
+ // process (prevents future tests from running or kills the browser
+ // process).
+ DLOG(ERROR) << "SymInitialize failed: " << init_error_;
+ }
+ }
+
+ DWORD init_error_;
+ base::Lock lock_;
+ DISALLOW_COPY_AND_ASSIGN(SymbolContext);
+};
+
+} // namespace
+
+#if !defined(COBALT_WIN)
+bool EnableInProcessStackDumping() {
+ // Add stack dumping support on exception on windows. Similar to OS_POSIX
+ // signal() handling in process_util_posix.cc.
+ g_previous_filter = SetUnhandledExceptionFilter(&StackDumpExceptionFilter);
+ RouteStdioToConsole();
+ return true;
+}
+#endif
+
+// Disable optimizations for the StackTrace::StackTrace function. It is
+// important to disable at least frame pointer optimization ("y"), since
+// that breaks CaptureStackBackTrace() and prevents StackTrace from working
+// in Release builds (it may still be janky if other frames are using FPO,
+// but at least it will make it further).
+#if defined(COMPILER_MSVC)
+#pragma optimize("", off)
+#endif
+
+StackTrace::StackTrace() {
+ // When walking our own stack, use CaptureStackBackTrace().
+ count_ = CaptureStackBackTrace(0, arraysize(trace_), trace_, NULL);
+}
+
+#if defined(COMPILER_MSVC)
+#pragma optimize("", on)
+#endif
+
+StackTrace::StackTrace(EXCEPTION_POINTERS* exception_pointers) {
+ // StackWalk64 requires symbol information to be loaded. The following
+ // will make sure that SymbolContext singleton is initialized which will
+ // load the required symbols.
+ static volatile SymbolContext* context = SymbolContext::GetInstance();
+ (void)context;
+
+ // When walking an exception stack, we need to use StackWalk64().
+ count_ = 0;
+ // Initialize stack walking.
+ STACKFRAME64 stack_frame;
+ memset(&stack_frame, 0, sizeof(stack_frame));
+#if defined(_WIN64)
+ int machine_type = IMAGE_FILE_MACHINE_AMD64;
+ stack_frame.AddrPC.Offset = exception_pointers->ContextRecord->Rip;
+ stack_frame.AddrFrame.Offset = exception_pointers->ContextRecord->Rbp;
+ stack_frame.AddrStack.Offset = exception_pointers->ContextRecord->Rsp;
+#else
+ int machine_type = IMAGE_FILE_MACHINE_I386;
+ stack_frame.AddrPC.Offset = exception_pointers->ContextRecord->Eip;
+ stack_frame.AddrFrame.Offset = exception_pointers->ContextRecord->Ebp;
+ stack_frame.AddrStack.Offset = exception_pointers->ContextRecord->Esp;
+#endif
+ stack_frame.AddrPC.Mode = AddrModeFlat;
+ stack_frame.AddrFrame.Mode = AddrModeFlat;
+ stack_frame.AddrStack.Mode = AddrModeFlat;
+ while (StackWalk64(machine_type,
+ GetCurrentProcess(),
+ GetCurrentThread(),
+ &stack_frame,
+ exception_pointers->ContextRecord,
+ NULL,
+ &SymFunctionTableAccess64,
+ &SymGetModuleBase64,
+ NULL) &&
+ count_ < arraysize(trace_)) {
+ trace_[count_++] = reinterpret_cast<void*>(stack_frame.AddrPC.Offset);
+ }
+
+ for (size_t i = count_; i < arraysize(trace_); ++i)
+ trace_[i] = NULL;
+}
+
+void StackTrace::PrintBacktrace() const {
+ OutputToStream(&std::cerr);
+}
+
+void StackTrace::OutputToStream(std::ostream* os) const {
+ SymbolContext* context = SymbolContext::GetInstance();
+ DWORD error = context->init_error();
+ if (error != ERROR_SUCCESS) {
+ (*os) << "Error initializing symbols (" << error
+ << "). Dumping unresolved backtrace:\n";
+ for (int i = 0; (i < count_) && os->good(); ++i) {
+ (*os) << "\t" << trace_[i] << "\n";
+ }
+ } else {
+ (*os) << "Backtrace:\n";
+ context->OutputTraceToStream(trace_, count_, os);
+ }
+}
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/trace_event.cc b/src/base/debug/trace_event.cc
new file mode 100644
index 0000000..682e065
--- /dev/null
+++ b/src/base/debug/trace_event.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 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/trace_event.h"
+
+namespace trace_event_internal {
+
+void TraceEndOnScopeClose::Initialize(const unsigned char* category_enabled,
+ const char* name) {
+ data_.category_enabled = category_enabled;
+ data_.name = name;
+ p_data_ = &data_;
+}
+
+void TraceEndOnScopeClose::AddEventIfEnabled() {
+ // Only called when p_data_ is non-null.
+ if (*p_data_->category_enabled) {
+ TRACE_EVENT_API_ADD_TRACE_EVENT(
+ TRACE_EVENT_PHASE_END,
+ p_data_->category_enabled,
+ p_data_->name, kNoEventId,
+ kZeroNumArgs, NULL, NULL, NULL,
+ TRACE_EVENT_FLAG_NONE);
+ }
+}
+
+} // namespace trace_event_internal
diff --git a/src/base/debug/trace_event.h b/src/base/debug/trace_event.h
new file mode 100644
index 0000000..67a79a6
--- /dev/null
+++ b/src/base/debug/trace_event.h
@@ -0,0 +1,960 @@
+// Copyright (c) 2012 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 header is designed to give you trace_event macros without specifying
+// how the events actually get collected and stored. If you need to expose trace
+// event to some other universe, you can copy-and-paste this file,
+// implement the TRACE_EVENT_API macros, and do any other necessary fixup for
+// the target platform. The end result is that multiple libraries can funnel
+// events through to a shared trace event collector.
+
+// Trace events are for tracking application performance and resource usage.
+// Macros are provided to track:
+// Begin and end of function calls
+// Counters
+//
+// Events are issued against categories. Whereas LOG's
+// categories are statically defined, TRACE categories are created
+// implicitly with a string. For example:
+// TRACE_EVENT_INSTANT0("MY_SUBSYSTEM", "SomeImportantEvent")
+//
+// Events can be INSTANT, or can be pairs of BEGIN and END in the same scope:
+// TRACE_EVENT_BEGIN0("MY_SUBSYSTEM", "SomethingCostly")
+// doSomethingCostly()
+// TRACE_EVENT_END0("MY_SUBSYSTEM", "SomethingCostly")
+// Note: our tools can't always determine the correct BEGIN/END pairs unless
+// these are used in the same scope. Use ASYNC_BEGIN/ASYNC_END macros if you
+// need them to be in separate scopes.
+//
+// A common use case is to trace entire function scopes. This
+// issues a trace BEGIN and END automatically:
+// void doSomethingCostly() {
+// TRACE_EVENT0("MY_SUBSYSTEM", "doSomethingCostly");
+// ...
+// }
+//
+// Additional parameters can be associated with an event:
+// void doSomethingCostly2(int howMuch) {
+// TRACE_EVENT1("MY_SUBSYSTEM", "doSomethingCostly",
+// "howMuch", howMuch);
+// ...
+// }
+//
+// The trace system will automatically add to this information the
+// current process id, thread id, and a timestamp in microseconds.
+//
+// To trace an asynchronous procedure such as an IPC send/receive, use
+// ASYNC_BEGIN and ASYNC_END:
+// [single threaded sender code]
+// static int send_count = 0;
+// ++send_count;
+// TRACE_EVENT_ASYNC_BEGIN0("ipc", "message", send_count);
+// Send(new MyMessage(send_count));
+// [receive code]
+// void OnMyMessage(send_count) {
+// TRACE_EVENT_ASYNC_END0("ipc", "message", send_count);
+// }
+// The third parameter is a unique ID to match ASYNC_BEGIN/ASYNC_END pairs.
+// ASYNC_BEGIN and ASYNC_END can occur on any thread of any traced process.
+// Pointers can be used for the ID parameter, and they will be mangled
+// internally so that the same pointer on two different processes will not
+// match. For example:
+// class MyTracedClass {
+// public:
+// MyTracedClass() {
+// TRACE_EVENT_ASYNC_BEGIN0("category", "MyTracedClass", this);
+// }
+// ~MyTracedClass() {
+// TRACE_EVENT_ASYNC_END0("category", "MyTracedClass", this);
+// }
+// }
+//
+// Trace event also supports counters, which is a way to track a quantity
+// as it varies over time. Counters are created with the following macro:
+// TRACE_COUNTER1("MY_SUBSYSTEM", "myCounter", g_myCounterValue);
+//
+// Counters are process-specific. The macro itself can be issued from any
+// thread, however.
+//
+// Sometimes, you want to track two counters at once. You can do this with two
+// counter macros:
+// TRACE_COUNTER1("MY_SUBSYSTEM", "myCounter0", g_myCounterValue[0]);
+// TRACE_COUNTER1("MY_SUBSYSTEM", "myCounter1", g_myCounterValue[1]);
+// Or you can do it with a combined macro:
+// TRACE_COUNTER2("MY_SUBSYSTEM", "myCounter",
+// "bytesPinned", g_myCounterValue[0],
+// "bytesAllocated", g_myCounterValue[1]);
+// This indicates to the tracing UI that these counters should be displayed
+// in a single graph, as a summed area chart.
+//
+// Since counters are in a global namespace, you may want to disembiguate with a
+// unique ID, by using the TRACE_COUNTER_ID* variations.
+//
+// By default, trace collection is compiled in, but turned off at runtime.
+// Collecting trace data is the responsibility of the embedding
+// application. In Chrome's case, navigating to about:tracing will turn on
+// tracing and display data collected across all active processes.
+//
+//
+// Memory scoping note:
+// Tracing copies the pointers, not the string content, of the strings passed
+// in for category, name, and arg_names. Thus, the following code will
+// cause problems:
+// char* str = strdup("impprtantName");
+// TRACE_EVENT_INSTANT0("SUBSYSTEM", str); // BAD!
+// free(str); // Trace system now has dangling pointer
+//
+// To avoid this issue with the |name| and |arg_name| parameters, use the
+// TRACE_EVENT_COPY_XXX overloads of the macros at additional runtime overhead.
+// Notes: The category must always be in a long-lived char* (i.e. static const).
+// The |arg_values|, when used, are always deep copied with the _COPY
+// macros.
+//
+// When are string argument values copied:
+// const char* arg_values are only referenced by default:
+// TRACE_EVENT1("category", "name",
+// "arg1", "literal string is only referenced");
+// Use TRACE_STR_COPY to force copying of a const char*:
+// TRACE_EVENT1("category", "name",
+// "arg1", TRACE_STR_COPY("string will be copied"));
+// std::string arg_values are always copied:
+// TRACE_EVENT1("category", "name",
+// "arg1", std::string("string will be copied"));
+//
+//
+// Thread Safety:
+// A thread safe singleton and mutex are used for thread safety. Category
+// enabled flags are used to limit the performance impact when the system
+// is not enabled.
+//
+// TRACE_EVENT macros first cache a pointer to a category. The categories are
+// statically allocated and safe at all times, even after exit. Fetching a
+// category is protected by the TraceLog::lock_. Multiple threads initializing
+// the static variable is safe, as they will be serialized by the lock and
+// multiple calls will return the same pointer to the category.
+//
+// Then the category_enabled flag is checked. This is a unsigned char, and
+// not intended to be multithread safe. It optimizes access to AddTraceEvent
+// which is threadsafe internally via TraceLog::lock_. The enabled flag may
+// cause some threads to incorrectly call or skip calling AddTraceEvent near
+// the time of the system being enabled or disabled. This is acceptable as
+// we tolerate some data loss while the system is being enabled/disabled and
+// because AddTraceEvent is threadsafe internally and checks the enabled state
+// again under lock.
+//
+// Without the use of these static category pointers and enabled flags all
+// trace points would carry a significant performance cost of aquiring a lock
+// and resolving the category.
+
+
+#ifndef BASE_DEBUG_TRACE_EVENT_H_
+#define BASE_DEBUG_TRACE_EVENT_H_
+
+#include <string>
+
+#include "base/atomicops.h"
+#include "base/debug/trace_event_impl.h"
+#include "build/build_config.h"
+
+#if defined(TRACING_DISABLED)
+
+inline void NullAddTraceEvent(
+ char phase,
+ const unsigned char* category_enabled,
+ const char* name,
+ unsigned long long id,
+ int num_args,
+ const char** arg_names,
+ const unsigned char* arg_types,
+ const unsigned long long* arg_values,
+ unsigned char flags) {
+ UNREFERENCED_PARAMETER(phase);
+ UNREFERENCED_PARAMETER(category_enabled);
+ UNREFERENCED_PARAMETER(name);
+ UNREFERENCED_PARAMETER(id);
+ UNREFERENCED_PARAMETER(num_args);
+ UNREFERENCED_PARAMETER(arg_names);
+ UNREFERENCED_PARAMETER(arg_types);
+ UNREFERENCED_PARAMETER(arg_values);
+ UNREFERENCED_PARAMETER(flags);
+}
+
+#define TRACE_EVENT_API_ADD_TRACE_EVENT NullAddTraceEvent
+
+#define INTERNAL_TRACE_EVENT_ADD(phase, category, name, flags, ...) \
+ (void)0
+#define INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name, ...) \
+ (void)0
+#define INTERNAL_TRACE_EVENT_ADD_WITH_ID(phase, category, name, id, flags, \
+ ...) \
+ (void)0
+
+#endif // #if defined(TRACING_DISABLED)
+
+// By default, const char* argument values are assumed to have long-lived scope
+// and will not be copied. Use this macro to force a const char* to be copied.
+#define TRACE_STR_COPY(str) \
+ trace_event_internal::TraceStringWithCopy(str)
+
+// By default, uint64 ID argument values are not mangled with the Process ID in
+// TRACE_EVENT_ASYNC macros. Use this macro to force Process ID mangling.
+#define TRACE_ID_MANGLE(id) \
+ trace_event_internal::TraceID::ForceMangle(id)
+
+// Records a pair of begin and end events called "name" for the current
+// scope, with 0, 1 or 2 associated arguments. If the category is not
+// enabled, then this does nothing.
+// - category and name strings must have application lifetime (statics or
+// literals). They may not include " chars.
+#define TRACE_EVENT0(category, name) \
+ INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name)
+#define TRACE_EVENT1(category, name, arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name, arg1_name, arg1_val)
+#define TRACE_EVENT2(category, name, arg1_name, arg1_val, arg2_name, arg2_val) \
+ INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name, arg1_name, arg1_val, \
+ arg2_name, arg2_val)
+
+// Same as TRACE_EVENT except that they are not included in official builds.
+#ifdef OFFICIAL_BUILD
+#define UNSHIPPED_TRACE_EVENT0(category, name) (void)0
+#define UNSHIPPED_TRACE_EVENT1(category, name, arg1_name, arg1_val) (void)0
+#define UNSHIPPED_TRACE_EVENT2(category, name, arg1_name, arg1_val, \
+ arg2_name, arg2_val) (void)0
+#define UNSHIPPED_TRACE_EVENT_INSTANT0(category, name) (void)0
+#define UNSHIPPED_TRACE_EVENT_INSTANT1(category, name, arg1_name, arg1_val) \
+ (void)0
+#define UNSHIPPED_TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, \
+ arg2_name, arg2_val) (void)0
+#else
+#define UNSHIPPED_TRACE_EVENT0(category, name) \
+ TRACE_EVENT0(category, name)
+#define UNSHIPPED_TRACE_EVENT1(category, name, arg1_name, arg1_val) \
+ TRACE_EVENT1(category, name, arg1_name, arg1_val)
+#define UNSHIPPED_TRACE_EVENT2(category, name, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ TRACE_EVENT2(category, name, arg1_name, arg1_val, arg2_name, arg2_val)
+#define UNSHIPPED_TRACE_EVENT_INSTANT0(category, name) \
+ TRACE_EVENT_INSTANT0(category, name)
+#define UNSHIPPED_TRACE_EVENT_INSTANT1(category, name, arg1_name, arg1_val) \
+ TRACE_EVENT_INSTANT1(category, name, arg1_name, arg1_val)
+#define UNSHIPPED_TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, \
+ arg2_name, arg2_val)
+#endif
+
+// Records a single event called "name" immediately, with 0, 1 or 2
+// associated arguments. If the category is not enabled, then this
+// does nothing.
+// - category and name strings must have application lifetime (statics or
+// literals). They may not include " chars.
+#define TRACE_EVENT_INSTANT0(category, name) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \
+ category, name, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_INSTANT1(category, name, arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \
+ category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \
+ category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, \
+ arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_INSTANT0(category, name) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \
+ category, name, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_INSTANT1(category, name, arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \
+ category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_INSTANT2(category, name, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \
+ category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \
+ arg2_name, arg2_val)
+
+// Records a single BEGIN event called "name" immediately, with 0, 1 or 2
+// associated arguments. If the category is not enabled, then this
+// does nothing.
+// - category and name strings must have application lifetime (statics or
+// literals). They may not include " chars.
+#define TRACE_EVENT_BEGIN0(category, name) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \
+ category, name, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_BEGIN1(category, name, arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \
+ category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_BEGIN2(category, name, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \
+ category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, \
+ arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_BEGIN0(category, name) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \
+ category, name, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_BEGIN1(category, name, arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \
+ category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_BEGIN2(category, name, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \
+ category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \
+ arg2_name, arg2_val)
+
+// Records a single END event for "name" immediately. If the category
+// is not enabled, then this does nothing.
+// - category and name strings must have application lifetime (statics or
+// literals). They may not include " chars.
+#define TRACE_EVENT_END0(category, name) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \
+ category, name, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_END1(category, name, arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \
+ category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_END2(category, name, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \
+ category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, \
+ arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_END0(category, name) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \
+ category, name, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_END1(category, name, arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \
+ category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_END2(category, name, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \
+ category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \
+ arg2_name, arg2_val)
+
+// Records the value of a counter called "name" immediately. Value
+// must be representable as a 32 bit integer.
+// - category and name strings must have application lifetime (statics or
+// literals). They may not include " chars.
+#define TRACE_COUNTER1(category, name, value) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, \
+ category, name, TRACE_EVENT_FLAG_NONE, \
+ "value", static_cast<int>(value))
+#define TRACE_COPY_COUNTER1(category, name, value) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, \
+ category, name, TRACE_EVENT_FLAG_COPY, \
+ "value", static_cast<int>(value))
+
+// Records the values of a multi-parted counter called "name" immediately.
+// The UI will treat value1 and value2 as parts of a whole, displaying their
+// values as a stacked-bar chart.
+// - category and name strings must have application lifetime (statics or
+// literals). They may not include " chars.
+#define TRACE_COUNTER2(category, name, value1_name, value1_val, \
+ value2_name, value2_val) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, \
+ category, name, TRACE_EVENT_FLAG_NONE, \
+ value1_name, static_cast<int>(value1_val), \
+ value2_name, static_cast<int>(value2_val))
+#define TRACE_COPY_COUNTER2(category, name, value1_name, value1_val, \
+ value2_name, value2_val) \
+ INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, \
+ category, name, TRACE_EVENT_FLAG_COPY, \
+ value1_name, static_cast<int>(value1_val), \
+ value2_name, static_cast<int>(value2_val))
+
+// Records the value of a counter called "name" immediately. Value
+// must be representable as a 32 bit integer.
+// - category and name strings must have application lifetime (statics or
+// literals). They may not include " chars.
+// - |id| is used to disambiguate counters with the same name. It must either
+// be a pointer or an integer value up to 64 bits. If it's a pointer, the bits
+// will be xored with a hash of the process ID so that the same pointer on
+// two different processes will not collide.
+#define TRACE_COUNTER_ID1(category, name, id, value) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, \
+ category, name, id, TRACE_EVENT_FLAG_NONE, \
+ "value", static_cast<int>(value))
+#define TRACE_COPY_COUNTER_ID1(category, name, id, value) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, \
+ category, name, id, TRACE_EVENT_FLAG_COPY, \
+ "value", static_cast<int>(value))
+
+// Records the values of a multi-parted counter called "name" immediately.
+// The UI will treat value1 and value2 as parts of a whole, displaying their
+// values as a stacked-bar chart.
+// - category and name strings must have application lifetime (statics or
+// literals). They may not include " chars.
+// - |id| is used to disambiguate counters with the same name. It must either
+// be a pointer or an integer value up to 64 bits. If it's a pointer, the bits
+// will be xored with a hash of the process ID so that the same pointer on
+// two different processes will not collide.
+#define TRACE_COUNTER_ID2(category, name, id, value1_name, value1_val, \
+ value2_name, value2_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, \
+ category, name, id, TRACE_EVENT_FLAG_NONE, \
+ value1_name, static_cast<int>(value1_val), \
+ value2_name, static_cast<int>(value2_val))
+#define TRACE_COPY_COUNTER_ID2(category, name, id, value1_name, value1_val, \
+ value2_name, value2_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, \
+ category, name, id, TRACE_EVENT_FLAG_COPY, \
+ value1_name, static_cast<int>(value1_val), \
+ value2_name, static_cast<int>(value2_val))
+
+
+// Records a single ASYNC_BEGIN event called "name" immediately, with 0, 1 or 2
+// associated arguments. If the category is not enabled, then this
+// does nothing.
+// - category and name strings must have application lifetime (statics or
+// literals). They may not include " chars.
+// - |id| is used to match the ASYNC_BEGIN event with the ASYNC_END event. ASYNC
+// events are considered to match if their category, name and id values all
+// match. |id| must either be a pointer or an integer value up to 64 bits. If
+// it's a pointer, the bits will be xored with a hash of the process ID so
+// that the same pointer on two different processes will not collide.
+// An asynchronous operation can consist of multiple phases. The first phase is
+// defined by the ASYNC_BEGIN calls. Additional phases can be defined using the
+// ASYNC_STEP macros. When the operation completes, call ASYNC_END.
+// An ASYNC trace typically occur on a single thread (if not, they will only be
+// drawn on the thread defined in the ASYNC_BEGIN event), but all events in that
+// operation must use the same |name| and |id|. Each event can have its own
+// args.
+#define TRACE_EVENT_ASYNC_BEGIN0(category, name, id) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \
+ category, name, id, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_ASYNC_BEGIN1(category, name, id, arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \
+ category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_ASYNC_BEGIN2(category, name, id, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \
+ category, name, id, TRACE_EVENT_FLAG_NONE, \
+ arg1_name, arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_ASYNC_BEGIN0(category, name, id) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \
+ category, name, id, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_ASYNC_BEGIN1(category, name, id, arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \
+ category, name, id, TRACE_EVENT_FLAG_COPY, \
+ arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_ASYNC_BEGIN2(category, name, id, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \
+ category, name, id, TRACE_EVENT_FLAG_COPY, \
+ arg1_name, arg1_val, arg2_name, arg2_val)
+
+// Records a single ASYNC_STEP event for |step| immediately. If the category
+// is not enabled, then this does nothing. The |name| and |id| must match the
+// ASYNC_BEGIN event above. The |step| param identifies this step within the
+// async event. This should be called at the beginning of the next phase of an
+// asynchronous operation.
+#define TRACE_EVENT_ASYNC_STEP0(category, name, id, step) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, \
+ category, name, id, TRACE_EVENT_FLAG_NONE, "step", step)
+#define TRACE_EVENT_ASYNC_STEP1(category, name, id, step, \
+ arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, \
+ category, name, id, TRACE_EVENT_FLAG_NONE, "step", step, \
+ arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_ASYNC_STEP0(category, name, id, step) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, \
+ category, name, id, TRACE_EVENT_FLAG_COPY, "step", step)
+#define TRACE_EVENT_COPY_ASYNC_STEP1(category, name, id, step, \
+ arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, \
+ category, name, id, TRACE_EVENT_FLAG_COPY, "step", step, \
+ arg1_name, arg1_val)
+
+// Records a single ASYNC_END event for "name" immediately. If the category
+// is not enabled, then this does nothing.
+#define TRACE_EVENT_ASYNC_END0(category, name, id) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \
+ category, name, id, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_ASYNC_END1(category, name, id, arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \
+ category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_ASYNC_END2(category, name, id, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \
+ category, name, id, TRACE_EVENT_FLAG_NONE, \
+ arg1_name, arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_ASYNC_END0(category, name, id) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \
+ category, name, id, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_ASYNC_END1(category, name, id, arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \
+ category, name, id, TRACE_EVENT_FLAG_COPY, \
+ arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_ASYNC_END2(category, name, id, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \
+ category, name, id, TRACE_EVENT_FLAG_COPY, \
+ arg1_name, arg1_val, arg2_name, arg2_val)
+
+
+// Records a single FLOW_BEGIN event called "name" immediately, with 0, 1 or 2
+// associated arguments. If the category is not enabled, then this
+// does nothing.
+// - category and name strings must have application lifetime (statics or
+// literals). They may not include " chars.
+// - |id| is used to match the FLOW_BEGIN event with the FLOW_END event. FLOW
+// events are considered to match if their category, name and id values all
+// match. |id| must either be a pointer or an integer value up to 64 bits. If
+// it's a pointer, the bits will be xored with a hash of the process ID so
+// that the same pointer on two different processes will not collide.
+// FLOW events are different from ASYNC events in how they are drawn by the
+// tracing UI. A FLOW defines asynchronous data flow, such as posting a task
+// (FLOW_BEGIN) and later executing that task (FLOW_END). Expect FLOWs to be
+// drawn as lines or arrows from FLOW_BEGIN scopes to FLOW_END scopes. Similar
+// to ASYNC, a FLOW can consist of multiple phases. The first phase is defined
+// by the FLOW_BEGIN calls. Additional phases can be defined using the FLOW_STEP
+// macros. When the operation completes, call FLOW_END. An async operation can
+// span threads and processes, but all events in that operation must use the
+// same |name| and |id|. Each event can have its own args.
+#define TRACE_EVENT_FLOW_BEGIN0(category, name, id) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
+ category, name, id, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_FLOW_BEGIN1(category, name, id, arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
+ category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_FLOW_BEGIN2(category, name, id, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
+ category, name, id, TRACE_EVENT_FLAG_NONE, \
+ arg1_name, arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_FLOW_BEGIN0(category, name, id) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
+ category, name, id, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_FLOW_BEGIN1(category, name, id, arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
+ category, name, id, TRACE_EVENT_FLAG_COPY, \
+ arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_FLOW_BEGIN2(category, name, id, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
+ category, name, id, TRACE_EVENT_FLAG_COPY, \
+ arg1_name, arg1_val, arg2_name, arg2_val)
+
+// Records a single FLOW_STEP event for |step| immediately. If the category
+// is not enabled, then this does nothing. The |name| and |id| must match the
+// FLOW_BEGIN event above. The |step| param identifies this step within the
+// async event. This should be called at the beginning of the next phase of an
+// asynchronous operation.
+#define TRACE_EVENT_FLOW_STEP0(category, name, id, step) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \
+ category, name, id, TRACE_EVENT_FLAG_NONE, "step", step)
+#define TRACE_EVENT_FLOW_STEP1(category, name, id, step, \
+ arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \
+ category, name, id, TRACE_EVENT_FLAG_NONE, "step", step, \
+ arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_FLOW_STEP0(category, name, id, step) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \
+ category, name, id, TRACE_EVENT_FLAG_COPY, "step", step)
+#define TRACE_EVENT_COPY_FLOW_STEP1(category, name, id, step, \
+ arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \
+ category, name, id, TRACE_EVENT_FLAG_COPY, "step", step, \
+ arg1_name, arg1_val)
+
+// Records a single FLOW_END event for "name" immediately. If the category
+// is not enabled, then this does nothing.
+#define TRACE_EVENT_FLOW_END0(category, name, id) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
+ category, name, id, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_FLOW_END1(category, name, id, arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
+ category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_FLOW_END2(category, name, id, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
+ category, name, id, TRACE_EVENT_FLAG_NONE, \
+ arg1_name, arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_FLOW_END0(category, name, id) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
+ category, name, id, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_FLOW_END1(category, name, id, arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
+ category, name, id, TRACE_EVENT_FLAG_COPY, \
+ arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_FLOW_END2(category, name, id, arg1_name, arg1_val, \
+ arg2_name, arg2_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
+ category, name, id, TRACE_EVENT_FLAG_COPY, \
+ arg1_name, arg1_val, arg2_name, arg2_val)
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Implementation specific tracing API definitions.
+
+// Get a pointer to the enabled state of the given trace category. Only
+// long-lived literal strings should be given as the category name. The returned
+// pointer can be held permanently in a local static for example. If the
+// unsigned char is non-zero, tracing is enabled. If tracing is enabled,
+// TRACE_EVENT_API_ADD_TRACE_EVENT can be called. It's OK if tracing is disabled
+// between the load of the tracing state and the call to
+// TRACE_EVENT_API_ADD_TRACE_EVENT, because this flag only provides an early out
+// for best performance when tracing is disabled.
+// const unsigned char*
+// TRACE_EVENT_API_GET_CATEGORY_ENABLED(const char* category_name)
+#define TRACE_EVENT_API_GET_CATEGORY_ENABLED \
+ base::debug::TraceLog::GetCategoryEnabled
+
+#if !defined(TRACING_DISABLED)
+// Add a trace event to the platform tracing system.
+// void TRACE_EVENT_API_ADD_TRACE_EVENT(
+// char phase,
+// const unsigned char* category_enabled,
+// const char* name,
+// unsigned long long id,
+// int num_args,
+// const char** arg_names,
+// const unsigned char* arg_types,
+// const unsigned long long* arg_values,
+// unsigned char flags)
+#define TRACE_EVENT_API_ADD_TRACE_EVENT \
+ base::debug::TraceLog::GetInstance()->AddTraceEvent
+#endif // !defined(TRACING_DISABLED)
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Implementation detail: trace event macros create temporary variables
+// to keep instrumentation overhead low. These macros give each temporary
+// variable a unique name based on the line number to prevent name collissions.
+#define INTERNAL_TRACE_EVENT_UID3(a,b) \
+ trace_event_unique_##a##b
+#define INTERNAL_TRACE_EVENT_UID2(a,b) \
+ INTERNAL_TRACE_EVENT_UID3(a,b)
+#define INTERNAL_TRACE_EVENT_UID(name_prefix) \
+ INTERNAL_TRACE_EVENT_UID2(name_prefix, __LINE__)
+
+// Implementation detail: internal macro to create static category.
+// No barriers are needed, because this code is designed to operate safely
+// even when the unsigned char* points to garbage data (which may be the case
+// on processors without cache coherency).
+#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category) \
+ static base::subtle::AtomicWord INTERNAL_TRACE_EVENT_UID(atomic) = 0; \
+ const uint8* INTERNAL_TRACE_EVENT_UID(catstatic) = \
+ reinterpret_cast<const uint8*>( \
+ base::subtle::NoBarrier_Load(&INTERNAL_TRACE_EVENT_UID(atomic))); \
+ if (!INTERNAL_TRACE_EVENT_UID(catstatic)) { \
+ INTERNAL_TRACE_EVENT_UID(catstatic) = \
+ TRACE_EVENT_API_GET_CATEGORY_ENABLED(category); \
+ base::subtle::NoBarrier_Store(&INTERNAL_TRACE_EVENT_UID(atomic), \
+ reinterpret_cast<base::subtle::AtomicWord>( \
+ INTERNAL_TRACE_EVENT_UID(catstatic))); \
+ }
+
+#if !defined(TRACING_DISABLED)
+// Implementation detail: internal macro to create static category and add
+// event if the category is enabled.
+#define INTERNAL_TRACE_EVENT_ADD(phase, category, name, flags, ...) \
+ do { \
+ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category); \
+ if (*INTERNAL_TRACE_EVENT_UID(catstatic)) { \
+ trace_event_internal::AddTraceEvent( \
+ phase, INTERNAL_TRACE_EVENT_UID(catstatic), name, \
+ trace_event_internal::kNoEventId, flags, ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+// Implementation detail: internal macro to create static category and add begin
+// event if the category is enabled. Also adds the end event when the scope
+// ends.
+#define INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name, ...) \
+ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category); \
+ trace_event_internal::TraceEndOnScopeClose \
+ INTERNAL_TRACE_EVENT_UID(profileScope); \
+ if (*INTERNAL_TRACE_EVENT_UID(catstatic)) { \
+ trace_event_internal::AddTraceEvent( \
+ TRACE_EVENT_PHASE_BEGIN, \
+ INTERNAL_TRACE_EVENT_UID(catstatic), \
+ name, trace_event_internal::kNoEventId, \
+ TRACE_EVENT_FLAG_NONE, ##__VA_ARGS__); \
+ INTERNAL_TRACE_EVENT_UID(profileScope).Initialize( \
+ INTERNAL_TRACE_EVENT_UID(catstatic), name); \
+ }
+
+// Implementation detail: internal macro to create static category and add
+// event if the category is enabled.
+#define INTERNAL_TRACE_EVENT_ADD_WITH_ID(phase, category, name, id, flags, \
+ ...) \
+ do { \
+ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category); \
+ if (*INTERNAL_TRACE_EVENT_UID(catstatic)) { \
+ unsigned char trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \
+ trace_event_internal::TraceID trace_event_trace_id( \
+ id, &trace_event_flags); \
+ trace_event_internal::AddTraceEvent( \
+ phase, INTERNAL_TRACE_EVENT_UID(catstatic), \
+ name, trace_event_trace_id.data(), trace_event_flags, \
+ ##__VA_ARGS__); \
+ } \
+ } while (0)
+#endif // #if !defined(TRACING_DISABLED)
+
+// Notes regarding the following definitions:
+// New values can be added and propagated to third party libraries, but existing
+// definitions must never be changed, because third party libraries may use old
+// definitions.
+
+// Phase indicates the nature of an event entry. E.g. part of a begin/end pair.
+#define TRACE_EVENT_PHASE_BEGIN ('B')
+#define TRACE_EVENT_PHASE_END ('E')
+#define TRACE_EVENT_PHASE_INSTANT ('I')
+#define TRACE_EVENT_PHASE_ASYNC_BEGIN ('S')
+#define TRACE_EVENT_PHASE_ASYNC_STEP ('T')
+#define TRACE_EVENT_PHASE_ASYNC_END ('F')
+#define TRACE_EVENT_PHASE_FLOW_BEGIN ('s')
+#define TRACE_EVENT_PHASE_FLOW_STEP ('t')
+#define TRACE_EVENT_PHASE_FLOW_END ('f')
+#define TRACE_EVENT_PHASE_METADATA ('M')
+#define TRACE_EVENT_PHASE_COUNTER ('C')
+
+// Flags for changing the behavior of TRACE_EVENT_API_ADD_TRACE_EVENT.
+#define TRACE_EVENT_FLAG_NONE (static_cast<unsigned char>(0))
+#define TRACE_EVENT_FLAG_COPY (static_cast<unsigned char>(1 << 0))
+#define TRACE_EVENT_FLAG_HAS_ID (static_cast<unsigned char>(1 << 1))
+#define TRACE_EVENT_FLAG_MANGLE_ID (static_cast<unsigned char>(1 << 2))
+
+// Type values for identifying types in the TraceValue union.
+#define TRACE_VALUE_TYPE_BOOL (static_cast<unsigned char>(1))
+#define TRACE_VALUE_TYPE_UINT (static_cast<unsigned char>(2))
+#define TRACE_VALUE_TYPE_INT (static_cast<unsigned char>(3))
+#define TRACE_VALUE_TYPE_DOUBLE (static_cast<unsigned char>(4))
+#define TRACE_VALUE_TYPE_POINTER (static_cast<unsigned char>(5))
+#define TRACE_VALUE_TYPE_STRING (static_cast<unsigned char>(6))
+#define TRACE_VALUE_TYPE_COPY_STRING (static_cast<unsigned char>(7))
+
+namespace trace_event_internal {
+
+// Specify these values when the corresponding argument of AddTraceEvent is not
+// used.
+const int kZeroNumArgs = 0;
+const unsigned long long kNoEventId = 0;
+
+// TraceID encapsulates an ID that can either be an integer or pointer. Pointers
+// are mangled with the Process ID so that they are unlikely to collide when the
+// same pointer is used on different processes.
+class TraceID {
+ public:
+ class ForceMangle {
+ public:
+ explicit ForceMangle(unsigned long long id) : data_(id) {}
+ explicit ForceMangle(unsigned long id) : data_(id) {}
+ explicit ForceMangle(unsigned int id) : data_(id) {}
+ explicit ForceMangle(unsigned short id) : data_(id) {}
+ explicit ForceMangle(unsigned char id) : data_(id) {}
+ explicit ForceMangle(long long id)
+ : data_(static_cast<unsigned long long>(id)) {}
+ explicit ForceMangle(long id)
+ : data_(static_cast<unsigned long long>(id)) {}
+ explicit ForceMangle(int id)
+ : data_(static_cast<unsigned long long>(id)) {}
+ explicit ForceMangle(short id)
+ : data_(static_cast<unsigned long long>(id)) {}
+ explicit ForceMangle(signed char id)
+ : data_(static_cast<unsigned long long>(id)) {}
+
+ unsigned long long data() const { return data_; }
+
+ private:
+ unsigned long long data_;
+ };
+
+ explicit TraceID(const void* id, unsigned char* flags)
+ : data_(reinterpret_cast<unsigned long long>(id)) {
+ *flags |= TRACE_EVENT_FLAG_MANGLE_ID;
+ }
+ explicit TraceID(ForceMangle id, unsigned char* flags) : data_(id.data()) {
+ *flags |= TRACE_EVENT_FLAG_MANGLE_ID;
+ }
+ explicit TraceID(unsigned long long id, unsigned char* flags)
+ : data_(id) { (void)flags; }
+ explicit TraceID(unsigned long id, unsigned char* flags)
+ : data_(id) { (void)flags; }
+ explicit TraceID(unsigned int id, unsigned char* flags)
+ : data_(id) { (void)flags; }
+ explicit TraceID(unsigned short id, unsigned char* flags)
+ : data_(id) { (void)flags; }
+ explicit TraceID(unsigned char id, unsigned char* flags)
+ : data_(id) { (void)flags; }
+ explicit TraceID(long long id, unsigned char* flags)
+ : data_(static_cast<unsigned long long>(id)) { (void)flags; }
+ explicit TraceID(long id, unsigned char* flags)
+ : data_(static_cast<unsigned long long>(id)) { (void)flags; }
+ explicit TraceID(int id, unsigned char* flags)
+ : data_(static_cast<unsigned long long>(id)) { (void)flags; }
+ explicit TraceID(short id, unsigned char* flags)
+ : data_(static_cast<unsigned long long>(id)) { (void)flags; }
+ explicit TraceID(signed char id, unsigned char* flags)
+ : data_(static_cast<unsigned long long>(id)) { (void)flags; }
+
+ unsigned long long data() const { return data_; }
+
+ private:
+ unsigned long long data_;
+};
+
+// Simple union to store various types as unsigned long long.
+union TraceValueUnion {
+ bool as_bool;
+ unsigned long long as_uint;
+ long long as_int;
+ double as_double;
+ const void* as_pointer;
+ const char* as_string;
+};
+
+// Simple container for const char* that should be copied instead of retained.
+class TraceStringWithCopy {
+ public:
+ explicit TraceStringWithCopy(const char* str) : str_(str) {}
+ operator const char* () const { return str_; }
+ private:
+ const char* str_;
+};
+
+// Define SetTraceValue for each allowed type. It stores the type and
+// value in the return arguments. This allows this API to avoid declaring any
+// structures so that it is portable to third_party libraries.
+#define INTERNAL_DECLARE_SET_TRACE_VALUE(actual_type, \
+ union_member, \
+ value_type_id) \
+ static inline void SetTraceValue(actual_type arg, \
+ unsigned char* type, \
+ unsigned long long* value) { \
+ TraceValueUnion type_value; \
+ type_value.union_member = arg; \
+ *type = value_type_id; \
+ *value = type_value.as_uint; \
+ }
+// Simpler form for int types that can be safely casted.
+#define INTERNAL_DECLARE_SET_TRACE_VALUE_INT(actual_type, \
+ value_type_id) \
+ static inline void SetTraceValue(actual_type arg, \
+ unsigned char* type, \
+ unsigned long long* value) { \
+ *type = value_type_id; \
+ *value = static_cast<unsigned long long>(arg); \
+ }
+
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned long long, TRACE_VALUE_TYPE_UINT)
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned long, TRACE_VALUE_TYPE_UINT)
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned int, TRACE_VALUE_TYPE_UINT)
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned short, TRACE_VALUE_TYPE_UINT)
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned char, TRACE_VALUE_TYPE_UINT)
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(long long, TRACE_VALUE_TYPE_INT)
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(long, TRACE_VALUE_TYPE_INT)
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(int, TRACE_VALUE_TYPE_INT)
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(short, TRACE_VALUE_TYPE_INT)
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(signed char, TRACE_VALUE_TYPE_INT)
+INTERNAL_DECLARE_SET_TRACE_VALUE(bool, as_bool, TRACE_VALUE_TYPE_BOOL)
+INTERNAL_DECLARE_SET_TRACE_VALUE(double, as_double, TRACE_VALUE_TYPE_DOUBLE)
+INTERNAL_DECLARE_SET_TRACE_VALUE(const void*, as_pointer,
+ TRACE_VALUE_TYPE_POINTER)
+INTERNAL_DECLARE_SET_TRACE_VALUE(const char*, as_string,
+ TRACE_VALUE_TYPE_STRING)
+INTERNAL_DECLARE_SET_TRACE_VALUE(const TraceStringWithCopy&, as_string,
+ TRACE_VALUE_TYPE_COPY_STRING)
+
+#undef INTERNAL_DECLARE_SET_TRACE_VALUE
+#undef INTERNAL_DECLARE_SET_TRACE_VALUE_INT
+
+// std::string version of SetTraceValue so that trace arguments can be strings.
+static inline void SetTraceValue(const std::string& arg,
+ unsigned char* type,
+ unsigned long long* value) {
+ TraceValueUnion type_value;
+ type_value.as_string = arg.c_str();
+ *type = TRACE_VALUE_TYPE_COPY_STRING;
+ *value = type_value.as_uint;
+}
+
+// These AddTraceEvent template functions are defined here instead of in the
+// macro, because the arg_values could be temporary objects, such as
+// std::string. In order to store pointers to the internal c_str and pass
+// through to the tracing API, the arg_values must live throughout
+// these procedures.
+
+static inline void AddTraceEvent(char phase,
+ const unsigned char* category_enabled,
+ const char* name,
+ unsigned long long id,
+ unsigned char flags) {
+ TRACE_EVENT_API_ADD_TRACE_EVENT(
+ phase, category_enabled, name, id,
+ kZeroNumArgs, NULL, NULL, NULL,
+ flags);
+}
+
+template<class ARG1_TYPE>
+static inline void AddTraceEvent(char phase,
+ const unsigned char* category_enabled,
+ const char* name,
+ unsigned long long id,
+ unsigned char flags,
+ const char* arg1_name,
+ const ARG1_TYPE& arg1_val) {
+ const int num_args = 1;
+ unsigned char arg_types[1];
+ unsigned long long arg_values[1];
+ trace_event_internal::SetTraceValue(arg1_val, &arg_types[0], &arg_values[0]);
+ TRACE_EVENT_API_ADD_TRACE_EVENT(
+ phase, category_enabled, name, id,
+ num_args, &arg1_name, arg_types, arg_values,
+ flags);
+}
+
+template<class ARG1_TYPE, class ARG2_TYPE>
+static inline void AddTraceEvent(char phase,
+ const unsigned char* category_enabled,
+ const char* name,
+ unsigned long long id,
+ unsigned char flags,
+ const char* arg1_name,
+ const ARG1_TYPE& arg1_val,
+ const char* arg2_name,
+ const ARG2_TYPE& arg2_val) {
+ const int num_args = 2;
+ const char* arg_names[2] = { arg1_name, arg2_name };
+ unsigned char arg_types[2];
+ unsigned long long arg_values[2];
+ trace_event_internal::SetTraceValue(arg1_val, &arg_types[0], &arg_values[0]);
+ trace_event_internal::SetTraceValue(arg2_val, &arg_types[1], &arg_values[1]);
+ TRACE_EVENT_API_ADD_TRACE_EVENT(
+ phase, category_enabled, name, id,
+ num_args, arg_names, arg_types, arg_values,
+ flags);
+}
+
+// Used by TRACE_EVENTx macro. Do not use directly.
+class BASE_EXPORT TraceEndOnScopeClose {
+ public:
+ // Note: members of data_ intentionally left uninitialized. See Initialize.
+ TraceEndOnScopeClose() : p_data_(NULL) {}
+ ~TraceEndOnScopeClose() {
+ if (p_data_)
+ AddEventIfEnabled();
+ }
+
+ void Initialize(const unsigned char* category_enabled,
+ const char* name);
+
+ private:
+ // Add the end event if the category is still enabled.
+ void AddEventIfEnabled();
+
+ // This Data struct workaround is to avoid initializing all the members
+ // in Data during construction of this object, since this object is always
+ // constructed, even when tracing is disabled. If the members of Data were
+ // members of this class instead, compiler warnings occur about potential
+ // uninitialized accesses.
+ struct Data {
+ const unsigned char* category_enabled;
+ const char* name;
+ };
+ Data* p_data_;
+ Data data_;
+};
+
+
+} // namespace trace_event_internal
+
+#endif // BASE_DEBUG_TRACE_EVENT_H_
diff --git a/src/base/debug/trace_event_android.cc b/src/base/debug/trace_event_android.cc
new file mode 100644
index 0000000..c56c3c1
--- /dev/null
+++ b/src/base/debug/trace_event_android.cc
@@ -0,0 +1,144 @@
+// Copyright (c) 2012 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/trace_event_impl.h"
+
+#include <fcntl.h>
+
+#include "base/debug/trace_event.h"
+#include "base/file_util.h"
+#include "base/format_macros.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+
+namespace {
+
+int g_atrace_fd = -1;
+const char* kATraceMarkerFile = "/sys/kernel/debug/tracing/trace_marker";
+
+} // namespace
+
+namespace base {
+namespace debug {
+
+// static
+void TraceLog::InitATrace() {
+ DCHECK(g_atrace_fd == -1);
+ g_atrace_fd = open(kATraceMarkerFile, O_WRONLY | O_APPEND);
+ if (g_atrace_fd == -1)
+ LOG(WARNING) << "Couldn't open " << kATraceMarkerFile;
+}
+
+void TraceLog::SendToATrace(char phase,
+ const char* category,
+ const char* name,
+ int num_args,
+ const char** arg_names,
+ const unsigned char* arg_types,
+ const unsigned long long* arg_values) {
+ if (g_atrace_fd == -1)
+ return;
+
+ switch (phase) {
+ case TRACE_EVENT_PHASE_BEGIN:
+ case TRACE_EVENT_PHASE_INSTANT: {
+ std::string out = StringPrintf("B|%d|%s-%s", getpid(), category, name);
+ for (int i = 0; i < num_args; ++i) {
+ out += (i == 0 ? '|' : ';');
+ out += arg_names[i];
+ out += '=';
+ TraceEvent::TraceValue value;
+ value.as_uint = arg_values[i];
+ std::string::size_type value_start = out.length();
+ TraceEvent::AppendValueAsJSON(arg_types[i], value, &out);
+ // Remove the quotes which may confuse the atrace script.
+ ReplaceSubstringsAfterOffset(&out, value_start, "\\\"", "'");
+ ReplaceSubstringsAfterOffset(&out, value_start, "\"", "");
+ // Replace chars used for separators with similar chars in the value.
+ std::replace(out.begin() + value_start, out.end(), ';', ',');
+ std::replace(out.begin() + value_start, out.end(), '|', '!');
+ }
+ write(g_atrace_fd, out.c_str(), out.size());
+
+ if (phase != TRACE_EVENT_PHASE_INSTANT)
+ break;
+ // Fall through. Simulate an instance event with a pair of begin/end.
+ }
+ case TRACE_EVENT_PHASE_END: {
+ // Though a single 'E' is enough, here append pid and name so that
+ // unpaired events can be found easily.
+ std::string out = StringPrintf("E|%d|%s-%s", getpid(), category, name);
+ write(g_atrace_fd, out.c_str(), out.size());
+ break;
+ }
+ case TRACE_EVENT_PHASE_COUNTER:
+ for (int i = 0; i < num_args; ++i) {
+ DCHECK(arg_types[i] == TRACE_VALUE_TYPE_INT);
+ std::string out = StringPrintf(
+ "C|%d|%s-%s-%s|%d",
+ getpid(), category, name,
+ arg_names[i], static_cast<int>(arg_values[i]));
+ write(g_atrace_fd, out.c_str(), out.size());
+ }
+ break;
+
+ default:
+ // Do nothing.
+ break;
+ }
+}
+
+void TraceLog::AddClockSyncMetadataEvents() {
+ // Since Android does not support sched_setaffinity, we cannot establish clock
+ // sync unless the scheduler clock is set to global. If the trace_clock file
+ // can't be read, we will assume the kernel doesn't support tracing and do
+ // nothing.
+ std::string clock_mode;
+ if (!file_util::ReadFileToString(
+ FilePath("/sys/kernel/debug/tracing/trace_clock"), &clock_mode))
+ return;
+
+ if (clock_mode != "local [global]\n") {
+ DLOG(WARNING) <<
+ "The kernel's tracing clock must be set to global in order for " <<
+ "trace_event to be synchronized with . Do this by\n" <<
+ " echo global > /sys/kerel/debug/tracing/trace_clock";
+ return;
+ }
+
+ int atrace_fd = g_atrace_fd;
+ if (atrace_fd == -1) {
+ // This function may be called when atrace is not enabled.
+ atrace_fd = open(kATraceMarkerFile, O_WRONLY | O_APPEND);
+ if (atrace_fd == -1) {
+ LOG(WARNING) << "Couldn't open " << kATraceMarkerFile;
+ return;
+ }
+ }
+
+ // Android's kernel trace system has a trace_marker feature: this is a file on
+ // debugfs that takes the written data and pushes it onto the trace
+ // buffer. So, to establish clock sync, we write our monotonic clock into that
+ // trace buffer.
+ TimeTicks now = TimeTicks::NowFromSystemTraceTime();
+ double now_in_seconds = now.ToInternalValue() / 1000000.0;
+ std::string marker = StringPrintf(
+ "trace_event_clock_sync: parent_ts=%f\n", now_in_seconds);
+ if (write(atrace_fd, marker.c_str(), marker.size()) != 0) {
+ DLOG(WARNING) << "Couldn't write to " << kATraceMarkerFile << ": "
+ << strerror(errno);
+ }
+
+ if (g_atrace_fd == -1)
+ close(atrace_fd);
+}
+
+// Must be called with lock_ locked.
+void TraceLog::ApplyATraceEnabledFlag(unsigned char* category_enabled) {
+ if (g_atrace_fd != -1)
+ *category_enabled |= ATRACE_ENABLED;
+}
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/trace_event_impl.cc b/src/base/debug/trace_event_impl.cc
new file mode 100644
index 0000000..5af61e5
--- /dev/null
+++ b/src/base/debug/trace_event_impl.cc
@@ -0,0 +1,830 @@
+// Copyright (c) 2012 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/trace_event_impl.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/debug/leak_annotations.h"
+#include "base/debug/trace_event.h"
+#include "base/format_macros.h"
+#include "base/lazy_instance.h"
+#include "base/memory/singleton.h"
+#include "base/process_util.h"
+#include "base/stl_util.h"
+#include "base/stringprintf.h"
+#include "base/string_tokenizer.h"
+#include "base/string_util.h"
+#include "base/sys_info.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread_local.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+
+#if defined(OS_WIN)
+#include "base/debug/trace_event_win.h"
+#endif
+
+#if defined(OS_STARBOARD)
+#include "starboard/types.h"
+#endif
+
+class DeleteTraceLogForTesting {
+ public:
+ static void Delete() {
+ Singleton<base::debug::TraceLog,
+ StaticMemorySingletonTraits<base::debug::TraceLog> >::OnExit(0);
+ }
+};
+
+namespace base {
+namespace debug {
+
+// Controls the number of trace events we will buffer in-memory
+// before throwing them away.
+const size_t kTraceEventBufferSize = 500000;
+const size_t kTraceEventBatchSize = 1000;
+
+#define TRACE_EVENT_MAX_CATEGORIES 100
+
+namespace {
+
+// Parallel arrays g_categories and g_category_enabled are separate so that
+// a pointer to a member of g_category_enabled can be easily converted to an
+// index into g_categories. This allows macros to deal only with char enabled
+// pointers from g_category_enabled, and we can convert internally to determine
+// the category name from the char enabled pointer.
+const char* g_categories[TRACE_EVENT_MAX_CATEGORIES] = {
+ "tracing already shutdown",
+ "tracing categories exhausted; must increase TRACE_EVENT_MAX_CATEGORIES",
+ "__metadata",
+};
+// The enabled flag is char instead of bool so that the API can be used from C.
+unsigned char g_category_enabled[TRACE_EVENT_MAX_CATEGORIES] = { 0 };
+const int g_category_already_shutdown = 0;
+const int g_category_categories_exhausted = 1;
+const int g_category_metadata = 2;
+int g_category_index = 3; // skip initial 3 categories
+
+// The most-recently captured name of the current thread
+LazyInstance<ThreadLocalPointer<const char> >::Leaky
+ g_current_thread_name = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// TraceEvent
+//
+////////////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+size_t GetAllocLength(const char* str) { return str ? strlen(str) + 1 : 0; }
+
+// Copies |*member| into |*buffer|, sets |*member| to point to this new
+// location, and then advances |*buffer| by the amount written.
+void CopyTraceEventParameter(char** buffer,
+ const char** member,
+ const char* end) {
+ if (*member) {
+ size_t written = strlcpy(*buffer, *member, end - *buffer) + 1;
+ DCHECK_LE(static_cast<int>(written), end - *buffer);
+ *member = *buffer;
+ *buffer += written;
+ }
+}
+
+} // namespace
+
+TraceEvent::TraceEvent()
+ : id_(0u),
+ category_enabled_(NULL),
+ name_(NULL),
+ thread_id_(0),
+ phase_(TRACE_EVENT_PHASE_BEGIN),
+ flags_(0) {
+ arg_names_[0] = NULL;
+ arg_names_[1] = NULL;
+ memset(arg_values_, 0, sizeof(arg_values_));
+}
+
+TraceEvent::TraceEvent(int thread_id,
+ TimeTicks timestamp,
+ char phase,
+ const unsigned char* category_enabled,
+ const char* name,
+ unsigned long long id,
+ int num_args,
+ const char** arg_names,
+ const unsigned char* arg_types,
+ const unsigned long long* arg_values,
+ unsigned char flags)
+ : timestamp_(timestamp),
+ id_(id),
+ category_enabled_(category_enabled),
+ name_(name),
+ thread_id_(thread_id),
+ phase_(phase),
+ flags_(flags) {
+ // Clamp num_args since it may have been set by a third_party library.
+ num_args = (num_args > kTraceMaxNumArgs) ? kTraceMaxNumArgs : num_args;
+ int i = 0;
+ for (; i < num_args; ++i) {
+ arg_names_[i] = arg_names[i];
+ arg_values_[i].as_uint = arg_values[i];
+ arg_types_[i] = arg_types[i];
+ }
+ for (; i < kTraceMaxNumArgs; ++i) {
+ arg_names_[i] = NULL;
+ arg_values_[i].as_uint = 0u;
+ arg_types_[i] = TRACE_VALUE_TYPE_UINT;
+ }
+
+ bool copy = !!(flags & TRACE_EVENT_FLAG_COPY);
+ size_t alloc_size = 0;
+ if (copy) {
+ alloc_size += GetAllocLength(name);
+ for (i = 0; i < num_args; ++i) {
+ alloc_size += GetAllocLength(arg_names_[i]);
+ if (arg_types_[i] == TRACE_VALUE_TYPE_STRING)
+ arg_types_[i] = TRACE_VALUE_TYPE_COPY_STRING;
+ }
+ }
+
+ bool arg_is_copy[kTraceMaxNumArgs];
+ for (i = 0; i < num_args; ++i) {
+ // We only take a copy of arg_vals if they are of type COPY_STRING.
+ arg_is_copy[i] = (arg_types_[i] == TRACE_VALUE_TYPE_COPY_STRING);
+ if (arg_is_copy[i])
+ alloc_size += GetAllocLength(arg_values_[i].as_string);
+ }
+
+ if (alloc_size) {
+ parameter_copy_storage_ = new base::RefCountedString;
+ parameter_copy_storage_->data().resize(alloc_size);
+ char* ptr = string_as_array(¶meter_copy_storage_->data());
+ const char* end = ptr + alloc_size;
+ if (copy) {
+ CopyTraceEventParameter(&ptr, &name_, end);
+ for (i = 0; i < num_args; ++i)
+ CopyTraceEventParameter(&ptr, &arg_names_[i], end);
+ }
+ for (i = 0; i < num_args; ++i) {
+ if (arg_is_copy[i])
+ CopyTraceEventParameter(&ptr, &arg_values_[i].as_string, end);
+ }
+ DCHECK_EQ(end, ptr) << "Overrun by " << ptr - end;
+ }
+}
+
+TraceEvent::~TraceEvent() {
+}
+
+// static
+void TraceEvent::AppendValueAsJSON(unsigned char type,
+ TraceEvent::TraceValue value,
+ std::string* out) {
+ std::string::size_type start_pos;
+ switch (type) {
+ case TRACE_VALUE_TYPE_BOOL:
+ *out += value.as_bool ? "true" : "false";
+ break;
+ case TRACE_VALUE_TYPE_UINT:
+ StringAppendF(out, "%" PRIu64, static_cast<uint64>(value.as_uint));
+ break;
+ case TRACE_VALUE_TYPE_INT:
+ StringAppendF(out, "%" PRId64, static_cast<int64>(value.as_int));
+ break;
+ case TRACE_VALUE_TYPE_DOUBLE:
+ StringAppendF(out, "%f", value.as_double);
+ break;
+ case TRACE_VALUE_TYPE_POINTER:
+ // JSON only supports double and int numbers.
+ // So as not to lose bits from a 64-bit pointer, output as a hex string.
+ StringAppendF(out, "\"%" PRIx64 "\"", static_cast<uint64>(
+ reinterpret_cast<intptr_t>(
+ value.as_pointer)));
+ break;
+ case TRACE_VALUE_TYPE_STRING:
+ case TRACE_VALUE_TYPE_COPY_STRING:
+ *out += "\"";
+ start_pos = out->size();
+ *out += value.as_string ? value.as_string : "NULL";
+ // insert backslash before special characters for proper json format.
+ while ((start_pos = out->find_first_of("\\\"", start_pos)) !=
+ std::string::npos) {
+ out->insert(start_pos, 1, '\\');
+ // skip inserted escape character and following character.
+ start_pos += 2;
+ }
+ *out += "\"";
+ break;
+ default:
+ NOTREACHED() << "Don't know how to print this value";
+ break;
+ }
+}
+
+void TraceEvent::AppendEventsAsJSON(const std::vector<TraceEvent>& events,
+ size_t start,
+ size_t count,
+ std::string* out) {
+ for (size_t i = 0; i < count && start + i < events.size(); ++i) {
+ if (i > 0)
+ *out += ",";
+ events[i + start].AppendAsJSON(out);
+ }
+}
+
+void TraceEvent::AppendAsJSON(std::string* out) const {
+ int64 time_int64 = timestamp_.ToInternalValue();
+ int process_id = TraceLog::GetInstance()->process_id();
+ // Category name checked at category creation time.
+ DCHECK(!strchr(name_, '"'));
+ StringAppendF(out,
+ "{\"cat\":\"%s\",\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64 ","
+ "\"ph\":\"%c\",\"name\":\"%s\",\"args\":{",
+ TraceLog::GetCategoryName(category_enabled_),
+ process_id,
+ thread_id_,
+ time_int64,
+ phase_,
+ name_);
+
+ // Output argument names and values, stop at first NULL argument name.
+ for (int i = 0; i < kTraceMaxNumArgs && arg_names_[i]; ++i) {
+ if (i > 0)
+ *out += ",";
+ *out += "\"";
+ *out += arg_names_[i];
+ *out += "\":";
+ AppendValueAsJSON(arg_types_[i], arg_values_[i], out);
+ }
+ *out += "}";
+
+ // If id_ is set, print it out as a hex string so we don't loose any
+ // bits (it might be a 64-bit pointer).
+ if (flags_ & TRACE_EVENT_FLAG_HAS_ID)
+ StringAppendF(out, ",\"id\":\"%" PRIx64 "\"", static_cast<uint64>(id_));
+ *out += "}";
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// TraceResultBuffer
+//
+////////////////////////////////////////////////////////////////////////////////
+
+TraceResultBuffer::OutputCallback
+ TraceResultBuffer::SimpleOutput::GetCallback() {
+ return base::Bind(&SimpleOutput::Append, base::Unretained(this));
+}
+
+void TraceResultBuffer::SimpleOutput::Append(
+ const std::string& json_trace_output) {
+ json_output += json_trace_output;
+}
+
+TraceResultBuffer::TraceResultBuffer() : append_comma_(false) {
+}
+
+TraceResultBuffer::~TraceResultBuffer() {
+}
+
+void TraceResultBuffer::SetOutputCallback(
+ const OutputCallback& json_chunk_callback) {
+ output_callback_ = json_chunk_callback;
+}
+
+void TraceResultBuffer::Start() {
+ append_comma_ = false;
+ output_callback_.Run("[");
+}
+
+void TraceResultBuffer::AddFragment(const std::string& trace_fragment) {
+ if (append_comma_)
+ output_callback_.Run(",");
+ append_comma_ = true;
+ output_callback_.Run(trace_fragment);
+}
+
+void TraceResultBuffer::Finish() {
+ output_callback_.Run("]");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// TraceLog
+//
+////////////////////////////////////////////////////////////////////////////////
+
+TraceLog::NotificationHelper::NotificationHelper(TraceLog* trace_log)
+ : trace_log_(trace_log),
+ notification_(0) {
+}
+
+TraceLog::NotificationHelper::~NotificationHelper() {
+}
+
+void TraceLog::NotificationHelper::AddNotificationWhileLocked(
+ int notification) {
+ if (trace_log_->notification_callback_.is_null())
+ return;
+ if (notification_ == 0)
+ callback_copy_ = trace_log_->notification_callback_;
+ notification_ |= notification;
+}
+
+void TraceLog::NotificationHelper::SendNotificationIfAny() {
+ if (notification_)
+ callback_copy_.Run(notification_);
+}
+
+// static
+TraceLog* TraceLog::GetInstance() {
+ return Singleton<TraceLog, StaticMemorySingletonTraits<TraceLog> >::get();
+}
+
+TraceLog::TraceLog()
+ : enabled_(false),
+ dispatching_to_observer_list_(false),
+ watch_category_(NULL) {
+ // Trace is enabled or disabled on one thread while other threads are
+ // accessing the enabled flag. We don't care whether edge-case events are
+ // traced or not, so we allow races on the enabled flag to keep the trace
+ // macros fast.
+ // TODO(jbates): ANNOTATE_BENIGN_RACE_SIZED crashes windows TSAN bots:
+ // ANNOTATE_BENIGN_RACE_SIZED(g_category_enabled, sizeof(g_category_enabled),
+ // "trace_event category enabled");
+ for (int i = 0; i < TRACE_EVENT_MAX_CATEGORIES; ++i) {
+ ANNOTATE_BENIGN_RACE(&g_category_enabled[i],
+ "trace_event category enabled");
+ }
+#if defined(OS_NACL) // NaCl shouldn't expose the process id.
+ SetProcessID(0);
+#else
+ SetProcessID(static_cast<int>(base::GetCurrentProcId()));
+#endif
+}
+
+TraceLog::~TraceLog() {
+}
+
+const unsigned char* TraceLog::GetCategoryEnabled(const char* name) {
+ TraceLog* tracelog = GetInstance();
+ if (!tracelog) {
+ DCHECK(!g_category_enabled[g_category_already_shutdown]);
+ return &g_category_enabled[g_category_already_shutdown];
+ }
+ return tracelog->GetCategoryEnabledInternal(name);
+}
+
+const char* TraceLog::GetCategoryName(const unsigned char* category_enabled) {
+ // Calculate the index of the category by finding category_enabled in
+ // g_category_enabled array.
+ uintptr_t category_begin = reinterpret_cast<uintptr_t>(g_category_enabled);
+ uintptr_t category_ptr = reinterpret_cast<uintptr_t>(category_enabled);
+ DCHECK(category_ptr >= category_begin &&
+ category_ptr < reinterpret_cast<uintptr_t>(g_category_enabled +
+ TRACE_EVENT_MAX_CATEGORIES)) <<
+ "out of bounds category pointer";
+ uintptr_t category_index =
+ (category_ptr - category_begin) / sizeof(g_category_enabled[0]);
+ return g_categories[category_index];
+}
+
+static void EnableMatchingCategory(int category_index,
+ const std::vector<std::string>& patterns,
+ unsigned char matched_value,
+ unsigned char unmatched_value) {
+ std::vector<std::string>::const_iterator ci = patterns.begin();
+ bool is_match = false;
+ for (; ci != patterns.end(); ++ci) {
+ is_match = MatchPattern(g_categories[category_index], ci->c_str());
+ if (is_match)
+ break;
+ }
+ g_category_enabled[category_index] = is_match ?
+ matched_value : unmatched_value;
+}
+
+// Enable/disable each category based on the category filters in |patterns|.
+// If the category name matches one of the patterns, its enabled status is set
+// to |matched_value|. Otherwise its enabled status is set to |unmatched_value|.
+static void EnableMatchingCategories(const std::vector<std::string>& patterns,
+ unsigned char matched_value,
+ unsigned char unmatched_value) {
+ for (int i = 0; i < g_category_index; i++)
+ EnableMatchingCategory(i, patterns, matched_value, unmatched_value);
+}
+
+const unsigned char* TraceLog::GetCategoryEnabledInternal(const char* name) {
+ AutoLock lock(lock_);
+ DCHECK(!strchr(name, '"')) << "Category names may not contain double quote";
+
+ unsigned char* category_enabled = NULL;
+ // Search for pre-existing category matching this name
+ for (int i = 0; i < g_category_index; i++) {
+ if (strcmp(g_categories[i], name) == 0) {
+ category_enabled = &g_category_enabled[i];
+ break;
+ }
+ }
+
+ if (!category_enabled) {
+ // Create a new category
+ DCHECK(g_category_index < TRACE_EVENT_MAX_CATEGORIES) <<
+ "must increase TRACE_EVENT_MAX_CATEGORIES";
+ if (g_category_index < TRACE_EVENT_MAX_CATEGORIES) {
+ int new_index = g_category_index++;
+ // Don't hold on to the name pointer, so that we can create categories
+ // with strings not known at compile time (this is required by
+ // SetWatchEvent).
+ const char* new_name = base::strdup(name);
+ ANNOTATE_LEAKING_OBJECT_PTR(new_name);
+ g_categories[new_index] = new_name;
+ DCHECK(!g_category_enabled[new_index]);
+ if (enabled_) {
+ // Note that if both included and excluded_categories are empty, the
+ // else clause below excludes nothing, thereby enabling this category.
+ if (!included_categories_.empty()) {
+ EnableMatchingCategory(new_index, included_categories_,
+ CATEGORY_ENABLED, 0);
+ } else {
+ EnableMatchingCategory(new_index, excluded_categories_,
+ 0, CATEGORY_ENABLED);
+ }
+ } else {
+ g_category_enabled[new_index] = 0;
+ }
+ category_enabled = &g_category_enabled[new_index];
+ } else {
+ category_enabled = &g_category_enabled[g_category_categories_exhausted];
+ }
+ }
+#if defined(OS_ANDROID)
+ ApplyATraceEnabledFlag(category_enabled);
+#endif
+ return category_enabled;
+}
+
+void TraceLog::GetKnownCategories(std::vector<std::string>* categories) {
+ AutoLock lock(lock_);
+ for (int i = 0; i < g_category_index; i++)
+ categories->push_back(g_categories[i]);
+}
+
+void TraceLog::SetEnabled(const std::vector<std::string>& included_categories,
+ const std::vector<std::string>& excluded_categories) {
+#if defined(TRACING_DISABLED)
+ return;
+#else // defined(TRACING_DISABLED)
+ AutoLock lock(lock_);
+ if (enabled_)
+ return;
+
+ if (dispatching_to_observer_list_) {
+ DLOG(ERROR) <<
+ "Cannot manipulate TraceLog::Enabled state from an observer.";
+ return;
+ }
+
+ dispatching_to_observer_list_ = true;
+ FOR_EACH_OBSERVER(EnabledStateChangedObserver, enabled_state_observer_list_,
+ OnTraceLogWillEnable());
+ dispatching_to_observer_list_ = false;
+
+ logged_events_.reserve(256 * 1024);
+ enabled_ = true;
+ included_categories_ = included_categories;
+ excluded_categories_ = excluded_categories;
+ // Note that if both included and excluded_categories are empty, the else
+ // clause below excludes nothing, thereby enabling all categories.
+ if (!included_categories_.empty())
+ EnableMatchingCategories(included_categories_, CATEGORY_ENABLED, 0);
+ else
+ EnableMatchingCategories(excluded_categories_, 0, CATEGORY_ENABLED);
+#endif // defined(TRACING_DISABLED)
+}
+
+void TraceLog::SetEnabled(const std::string& categories) {
+ std::vector<std::string> included, excluded;
+ // Tokenize list of categories, delimited by ','.
+ StringTokenizer tokens(categories, ",");
+ while (tokens.GetNext()) {
+ bool is_included = true;
+ std::string category = tokens.token();
+ // Excluded categories start with '-'.
+ if (category.at(0) == '-') {
+ // Remove '-' from category string.
+ category = category.substr(1);
+ is_included = false;
+ }
+ if (is_included)
+ included.push_back(category);
+ else
+ excluded.push_back(category);
+ }
+ SetEnabled(included, excluded);
+}
+
+void TraceLog::GetEnabledTraceCategories(
+ std::vector<std::string>* included_out,
+ std::vector<std::string>* excluded_out) {
+ AutoLock lock(lock_);
+ if (enabled_) {
+ *included_out = included_categories_;
+ *excluded_out = excluded_categories_;
+ }
+}
+
+void TraceLog::SetDisabled() {
+ AutoLock lock(lock_);
+ if (!enabled_)
+ return;
+
+ if (dispatching_to_observer_list_) {
+ DLOG(ERROR)
+ << "Cannot manipulate TraceLog::Enabled state from an observer.";
+ return;
+ }
+
+ dispatching_to_observer_list_ = true;
+ FOR_EACH_OBSERVER(EnabledStateChangedObserver, enabled_state_observer_list_,
+ OnTraceLogWillDisable());
+ dispatching_to_observer_list_ = false;
+
+ enabled_ = false;
+ included_categories_.clear();
+ excluded_categories_.clear();
+ watch_category_ = NULL;
+ watch_event_name_ = "";
+ for (int i = 0; i < g_category_index; i++)
+ g_category_enabled[i] = 0;
+ AddThreadNameMetadataEvents();
+#if defined(OS_ANDROID)
+ AddClockSyncMetadataEvents();
+#endif
+}
+
+void TraceLog::SetEnabled(bool enabled) {
+ if (enabled)
+ SetEnabled(std::vector<std::string>(), std::vector<std::string>());
+ else
+ SetDisabled();
+}
+
+void TraceLog::AddEnabledStateObserver(EnabledStateChangedObserver* listener) {
+ enabled_state_observer_list_.AddObserver(listener);
+}
+
+void TraceLog::RemoveEnabledStateObserver(
+ EnabledStateChangedObserver* listener) {
+ enabled_state_observer_list_.RemoveObserver(listener);
+}
+
+float TraceLog::GetBufferPercentFull() const {
+ return (float)((double)logged_events_.size()/(double)kTraceEventBufferSize);
+}
+
+void TraceLog::SetNotificationCallback(
+ const TraceLog::NotificationCallback& cb) {
+ AutoLock lock(lock_);
+ notification_callback_ = cb;
+}
+
+void TraceLog::Flush(const TraceLog::OutputCallback& cb) {
+ std::vector<TraceEvent> previous_logged_events;
+ {
+ AutoLock lock(lock_);
+ previous_logged_events.swap(logged_events_);
+ } // release lock
+
+ for (size_t i = 0;
+ i < previous_logged_events.size();
+ i += kTraceEventBatchSize) {
+ scoped_refptr<RefCountedString> json_events_str_ptr =
+ new RefCountedString();
+ TraceEvent::AppendEventsAsJSON(previous_logged_events,
+ i,
+ kTraceEventBatchSize,
+ &(json_events_str_ptr->data()));
+ cb.Run(json_events_str_ptr);
+ }
+}
+
+void TraceLog::FlushWithRawEvents(
+ const TraceLog::RawEventOutputCallback& raw_event_callback,
+ const TraceLog::OutputCallback& json_output_callback) {
+ std::vector<TraceEvent> previous_logged_events;
+ {
+ AutoLock lock(lock_);
+ previous_logged_events.swap(logged_events_);
+ } // release lock
+
+ for (size_t i = 0;
+ i < previous_logged_events.size();
+ i += kTraceEventBatchSize) {
+ if (!raw_event_callback.is_null()) {
+ for (size_t j = i; j < i + kTraceEventBatchSize; ++j) {
+ if (j >= previous_logged_events.size()) {
+ break;
+ }
+ raw_event_callback.Run(previous_logged_events[j]);
+ }
+ }
+
+ if (!json_output_callback.is_null()) {
+ scoped_refptr<RefCountedString> json_events_str_ptr =
+ new RefCountedString();
+ TraceEvent::AppendEventsAsJSON(previous_logged_events,
+ i,
+ kTraceEventBatchSize,
+ &(json_events_str_ptr->data()));
+ json_output_callback.Run(json_events_str_ptr);
+ }
+ }
+}
+
+void TraceLog::AddTraceEvent(char phase,
+ const unsigned char* category_enabled,
+ const char* name,
+ unsigned long long id,
+ int num_args,
+ const char** arg_names,
+ const unsigned char* arg_types,
+ const unsigned long long* arg_values,
+ unsigned char flags) {
+ DCHECK(name);
+
+#if defined(OS_ANDROID)
+ SendToATrace(phase, GetCategoryName(category_enabled), name,
+ num_args, arg_names, arg_types, arg_values);
+#endif
+
+ TimeTicks now = TimeTicks::NowFromSystemTraceTime() - time_offset_;
+ NotificationHelper notifier(this);
+ {
+ AutoLock lock(lock_);
+ if (*category_enabled != CATEGORY_ENABLED)
+ return;
+ if (logged_events_.size() >= kTraceEventBufferSize)
+ return;
+
+ int thread_id = static_cast<int>(PlatformThread::CurrentId());
+
+ const char* new_name = PlatformThread::GetName();
+ // Check if the thread name has been set or changed since the previous
+ // call (if any), but don't bother if the new name is empty. Note this will
+ // not detect a thread name change within the same char* buffer address: we
+ // favor common case performance over corner case correctness.
+ if (new_name != g_current_thread_name.Get().Get() &&
+ new_name && *new_name) {
+ g_current_thread_name.Get().Set(new_name);
+ base::hash_map<int, std::string>::iterator existing_name =
+ thread_names_.find(thread_id);
+ if (existing_name == thread_names_.end()) {
+ // This is a new thread id, and a new name.
+ thread_names_[thread_id] = new_name;
+ } else {
+ // This is a thread id that we've seen before, but potentially with a
+ // new name.
+ std::vector<base::StringPiece> existing_names;
+ Tokenize(existing_name->second, ",", &existing_names);
+ bool found = std::find(existing_names.begin(),
+ existing_names.end(),
+ new_name) != existing_names.end();
+ if (!found) {
+ existing_name->second.push_back(',');
+ existing_name->second.append(new_name);
+ }
+ }
+ }
+
+ if (flags & TRACE_EVENT_FLAG_MANGLE_ID)
+ id ^= process_id_hash_;
+
+ logged_events_.push_back(
+ TraceEvent(thread_id,
+ now, phase, category_enabled, name, id,
+ num_args, arg_names, arg_types, arg_values,
+ flags));
+
+ if (logged_events_.size() == kTraceEventBufferSize)
+ notifier.AddNotificationWhileLocked(TRACE_BUFFER_FULL);
+
+ if (watch_category_ == category_enabled && watch_event_name_ == name)
+ notifier.AddNotificationWhileLocked(EVENT_WATCH_NOTIFICATION);
+ } // release lock
+
+ notifier.SendNotificationIfAny();
+}
+
+void TraceLog::AddTraceEventEtw(char phase,
+ const char* name,
+ const void* id,
+ const char* extra) {
+#if defined(OS_WIN)
+ TraceEventETWProvider::Trace(name, phase, id, extra);
+#endif
+ INTERNAL_TRACE_EVENT_ADD(phase, "ETW Trace Event", name,
+ TRACE_EVENT_FLAG_COPY, "id", id, "extra", extra);
+}
+
+void TraceLog::AddTraceEventEtw(char phase,
+ const char* name,
+ const void* id,
+ const std::string& extra)
+{
+#if defined(OS_WIN)
+ TraceEventETWProvider::Trace(name, phase, id, extra);
+#endif
+ INTERNAL_TRACE_EVENT_ADD(phase, "ETW Trace Event", name,
+ TRACE_EVENT_FLAG_COPY, "id", id, "extra", extra);
+}
+
+void TraceLog::SetWatchEvent(const std::string& category_name,
+ const std::string& event_name) {
+ const unsigned char* category = GetCategoryEnabled(category_name.c_str());
+ int notify_count = 0;
+ {
+ AutoLock lock(lock_);
+ watch_category_ = category;
+ watch_event_name_ = event_name;
+
+ // First, search existing events for watch event because we want to catch it
+ // even if it has already occurred.
+ for (size_t i = 0u; i < logged_events_.size(); ++i) {
+ if (category == logged_events_[i].category_enabled() &&
+ strcmp(event_name.c_str(), logged_events_[i].name()) == 0) {
+ ++notify_count;
+ }
+ }
+ } // release lock
+
+ // Send notification for each event found.
+ for (int i = 0; i < notify_count; ++i) {
+ NotificationHelper notifier(this);
+ lock_.Acquire();
+ notifier.AddNotificationWhileLocked(EVENT_WATCH_NOTIFICATION);
+ lock_.Release();
+ notifier.SendNotificationIfAny();
+ }
+}
+
+void TraceLog::CancelWatchEvent() {
+ AutoLock lock(lock_);
+ watch_category_ = NULL;
+ watch_event_name_ = "";
+}
+
+void TraceLog::AddThreadNameMetadataEvents() {
+ lock_.AssertAcquired();
+ for(base::hash_map<int, std::string>::iterator it = thread_names_.begin();
+ it != thread_names_.end();
+ it++) {
+ if (!it->second.empty()) {
+ int num_args = 1;
+ const char* arg_name = "name";
+ unsigned char arg_type;
+ unsigned long long arg_value;
+ trace_event_internal::SetTraceValue(it->second, &arg_type, &arg_value);
+ logged_events_.push_back(
+ TraceEvent(it->first,
+ TimeTicks(), TRACE_EVENT_PHASE_METADATA,
+ &g_category_enabled[g_category_metadata],
+ "thread_name", trace_event_internal::kNoEventId,
+ num_args, &arg_name, &arg_type, &arg_value,
+ TRACE_EVENT_FLAG_NONE));
+ }
+ }
+}
+
+void TraceLog::DeleteForTesting() {
+ DeleteTraceLogForTesting::Delete();
+}
+
+void TraceLog::Resurrect() {
+ StaticMemorySingletonTraits<TraceLog>::Resurrect();
+}
+
+void TraceLog::SetProcessID(int process_id) {
+ process_id_ = process_id;
+ // Create a FNV hash from the process ID for XORing.
+ // See http://isthe.com/chongo/tech/comp/fnv/ for algorithm details.
+ unsigned long long offset_basis = 14695981039346656037ull;
+ unsigned long long fnv_prime = 1099511628211ull;
+ unsigned long long pid = static_cast<unsigned long long>(process_id_);
+ process_id_hash_ = (offset_basis ^ pid) * fnv_prime;
+}
+
+void TraceLog::SetTimeOffset(TimeDelta offset) {
+ time_offset_ = offset;
+}
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/trace_event_impl.h b/src/base/debug/trace_event_impl.h
new file mode 100644
index 0000000..b8b4110
--- /dev/null
+++ b/src/base/debug/trace_event_impl.h
@@ -0,0 +1,412 @@
+// Copyright (c) 2012 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_DEBUG_TRACE_EVENT_IMPL_H_
+#define BASE_DEBUG_TRACE_EVENT_IMPL_H_
+
+#include "build/build_config.h"
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/hash_tables.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/observer_list.h"
+#include "base/string_util.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/timer.h"
+
+// Older style trace macros with explicit id and extra data
+// Only these macros result in publishing data to ETW as currently implemented.
+#define TRACE_EVENT_BEGIN_ETW(name, id, extra) \
+ base::debug::TraceLog::AddTraceEventEtw( \
+ TRACE_EVENT_PHASE_BEGIN, \
+ name, reinterpret_cast<const void*>(id), extra)
+
+#define TRACE_EVENT_END_ETW(name, id, extra) \
+ base::debug::TraceLog::AddTraceEventEtw( \
+ TRACE_EVENT_PHASE_END, \
+ name, reinterpret_cast<const void*>(id), extra)
+
+#define TRACE_EVENT_INSTANT_ETW(name, id, extra) \
+ base::debug::TraceLog::AddTraceEventEtw( \
+ TRACE_EVENT_PHASE_INSTANT, \
+ name, reinterpret_cast<const void*>(id), extra)
+
+template <typename Type>
+struct StaticMemorySingletonTraits;
+
+namespace base {
+
+namespace debug {
+
+const int kTraceMaxNumArgs = 2;
+
+// Output records are "Events" and can be obtained via the
+// OutputCallback whenever the tracing system decides to flush. This
+// can happen at any time, on any thread, or you can programatically
+// force it to happen.
+class BASE_EXPORT TraceEvent {
+ public:
+ union TraceValue {
+ bool as_bool;
+ unsigned long long as_uint;
+ long long as_int;
+ double as_double;
+ const void* as_pointer;
+ const char* as_string;
+ };
+
+ TraceEvent();
+ TraceEvent(int thread_id,
+ TimeTicks timestamp,
+ char phase,
+ const unsigned char* category_enabled,
+ const char* name,
+ unsigned long long id,
+ int num_args,
+ const char** arg_names,
+ const unsigned char* arg_types,
+ const unsigned long long* arg_values,
+ unsigned char flags);
+ ~TraceEvent();
+
+ // Serialize event data to JSON
+ static void AppendEventsAsJSON(const std::vector<TraceEvent>& events,
+ size_t start,
+ size_t count,
+ std::string* out);
+ void AppendAsJSON(std::string* out) const;
+
+ static void AppendValueAsJSON(unsigned char type,
+ TraceValue value,
+ std::string* out);
+
+ TimeTicks timestamp() const { return timestamp_; }
+
+ // Exposed for unittesting:
+
+ const base::RefCountedString* parameter_copy_storage() const {
+ return parameter_copy_storage_.get();
+ }
+
+ const unsigned char* category_enabled() const { return category_enabled_; }
+ const char* name() const { return name_; }
+
+#if defined(COBALT)
+ unsigned long long id() const { return id_; }
+ const TraceValue* arg_values() const { return arg_values_; }
+ int thread_id() const { return thread_id_; }
+ char phase() const { return phase_; }
+#endif
+
+ private:
+ // Note: these are ordered by size (largest first) for optimal packing.
+ TimeTicks timestamp_;
+ // id_ can be used to store phase-specific data.
+ unsigned long long id_;
+ TraceValue arg_values_[kTraceMaxNumArgs];
+ const char* arg_names_[kTraceMaxNumArgs];
+ const unsigned char* category_enabled_;
+ const char* name_;
+ scoped_refptr<base::RefCountedString> parameter_copy_storage_;
+ int thread_id_;
+ char phase_;
+ unsigned char flags_;
+ unsigned char arg_types_[kTraceMaxNumArgs];
+};
+
+
+// TraceResultBuffer collects and converts trace fragments returned by TraceLog
+// to JSON output.
+class BASE_EXPORT TraceResultBuffer {
+ public:
+ typedef base::Callback<void(const std::string&)> OutputCallback;
+
+ // If you don't need to stream JSON chunks out efficiently, and just want to
+ // get a complete JSON string after calling Finish, use this struct to collect
+ // JSON trace output.
+ struct BASE_EXPORT SimpleOutput {
+ OutputCallback GetCallback();
+ void Append(const std::string& json_string);
+
+ // Do what you want with the json_output_ string after calling
+ // TraceResultBuffer::Finish.
+ std::string json_output;
+ };
+
+ TraceResultBuffer();
+ ~TraceResultBuffer();
+
+ // Set callback. The callback will be called during Start with the initial
+ // JSON output and during AddFragment and Finish with following JSON output
+ // chunks. The callback target must live past the last calls to
+ // TraceResultBuffer::Start/AddFragment/Finish.
+ void SetOutputCallback(const OutputCallback& json_chunk_callback);
+
+ // Start JSON output. This resets all internal state, so you can reuse
+ // the TraceResultBuffer by calling Start.
+ void Start();
+
+ // Call AddFragment 0 or more times to add trace fragments from TraceLog.
+ void AddFragment(const std::string& trace_fragment);
+
+ // When all fragments have been added, call Finish to complete the JSON
+ // formatted output.
+ void Finish();
+
+ private:
+ OutputCallback output_callback_;
+ bool append_comma_;
+};
+
+
+class BASE_EXPORT TraceLog {
+ public:
+ // Notification is a mask of one or more of the following events.
+ enum Notification {
+ // The trace buffer does not flush dynamically, so when it fills up,
+ // subsequent trace events will be dropped. This callback is generated when
+ // the trace buffer is full. The callback must be thread safe.
+ TRACE_BUFFER_FULL = 1 << 0,
+ // A subscribed trace-event occurred.
+ EVENT_WATCH_NOTIFICATION = 1 << 1
+ };
+
+ static TraceLog* GetInstance();
+
+ // Get set of known categories. This can change as new code paths are reached.
+ // The known categories are inserted into |categories|.
+ void GetKnownCategories(std::vector<std::string>* categories);
+
+ // Enable tracing for provided list of categories. If tracing is already
+ // enabled, this method does nothing -- changing categories during trace is
+ // not supported.
+ // If both included_categories and excluded_categories are empty,
+ // all categories are traced.
+ // Else if included_categories is non-empty, only those are traced.
+ // Else if excluded_categories is non-empty, everything but those are traced.
+ // Wildcards * and ? are supported (see MatchPattern in string_util.h).
+ void SetEnabled(const std::vector<std::string>& included_categories,
+ const std::vector<std::string>& excluded_categories);
+
+ // |categories| is a comma-delimited list of category wildcards.
+ // A category can have an optional '-' prefix to make it an excluded category.
+ // All the same rules apply above, so for example, having both included and
+ // excluded categories in the same list would not be supported.
+ //
+ // Example: SetEnabled("test_MyTest*");
+ // Example: SetEnabled("test_MyTest*,test_OtherStuff");
+ // Example: SetEnabled("-excluded_category1,-excluded_category2");
+ void SetEnabled(const std::string& categories);
+
+ // Retieves the categories set via a prior call to SetEnabled(). Only
+ // meaningful if |IsEnabled()| is true.
+ void GetEnabledTraceCategories(std::vector<std::string>* included_out,
+ std::vector<std::string>* excluded_out);
+
+ // Disable tracing for all categories.
+ void SetDisabled();
+ // Helper method to enable/disable tracing for all categories.
+ void SetEnabled(bool enabled);
+ bool IsEnabled() { return enabled_; }
+
+#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
+ static void InitATrace();
+#endif
+
+ // Enabled state listeners give a callback when tracing is enabled or
+ // disabled. This can be used to tie into other library's tracing systems
+ // on-demand.
+ class EnabledStateChangedObserver {
+ public:
+ // Called just before the tracing system becomes
+ // enabled. TraceLog::IsEnabled will return false at this point and trace
+ // macros and methods called within the observer will deadlock.
+ virtual void OnTraceLogWillEnable() { }
+
+ // Called just before the tracing system disables. TraceLog::IsEnabled is
+ // still false at this point TRACE macros will still be capturing
+ // data. However, trace macros and methods called within the observer will
+ // deadlock.
+ virtual void OnTraceLogWillDisable() { }
+ };
+ void AddEnabledStateObserver(EnabledStateChangedObserver* listener);
+ void RemoveEnabledStateObserver(EnabledStateChangedObserver* listener);
+
+ float GetBufferPercentFull() const;
+
+ // Set the thread-safe notification callback. The callback can occur at any
+ // time and from any thread. WARNING: It is possible for the previously set
+ // callback to be called during OR AFTER a call to SetNotificationCallback.
+ // Therefore, the target of the callback must either be a global function,
+ // ref-counted object or a LazyInstance with Leaky traits (or equivalent).
+ typedef base::Callback<void(int)> NotificationCallback;
+ void SetNotificationCallback(const NotificationCallback& cb);
+
+ // Flush all collected events to the given output callback. The callback will
+ // be called one or more times with IPC-bite-size chunks. The string format is
+ // undefined. Use TraceResultBuffer to convert one or more trace strings to
+ // JSON.
+ typedef base::Callback<void(const scoped_refptr<base::RefCountedString>&)>
+ OutputCallback;
+ void Flush(const OutputCallback& cb);
+
+#if defined(COBALT)
+ // Flush out events as raw TraceEvent structures. Optinally also flush out
+ // JSON output as well.
+ typedef base::Callback<void(const TraceEvent&)> RawEventOutputCallback;
+ void FlushWithRawEvents(const RawEventOutputCallback& raw_event_callback,
+ const OutputCallback& json_output_callback);
+#endif
+
+ // Called by TRACE_EVENT* macros, don't call this directly.
+ static const unsigned char* GetCategoryEnabled(const char* name);
+ static const char* GetCategoryName(const unsigned char* category_enabled);
+
+ // Called by TRACE_EVENT* macros, don't call this directly.
+ // If |copy| is set, |name|, |arg_name1| and |arg_name2| will be deep copied
+ // into the event; see "Memory scoping note" and TRACE_EVENT_COPY_XXX above.
+ void AddTraceEvent(char phase,
+ const unsigned char* category_enabled,
+ const char* name,
+ unsigned long long id,
+ int num_args,
+ const char** arg_names,
+ const unsigned char* arg_types,
+ const unsigned long long* arg_values,
+ unsigned char flags);
+ static void AddTraceEventEtw(char phase,
+ const char* name,
+ const void* id,
+ const char* extra);
+ static void AddTraceEventEtw(char phase,
+ const char* name,
+ const void* id,
+ const std::string& extra);
+
+ // For every matching event, a notification will be fired. NOTE: the
+ // notification will fire for each matching event that has already occurred
+ // since tracing was started (including before tracing if the process was
+ // started with tracing turned on).
+ void SetWatchEvent(const std::string& category_name,
+ const std::string& event_name);
+ // Cancel the watch event. If tracing is enabled, this may race with the
+ // watch event notification firing.
+ void CancelWatchEvent();
+
+ int process_id() const { return process_id_; }
+
+ // Exposed for unittesting:
+
+ // Allows deleting our singleton instance.
+ static void DeleteForTesting();
+
+ // Allows resurrecting our singleton instance post-AtExit processing.
+ static void Resurrect();
+
+ // Allow tests to inspect TraceEvents.
+ size_t GetEventsSize() const { return logged_events_.size(); }
+ const TraceEvent& GetEventAt(size_t index) const {
+ DCHECK(index < logged_events_.size());
+ return logged_events_[index];
+ }
+
+ void SetProcessID(int process_id);
+
+ // Allow setting an offset between the current TimeTicks time and the time
+ // that should be reported.
+ void SetTimeOffset(TimeDelta offset);
+
+ private:
+ // This allows constructor and destructor to be private and usable only
+ // by the Singleton class.
+ friend struct StaticMemorySingletonTraits<TraceLog>;
+
+ // The pointer returned from GetCategoryEnabledInternal() points to a value
+ // with zero or more of the following bits. Used in this class only.
+ // The TRACE_EVENT macros should only use the value as a bool.
+ enum CategoryEnabledFlags {
+ // Normal enabled flag for categories enabled with Enable().
+ CATEGORY_ENABLED = 1 << 0,
+ // On Android if ATrace is enabled, all categories will have this bit.
+ // Not used on other platforms.
+ ATRACE_ENABLED = 1 << 1
+ };
+
+ // Helper class for managing notification_thread_count_ and running
+ // notification callbacks. This is very similar to a reader-writer lock, but
+ // shares the lock with TraceLog and manages the notification flags.
+ class NotificationHelper {
+ public:
+ inline explicit NotificationHelper(TraceLog* trace_log);
+ inline ~NotificationHelper();
+
+ // Called only while TraceLog::lock_ is held. This ORs the given
+ // notification with any existing notifcations.
+ inline void AddNotificationWhileLocked(int notification);
+
+ // Called only while TraceLog::lock_ is NOT held. If there are any pending
+ // notifications from previous calls to AddNotificationWhileLocked, this
+ // will call the NotificationCallback.
+ inline void SendNotificationIfAny();
+
+ private:
+ TraceLog* trace_log_;
+ NotificationCallback callback_copy_;
+ int notification_;
+ };
+
+ TraceLog();
+ ~TraceLog();
+ const unsigned char* GetCategoryEnabledInternal(const char* name);
+ void AddThreadNameMetadataEvents();
+
+#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
+ void SendToATrace(char phase,
+ const char* category,
+ const char* name,
+ int num_args,
+ const char** arg_names,
+ const unsigned char* arg_types,
+ const unsigned long long* arg_values);
+ void AddClockSyncMetadataEvents();
+ static void ApplyATraceEnabledFlag(unsigned char* category_enabled);
+#endif
+
+ // TODO(nduca): switch to per-thread trace buffers to reduce thread
+ // synchronization.
+ // This lock protects TraceLog member accesses from arbitrary threads.
+ Lock lock_;
+ bool enabled_;
+ NotificationCallback notification_callback_;
+ std::vector<TraceEvent> logged_events_;
+ std::vector<std::string> included_categories_;
+ std::vector<std::string> excluded_categories_;
+ bool dispatching_to_observer_list_;
+ ObserverList<EnabledStateChangedObserver> enabled_state_observer_list_;
+
+ base::hash_map<int, std::string> thread_names_;
+
+ // XORed with TraceID to make it unlikely to collide with other processes.
+ unsigned long long process_id_hash_;
+
+ int process_id_;
+
+ TimeDelta time_offset_;
+
+ // Allow tests to wake up when certain events occur.
+ const unsigned char* watch_category_;
+ std::string watch_event_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(TraceLog);
+};
+
+} // namespace debug
+} // namespace base
+
+#endif // BASE_DEBUG_TRACE_EVENT_IMPL_H_
diff --git a/src/base/debug/trace_event_unittest.cc b/src/base/debug/trace_event_unittest.cc
new file mode 100644
index 0000000..445118b
--- /dev/null
+++ b/src/base/debug/trace_event_unittest.cc
@@ -0,0 +1,1341 @@
+// Copyright (c) 2012 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/trace_event_unittest.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/debug/trace_event.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/process_util.h"
+#include "base/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
+#include "base/values.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::debug::HighResSleepForTraceTest;
+
+namespace base {
+namespace debug {
+
+namespace {
+
+enum CompareOp {
+ IS_EQUAL,
+ IS_NOT_EQUAL,
+};
+
+struct JsonKeyValue {
+ const char* key;
+ const char* value;
+ CompareOp op;
+};
+
+class TraceEventTestFixture : public testing::Test {
+ public:
+ // This fixture does not use SetUp() because the fixture must be manually set
+ // up multiple times when testing AtExit. Use ManualTestSetUp for this.
+ void ManualTestSetUp();
+ void OnTraceDataCollected(
+ const scoped_refptr<base::RefCountedString>& events_str);
+ void OnTraceNotification(int notification) {
+ if (notification & TraceLog::EVENT_WATCH_NOTIFICATION)
+ ++event_watch_notification_;
+ }
+ DictionaryValue* FindMatchingTraceEntry(const JsonKeyValue* key_values);
+ DictionaryValue* FindNamePhase(const char* name, const char* phase);
+ DictionaryValue* FindNamePhaseKeyValue(const char* name,
+ const char* phase,
+ const char* key,
+ const char* value);
+ bool FindMatchingValue(const char* key,
+ const char* value);
+ bool FindNonMatchingValue(const char* key,
+ const char* value);
+ void Clear() {
+ trace_parsed_.Clear();
+ json_output_.json_output.clear();
+ }
+
+ void BeginTrace() {
+ event_watch_notification_ = 0;
+ TraceLog::GetInstance()->SetEnabled("*");
+ }
+
+ void EndTraceAndFlush() {
+ TraceLog::GetInstance()->SetDisabled();
+ TraceLog::GetInstance()->Flush(
+ base::Bind(&TraceEventTestFixture::OnTraceDataCollected,
+ base::Unretained(this)));
+ }
+
+ virtual void SetUp() OVERRIDE {
+ old_thread_name_ = PlatformThread::GetName();
+ }
+ virtual void TearDown() OVERRIDE {
+ if (TraceLog::GetInstance())
+ EXPECT_FALSE(TraceLog::GetInstance()->IsEnabled());
+ PlatformThread::SetName(old_thread_name_ ? old_thread_name_ : "");
+ }
+
+ const char* old_thread_name_;
+ ListValue trace_parsed_;
+ base::debug::TraceResultBuffer trace_buffer_;
+ base::debug::TraceResultBuffer::SimpleOutput json_output_;
+ int event_watch_notification_;
+
+ private:
+ // We want our singleton torn down after each test.
+ ShadowingAtExitManager at_exit_manager_;
+ Lock lock_;
+};
+
+void TraceEventTestFixture::ManualTestSetUp() {
+ TraceLog::DeleteForTesting();
+ TraceLog::Resurrect();
+ TraceLog* tracelog = TraceLog::GetInstance();
+ ASSERT_TRUE(tracelog);
+ ASSERT_FALSE(tracelog->IsEnabled());
+ tracelog->SetNotificationCallback(
+ base::Bind(&TraceEventTestFixture::OnTraceNotification,
+ base::Unretained(this)));
+ trace_buffer_.SetOutputCallback(json_output_.GetCallback());
+}
+
+void TraceEventTestFixture::OnTraceDataCollected(
+ const scoped_refptr<base::RefCountedString>& events_str) {
+ AutoLock lock(lock_);
+ json_output_.json_output.clear();
+ trace_buffer_.Start();
+ trace_buffer_.AddFragment(events_str->data());
+ trace_buffer_.Finish();
+
+ scoped_ptr<Value> root;
+ root.reset(base::JSONReader::Read(json_output_.json_output,
+ JSON_PARSE_RFC | JSON_DETACHABLE_CHILDREN));
+
+ if (!root.get()) {
+ LOG(ERROR) << json_output_.json_output;
+ }
+
+ ListValue* root_list = NULL;
+ ASSERT_TRUE(root.get());
+ ASSERT_TRUE(root->GetAsList(&root_list));
+
+ // Move items into our aggregate collection
+ while (root_list->GetSize()) {
+ Value* item = NULL;
+ root_list->Remove(0, &item);
+ trace_parsed_.Append(item);
+ }
+}
+
+static bool CompareJsonValues(const std::string& lhs,
+ const std::string& rhs,
+ CompareOp op) {
+ switch (op) {
+ case IS_EQUAL:
+ return lhs == rhs;
+ case IS_NOT_EQUAL:
+ return lhs != rhs;
+ default:
+ CHECK(0);
+ }
+ return false;
+}
+
+static bool IsKeyValueInDict(const JsonKeyValue* key_value,
+ DictionaryValue* dict) {
+ Value* value = NULL;
+ std::string value_str;
+ if (dict->Get(key_value->key, &value) &&
+ value->GetAsString(&value_str) &&
+ CompareJsonValues(value_str, key_value->value, key_value->op))
+ return true;
+
+ // Recurse to test arguments
+ DictionaryValue* args_dict = NULL;
+ dict->GetDictionary("args", &args_dict);
+ if (args_dict)
+ return IsKeyValueInDict(key_value, args_dict);
+
+ return false;
+}
+
+static bool IsAllKeyValueInDict(const JsonKeyValue* key_values,
+ DictionaryValue* dict) {
+ // Scan all key_values, they must all be present and equal.
+ while (key_values && key_values->key) {
+ if (!IsKeyValueInDict(key_values, dict))
+ return false;
+ ++key_values;
+ }
+ return true;
+}
+
+DictionaryValue* TraceEventTestFixture::FindMatchingTraceEntry(
+ const JsonKeyValue* key_values) {
+ // Scan all items
+ size_t trace_parsed_count = trace_parsed_.GetSize();
+ for (size_t i = 0; i < trace_parsed_count; i++) {
+ Value* value = NULL;
+ trace_parsed_.Get(i, &value);
+ if (!value || value->GetType() != Value::TYPE_DICTIONARY)
+ continue;
+ DictionaryValue* dict = static_cast<DictionaryValue*>(value);
+
+ if (IsAllKeyValueInDict(key_values, dict))
+ return dict;
+ }
+ return NULL;
+}
+
+DictionaryValue* TraceEventTestFixture::FindNamePhase(const char* name,
+ const char* phase) {
+ JsonKeyValue key_values[] = {
+ {"name", name, IS_EQUAL},
+ {"ph", phase, IS_EQUAL},
+ {0, 0, IS_EQUAL}
+ };
+ return FindMatchingTraceEntry(key_values);
+}
+
+DictionaryValue* TraceEventTestFixture::FindNamePhaseKeyValue(
+ const char* name,
+ const char* phase,
+ const char* key,
+ const char* value) {
+ JsonKeyValue key_values[] = {
+ {"name", name, IS_EQUAL},
+ {"ph", phase, IS_EQUAL},
+ {key, value, IS_EQUAL},
+ {0, 0, IS_EQUAL}
+ };
+ return FindMatchingTraceEntry(key_values);
+}
+
+bool TraceEventTestFixture::FindMatchingValue(const char* key,
+ const char* value) {
+ JsonKeyValue key_values[] = {
+ {key, value, IS_EQUAL},
+ {0, 0, IS_EQUAL}
+ };
+ return FindMatchingTraceEntry(key_values);
+}
+
+bool TraceEventTestFixture::FindNonMatchingValue(const char* key,
+ const char* value) {
+ JsonKeyValue key_values[] = {
+ {key, value, IS_NOT_EQUAL},
+ {0, 0, IS_EQUAL}
+ };
+ return FindMatchingTraceEntry(key_values);
+}
+
+bool IsStringInDict(const char* string_to_match, const DictionaryValue* dict) {
+ for (DictionaryValue::key_iterator ikey = dict->begin_keys();
+ ikey != dict->end_keys(); ++ikey) {
+ const Value* child = NULL;
+ if (!dict->GetWithoutPathExpansion(*ikey, &child))
+ continue;
+
+ if ((*ikey).find(string_to_match) != std::string::npos)
+ return true;
+
+ std::string value_str;
+ child->GetAsString(&value_str);
+ if (value_str.find(string_to_match) != std::string::npos)
+ return true;
+ }
+
+ // Recurse to test arguments
+ const DictionaryValue* args_dict = NULL;
+ dict->GetDictionary("args", &args_dict);
+ if (args_dict)
+ return IsStringInDict(string_to_match, args_dict);
+
+ return false;
+}
+
+const DictionaryValue* FindTraceEntry(
+ const ListValue& trace_parsed,
+ const char* string_to_match,
+ const DictionaryValue* match_after_this_item = NULL) {
+ // Scan all items
+ size_t trace_parsed_count = trace_parsed.GetSize();
+ for (size_t i = 0; i < trace_parsed_count; i++) {
+ const Value* value = NULL;
+ trace_parsed.Get(i, &value);
+ if (match_after_this_item) {
+ if (value == match_after_this_item)
+ match_after_this_item = NULL;
+ continue;
+ }
+ if (!value || value->GetType() != Value::TYPE_DICTIONARY)
+ continue;
+ const DictionaryValue* dict = static_cast<const DictionaryValue*>(value);
+
+ if (IsStringInDict(string_to_match, dict))
+ return dict;
+ }
+ return NULL;
+}
+
+std::vector<const DictionaryValue*> FindTraceEntries(
+ const ListValue& trace_parsed,
+ const char* string_to_match) {
+ std::vector<const DictionaryValue*> hits;
+ size_t trace_parsed_count = trace_parsed.GetSize();
+ for (size_t i = 0; i < trace_parsed_count; i++) {
+ const Value* value = NULL;
+ trace_parsed.Get(i, &value);
+ if (!value || value->GetType() != Value::TYPE_DICTIONARY)
+ continue;
+ const DictionaryValue* dict = static_cast<const DictionaryValue*>(value);
+
+ if (IsStringInDict(string_to_match, dict))
+ hits.push_back(dict);
+ }
+ return hits;
+}
+
+void TraceWithAllMacroVariants(WaitableEvent* task_complete_event) {
+ {
+ TRACE_EVENT_BEGIN_ETW("TRACE_EVENT_BEGIN_ETW call", 0x1122, "extrastring1");
+ TRACE_EVENT_END_ETW("TRACE_EVENT_END_ETW call", 0x3344, "extrastring2");
+ TRACE_EVENT_INSTANT_ETW("TRACE_EVENT_INSTANT_ETW call",
+ 0x5566, "extrastring3");
+
+ TRACE_EVENT0("all", "TRACE_EVENT0 call");
+ TRACE_EVENT1("all", "TRACE_EVENT1 call", "name1", "value1");
+ TRACE_EVENT2("all", "TRACE_EVENT2 call",
+ "name1", "\"value1\"",
+ "name2", "value\\2");
+
+ TRACE_EVENT_INSTANT0("all", "TRACE_EVENT_INSTANT0 call");
+ TRACE_EVENT_INSTANT1("all", "TRACE_EVENT_INSTANT1 call", "name1", "value1");
+ TRACE_EVENT_INSTANT2("all", "TRACE_EVENT_INSTANT2 call",
+ "name1", "value1",
+ "name2", "value2");
+
+ TRACE_EVENT_BEGIN0("all", "TRACE_EVENT_BEGIN0 call");
+ TRACE_EVENT_BEGIN1("all", "TRACE_EVENT_BEGIN1 call", "name1", "value1");
+ TRACE_EVENT_BEGIN2("all", "TRACE_EVENT_BEGIN2 call",
+ "name1", "value1",
+ "name2", "value2");
+
+ TRACE_EVENT_END0("all", "TRACE_EVENT_END0 call");
+ TRACE_EVENT_END1("all", "TRACE_EVENT_END1 call", "name1", "value1");
+ TRACE_EVENT_END2("all", "TRACE_EVENT_END2 call",
+ "name1", "value1",
+ "name2", "value2");
+
+ TRACE_EVENT_ASYNC_BEGIN0("all", "TRACE_EVENT_ASYNC_BEGIN0 call", 5);
+ TRACE_EVENT_ASYNC_BEGIN1("all", "TRACE_EVENT_ASYNC_BEGIN1 call", 5,
+ "name1", "value1");
+ TRACE_EVENT_ASYNC_BEGIN2("all", "TRACE_EVENT_ASYNC_BEGIN2 call", 5,
+ "name1", "value1",
+ "name2", "value2");
+
+ TRACE_EVENT_ASYNC_STEP0("all", "TRACE_EVENT_ASYNC_STEP0 call",
+ 5, "step1");
+ TRACE_EVENT_ASYNC_STEP1("all", "TRACE_EVENT_ASYNC_STEP1 call",
+ 5, "step2", "name1", "value1");
+
+ TRACE_EVENT_ASYNC_END0("all", "TRACE_EVENT_ASYNC_END0 call", 5);
+ TRACE_EVENT_ASYNC_END1("all", "TRACE_EVENT_ASYNC_END1 call", 5,
+ "name1", "value1");
+ TRACE_EVENT_ASYNC_END2("all", "TRACE_EVENT_ASYNC_END2 call", 5,
+ "name1", "value1",
+ "name2", "value2");
+
+ TRACE_EVENT_BEGIN_ETW("TRACE_EVENT_BEGIN_ETW0 call", 5, NULL);
+ TRACE_EVENT_BEGIN_ETW("TRACE_EVENT_BEGIN_ETW1 call", 5, "value");
+ TRACE_EVENT_END_ETW("TRACE_EVENT_END_ETW0 call", 5, NULL);
+ TRACE_EVENT_END_ETW("TRACE_EVENT_END_ETW1 call", 5, "value");
+ TRACE_EVENT_INSTANT_ETW("TRACE_EVENT_INSTANT_ETW0 call", 5, NULL);
+ TRACE_EVENT_INSTANT_ETW("TRACE_EVENT_INSTANT_ETW1 call", 5, "value");
+
+ TRACE_COUNTER1("all", "TRACE_COUNTER1 call", 31415);
+ TRACE_COUNTER2("all", "TRACE_COUNTER2 call",
+ "a", 30000,
+ "b", 1415);
+
+ TRACE_COUNTER_ID1("all", "TRACE_COUNTER_ID1 call", 0x319009, 31415);
+ TRACE_COUNTER_ID2("all", "TRACE_COUNTER_ID2 call", 0x319009,
+ "a", 30000, "b", 1415);
+ } // Scope close causes TRACE_EVENT0 etc to send their END events.
+
+ if (task_complete_event)
+ task_complete_event->Signal();
+}
+
+void ValidateAllTraceMacrosCreatedData(const ListValue& trace_parsed) {
+ const DictionaryValue* item = NULL;
+
+#define EXPECT_FIND_(string) \
+ EXPECT_TRUE((item = FindTraceEntry(trace_parsed, string)));
+#define EXPECT_NOT_FIND_(string) \
+ EXPECT_FALSE((item = FindTraceEntry(trace_parsed, string)));
+#define EXPECT_SUB_FIND_(string) \
+ if (item) EXPECT_TRUE((IsStringInDict(string, item)));
+
+ EXPECT_FIND_("ETW Trace Event");
+ EXPECT_FIND_("all");
+ EXPECT_FIND_("TRACE_EVENT_BEGIN_ETW call");
+ {
+ std::string str_val;
+ EXPECT_TRUE(item && item->GetString("args.id", &str_val));
+ EXPECT_STREQ("1122", str_val.c_str());
+ }
+ EXPECT_SUB_FIND_("extrastring1");
+ EXPECT_FIND_("TRACE_EVENT_END_ETW call");
+ EXPECT_FIND_("TRACE_EVENT_INSTANT_ETW call");
+ EXPECT_FIND_("TRACE_EVENT0 call");
+ {
+ std::string ph_begin;
+ std::string ph_end;
+ EXPECT_TRUE((item = FindTraceEntry(trace_parsed, "TRACE_EVENT0 call")));
+ EXPECT_TRUE((item && item->GetString("ph", &ph_begin)));
+ EXPECT_TRUE((item = FindTraceEntry(trace_parsed, "TRACE_EVENT0 call",
+ item)));
+ EXPECT_TRUE((item && item->GetString("ph", &ph_end)));
+ EXPECT_EQ("B", ph_begin);
+ EXPECT_EQ("E", ph_end);
+ }
+ EXPECT_FIND_("TRACE_EVENT1 call");
+ EXPECT_SUB_FIND_("name1");
+ EXPECT_SUB_FIND_("value1");
+ EXPECT_FIND_("TRACE_EVENT2 call");
+ EXPECT_SUB_FIND_("name1");
+ EXPECT_SUB_FIND_("\"value1\"");
+ EXPECT_SUB_FIND_("name2");
+ EXPECT_SUB_FIND_("value\\2");
+
+ EXPECT_FIND_("TRACE_EVENT_INSTANT0 call");
+ EXPECT_FIND_("TRACE_EVENT_INSTANT1 call");
+ EXPECT_SUB_FIND_("name1");
+ EXPECT_SUB_FIND_("value1");
+ EXPECT_FIND_("TRACE_EVENT_INSTANT2 call");
+ EXPECT_SUB_FIND_("name1");
+ EXPECT_SUB_FIND_("value1");
+ EXPECT_SUB_FIND_("name2");
+ EXPECT_SUB_FIND_("value2");
+
+ EXPECT_FIND_("TRACE_EVENT_BEGIN0 call");
+ EXPECT_FIND_("TRACE_EVENT_BEGIN1 call");
+ EXPECT_SUB_FIND_("name1");
+ EXPECT_SUB_FIND_("value1");
+ EXPECT_FIND_("TRACE_EVENT_BEGIN2 call");
+ EXPECT_SUB_FIND_("name1");
+ EXPECT_SUB_FIND_("value1");
+ EXPECT_SUB_FIND_("name2");
+ EXPECT_SUB_FIND_("value2");
+
+ EXPECT_FIND_("TRACE_EVENT_END0 call");
+ EXPECT_FIND_("TRACE_EVENT_END1 call");
+ EXPECT_SUB_FIND_("name1");
+ EXPECT_SUB_FIND_("value1");
+ EXPECT_FIND_("TRACE_EVENT_END2 call");
+ EXPECT_SUB_FIND_("name1");
+ EXPECT_SUB_FIND_("value1");
+ EXPECT_SUB_FIND_("name2");
+ EXPECT_SUB_FIND_("value2");
+
+ EXPECT_FIND_("TRACE_EVENT_ASYNC_BEGIN0 call");
+ EXPECT_SUB_FIND_("id");
+ EXPECT_SUB_FIND_("5");
+ EXPECT_FIND_("TRACE_EVENT_ASYNC_BEGIN1 call");
+ EXPECT_SUB_FIND_("id");
+ EXPECT_SUB_FIND_("5");
+ EXPECT_SUB_FIND_("name1");
+ EXPECT_SUB_FIND_("value1");
+ EXPECT_FIND_("TRACE_EVENT_ASYNC_BEGIN2 call");
+ EXPECT_SUB_FIND_("id");
+ EXPECT_SUB_FIND_("5");
+ EXPECT_SUB_FIND_("name1");
+ EXPECT_SUB_FIND_("value1");
+ EXPECT_SUB_FIND_("name2");
+ EXPECT_SUB_FIND_("value2");
+
+ EXPECT_FIND_("TRACE_EVENT_ASYNC_STEP0 call");
+ EXPECT_SUB_FIND_("id");
+ EXPECT_SUB_FIND_("5");
+ EXPECT_SUB_FIND_("step1");
+ EXPECT_FIND_("TRACE_EVENT_ASYNC_STEP1 call");
+ EXPECT_SUB_FIND_("id");
+ EXPECT_SUB_FIND_("5");
+ EXPECT_SUB_FIND_("step2");
+ EXPECT_SUB_FIND_("name1");
+ EXPECT_SUB_FIND_("value1");
+
+ EXPECT_FIND_("TRACE_EVENT_ASYNC_END0 call");
+ EXPECT_SUB_FIND_("id");
+ EXPECT_SUB_FIND_("5");
+ EXPECT_FIND_("TRACE_EVENT_ASYNC_END1 call");
+ EXPECT_SUB_FIND_("id");
+ EXPECT_SUB_FIND_("5");
+ EXPECT_SUB_FIND_("name1");
+ EXPECT_SUB_FIND_("value1");
+ EXPECT_FIND_("TRACE_EVENT_ASYNC_END2 call");
+ EXPECT_SUB_FIND_("id");
+ EXPECT_SUB_FIND_("5");
+ EXPECT_SUB_FIND_("name1");
+ EXPECT_SUB_FIND_("value1");
+ EXPECT_SUB_FIND_("name2");
+ EXPECT_SUB_FIND_("value2");
+
+ EXPECT_FIND_("TRACE_EVENT_BEGIN_ETW0 call");
+ EXPECT_SUB_FIND_("id");
+ EXPECT_SUB_FIND_("5");
+ EXPECT_SUB_FIND_("extra");
+ EXPECT_SUB_FIND_("NULL");
+ EXPECT_FIND_("TRACE_EVENT_BEGIN_ETW1 call");
+ EXPECT_SUB_FIND_("id");
+ EXPECT_SUB_FIND_("5");
+ EXPECT_SUB_FIND_("extra");
+ EXPECT_SUB_FIND_("value");
+ EXPECT_FIND_("TRACE_EVENT_END_ETW0 call");
+ EXPECT_SUB_FIND_("id");
+ EXPECT_SUB_FIND_("5");
+ EXPECT_SUB_FIND_("extra");
+ EXPECT_SUB_FIND_("NULL");
+ EXPECT_FIND_("TRACE_EVENT_END_ETW1 call");
+ EXPECT_SUB_FIND_("id");
+ EXPECT_SUB_FIND_("5");
+ EXPECT_SUB_FIND_("extra");
+ EXPECT_SUB_FIND_("value");
+ EXPECT_FIND_("TRACE_EVENT_INSTANT_ETW0 call");
+ EXPECT_SUB_FIND_("id");
+ EXPECT_SUB_FIND_("5");
+ EXPECT_SUB_FIND_("extra");
+ EXPECT_SUB_FIND_("NULL");
+ EXPECT_FIND_("TRACE_EVENT_INSTANT_ETW1 call");
+ EXPECT_SUB_FIND_("id");
+ EXPECT_SUB_FIND_("5");
+ EXPECT_SUB_FIND_("extra");
+ EXPECT_SUB_FIND_("value");
+
+ EXPECT_FIND_("TRACE_COUNTER1 call");
+ {
+ std::string ph;
+ EXPECT_TRUE((item && item->GetString("ph", &ph)));
+ EXPECT_EQ("C", ph);
+
+ int value;
+ EXPECT_TRUE((item && item->GetInteger("args.value", &value)));
+ EXPECT_EQ(31415, value);
+ }
+
+ EXPECT_FIND_("TRACE_COUNTER2 call");
+ {
+ std::string ph;
+ EXPECT_TRUE((item && item->GetString("ph", &ph)));
+ EXPECT_EQ("C", ph);
+
+ int value;
+ EXPECT_TRUE((item && item->GetInteger("args.a", &value)));
+ EXPECT_EQ(30000, value);
+
+ EXPECT_TRUE((item && item->GetInteger("args.b", &value)));
+ EXPECT_EQ(1415, value);
+ }
+
+ EXPECT_FIND_("TRACE_COUNTER_ID1 call");
+ {
+ std::string id;
+ EXPECT_TRUE((item && item->GetString("id", &id)));
+ EXPECT_EQ("319009", id);
+
+ std::string ph;
+ EXPECT_TRUE((item && item->GetString("ph", &ph)));
+ EXPECT_EQ("C", ph);
+
+ int value;
+ EXPECT_TRUE((item && item->GetInteger("args.value", &value)));
+ EXPECT_EQ(31415, value);
+ }
+
+ EXPECT_FIND_("TRACE_COUNTER_ID2 call");
+ {
+ std::string id;
+ EXPECT_TRUE((item && item->GetString("id", &id)));
+ EXPECT_EQ("319009", id);
+
+ std::string ph;
+ EXPECT_TRUE((item && item->GetString("ph", &ph)));
+ EXPECT_EQ("C", ph);
+
+ int value;
+ EXPECT_TRUE((item && item->GetInteger("args.a", &value)));
+ EXPECT_EQ(30000, value);
+
+ EXPECT_TRUE((item && item->GetInteger("args.b", &value)));
+ EXPECT_EQ(1415, value);
+ }
+}
+
+void TraceManyInstantEvents(int thread_id, int num_events,
+ WaitableEvent* task_complete_event) {
+ for (int i = 0; i < num_events; i++) {
+ TRACE_EVENT_INSTANT2("all", "multi thread event",
+ "thread", thread_id,
+ "event", i);
+ }
+
+ if (task_complete_event)
+ task_complete_event->Signal();
+}
+
+void ValidateInstantEventPresentOnEveryThread(const ListValue& trace_parsed,
+ int num_threads,
+ int num_events) {
+ std::map<int, std::map<int, bool> > results;
+
+ size_t trace_parsed_count = trace_parsed.GetSize();
+ for (size_t i = 0; i < trace_parsed_count; i++) {
+ const Value* value = NULL;
+ trace_parsed.Get(i, &value);
+ if (!value || value->GetType() != Value::TYPE_DICTIONARY)
+ continue;
+ const DictionaryValue* dict = static_cast<const DictionaryValue*>(value);
+ std::string name;
+ dict->GetString("name", &name);
+ if (name != "multi thread event")
+ continue;
+
+ int thread = 0;
+ int event = 0;
+ EXPECT_TRUE(dict->GetInteger("args.thread", &thread));
+ EXPECT_TRUE(dict->GetInteger("args.event", &event));
+ results[thread][event] = true;
+ }
+
+ EXPECT_FALSE(results[-1][-1]);
+ for (int thread = 0; thread < num_threads; thread++) {
+ for (int event = 0; event < num_events; event++) {
+ EXPECT_TRUE(results[thread][event]);
+ }
+ }
+}
+
+void TraceCallsWithCachedCategoryPointersPointers(const char* name_str) {
+ TRACE_EVENT0("category name1", name_str);
+ TRACE_EVENT_INSTANT0("category name2", name_str);
+ TRACE_EVENT_BEGIN0("category name3", name_str);
+ TRACE_EVENT_END0("category name4", name_str);
+}
+
+} // namespace
+
+void HighResSleepForTraceTest(base::TimeDelta elapsed) {
+ base::TimeTicks end_time = base::TimeTicks::HighResNow() + elapsed;
+ do {
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
+ } while (base::TimeTicks::HighResNow() < end_time);
+}
+
+// Simple Test for emitting data and validating it was received.
+TEST_F(TraceEventTestFixture, DataCaptured) {
+ ManualTestSetUp();
+ TraceLog::GetInstance()->SetEnabled(true);
+
+ TraceWithAllMacroVariants(NULL);
+
+ EndTraceAndFlush();
+
+ ValidateAllTraceMacrosCreatedData(trace_parsed_);
+}
+
+class MockEnabledStateChangedObserver :
+ public base::debug::TraceLog::EnabledStateChangedObserver {
+ public:
+ MOCK_METHOD0(OnTraceLogWillEnable, void());
+ MOCK_METHOD0(OnTraceLogWillDisable, void());
+};
+
+TEST_F(TraceEventTestFixture, EnabledObserverFiresOnEnable) {
+ ManualTestSetUp();
+
+ MockEnabledStateChangedObserver observer;
+ TraceLog::GetInstance()->AddEnabledStateObserver(&observer);
+
+ EXPECT_CALL(observer, OnTraceLogWillEnable())
+ .Times(1);
+ TraceLog::GetInstance()->SetEnabled(true);
+ testing::Mock::VerifyAndClear(&observer);
+
+ // Cleanup.
+ TraceLog::GetInstance()->RemoveEnabledStateObserver(&observer);
+ TraceLog::GetInstance()->SetEnabled(false);
+}
+
+TEST_F(TraceEventTestFixture, EnabledObserverDoesntFireOnSecondEnable) {
+ ManualTestSetUp();
+
+ TraceLog::GetInstance()->SetEnabled(true);
+
+ testing::StrictMock<MockEnabledStateChangedObserver> observer;
+ TraceLog::GetInstance()->AddEnabledStateObserver(&observer);
+
+ EXPECT_CALL(observer, OnTraceLogWillEnable())
+ .Times(0);
+ EXPECT_CALL(observer, OnTraceLogWillDisable())
+ .Times(0);
+ TraceLog::GetInstance()->SetEnabled(true);
+ testing::Mock::VerifyAndClear(&observer);
+
+ // Cleanup.
+ TraceLog::GetInstance()->RemoveEnabledStateObserver(&observer);
+ TraceLog::GetInstance()->SetEnabled(false);
+}
+
+TEST_F(TraceEventTestFixture, EnabledObserverDoesntFireOnUselessDisable) {
+ ManualTestSetUp();
+
+
+ testing::StrictMock<MockEnabledStateChangedObserver> observer;
+ TraceLog::GetInstance()->AddEnabledStateObserver(&observer);
+
+ EXPECT_CALL(observer, OnTraceLogWillEnable())
+ .Times(0);
+ EXPECT_CALL(observer, OnTraceLogWillDisable())
+ .Times(0);
+ TraceLog::GetInstance()->SetEnabled(false);
+ testing::Mock::VerifyAndClear(&observer);
+
+ // Cleanup.
+ TraceLog::GetInstance()->RemoveEnabledStateObserver(&observer);
+}
+
+TEST_F(TraceEventTestFixture, EnabledObserverFiresOnDisable) {
+ ManualTestSetUp();
+
+ TraceLog::GetInstance()->SetEnabled(true);
+
+ MockEnabledStateChangedObserver observer;
+ TraceLog::GetInstance()->AddEnabledStateObserver(&observer);
+
+ EXPECT_CALL(observer, OnTraceLogWillDisable())
+ .Times(1);
+ TraceLog::GetInstance()->SetEnabled(false);
+ testing::Mock::VerifyAndClear(&observer);
+
+ // Cleanup.
+ TraceLog::GetInstance()->RemoveEnabledStateObserver(&observer);
+}
+
+// Test that categories work.
+TEST_F(TraceEventTestFixture, Categories) {
+ ManualTestSetUp();
+
+ // Test that categories that are used can be retrieved whether trace was
+ // enabled or disabled when the trace event was encountered.
+ TRACE_EVENT_INSTANT0("c1", "name");
+ TRACE_EVENT_INSTANT0("c2", "name");
+ BeginTrace();
+ TRACE_EVENT_INSTANT0("c3", "name");
+ TRACE_EVENT_INSTANT0("c4", "name");
+ EndTraceAndFlush();
+ std::vector<std::string> cats;
+ TraceLog::GetInstance()->GetKnownCategories(&cats);
+ EXPECT_TRUE(std::find(cats.begin(), cats.end(), "c1") != cats.end());
+ EXPECT_TRUE(std::find(cats.begin(), cats.end(), "c2") != cats.end());
+ EXPECT_TRUE(std::find(cats.begin(), cats.end(), "c3") != cats.end());
+ EXPECT_TRUE(std::find(cats.begin(), cats.end(), "c4") != cats.end());
+
+ const std::vector<std::string> empty_categories;
+ std::vector<std::string> included_categories;
+ std::vector<std::string> excluded_categories;
+
+ // Test that category filtering works.
+
+ // Include nonexistent category -> no events
+ Clear();
+ included_categories.clear();
+ included_categories.push_back("not_found823564786");
+ TraceLog::GetInstance()->SetEnabled(included_categories, empty_categories);
+ TRACE_EVENT_INSTANT0("cat1", "name");
+ TRACE_EVENT_INSTANT0("cat2", "name");
+ EndTraceAndFlush();
+ EXPECT_TRUE(trace_parsed_.empty());
+
+ // Include existent category -> only events of that category
+ Clear();
+ included_categories.clear();
+ included_categories.push_back("inc");
+ TraceLog::GetInstance()->SetEnabled(included_categories, empty_categories);
+ TRACE_EVENT_INSTANT0("inc", "name");
+ TRACE_EVENT_INSTANT0("inc2", "name");
+ EndTraceAndFlush();
+ EXPECT_TRUE(FindMatchingValue("cat", "inc"));
+ EXPECT_FALSE(FindNonMatchingValue("cat", "inc"));
+
+ // Include existent wildcard -> all categories matching wildcard
+ Clear();
+ included_categories.clear();
+ included_categories.push_back("inc_wildcard_*");
+ included_categories.push_back("inc_wildchar_?_end");
+ TraceLog::GetInstance()->SetEnabled(included_categories, empty_categories);
+ TRACE_EVENT_INSTANT0("inc_wildcard_abc", "included");
+ TRACE_EVENT_INSTANT0("inc_wildcard_", "included");
+ TRACE_EVENT_INSTANT0("inc_wildchar_x_end", "included");
+ TRACE_EVENT_INSTANT0("inc_wildchar_bla_end", "not_inc");
+ TRACE_EVENT_INSTANT0("cat1", "not_inc");
+ TRACE_EVENT_INSTANT0("cat2", "not_inc");
+ EndTraceAndFlush();
+ EXPECT_TRUE(FindMatchingValue("cat", "inc_wildcard_abc"));
+ EXPECT_TRUE(FindMatchingValue("cat", "inc_wildcard_"));
+ EXPECT_TRUE(FindMatchingValue("cat", "inc_wildchar_x_end"));
+ EXPECT_FALSE(FindMatchingValue("name", "not_inc"));
+
+ included_categories.clear();
+
+ // Exclude nonexistent category -> all events
+ Clear();
+ excluded_categories.clear();
+ excluded_categories.push_back("not_found823564786");
+ TraceLog::GetInstance()->SetEnabled(empty_categories, excluded_categories);
+ TRACE_EVENT_INSTANT0("cat1", "name");
+ TRACE_EVENT_INSTANT0("cat2", "name");
+ EndTraceAndFlush();
+ EXPECT_TRUE(FindMatchingValue("cat", "cat1"));
+ EXPECT_TRUE(FindMatchingValue("cat", "cat2"));
+
+ // Exclude existent category -> only events of other categories
+ Clear();
+ excluded_categories.clear();
+ excluded_categories.push_back("inc");
+ TraceLog::GetInstance()->SetEnabled(empty_categories, excluded_categories);
+ TRACE_EVENT_INSTANT0("inc", "name");
+ TRACE_EVENT_INSTANT0("inc2", "name");
+ EndTraceAndFlush();
+ EXPECT_TRUE(FindMatchingValue("cat", "inc2"));
+ EXPECT_FALSE(FindMatchingValue("cat", "inc"));
+
+ // Exclude existent wildcard -> all categories not matching wildcard
+ Clear();
+ excluded_categories.clear();
+ excluded_categories.push_back("inc_wildcard_*");
+ excluded_categories.push_back("inc_wildchar_?_end");
+ TraceLog::GetInstance()->SetEnabled(empty_categories, excluded_categories);
+ TRACE_EVENT_INSTANT0("inc_wildcard_abc", "not_inc");
+ TRACE_EVENT_INSTANT0("inc_wildcard_", "not_inc");
+ TRACE_EVENT_INSTANT0("inc_wildchar_x_end", "not_inc");
+ TRACE_EVENT_INSTANT0("inc_wildchar_bla_end", "included");
+ TRACE_EVENT_INSTANT0("cat1", "included");
+ TRACE_EVENT_INSTANT0("cat2", "included");
+ EndTraceAndFlush();
+ EXPECT_TRUE(FindMatchingValue("cat", "inc_wildchar_bla_end"));
+ EXPECT_TRUE(FindMatchingValue("cat", "cat1"));
+ EXPECT_TRUE(FindMatchingValue("cat", "cat2"));
+ EXPECT_FALSE(FindMatchingValue("name", "not_inc"));
+}
+
+
+// Test EVENT_WATCH_NOTIFICATION
+TEST_F(TraceEventTestFixture, EventWatchNotification) {
+ ManualTestSetUp();
+
+ // Basic one occurrence.
+ BeginTrace();
+ TraceLog::GetInstance()->SetWatchEvent("cat", "event");
+ TRACE_EVENT_INSTANT0("cat", "event");
+ EndTraceAndFlush();
+ EXPECT_EQ(event_watch_notification_, 1);
+
+ // Basic one occurrence before Set.
+ BeginTrace();
+ TRACE_EVENT_INSTANT0("cat", "event");
+ TraceLog::GetInstance()->SetWatchEvent("cat", "event");
+ EndTraceAndFlush();
+ EXPECT_EQ(event_watch_notification_, 1);
+
+ // Auto-reset after end trace.
+ BeginTrace();
+ TraceLog::GetInstance()->SetWatchEvent("cat", "event");
+ EndTraceAndFlush();
+ BeginTrace();
+ TRACE_EVENT_INSTANT0("cat", "event");
+ EndTraceAndFlush();
+ EXPECT_EQ(event_watch_notification_, 0);
+
+ // Multiple occurrence.
+ BeginTrace();
+ int num_occurrences = 5;
+ TraceLog::GetInstance()->SetWatchEvent("cat", "event");
+ for (int i = 0; i < num_occurrences; ++i)
+ TRACE_EVENT_INSTANT0("cat", "event");
+ EndTraceAndFlush();
+ EXPECT_EQ(event_watch_notification_, num_occurrences);
+
+ // Wrong category.
+ BeginTrace();
+ TraceLog::GetInstance()->SetWatchEvent("cat", "event");
+ TRACE_EVENT_INSTANT0("wrong_cat", "event");
+ EndTraceAndFlush();
+ EXPECT_EQ(event_watch_notification_, 0);
+
+ // Wrong name.
+ BeginTrace();
+ TraceLog::GetInstance()->SetWatchEvent("cat", "event");
+ TRACE_EVENT_INSTANT0("cat", "wrong_event");
+ EndTraceAndFlush();
+ EXPECT_EQ(event_watch_notification_, 0);
+
+ // Canceled.
+ BeginTrace();
+ TraceLog::GetInstance()->SetWatchEvent("cat", "event");
+ TraceLog::GetInstance()->CancelWatchEvent();
+ TRACE_EVENT_INSTANT0("cat", "event");
+ EndTraceAndFlush();
+ EXPECT_EQ(event_watch_notification_, 0);
+}
+
+// Test ASYNC_BEGIN/END events
+TEST_F(TraceEventTestFixture, AsyncBeginEndEvents) {
+ ManualTestSetUp();
+ BeginTrace();
+
+ unsigned long long id = 0xfeedbeeffeedbeefull;
+ TRACE_EVENT_ASYNC_BEGIN0( "cat", "name1", id);
+ TRACE_EVENT_ASYNC_STEP0( "cat", "name1", id, "step1");
+ TRACE_EVENT_ASYNC_END0("cat", "name1", id);
+ TRACE_EVENT_BEGIN0( "cat", "name2");
+ TRACE_EVENT_ASYNC_BEGIN0( "cat", "name3", 0);
+
+ EndTraceAndFlush();
+
+ EXPECT_TRUE(FindNamePhase("name1", "S"));
+ EXPECT_TRUE(FindNamePhase("name1", "T"));
+ EXPECT_TRUE(FindNamePhase("name1", "F"));
+
+ std::string id_str;
+ StringAppendF(&id_str, "%llx", id);
+
+ EXPECT_TRUE(FindNamePhaseKeyValue("name1", "S", "id", id_str.c_str()));
+ EXPECT_TRUE(FindNamePhaseKeyValue("name1", "T", "id", id_str.c_str()));
+ EXPECT_TRUE(FindNamePhaseKeyValue("name1", "F", "id", id_str.c_str()));
+ EXPECT_TRUE(FindNamePhaseKeyValue("name3", "S", "id", "0"));
+
+ // BEGIN events should not have id
+ EXPECT_FALSE(FindNamePhaseKeyValue("name2", "B", "id", "0"));
+}
+
+// Test ASYNC_BEGIN/END events
+TEST_F(TraceEventTestFixture, AsyncBeginEndPointerMangling) {
+ ManualTestSetUp();
+
+ void* ptr = this;
+
+ TraceLog::GetInstance()->SetProcessID(100);
+ BeginTrace();
+ TRACE_EVENT_ASYNC_BEGIN0( "cat", "name1", ptr);
+ TRACE_EVENT_ASYNC_BEGIN0( "cat", "name2", ptr);
+ EndTraceAndFlush();
+
+ TraceLog::GetInstance()->SetProcessID(200);
+ BeginTrace();
+ TRACE_EVENT_ASYNC_END0( "cat", "name1", ptr);
+ EndTraceAndFlush();
+
+ DictionaryValue* async_begin = FindNamePhase("name1", "S");
+ DictionaryValue* async_begin2 = FindNamePhase("name2", "S");
+ DictionaryValue* async_end = FindNamePhase("name1", "F");
+ EXPECT_TRUE(async_begin);
+ EXPECT_TRUE(async_begin2);
+ EXPECT_TRUE(async_end);
+
+ Value* value = NULL;
+ std::string async_begin_id_str;
+ std::string async_begin2_id_str;
+ std::string async_end_id_str;
+ ASSERT_TRUE(async_begin->Get("id", &value));
+ ASSERT_TRUE(value->GetAsString(&async_begin_id_str));
+ ASSERT_TRUE(async_begin2->Get("id", &value));
+ ASSERT_TRUE(value->GetAsString(&async_begin2_id_str));
+ ASSERT_TRUE(async_end->Get("id", &value));
+ ASSERT_TRUE(value->GetAsString(&async_end_id_str));
+
+ EXPECT_STREQ(async_begin_id_str.c_str(), async_begin2_id_str.c_str());
+ EXPECT_STRNE(async_begin_id_str.c_str(), async_end_id_str.c_str());
+}
+
+// Test that static strings are not copied.
+TEST_F(TraceEventTestFixture, StaticStringVsString) {
+ ManualTestSetUp();
+ TraceLog* tracer = TraceLog::GetInstance();
+ // Make sure old events are flushed:
+ EndTraceAndFlush();
+ EXPECT_EQ(0u, tracer->GetEventsSize());
+
+ {
+ BeginTrace();
+ // Test that string arguments are copied.
+ TRACE_EVENT2("cat", "name1",
+ "arg1", std::string("argval"), "arg2", std::string("argval"));
+ // Test that static TRACE_STR_COPY string arguments are copied.
+ TRACE_EVENT2("cat", "name2",
+ "arg1", TRACE_STR_COPY("argval"),
+ "arg2", TRACE_STR_COPY("argval"));
+ size_t num_events = tracer->GetEventsSize();
+ EXPECT_GT(num_events, 1u);
+ const TraceEvent& event1 = tracer->GetEventAt(num_events - 2);
+ const TraceEvent& event2 = tracer->GetEventAt(num_events - 1);
+ EXPECT_STREQ("name1", event1.name());
+ EXPECT_STREQ("name2", event2.name());
+ EXPECT_TRUE(event1.parameter_copy_storage() != NULL);
+ EXPECT_TRUE(event2.parameter_copy_storage() != NULL);
+ EXPECT_GT(event1.parameter_copy_storage()->size(), 0u);
+ EXPECT_GT(event2.parameter_copy_storage()->size(), 0u);
+ EndTraceAndFlush();
+ }
+
+ {
+ BeginTrace();
+ // Test that static literal string arguments are not copied.
+ TRACE_EVENT2("cat", "name1",
+ "arg1", "argval", "arg2", "argval");
+ // Test that static TRACE_STR_COPY NULL string arguments are not copied.
+ const char* str1 = NULL;
+ const char* str2 = NULL;
+ TRACE_EVENT2("cat", "name2",
+ "arg1", TRACE_STR_COPY(str1),
+ "arg2", TRACE_STR_COPY(str2));
+ size_t num_events = tracer->GetEventsSize();
+ EXPECT_GT(num_events, 1u);
+ const TraceEvent& event1 = tracer->GetEventAt(num_events - 2);
+ const TraceEvent& event2 = tracer->GetEventAt(num_events - 1);
+ EXPECT_STREQ("name1", event1.name());
+ EXPECT_STREQ("name2", event2.name());
+ EXPECT_TRUE(event1.parameter_copy_storage() == NULL);
+ EXPECT_TRUE(event2.parameter_copy_storage() == NULL);
+ EndTraceAndFlush();
+ }
+}
+
+// Test that data sent from other threads is gathered
+TEST_F(TraceEventTestFixture, DataCapturedOnThread) {
+ ManualTestSetUp();
+ BeginTrace();
+
+ Thread thread("1");
+ WaitableEvent task_complete_event(false, false);
+ thread.Start();
+
+ thread.message_loop()->PostTask(
+ FROM_HERE, base::Bind(&TraceWithAllMacroVariants, &task_complete_event));
+ task_complete_event.Wait();
+ thread.Stop();
+
+ EndTraceAndFlush();
+ ValidateAllTraceMacrosCreatedData(trace_parsed_);
+}
+
+// Test that data sent from multiple threads is gathered
+TEST_F(TraceEventTestFixture, DataCapturedManyThreads) {
+ ManualTestSetUp();
+ BeginTrace();
+
+ const int num_threads = 4;
+ const int num_events = 4000;
+ Thread* threads[num_threads];
+ WaitableEvent* task_complete_events[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ threads[i] = new Thread(StringPrintf("Thread %d", i).c_str());
+ task_complete_events[i] = new WaitableEvent(false, false);
+ threads[i]->Start();
+ threads[i]->message_loop()->PostTask(
+ FROM_HERE, base::Bind(&TraceManyInstantEvents,
+ i, num_events, task_complete_events[i]));
+ }
+
+ for (int i = 0; i < num_threads; i++) {
+ task_complete_events[i]->Wait();
+ }
+
+ for (int i = 0; i < num_threads; i++) {
+ threads[i]->Stop();
+ delete threads[i];
+ delete task_complete_events[i];
+ }
+
+ EndTraceAndFlush();
+
+ ValidateInstantEventPresentOnEveryThread(trace_parsed_,
+ num_threads, num_events);
+}
+
+// Test that thread and process names show up in the trace
+TEST_F(TraceEventTestFixture, ThreadNames) {
+ ManualTestSetUp();
+
+ // Create threads before we enable tracing to make sure
+ // that tracelog still captures them.
+ const int num_threads = 4;
+ const int num_events = 10;
+ Thread* threads[num_threads];
+ PlatformThreadId thread_ids[num_threads];
+ for (int i = 0; i < num_threads; i++)
+ threads[i] = new Thread(StringPrintf("Thread %d", i).c_str());
+
+ // Enable tracing.
+ BeginTrace();
+
+ // Now run some trace code on these threads.
+ WaitableEvent* task_complete_events[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ task_complete_events[i] = new WaitableEvent(false, false);
+ threads[i]->Start();
+ thread_ids[i] = threads[i]->thread_id();
+ threads[i]->message_loop()->PostTask(
+ FROM_HERE, base::Bind(&TraceManyInstantEvents,
+ i, num_events, task_complete_events[i]));
+ }
+ for (int i = 0; i < num_threads; i++) {
+ task_complete_events[i]->Wait();
+ }
+
+ // Shut things down.
+ for (int i = 0; i < num_threads; i++) {
+ threads[i]->Stop();
+ delete threads[i];
+ delete task_complete_events[i];
+ }
+
+ EndTraceAndFlush();
+
+ std::string tmp;
+ int tmp_int;
+ const DictionaryValue* item;
+
+ // Make sure we get thread name metadata.
+ // Note, the test suite may have created a ton of threads.
+ // So, we'll have thread names for threads we didn't create.
+ std::vector<const DictionaryValue*> items =
+ FindTraceEntries(trace_parsed_, "thread_name");
+ for (int i = 0; i < static_cast<int>(items.size()); i++) {
+ item = items[i];
+ ASSERT_TRUE(item);
+ EXPECT_TRUE(item->GetInteger("tid", &tmp_int));
+
+ // See if this thread name is one of the threads we just created
+ for (int j = 0; j < num_threads; j++) {
+ if(static_cast<int>(thread_ids[j]) != tmp_int)
+ continue;
+
+ std::string expected_name = StringPrintf("Thread %d", j);
+ EXPECT_TRUE(item->GetString("ph", &tmp) && tmp == "M");
+ EXPECT_TRUE(item->GetInteger("pid", &tmp_int) &&
+ tmp_int == static_cast<int>(base::GetCurrentProcId()));
+ // If the thread name changes or the tid gets reused, the name will be
+ // a comma-separated list of thread names, so look for a substring.
+ EXPECT_TRUE(item->GetString("args.name", &tmp) &&
+ tmp.find(expected_name) != std::string::npos);
+ }
+ }
+}
+
+TEST_F(TraceEventTestFixture, ThreadNameChanges) {
+ ManualTestSetUp();
+
+ BeginTrace();
+
+ PlatformThread::SetName("");
+ TRACE_EVENT_INSTANT0("drink", "water");
+
+ PlatformThread::SetName("cafe");
+ TRACE_EVENT_INSTANT0("drink", "coffee");
+
+ PlatformThread::SetName("shop");
+ // No event here, so won't appear in combined name.
+
+ PlatformThread::SetName("pub");
+ TRACE_EVENT_INSTANT0("drink", "beer");
+ TRACE_EVENT_INSTANT0("drink", "wine");
+
+ PlatformThread::SetName(" bar");
+ TRACE_EVENT_INSTANT0("drink", "whisky");
+
+ EndTraceAndFlush();
+
+ std::vector<const DictionaryValue*> items =
+ FindTraceEntries(trace_parsed_, "thread_name");
+ EXPECT_EQ(1u, items.size());
+ ASSERT_GT(items.size(), 0u);
+ const DictionaryValue* item = items[0];
+ ASSERT_TRUE(item);
+ int tid;
+ EXPECT_TRUE(item->GetInteger("tid", &tid));
+ EXPECT_EQ(PlatformThread::CurrentId(), static_cast<PlatformThreadId>(tid));
+
+ std::string expected_name = "cafe,pub, bar";
+ std::string tmp;
+ EXPECT_TRUE(item->GetString("args.name", &tmp));
+ EXPECT_EQ(expected_name, tmp);
+}
+
+// Test trace calls made after tracing singleton shut down.
+//
+// The singleton is destroyed by our base::AtExitManager, but there can be
+// code still executing as the C++ static objects are destroyed. This test
+// forces the singleton to destroy early, and intentinally makes trace calls
+// afterwards.
+TEST_F(TraceEventTestFixture, AtExit) {
+ // Repeat this test a few times. Besides just showing robustness, it also
+ // allows us to test that events at shutdown do not appear with valid events
+ // recorded after the system is started again.
+ for (int i = 0; i < 4; i++) {
+ // Scope to contain the then destroy the TraceLog singleton.
+ {
+ base::ShadowingAtExitManager exit_manager_will_destroy_singletons;
+
+ // Setup TraceLog singleton inside this test's exit manager scope
+ // so that it will be destroyed when this scope closes.
+ ManualTestSetUp();
+
+ TRACE_EVENT_INSTANT0("all", "not recorded; system not enabled");
+
+ BeginTrace();
+
+ TRACE_EVENT_INSTANT0("all", "is recorded 1; system has been enabled");
+ // Trace calls that will cache pointers to categories; they're valid here
+ TraceCallsWithCachedCategoryPointersPointers(
+ "is recorded 2; system has been enabled");
+
+ EndTraceAndFlush();
+ } // scope to destroy singleton
+ ASSERT_FALSE(TraceLog::GetInstance());
+
+ // Now that singleton is destroyed, check what trace events were recorded
+ const DictionaryValue* item = NULL;
+ ListValue& trace_parsed = trace_parsed_;
+ EXPECT_FIND_("is recorded 1");
+ EXPECT_FIND_("is recorded 2");
+ EXPECT_NOT_FIND_("not recorded");
+
+ // Make additional trace event calls on the shutdown system. They should
+ // all pass cleanly, but the data not be recorded. We'll verify that next
+ // time around the loop (the only way to flush the trace buffers).
+ TRACE_EVENT_BEGIN_ETW("not recorded; system shutdown", 0, NULL);
+ TRACE_EVENT_END_ETW("not recorded; system shutdown", 0, NULL);
+ TRACE_EVENT_INSTANT_ETW("not recorded; system shutdown", 0, NULL);
+ TRACE_EVENT0("all", "not recorded; system shutdown");
+ TRACE_EVENT_INSTANT0("all", "not recorded; system shutdown");
+ TRACE_EVENT_BEGIN0("all", "not recorded; system shutdown");
+ TRACE_EVENT_END0("all", "not recorded; system shutdown");
+
+ TRACE_EVENT0("new category 0!", "not recorded; system shutdown");
+ TRACE_EVENT_INSTANT0("new category 1!", "not recorded; system shutdown");
+ TRACE_EVENT_BEGIN0("new category 2!", "not recorded; system shutdown");
+ TRACE_EVENT_END0("new category 3!", "not recorded; system shutdown");
+
+ // Cached categories should be safe to check, and still disable traces
+ TraceCallsWithCachedCategoryPointersPointers(
+ "not recorded; system shutdown");
+ }
+}
+
+TEST_F(TraceEventTestFixture, NormallyNoDeepCopy) {
+ // Test that the TRACE_EVENT macros do not deep-copy their string. If they
+ // do so it may indicate a performance regression, but more-over it would
+ // make the DEEP_COPY overloads redundant.
+ ManualTestSetUp();
+
+ std::string name_string("event name");
+
+ BeginTrace();
+ TRACE_EVENT_INSTANT0("category", name_string.c_str());
+
+ // Modify the string in place (a wholesale reassignment may leave the old
+ // string intact on the heap).
+ name_string[0] = '@';
+
+ EndTraceAndFlush();
+
+ EXPECT_FALSE(FindTraceEntry(trace_parsed_, "event name"));
+ EXPECT_TRUE(FindTraceEntry(trace_parsed_, name_string.c_str()));
+}
+
+TEST_F(TraceEventTestFixture, DeepCopy) {
+ ManualTestSetUp();
+
+ static const char kOriginalName1[] = "name1";
+ static const char kOriginalName2[] = "name2";
+ static const char kOriginalName3[] = "name3";
+ std::string name1(kOriginalName1);
+ std::string name2(kOriginalName2);
+ std::string name3(kOriginalName3);
+ std::string arg1("arg1");
+ std::string arg2("arg2");
+ std::string val1("val1");
+ std::string val2("val2");
+
+ BeginTrace();
+ TRACE_EVENT_COPY_INSTANT0("category", name1.c_str());
+ TRACE_EVENT_COPY_BEGIN1("category", name2.c_str(),
+ arg1.c_str(), 5);
+ TRACE_EVENT_COPY_END2("category", name3.c_str(),
+ arg1.c_str(), val1,
+ arg2.c_str(), val2);
+
+ // As per NormallyNoDeepCopy, modify the strings in place.
+ name1[0] = name2[0] = name3[0] = arg1[0] = arg2[0] = val1[0] = val2[0] = '@';
+
+ EndTraceAndFlush();
+
+ EXPECT_FALSE(FindTraceEntry(trace_parsed_, name1.c_str()));
+ EXPECT_FALSE(FindTraceEntry(trace_parsed_, name2.c_str()));
+ EXPECT_FALSE(FindTraceEntry(trace_parsed_, name3.c_str()));
+
+ const DictionaryValue* entry1 = FindTraceEntry(trace_parsed_, kOriginalName1);
+ const DictionaryValue* entry2 = FindTraceEntry(trace_parsed_, kOriginalName2);
+ const DictionaryValue* entry3 = FindTraceEntry(trace_parsed_, kOriginalName3);
+ ASSERT_TRUE(entry1);
+ ASSERT_TRUE(entry2);
+ ASSERT_TRUE(entry3);
+
+ int i;
+ EXPECT_FALSE(entry2->GetInteger("args.@rg1", &i));
+ EXPECT_TRUE(entry2->GetInteger("args.arg1", &i));
+ EXPECT_EQ(5, i);
+
+ std::string s;
+ EXPECT_TRUE(entry3->GetString("args.arg1", &s));
+ EXPECT_EQ("val1", s);
+ EXPECT_TRUE(entry3->GetString("args.arg2", &s));
+ EXPECT_EQ("val2", s);
+}
+
+// Test that TraceResultBuffer outputs the correct result whether it is added
+// in chunks or added all at once.
+TEST_F(TraceEventTestFixture, TraceResultBuffer) {
+ ManualTestSetUp();
+
+ Clear();
+
+ trace_buffer_.Start();
+ trace_buffer_.AddFragment("bla1");
+ trace_buffer_.AddFragment("bla2");
+ trace_buffer_.AddFragment("bla3,bla4");
+ trace_buffer_.Finish();
+ EXPECT_STREQ(json_output_.json_output.c_str(), "[bla1,bla2,bla3,bla4]");
+
+ Clear();
+
+ trace_buffer_.Start();
+ trace_buffer_.AddFragment("bla1,bla2,bla3,bla4");
+ trace_buffer_.Finish();
+ EXPECT_STREQ(json_output_.json_output.c_str(), "[bla1,bla2,bla3,bla4]");
+}
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/trace_event_unittest.h b/src/base/debug/trace_event_unittest.h
new file mode 100644
index 0000000..3b570e1
--- /dev/null
+++ b/src/base/debug/trace_event_unittest.h
@@ -0,0 +1,14 @@
+// Copyright (c) 2012 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/time.h"
+
+namespace base {
+namespace debug {
+
+// Sleep until HighResNow has advanced by at least |elapsed|.
+void HighResSleepForTraceTest(base::TimeDelta elapsed);
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/trace_event_win.cc b/src/base/debug/trace_event_win.cc
new file mode 100644
index 0000000..d5a21f4
--- /dev/null
+++ b/src/base/debug/trace_event_win.cc
@@ -0,0 +1,120 @@
+// Copyright (c) 2012 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/trace_event_win.h"
+
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include <initguid.h> // NOLINT
+
+namespace base {
+namespace debug {
+
+using base::win::EtwEventType;
+using base::win::EtwMofEvent;
+
+// {3DADA31D-19EF-4dc1-B345-037927193422}
+const GUID kChromeTraceProviderName = {
+ 0x3dada31d, 0x19ef, 0x4dc1, 0xb3, 0x45, 0x3, 0x79, 0x27, 0x19, 0x34, 0x22 };
+
+// {B967AE67-BB22-49d7-9406-55D91EE1D560}
+const GUID kTraceEventClass32 = {
+ 0xb967ae67, 0xbb22, 0x49d7, 0x94, 0x6, 0x55, 0xd9, 0x1e, 0xe1, 0xd5, 0x60 };
+
+// {97BE602D-2930-4ac3-8046-B6763B631DFE}
+const GUID kTraceEventClass64 = {
+ 0x97be602d, 0x2930, 0x4ac3, 0x80, 0x46, 0xb6, 0x76, 0x3b, 0x63, 0x1d, 0xfe};
+
+
+TraceEventETWProvider::TraceEventETWProvider() :
+ EtwTraceProvider(kChromeTraceProviderName) {
+ Register();
+}
+
+TraceEventETWProvider* TraceEventETWProvider::GetInstance() {
+ return Singleton<TraceEventETWProvider,
+ StaticMemorySingletonTraits<TraceEventETWProvider> >::get();
+}
+
+bool TraceEventETWProvider::StartTracing() {
+ return true;
+}
+
+void TraceEventETWProvider::TraceEvent(const char* name,
+ size_t name_len,
+ char type,
+ const void* id,
+ const char* extra,
+ size_t extra_len) {
+ // Make sure we don't touch NULL.
+ if (name == NULL)
+ name = "";
+ if (extra == NULL)
+ extra = "";
+
+ EtwEventType etw_type = 0;
+ switch (type) {
+ case TRACE_EVENT_PHASE_BEGIN:
+ etw_type = kTraceEventTypeBegin;
+ break;
+ case TRACE_EVENT_PHASE_END:
+ etw_type = kTraceEventTypeEnd;
+ break;
+
+ case TRACE_EVENT_PHASE_INSTANT:
+ etw_type = kTraceEventTypeInstant;
+ break;
+
+ default:
+ NOTREACHED() << "Unknown event type";
+ etw_type = kTraceEventTypeInstant;
+ break;
+ }
+
+ EtwMofEvent<5> event(kTraceEventClass32,
+ etw_type,
+ TRACE_LEVEL_INFORMATION);
+ event.SetField(0, name_len + 1, name);
+ event.SetField(1, sizeof(id), &id);
+ event.SetField(2, extra_len + 1, extra);
+
+ // See whether we're to capture a backtrace.
+ void* backtrace[32];
+ if (enable_flags() & CAPTURE_STACK_TRACE) {
+ DWORD hash = 0;
+ DWORD depth = CaptureStackBackTrace(0,
+ arraysize(backtrace),
+ backtrace,
+ &hash);
+ event.SetField(3, sizeof(depth), &depth);
+ event.SetField(4, sizeof(backtrace[0]) * depth, backtrace);
+ }
+
+ // Trace the event.
+ Log(event.get());
+}
+
+void TraceEventETWProvider::Trace(const char* name,
+ size_t name_len,
+ char type,
+ const void* id,
+ const char* extra,
+ size_t extra_len) {
+ TraceEventETWProvider* provider = TraceEventETWProvider::GetInstance();
+ if (provider && provider->IsTracing()) {
+ // Compute the name & extra lengths if not supplied already.
+ if (name_len == -1)
+ name_len = (name == NULL) ? 0 : strlen(name);
+ if (extra_len == -1)
+ extra_len = (extra == NULL) ? 0 : strlen(extra);
+
+ provider->TraceEvent(name, name_len, type, id, extra, extra_len);
+ }
+}
+
+void TraceEventETWProvider::Resurrect() {
+ StaticMemorySingletonTraits<TraceEventETWProvider>::Resurrect();
+}
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug/trace_event_win.h b/src/base/debug/trace_event_win.h
new file mode 100644
index 0000000..2a900bb
--- /dev/null
+++ b/src/base/debug/trace_event_win.h
@@ -0,0 +1,123 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains the Windows-specific declarations for trace_event.h.
+#ifndef BASE_DEBUG_TRACE_EVENT_WIN_H_
+#define BASE_DEBUG_TRACE_EVENT_WIN_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/debug/trace_event.h"
+#include "base/win/event_trace_provider.h"
+
+// Fwd.
+template <typename Type>
+struct StaticMemorySingletonTraits;
+
+namespace base {
+namespace debug {
+
+// This EtwTraceProvider subclass implements ETW logging
+// for the macros above on Windows.
+class BASE_EXPORT TraceEventETWProvider : public base::win::EtwTraceProvider {
+ public:
+ // Start logging trace events.
+ // This is a noop in this implementation.
+ static bool StartTracing();
+
+ // Trace begin/end/instant events, this is the bottleneck implementation
+ // all the others defer to.
+ // Allowing the use of std::string for name or extra is a convenience,
+ // whereas passing name or extra as a const char* avoids the construction
+ // of temporary std::string instances.
+ // If -1 is passed for name_len or extra_len, the strlen of the string will
+ // be used for length.
+ static void Trace(const char* name,
+ size_t name_len,
+ char type,
+ const void* id,
+ const char* extra,
+ size_t extra_len);
+
+ // Allows passing extra as a std::string for convenience.
+ static void Trace(const char* name,
+ char type,
+ const void* id,
+ const std::string& extra) {
+ return Trace(name, -1, type, id, extra.c_str(), extra.length());
+ }
+
+ // Allows passing extra as a const char* to avoid constructing temporary
+ // std::string instances where not needed.
+ static void Trace(const char* name,
+ char type,
+ const void* id,
+ const char* extra) {
+ return Trace(name, -1, type, id, extra, -1);
+ }
+
+ // Retrieves the singleton.
+ // Note that this may return NULL post-AtExit processing.
+ static TraceEventETWProvider* GetInstance();
+
+ // Returns true iff tracing is turned on.
+ bool IsTracing() {
+ return enable_level() >= TRACE_LEVEL_INFORMATION;
+ }
+
+ // Emit a trace of type |type| containing |name|, |id|, and |extra|.
+ // Note: |name| and |extra| must be NULL, or a zero-terminated string of
+ // length |name_len| or |extra_len| respectively.
+ // Note: if name_len or extra_len are -1, the length of the corresponding
+ // string will be used.
+ void TraceEvent(const char* name,
+ size_t name_len,
+ char type,
+ const void* id,
+ const char* extra,
+ size_t extra_len);
+
+ // Exposed for unittesting only, allows resurrecting our
+ // singleton instance post-AtExit processing.
+ static void Resurrect();
+
+ private:
+ // Ensure only the provider can construct us.
+ friend struct StaticMemorySingletonTraits<TraceEventETWProvider>;
+ TraceEventETWProvider();
+
+ DISALLOW_COPY_AND_ASSIGN(TraceEventETWProvider);
+};
+
+// The ETW trace provider GUID.
+BASE_EXPORT extern const GUID kChromeTraceProviderName;
+
+// The ETW event class GUID for 32 bit events.
+BASE_EXPORT extern const GUID kTraceEventClass32;
+
+// The ETW event class GUID for 64 bit events.
+BASE_EXPORT extern const GUID kTraceEventClass64;
+
+// The ETW event types, IDs 0x00-0x09 are reserved, so start at 0x10.
+const base::win::EtwEventType kTraceEventTypeBegin = 0x10;
+const base::win::EtwEventType kTraceEventTypeEnd = 0x11;
+const base::win::EtwEventType kTraceEventTypeInstant = 0x12;
+
+// If this flag is set in enable flags
+enum TraceEventETWFlags {
+ CAPTURE_STACK_TRACE = 0x0001,
+};
+
+// The event format consists of:
+// The "name" string as a zero-terminated ASCII string.
+// The id pointer in the machine bitness.
+// The "extra" string as a zero-terminated ASCII string.
+// Optionally the stack trace, consisting of a DWORD "depth", followed
+// by an array of void* (machine bitness) of length "depth".
+
+} // namespace debug
+} // namespace base
+
+#endif // BASE_DEBUG_TRACE_EVENT_WIN_H_
diff --git a/src/base/debug/trace_event_win_unittest.cc b/src/base/debug/trace_event_win_unittest.cc
new file mode 100644
index 0000000..786b9d5
--- /dev/null
+++ b/src/base/debug/trace_event_win_unittest.cc
@@ -0,0 +1,316 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/debug/trace_event.h"
+
+#include <strstream>
+
+#include "base/at_exit.h"
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/debug/trace_event.h"
+#include "base/debug/trace_event_win.h"
+#include "base/win/event_trace_consumer.h"
+#include "base/win/event_trace_controller.h"
+#include "base/win/event_trace_provider.h"
+#include "base/win/windows_version.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include <initguid.h> // NOLINT - must be last include.
+
+namespace base {
+namespace debug {
+
+namespace {
+
+using testing::_;
+using testing::AnyNumber;
+using testing::InSequence;
+using testing::Ge;
+using testing::Le;
+using testing::NotNull;
+
+using base::win::EtwEventType;
+using base::win::EtwTraceConsumerBase;
+using base::win::EtwTraceController;
+using base::win::EtwTraceProperties;
+
+// Data for unittests traces.
+const char kEmpty[] = "";
+const char kName[] = "unittest.trace_name";
+const char kExtra[] = "UnittestDummyExtraString";
+const void* kId = kName;
+
+const wchar_t kTestSessionName[] = L"TraceEvent unittest session";
+
+MATCHER_P(BufferStartsWith, str, "Buffer starts with") {
+ return memcmp(arg, str.c_str(), str.length()) == 0;
+}
+
+// Duplicated from <evntrace.h> to fix link problems.
+DEFINE_GUID( /* 68fdd900-4a3e-11d1-84f4-0000f80464e3 */
+ kEventTraceGuid,
+ 0x68fdd900,
+ 0x4a3e,
+ 0x11d1,
+ 0x84, 0xf4, 0x00, 0x00, 0xf8, 0x04, 0x64, 0xe3);
+
+class TestEventConsumer: public EtwTraceConsumerBase<TestEventConsumer> {
+ public:
+ TestEventConsumer() {
+ EXPECT_TRUE(current_ == NULL);
+ current_ = this;
+ }
+
+ ~TestEventConsumer() {
+ EXPECT_TRUE(current_ == this);
+ current_ = NULL;
+ }
+
+ MOCK_METHOD4(Event, void(REFGUID event_class,
+ EtwEventType event_type,
+ size_t buf_len,
+ const void* buf));
+
+ static void ProcessEvent(EVENT_TRACE* event) {
+ ASSERT_TRUE(current_ != NULL);
+ current_->Event(event->Header.Guid,
+ event->Header.Class.Type,
+ event->MofLength,
+ event->MofData);
+ }
+
+ private:
+ static TestEventConsumer* current_;
+};
+
+TestEventConsumer* TestEventConsumer::current_ = NULL;
+
+class TraceEventWinTest: public testing::Test {
+ public:
+ TraceEventWinTest() {
+ }
+
+ void SetUp() {
+ bool is_xp = win::GetVersion() < base::win::VERSION_VISTA;
+
+ if (is_xp) {
+ // Tear down any dangling session from an earlier failing test.
+ EtwTraceProperties ignore;
+ EtwTraceController::Stop(kTestSessionName, &ignore);
+ }
+
+ // Resurrect and initialize the TraceLog singleton instance.
+ // On Vista and better, we need the provider registered before we
+ // start the private, in-proc session, but on XP we need the global
+ // session created and the provider enabled before we register our
+ // provider.
+ TraceEventETWProvider* tracelog = NULL;
+ if (!is_xp) {
+ TraceEventETWProvider::Resurrect();
+ tracelog = TraceEventETWProvider::GetInstance();
+ ASSERT_TRUE(tracelog != NULL);
+ ASSERT_FALSE(tracelog->IsTracing());
+ }
+
+ // Create the log file.
+ ASSERT_TRUE(file_util::CreateTemporaryFile(&log_file_));
+
+ // Create a private log session on the file.
+ EtwTraceProperties prop;
+ ASSERT_HRESULT_SUCCEEDED(prop.SetLoggerFileName(log_file_.value().c_str()));
+ EVENT_TRACE_PROPERTIES& p = *prop.get();
+ p.Wnode.ClientContext = 1; // QPC timer accuracy.
+ p.LogFileMode = EVENT_TRACE_FILE_MODE_SEQUENTIAL; // Sequential log.
+
+ // On Vista and later, we create a private in-process log session, because
+ // otherwise we'd need administrator privileges. Unfortunately we can't
+ // do the same on XP and better, because the semantics of a private
+ // logger session are different, and the IN_PROC flag is not supported.
+ if (!is_xp) {
+ p.LogFileMode |= EVENT_TRACE_PRIVATE_IN_PROC | // In-proc for non-admin.
+ EVENT_TRACE_PRIVATE_LOGGER_MODE; // Process-private log.
+ }
+
+ p.MaximumFileSize = 100; // 100M file size.
+ p.FlushTimer = 1; // 1 second flush lag.
+ ASSERT_HRESULT_SUCCEEDED(controller_.Start(kTestSessionName, &prop));
+
+ // Enable the TraceLog provider GUID.
+ ASSERT_HRESULT_SUCCEEDED(
+ controller_.EnableProvider(kChromeTraceProviderName,
+ TRACE_LEVEL_INFORMATION,
+ 0));
+
+ if (is_xp) {
+ TraceEventETWProvider::Resurrect();
+ tracelog = TraceEventETWProvider::GetInstance();
+ }
+ ASSERT_TRUE(tracelog != NULL);
+ EXPECT_TRUE(tracelog->IsTracing());
+ }
+
+ void TearDown() {
+ EtwTraceProperties prop;
+ if (controller_.session() != 0)
+ EXPECT_HRESULT_SUCCEEDED(controller_.Stop(&prop));
+
+ if (!log_file_.value().empty())
+ file_util::Delete(log_file_, false);
+ }
+
+ void ExpectEvent(REFGUID guid,
+ EtwEventType type,
+ const char* name,
+ size_t name_len,
+ const void* id,
+ const char* extra,
+ size_t extra_len) {
+ // Build the trace event buffer we expect will result from this.
+ std::stringbuf str;
+ str.sputn(name, name_len + 1);
+ str.sputn(reinterpret_cast<const char*>(&id), sizeof(id));
+ str.sputn(extra, extra_len + 1);
+
+ // And set up the expectation for the event callback.
+ EXPECT_CALL(consumer_, Event(guid,
+ type,
+ testing::Ge(str.str().length()),
+ BufferStartsWith(str.str())));
+ }
+
+ void ExpectPlayLog() {
+ // Ignore EventTraceGuid events.
+ EXPECT_CALL(consumer_, Event(kEventTraceGuid, _, _, _))
+ .Times(AnyNumber());
+ }
+
+ void PlayLog() {
+ EtwTraceProperties prop;
+ EXPECT_HRESULT_SUCCEEDED(controller_.Flush(&prop));
+ EXPECT_HRESULT_SUCCEEDED(controller_.Stop(&prop));
+ ASSERT_HRESULT_SUCCEEDED(
+ consumer_.OpenFileSession(log_file_.value().c_str()));
+
+ ASSERT_HRESULT_SUCCEEDED(consumer_.Consume());
+ }
+
+ private:
+ // We want our singleton torn down after each test.
+ ShadowingAtExitManager at_exit_manager_;
+ EtwTraceController controller_;
+ FilePath log_file_;
+ TestEventConsumer consumer_;
+};
+
+} // namespace
+
+
+TEST_F(TraceEventWinTest, TraceLog) {
+ ExpectPlayLog();
+
+ // The events should arrive in the same sequence as the expects.
+ InSequence in_sequence;
+
+ // Full argument version, passing lengths explicitly.
+ TraceEventETWProvider::Trace(kName,
+ strlen(kName),
+ TRACE_EVENT_PHASE_BEGIN,
+ kId,
+ kExtra,
+ strlen(kExtra));
+
+ ExpectEvent(kTraceEventClass32,
+ kTraceEventTypeBegin,
+ kName, strlen(kName),
+ kId,
+ kExtra, strlen(kExtra));
+
+ // Const char* version.
+ TraceEventETWProvider::Trace(static_cast<const char*>(kName),
+ TRACE_EVENT_PHASE_END,
+ kId,
+ static_cast<const char*>(kExtra));
+
+ ExpectEvent(kTraceEventClass32,
+ kTraceEventTypeEnd,
+ kName, strlen(kName),
+ kId,
+ kExtra, strlen(kExtra));
+
+ // std::string extra version.
+ TraceEventETWProvider::Trace(static_cast<const char*>(kName),
+ TRACE_EVENT_PHASE_INSTANT,
+ kId,
+ std::string(kExtra));
+
+ ExpectEvent(kTraceEventClass32,
+ kTraceEventTypeInstant,
+ kName, strlen(kName),
+ kId,
+ kExtra, strlen(kExtra));
+
+
+ // Test for sanity on NULL inputs.
+ TraceEventETWProvider::Trace(NULL,
+ 0,
+ TRACE_EVENT_PHASE_BEGIN,
+ kId,
+ NULL,
+ 0);
+
+ ExpectEvent(kTraceEventClass32,
+ kTraceEventTypeBegin,
+ kEmpty, 0,
+ kId,
+ kEmpty, 0);
+
+ TraceEventETWProvider::Trace(NULL,
+ -1,
+ TRACE_EVENT_PHASE_END,
+ kId,
+ NULL,
+ -1);
+
+ ExpectEvent(kTraceEventClass32,
+ kTraceEventTypeEnd,
+ kEmpty, 0,
+ kId,
+ kEmpty, 0);
+
+ PlayLog();
+}
+
+TEST_F(TraceEventWinTest, Macros) {
+ ExpectPlayLog();
+
+ // The events should arrive in the same sequence as the expects.
+ InSequence in_sequence;
+
+ TRACE_EVENT_BEGIN_ETW(kName, kId, kExtra);
+ ExpectEvent(kTraceEventClass32,
+ kTraceEventTypeBegin,
+ kName, strlen(kName),
+ kId,
+ kExtra, strlen(kExtra));
+
+ TRACE_EVENT_END_ETW(kName, kId, kExtra);
+ ExpectEvent(kTraceEventClass32,
+ kTraceEventTypeEnd,
+ kName, strlen(kName),
+ kId,
+ kExtra, strlen(kExtra));
+
+ TRACE_EVENT_INSTANT_ETW(kName, kId, kExtra);
+ ExpectEvent(kTraceEventClass32,
+ kTraceEventTypeInstant,
+ kName, strlen(kName),
+ kId,
+ kExtra, strlen(kExtra));
+
+ PlayLog();
+}
+
+} // namespace debug
+} // namespace base
diff --git a/src/base/debug_message.cc b/src/base/debug_message.cc
new file mode 100644
index 0000000..10f441d
--- /dev/null
+++ b/src/base/debug_message.cc
@@ -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;
+}
diff --git a/src/base/environment.cc b/src/base/environment.cc
new file mode 100644
index 0000000..8c61591
--- /dev/null
+++ b/src/base/environment.cc
@@ -0,0 +1,127 @@
+// Copyright (c) 2012 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/environment.h"
+
+#if defined(OS_POSIX)
+#include <stdlib.h>
+#elif defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#include "base/string_util.h"
+
+#if defined(OS_WIN)
+#include "base/memory/scoped_ptr.h"
+#include "base/utf_string_conversions.h"
+#endif
+
+namespace {
+
+class EnvironmentImpl : public base::Environment {
+ public:
+ virtual bool GetVar(const char* variable_name,
+ std::string* result) OVERRIDE {
+ if (GetVarImpl(variable_name, result))
+ return true;
+
+ // Some commonly used variable names are uppercase while others
+ // are lowercase, which is inconsistent. Let's try to be helpful
+ // and look for a variable name with the reverse case.
+ // I.e. HTTP_PROXY may be http_proxy for some users/systems.
+ char first_char = variable_name[0];
+ std::string alternate_case_var;
+ if (first_char >= 'a' && first_char <= 'z')
+ alternate_case_var = StringToUpperASCII(std::string(variable_name));
+ else if (first_char >= 'A' && first_char <= 'Z')
+ alternate_case_var = StringToLowerASCII(std::string(variable_name));
+ else
+ return false;
+ return GetVarImpl(alternate_case_var.c_str(), result);
+ }
+
+ virtual bool SetVar(const char* variable_name,
+ const std::string& new_value) OVERRIDE {
+ return SetVarImpl(variable_name, new_value);
+ }
+
+ virtual bool UnSetVar(const char* variable_name) OVERRIDE {
+ return UnSetVarImpl(variable_name);
+ }
+
+ private:
+ bool GetVarImpl(const char* variable_name, std::string* result) {
+#if defined(OS_POSIX)
+ const char* env_value = getenv(variable_name);
+ if (!env_value)
+ return false;
+ // Note that the variable may be defined but empty.
+ if (result)
+ *result = env_value;
+ return true;
+#elif defined(OS_WIN)
+ DWORD value_length = ::GetEnvironmentVariable(
+ UTF8ToWide(variable_name).c_str(), NULL, 0);
+ if (value_length == 0)
+ return false;
+ if (result) {
+ scoped_array<wchar_t> value(new wchar_t[value_length]);
+ ::GetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), value.get(),
+ value_length);
+ *result = WideToUTF8(value.get());
+ }
+ return true;
+#else
+#error need to port
+#endif
+ }
+
+ bool SetVarImpl(const char* variable_name, const std::string& new_value) {
+#if defined(OS_POSIX)
+ // On success, zero is returned.
+ return !setenv(variable_name, new_value.c_str(), 1);
+#elif defined(OS_WIN)
+ // On success, a nonzero value is returned.
+ return !!SetEnvironmentVariable(UTF8ToWide(variable_name).c_str(),
+ UTF8ToWide(new_value).c_str());
+#endif
+ }
+
+ bool UnSetVarImpl(const char* variable_name) {
+#if defined(OS_POSIX)
+ // On success, zero is returned.
+ return !unsetenv(variable_name);
+#elif defined(OS_WIN)
+ // On success, a nonzero value is returned.
+ return !!SetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), NULL);
+#endif
+ }
+};
+
+} // namespace
+
+namespace base {
+
+namespace env_vars {
+
+#if defined(OS_POSIX)
+// On Posix systems, this variable contains the location of the user's home
+// directory. (e.g, /home/username/).
+const char kHome[] = "HOME";
+#endif
+
+} // namespace env_vars
+
+Environment::~Environment() {}
+
+// static
+Environment* Environment::Create() {
+ return new EnvironmentImpl();
+}
+
+bool Environment::HasVar(const char* variable_name) {
+ return GetVar(variable_name, NULL);
+}
+
+} // namespace base
diff --git a/src/base/environment.h b/src/base/environment.h
new file mode 100644
index 0000000..5160ff2
--- /dev/null
+++ b/src/base/environment.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ENVIRONMENT_H_
+#define BASE_ENVIRONMENT_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "build/build_config.h"
+
+namespace base {
+
+namespace env_vars {
+
+#if defined(OS_POSIX)
+BASE_EXPORT extern const char kHome[];
+#endif
+
+} // namespace env_vars
+
+class BASE_EXPORT Environment {
+ public:
+ virtual ~Environment();
+
+ // Static factory method that returns the implementation that provide the
+ // appropriate platform-specific instance.
+ static Environment* Create();
+
+ // Gets an environment variable's value and stores it in |result|.
+ // Returns false if the key is unset.
+ virtual bool GetVar(const char* variable_name, std::string* result) = 0;
+
+ // Syntactic sugar for GetVar(variable_name, NULL);
+ virtual bool HasVar(const char* variable_name);
+
+ // Returns true on success, otherwise returns false.
+ virtual bool SetVar(const char* variable_name,
+ const std::string& new_value) = 0;
+
+ // Returns true on success, otherwise returns false.
+ virtual bool UnSetVar(const char* variable_name) = 0;
+};
+
+} // namespace base
+
+#endif // BASE_ENVIRONMENT_H_
diff --git a/src/base/environment_unittest.cc b/src/base/environment_unittest.cc
new file mode 100644
index 0000000..b6654c9
--- /dev/null
+++ b/src/base/environment_unittest.cc
@@ -0,0 +1,85 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/environment.h"
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+typedef PlatformTest EnvironmentTest;
+
+TEST_F(EnvironmentTest, GetVar) {
+ // Every setup should have non-empty PATH...
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ std::string env_value;
+ EXPECT_TRUE(env->GetVar("PATH", &env_value));
+ EXPECT_NE(env_value, "");
+}
+
+TEST_F(EnvironmentTest, GetVarReverse) {
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ const char* kFooUpper = "FOO";
+ const char* kFooLower = "foo";
+
+ // Set a variable in UPPER case.
+ EXPECT_TRUE(env->SetVar(kFooUpper, kFooLower));
+
+ // And then try to get this variable passing the lower case.
+ std::string env_value;
+ EXPECT_TRUE(env->GetVar(kFooLower, &env_value));
+
+ EXPECT_STREQ(env_value.c_str(), kFooLower);
+
+ EXPECT_TRUE(env->UnSetVar(kFooUpper));
+
+ const char* kBar = "bar";
+ // Now do the opposite, set the variable in the lower case.
+ EXPECT_TRUE(env->SetVar(kFooLower, kBar));
+
+ // And then try to get this variable passing the UPPER case.
+ EXPECT_TRUE(env->GetVar(kFooUpper, &env_value));
+
+ EXPECT_STREQ(env_value.c_str(), kBar);
+
+ EXPECT_TRUE(env->UnSetVar(kFooLower));
+}
+
+TEST_F(EnvironmentTest, HasVar) {
+ // Every setup should have PATH...
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ EXPECT_TRUE(env->HasVar("PATH"));
+}
+
+TEST_F(EnvironmentTest, SetVar) {
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+
+ const char* kFooUpper = "FOO";
+ const char* kFooLower = "foo";
+ EXPECT_TRUE(env->SetVar(kFooUpper, kFooLower));
+
+ // Now verify that the environment has the new variable.
+ EXPECT_TRUE(env->HasVar(kFooUpper));
+
+ std::string var_value;
+ EXPECT_TRUE(env->GetVar(kFooUpper, &var_value));
+ EXPECT_EQ(var_value, kFooLower);
+}
+
+TEST_F(EnvironmentTest, UnSetVar) {
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+
+ const char* kFooUpper = "FOO";
+ const char* kFooLower = "foo";
+ // First set some environment variable.
+ EXPECT_TRUE(env->SetVar(kFooUpper, kFooLower));
+
+ // Now verify that the environment has the new variable.
+ EXPECT_TRUE(env->HasVar(kFooUpper));
+
+ // Finally verify that the environment variable was erased.
+ EXPECT_TRUE(env->UnSetVar(kFooUpper));
+
+ // And check that the variable has been unset.
+ EXPECT_FALSE(env->HasVar(kFooUpper));
+}
diff --git a/src/base/event_recorder.h b/src/base/event_recorder.h
new file mode 100644
index 0000000..32e1124
--- /dev/null
+++ b/src/base/event_recorder.h
@@ -0,0 +1,109 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_EVENT_RECORDER_H_
+#define BASE_EVENT_RECORDER_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <stdio.h>
+#include <string.h>
+#include <windows.h>
+#endif
+
+class FilePath;
+
+namespace base {
+
+// A class for recording and playing back keyboard and mouse input events.
+//
+// Note - if you record events, and the playback with the windows in
+// different sizes or positions, the playback will fail. When
+// recording and playing, you should move the relevant windows
+// to constant sizes and locations.
+// TODO(mbelshe) For now this is a singleton. I believe that this class
+// could be easily modified to:
+// support two simultaneous recorders
+// be playing back events while already recording events.
+// Why? Imagine if the product had a "record a macro" feature.
+// You might be recording globally, while recording or playing back
+// a macro. I don't think two playbacks make sense.
+class BASE_EXPORT EventRecorder {
+ public:
+ // Get the singleton EventRecorder.
+ // We can only handle one recorder/player at a time.
+ static EventRecorder* current() {
+ if (!current_)
+ current_ = new EventRecorder();
+ return current_;
+ }
+
+ // Starts recording events.
+ // Will clobber the file if it already exists.
+ // Returns true on success, or false if an error occurred.
+ bool StartRecording(const FilePath& filename);
+
+ // Stops recording.
+ void StopRecording();
+
+ // Is the EventRecorder currently recording.
+ bool is_recording() const { return is_recording_; }
+
+ // Plays events previously recorded.
+ // Returns true on success, or false if an error occurred.
+ bool StartPlayback(const FilePath& filename);
+
+ // Stops playback.
+ void StopPlayback();
+
+ // Is the EventRecorder currently playing.
+ bool is_playing() const { return is_playing_; }
+
+#if defined(OS_WIN)
+ // C-style callbacks for the EventRecorder.
+ // Used for internal purposes only.
+ LRESULT RecordWndProc(int nCode, WPARAM wParam, LPARAM lParam);
+ LRESULT PlaybackWndProc(int nCode, WPARAM wParam, LPARAM lParam);
+#endif
+
+ private:
+ // Create a new EventRecorder. Events are saved to the file filename.
+ // If the file already exists, it will be deleted before recording
+ // starts.
+ explicit EventRecorder()
+ : is_recording_(false),
+ is_playing_(false),
+#if defined(OS_WIN)
+ journal_hook_(NULL),
+ file_(NULL),
+#endif
+ playback_first_msg_time_(0),
+ playback_start_time_(0) {
+#if defined(OS_WIN)
+ memset(&playback_msg_, 0, sizeof(playback_msg_));
+#endif
+ }
+ ~EventRecorder();
+
+ static EventRecorder* current_; // Our singleton.
+
+ bool is_recording_;
+ bool is_playing_;
+#if defined(OS_WIN)
+ HHOOK journal_hook_;
+ FILE* file_;
+ EVENTMSG playback_msg_;
+#endif
+ int playback_first_msg_time_;
+ int playback_start_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(EventRecorder);
+};
+
+} // namespace base
+
+#endif // BASE_EVENT_RECORDER_H_
diff --git a/src/base/event_recorder_stubs.cc b/src/base/event_recorder_stubs.cc
new file mode 100644
index 0000000..91f2e07
--- /dev/null
+++ b/src/base/event_recorder_stubs.cc
@@ -0,0 +1,28 @@
+// 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/event_recorder.h"
+
+// This file implements a link stub for EventRecorder that can be used on
+// platforms that don't have a working EventRecorder implementation.
+
+namespace base {
+
+EventRecorder* EventRecorder::current_; // Our singleton.
+
+bool EventRecorder::StartRecording(const FilePath& filename) {
+ return true;
+}
+
+void EventRecorder::StopRecording() {
+}
+
+bool EventRecorder::StartPlayback(const FilePath& filename) {
+ return false;
+}
+
+void EventRecorder::StopPlayback() {
+}
+
+} // namespace
diff --git a/src/base/event_recorder_win.cc b/src/base/event_recorder_win.cc
new file mode 100644
index 0000000..11bf0f0
--- /dev/null
+++ b/src/base/event_recorder_win.cc
@@ -0,0 +1,258 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.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) {
+ DCHECK(EventRecorder::current());
+ return EventRecorder::current()->RecordWndProc(nCode, wParam, lParam);
+}
+
+LRESULT CALLBACK StaticPlaybackWndProc(int nCode, WPARAM wParam,
+ LPARAM lParam) {
+ DCHECK(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_);
+ 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_);
+ 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
diff --git a/src/base/event_types.h b/src/base/event_types.h
new file mode 100644
index 0000000..af586e4
--- /dev/null
+++ b/src/base/event_types.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 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_EVENT_TYPES_H
+#define BASE_EVENT_TYPES_H
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(USE_X11)
+typedef union _XEvent XEvent;
+#elif defined(OS_MACOSX)
+#if defined(__OBJC__)
+@class NSEvent;
+#else // __OBJC__
+class NSEvent;
+#endif // __OBJC__
+#endif
+
+namespace base {
+
+// Cross platform typedefs for native event types.
+#if defined(OS_WIN)
+typedef MSG NativeEvent;
+#elif defined(USE_X11)
+typedef XEvent* NativeEvent;
+#elif defined(OS_MACOSX)
+typedef NSEvent* NativeEvent;
+#else
+typedef void* NativeEvent;
+#endif
+
+} // namespace base
+
+#endif // BASE_EVENT_TYPES_H
diff --git a/src/base/file_descriptor_posix.h b/src/base/file_descriptor_posix.h
new file mode 100644
index 0000000..abc0789
--- /dev/null
+++ b/src/base/file_descriptor_posix.h
@@ -0,0 +1,45 @@
+// 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.
+
+#ifndef BASE_FILE_DESCRIPTOR_POSIX_H_
+#define BASE_FILE_DESCRIPTOR_POSIX_H_
+
+namespace base {
+
+// -----------------------------------------------------------------------------
+// We introduct a special structure for file descriptors in order that we are
+// able to use template specialisation to special-case their handling.
+//
+// WARNING: (Chromium only) There are subtleties to consider if serialising
+// these objects over IPC. See comments in ipc/ipc_message_utils.h
+// above the template specialisation for this structure.
+// -----------------------------------------------------------------------------
+struct FileDescriptor {
+ FileDescriptor()
+ : fd(-1),
+ auto_close(false) { }
+
+ FileDescriptor(int ifd, bool iauto_close)
+ : fd(ifd),
+ auto_close(iauto_close) { }
+
+ bool operator==(const FileDescriptor& other) const {
+ return (fd == other.fd && auto_close == other.auto_close);
+ }
+
+ // A comparison operator so that we can use these as keys in a std::map.
+ bool operator<(const FileDescriptor& other) const {
+ return other.fd < fd;
+ }
+
+ int fd;
+ // If true, this file descriptor should be closed after it has been used. For
+ // example an IPC system might interpret this flag as indicating that the
+ // file descriptor it has been given should be closed after use.
+ bool auto_close;
+};
+
+} // namespace base
+
+#endif // BASE_FILE_DESCRIPTOR_POSIX_H_
diff --git a/src/base/file_path.cc b/src/base/file_path.cc
new file mode 100644
index 0000000..acada54
--- /dev/null
+++ b/src/base/file_path.cc
@@ -0,0 +1,1264 @@
+// Copyright (c) 2012 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/file_path.h"
+
+#include <string.h>
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/pickle.h"
+
+// These includes are just for the *Hack functions, and should be removed
+// when those functions are removed.
+#include "base/string_piece.h"
+#include "base/string_util.h"
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_cftyperef.h"
+#include "base/third_party/icu/icu_utf.h"
+#endif
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_MACOSX)
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("\\/");
+#else // FILE_PATH_USES_WIN_SEPARATORS
+const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/");
+#endif // FILE_PATH_USES_WIN_SEPARATORS
+
+const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL(".");
+const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL("..");
+
+const FilePath::CharType FilePath::kExtensionSeparator = FILE_PATH_LITERAL('.');
+
+typedef FilePath::StringType StringType;
+
+namespace {
+
+#if defined(FILE_PATH_USES_WIN_SEPARATORS) && defined(OS_POSIX)
+inline bool StartsWith(const std::string& str,
+ const std::string& search,
+ bool case_sensitive) {
+ return StartsWithASCII(str, search, case_sensitive);
+}
+#endif
+
+const char* kCommonDoubleExtensionSuffixes[] = { "gz", "z", "bz2" };
+const char* kCommonDoubleExtensions[] = { "user.js" };
+
+const FilePath::CharType kStringTerminator = FILE_PATH_LITERAL('\0');
+
+// If this FilePath contains a drive letter specification, returns the
+// position of the last character of the drive letter specification,
+// otherwise returns npos. This can only be true on Windows, when a pathname
+// begins with a letter followed by a colon. On other platforms, this always
+// returns npos.
+StringType::size_type FindDriveLetter(const StringType& path) {
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ // This is dependent on an ASCII-based character set, but that's a
+ // reasonable assumption. iswalpha can be too inclusive here.
+ if (path.length() >= 2 && path[1] == L':' &&
+ ((path[0] >= L'A' && path[0] <= L'Z') ||
+ (path[0] >= L'a' && path[0] <= L'z'))) {
+ return 1;
+ }
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+ return StringType::npos;
+}
+
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+bool EqualDriveLetterCaseInsensitive(const StringType& a,
+ const StringType& b) {
+ size_t a_letter_pos = FindDriveLetter(a);
+ size_t b_letter_pos = FindDriveLetter(b);
+
+ if (a_letter_pos == StringType::npos || b_letter_pos == StringType::npos)
+ return a == b;
+
+ StringType a_letter(a.substr(0, a_letter_pos + 1));
+ StringType b_letter(b.substr(0, b_letter_pos + 1));
+ if (!StartsWith(a_letter, b_letter, false))
+ return false;
+
+ StringType a_rest(a.substr(a_letter_pos + 1));
+ StringType b_rest(b.substr(b_letter_pos + 1));
+ return a_rest == b_rest;
+}
+#endif // defined(FILE_PATH_USES_DRIVE_LETTERS)
+
+bool IsPathAbsolute(const StringType& path) {
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ StringType::size_type letter = FindDriveLetter(path);
+ if (letter != StringType::npos) {
+ // Look for a separator right after the drive specification.
+ return path.length() > letter + 1 &&
+ FilePath::IsSeparator(path[letter + 1]);
+ }
+ // Look for a pair of leading separators.
+ return path.length() > 1 &&
+ FilePath::IsSeparator(path[0]) && FilePath::IsSeparator(path[1]);
+#else // FILE_PATH_USES_DRIVE_LETTERS
+ // Look for a separator in the first position.
+ return path.length() > 0 && FilePath::IsSeparator(path[0]);
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+}
+
+bool AreAllSeparators(const StringType& input) {
+ for (StringType::const_iterator it = input.begin();
+ it != input.end(); ++it) {
+ if (!FilePath::IsSeparator(*it))
+ return false;
+ }
+
+ return true;
+}
+
+// Find the position of the '.' that separates the extension from the rest
+// of the file name. The position is relative to BaseName(), not value().
+// This allows a second extension component of up to 4 characters when the
+// rightmost extension component is a common double extension (gz, bz2, Z).
+// For example, foo.tar.gz or foo.tar.Z would have extension components of
+// '.tar.gz' and '.tar.Z' respectively. Returns npos if it can't find an
+// extension.
+StringType::size_type ExtensionSeparatorPosition(const StringType& path) {
+ // Special case "." and ".."
+ if (path == FilePath::kCurrentDirectory || path == FilePath::kParentDirectory)
+ return StringType::npos;
+
+ const StringType::size_type last_dot =
+ path.rfind(FilePath::kExtensionSeparator);
+
+ // No extension, or the extension is the whole filename.
+ if (last_dot == StringType::npos || last_dot == 0U)
+ return last_dot;
+
+ const StringType::size_type penultimate_dot =
+ path.rfind(FilePath::kExtensionSeparator, last_dot - 1);
+ const StringType::size_type last_separator =
+ path.find_last_of(FilePath::kSeparators, last_dot - 1,
+ arraysize(FilePath::kSeparators) - 1);
+
+ if (penultimate_dot == StringType::npos ||
+ (last_separator != StringType::npos &&
+ penultimate_dot < last_separator)) {
+ return last_dot;
+ }
+
+ for (size_t i = 0; i < arraysize(kCommonDoubleExtensions); ++i) {
+ StringType extension(path, penultimate_dot + 1);
+ if (LowerCaseEqualsASCII(extension, kCommonDoubleExtensions[i]))
+ return penultimate_dot;
+ }
+
+ StringType extension(path, last_dot + 1);
+ for (size_t i = 0; i < arraysize(kCommonDoubleExtensionSuffixes); ++i) {
+ if (LowerCaseEqualsASCII(extension, kCommonDoubleExtensionSuffixes[i])) {
+ if ((last_dot - penultimate_dot) <= 5U &&
+ (last_dot - penultimate_dot) > 1U) {
+ return penultimate_dot;
+ }
+ }
+ }
+
+ return last_dot;
+}
+
+// Returns true if path is "", ".", or "..".
+bool IsEmptyOrSpecialCase(const StringType& path) {
+ // Special cases "", ".", and ".."
+ if (path.empty() || path == FilePath::kCurrentDirectory ||
+ path == FilePath::kParentDirectory) {
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace
+
+FilePath::FilePath() {
+}
+
+FilePath::FilePath(const FilePath& that) : path_(that.path_) {
+}
+
+FilePath::FilePath(const StringType& path) : path_(path) {
+ StringType::size_type nul_pos = path_.find(kStringTerminator);
+ if (nul_pos != StringType::npos)
+ path_.erase(nul_pos, StringType::npos);
+}
+
+FilePath::~FilePath() {
+}
+
+FilePath& FilePath::operator=(const FilePath& that) {
+ path_ = that.path_;
+ return *this;
+}
+
+bool FilePath::operator==(const FilePath& that) const {
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ return EqualDriveLetterCaseInsensitive(this->path_, that.path_);
+#else // defined(FILE_PATH_USES_DRIVE_LETTERS)
+ return path_ == that.path_;
+#endif // defined(FILE_PATH_USES_DRIVE_LETTERS)
+}
+
+bool FilePath::operator!=(const FilePath& that) const {
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ return !EqualDriveLetterCaseInsensitive(this->path_, that.path_);
+#else // defined(FILE_PATH_USES_DRIVE_LETTERS)
+ return path_ != that.path_;
+#endif // defined(FILE_PATH_USES_DRIVE_LETTERS)
+}
+
+// static
+bool FilePath::IsSeparator(CharType character) {
+ for (size_t i = 0; i < arraysize(kSeparators) - 1; ++i) {
+ if (character == kSeparators[i]) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void FilePath::GetComponents(std::vector<StringType>* components) const {
+ DCHECK(components);
+ if (!components)
+ return;
+ components->clear();
+ if (value().empty())
+ return;
+
+ std::vector<StringType> ret_val;
+ FilePath current = *this;
+ FilePath base;
+
+ // Capture path components.
+ while (current != current.DirName()) {
+ base = current.BaseName();
+ if (!AreAllSeparators(base.value()))
+ ret_val.push_back(base.value());
+ current = current.DirName();
+ }
+
+ // Capture root, if any.
+ base = current.BaseName();
+ if (!base.value().empty() && base.value() != kCurrentDirectory)
+ ret_val.push_back(current.BaseName().value());
+
+ // Capture drive letter, if any.
+ FilePath dir = current.DirName();
+ StringType::size_type letter = FindDriveLetter(dir.value());
+ if (letter != StringType::npos) {
+ ret_val.push_back(StringType(dir.value(), 0, letter + 1));
+ }
+
+ *components = std::vector<StringType>(ret_val.rbegin(), ret_val.rend());
+}
+
+bool FilePath::IsParent(const FilePath& child) const {
+ return AppendRelativePath(child, NULL);
+}
+
+bool FilePath::AppendRelativePath(const FilePath& child,
+ FilePath* path) const {
+ std::vector<StringType> parent_components;
+ std::vector<StringType> child_components;
+ GetComponents(&parent_components);
+ child.GetComponents(&child_components);
+
+ if (parent_components.empty() ||
+ parent_components.size() >= child_components.size())
+ return false;
+
+ std::vector<StringType>::const_iterator parent_comp =
+ parent_components.begin();
+ std::vector<StringType>::const_iterator child_comp =
+ child_components.begin();
+
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ // Windows can access case sensitive filesystems, so component
+ // comparisions must be case sensitive, but drive letters are
+ // never case sensitive.
+ if ((FindDriveLetter(*parent_comp) != StringType::npos) &&
+ (FindDriveLetter(*child_comp) != StringType::npos)) {
+ if (!StartsWith(*parent_comp, *child_comp, false))
+ return false;
+ ++parent_comp;
+ ++child_comp;
+ }
+#endif // defined(FILE_PATH_USES_DRIVE_LETTERS)
+
+ while (parent_comp != parent_components.end()) {
+ if (*parent_comp != *child_comp)
+ return false;
+ ++parent_comp;
+ ++child_comp;
+ }
+
+ if (path != NULL) {
+ for (; child_comp != child_components.end(); ++child_comp) {
+ *path = path->Append(*child_comp);
+ }
+ }
+ return true;
+}
+
+// libgen's dirname and basename aren't guaranteed to be thread-safe and aren't
+// guaranteed to not modify their input strings, and in fact are implemented
+// differently in this regard on different platforms. Don't use them, but
+// adhere to their behavior.
+FilePath FilePath::DirName() const {
+ FilePath new_path(path_);
+ new_path.StripTrailingSeparatorsInternal();
+
+ // The drive letter, if any, always needs to remain in the output. If there
+ // is no drive letter, as will always be the case on platforms which do not
+ // support drive letters, letter will be npos, or -1, so the comparisons and
+ // resizes below using letter will still be valid.
+ StringType::size_type letter = FindDriveLetter(new_path.path_);
+
+ StringType::size_type last_separator =
+ new_path.path_.find_last_of(kSeparators, StringType::npos,
+ arraysize(kSeparators) - 1);
+ if (last_separator == StringType::npos) {
+ // path_ is in the current directory.
+ new_path.path_.resize(letter + 1);
+ } else if (last_separator == letter + 1) {
+ // path_ is in the root directory.
+ new_path.path_.resize(letter + 2);
+ } else if (last_separator == letter + 2 &&
+ IsSeparator(new_path.path_[letter + 1])) {
+ // path_ is in "//" (possibly with a drive letter); leave the double
+ // separator intact indicating alternate root.
+ new_path.path_.resize(letter + 3);
+ } else if (last_separator != 0) {
+ // path_ is somewhere else, trim the basename.
+ new_path.path_.resize(last_separator);
+ }
+
+ new_path.StripTrailingSeparatorsInternal();
+ if (!new_path.path_.length())
+ new_path.path_ = kCurrentDirectory;
+
+ return new_path;
+}
+
+FilePath FilePath::BaseName() const {
+ FilePath new_path(path_);
+ new_path.StripTrailingSeparatorsInternal();
+
+ // The drive letter, if any, is always stripped.
+ StringType::size_type letter = FindDriveLetter(new_path.path_);
+ if (letter != StringType::npos) {
+ new_path.path_.erase(0, letter + 1);
+ }
+
+ // Keep everything after the final separator, but if the pathname is only
+ // one character and it's a separator, leave it alone.
+ StringType::size_type last_separator =
+ new_path.path_.find_last_of(kSeparators, StringType::npos,
+ arraysize(kSeparators) - 1);
+ if (last_separator != StringType::npos &&
+ last_separator < new_path.path_.length() - 1) {
+ new_path.path_.erase(0, last_separator + 1);
+ }
+
+ return new_path;
+}
+
+StringType FilePath::Extension() const {
+ FilePath base(BaseName());
+ const StringType::size_type dot = ExtensionSeparatorPosition(base.path_);
+ if (dot == StringType::npos)
+ return StringType();
+
+ return base.path_.substr(dot, StringType::npos);
+}
+
+FilePath FilePath::RemoveExtension() const {
+ if (Extension().empty())
+ return *this;
+
+ const StringType::size_type dot = ExtensionSeparatorPosition(path_);
+ if (dot == StringType::npos)
+ return *this;
+
+ return FilePath(path_.substr(0, dot));
+}
+
+FilePath FilePath::InsertBeforeExtension(const StringType& suffix) const {
+ if (suffix.empty())
+ return FilePath(path_);
+
+ if (IsEmptyOrSpecialCase(BaseName().value()))
+ return FilePath();
+
+ StringType ext = Extension();
+ StringType ret = RemoveExtension().value();
+ ret.append(suffix);
+ ret.append(ext);
+ return FilePath(ret);
+}
+
+FilePath FilePath::InsertBeforeExtensionASCII(const base::StringPiece& suffix)
+ const {
+ DCHECK(IsStringASCII(suffix));
+#if defined(OS_WIN)
+ return InsertBeforeExtension(ASCIIToUTF16(suffix.as_string()));
+#elif defined(OS_POSIX) || defined(OS_STARBOARD)
+ return InsertBeforeExtension(suffix.as_string());
+#endif
+}
+
+FilePath FilePath::AddExtension(const StringType& extension) const {
+ if (IsEmptyOrSpecialCase(BaseName().value()))
+ return FilePath();
+
+ // If the new extension is "" or ".", then just return the current FilePath.
+ if (extension.empty() || extension == StringType(1, kExtensionSeparator))
+ return *this;
+
+ StringType str = path_;
+ if (extension[0] != kExtensionSeparator &&
+ *(str.end() - 1) != kExtensionSeparator) {
+ str.append(1, kExtensionSeparator);
+ }
+ str.append(extension);
+ return FilePath(str);
+}
+
+FilePath FilePath::ReplaceExtension(const StringType& extension) const {
+ if (IsEmptyOrSpecialCase(BaseName().value()))
+ return FilePath();
+
+ FilePath no_ext = RemoveExtension();
+ // If the new extension is "" or ".", then just remove the current extension.
+ if (extension.empty() || extension == StringType(1, kExtensionSeparator))
+ return no_ext;
+
+ StringType str = no_ext.value();
+ if (extension[0] != kExtensionSeparator)
+ str.append(1, kExtensionSeparator);
+ str.append(extension);
+ return FilePath(str);
+}
+
+bool FilePath::MatchesExtension(const StringType& extension) const {
+ DCHECK(extension.empty() || extension[0] == kExtensionSeparator);
+
+ StringType current_extension = Extension();
+
+ if (current_extension.length() != extension.length())
+ return false;
+
+ return FilePath::CompareEqualIgnoreCase(extension, current_extension);
+}
+
+FilePath FilePath::Append(const StringType& component) const {
+ const StringType* appended = &component;
+ StringType without_nuls;
+
+ StringType::size_type nul_pos = component.find(kStringTerminator);
+ if (nul_pos != StringType::npos) {
+ without_nuls = component.substr(0, nul_pos);
+ appended = &without_nuls;
+ }
+
+ DCHECK(!IsPathAbsolute(*appended));
+
+ if (path_.compare(kCurrentDirectory) == 0) {
+ // Append normally doesn't do any normalization, but as a special case,
+ // when appending to kCurrentDirectory, just return a new path for the
+ // component argument. Appending component to kCurrentDirectory would
+ // serve no purpose other than needlessly lengthening the path, and
+ // it's likely in practice to wind up with FilePath objects containing
+ // only kCurrentDirectory when calling DirName on a single relative path
+ // component.
+ return FilePath(*appended);
+ }
+
+ FilePath new_path(path_);
+ new_path.StripTrailingSeparatorsInternal();
+
+ // Don't append a separator if the path is empty (indicating the current
+ // directory) or if the path component is empty (indicating nothing to
+ // append).
+ if (appended->length() > 0 && new_path.path_.length() > 0) {
+ // Don't append a separator if the path still ends with a trailing
+ // separator after stripping (indicating the root directory).
+ if (!IsSeparator(new_path.path_[new_path.path_.length() - 1])) {
+ // Don't append a separator if the path is just a drive letter.
+ if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) {
+ new_path.path_.append(1, kSeparators[0]);
+ }
+ }
+ }
+
+ new_path.path_.append(*appended);
+ return new_path;
+}
+
+FilePath FilePath::Append(const FilePath& component) const {
+ return Append(component.value());
+}
+
+FilePath FilePath::AppendASCII(const base::StringPiece& component) const {
+ DCHECK(IsStringASCII(component));
+#if defined(OS_WIN)
+ return Append(ASCIIToUTF16(component.as_string()));
+#elif defined(OS_POSIX) || defined(OS_STARBOARD)
+ return Append(component.as_string());
+#endif
+}
+
+bool FilePath::IsAbsolute() const {
+ return IsPathAbsolute(path_);
+}
+
+FilePath FilePath::StripTrailingSeparators() const {
+ FilePath new_path(path_);
+ new_path.StripTrailingSeparatorsInternal();
+
+ return new_path;
+}
+
+bool FilePath::ReferencesParent() const {
+ std::vector<StringType> components;
+ GetComponents(&components);
+
+ std::vector<StringType>::const_iterator it = components.begin();
+ for (; it != components.end(); ++it) {
+ const StringType& component = *it;
+ if (component == kParentDirectory)
+ return true;
+ }
+ return false;
+}
+
+#if defined(OS_POSIX) || defined(OS_STARBOARD)
+// See file_path.h for a discussion of the encoding of paths on POSIX
+// platforms. These encoding conversion functions are not quite correct.
+
+string16 FilePath::LossyDisplayName() const {
+ return WideToUTF16(base::SysNativeMBToWide(path_));
+}
+
+std::string FilePath::MaybeAsASCII() const {
+ if (IsStringASCII(path_))
+ return path_;
+ return "";
+}
+
+std::string FilePath::AsUTF8Unsafe() const {
+#if defined(OS_MACOSX) || defined(OS_CHROMEOS)
+ return value();
+#else
+ return WideToUTF8(base::SysNativeMBToWide(value()));
+#endif
+}
+
+// The *Hack functions are temporary while we fix the remainder of the code.
+// Remember to remove the #includes at the top when you remove these.
+
+// static
+FilePath FilePath::FromWStringHack(const std::wstring& wstring) {
+ return FilePath(base::SysWideToNativeMB(wstring));
+}
+
+// static
+FilePath FilePath::FromUTF8Unsafe(const std::string& utf8) {
+#if defined(OS_MACOSX) || defined(OS_CHROMEOS)
+ return FilePath(utf8);
+#else
+ return FilePath(base::SysWideToNativeMB(UTF8ToWide(utf8)));
+#endif
+}
+
+#elif defined(OS_WIN)
+string16 FilePath::LossyDisplayName() const {
+ return path_;
+}
+
+std::string FilePath::MaybeAsASCII() const {
+ if (IsStringASCII(path_))
+ return WideToASCII(path_);
+ return "";
+}
+
+std::string FilePath::AsUTF8Unsafe() const {
+ return WideToUTF8(value());
+}
+
+// static
+FilePath FilePath::FromWStringHack(const std::wstring& wstring) {
+ return FilePath(wstring);
+}
+
+// static
+FilePath FilePath::FromUTF8Unsafe(const std::string& utf8) {
+ return FilePath(UTF8ToWide(utf8));
+}
+#endif
+
+void FilePath::WriteToPickle(Pickle* pickle) const {
+#if defined(OS_WIN)
+ pickle->WriteString16(path_);
+#else
+ pickle->WriteString(path_);
+#endif
+}
+
+bool FilePath::ReadFromPickle(PickleIterator* iter) {
+#if defined(OS_WIN)
+ if (!iter->ReadString16(&path_))
+ return false;
+#else
+ if (!iter->ReadString(&path_))
+ return false;
+#endif
+
+ if (path_.find(kStringTerminator) != StringType::npos)
+ return false;
+
+ return true;
+}
+
+#if defined(OS_WIN)
+// Windows specific implementation of file string comparisons
+
+int FilePath::CompareIgnoreCase(const StringType& string1,
+ const StringType& string2) {
+ // Perform character-wise upper case comparison rather than using the
+ // fully Unicode-aware CompareString(). For details see:
+ // http://blogs.msdn.com/michkap/archive/2005/10/17/481600.aspx
+ StringType::const_iterator i1 = string1.begin();
+ StringType::const_iterator i2 = string2.begin();
+ StringType::const_iterator string1end = string1.end();
+ StringType::const_iterator string2end = string2.end();
+ for ( ; i1 != string1end && i2 != string2end; ++i1, ++i2) {
+ wchar_t c1 = (wchar_t)LOWORD(::CharUpperW((LPWSTR)MAKELONG(*i1, 0)));
+ wchar_t c2 = (wchar_t)LOWORD(::CharUpperW((LPWSTR)MAKELONG(*i2, 0)));
+ if (c1 < c2)
+ return -1;
+ if (c1 > c2)
+ return 1;
+ }
+ if (i1 != string1end)
+ return 1;
+ if (i2 != string2end)
+ return -1;
+ return 0;
+}
+
+#elif defined(OS_MACOSX)
+// Mac OS X specific implementation of file string comparisons
+
+// cf. http://developer.apple.com/mac/library/technotes/tn/tn1150.html#UnicodeSubtleties
+//
+// "When using CreateTextEncoding to create a text encoding, you should set
+// the TextEncodingBase to kTextEncodingUnicodeV2_0, set the
+// TextEncodingVariant to kUnicodeCanonicalDecompVariant, and set the
+// TextEncodingFormat to kUnicode16BitFormat. Using these values ensures that
+// the Unicode will be in the same form as on an HFS Plus volume, even as the
+// Unicode standard evolves."
+//
+// Another technical article for X 10.4 updates this: one should use
+// the new (unambiguous) kUnicodeHFSPlusDecompVariant.
+// cf. http://developer.apple.com/mac/library/releasenotes/TextFonts/RN-TEC/index.html
+//
+// This implementation uses CFStringGetFileSystemRepresentation() to get the
+// decomposed form, and an adapted version of the FastUnicodeCompare as
+// described in the tech note to compare the strings.
+
+// Character conversion table for FastUnicodeCompare()
+//
+// The lower case table consists of a 256-entry high-byte table followed by
+// some number of 256-entry subtables. The high-byte table contains either an
+// offset to the subtable for characters with that high byte or zero, which
+// means that there are no case mappings or ignored characters in that block.
+// Ignored characters are mapped to zero.
+//
+// cf. downloadable file linked in
+// http://developer.apple.com/mac/library/technotes/tn/tn1150.html#StringComparisonAlgorithm
+
+namespace {
+
+const UInt16 lower_case_table[] = {
+ // High-byte indices ( == 0 iff no case mapping and no ignorables )
+
+ /* 0 */ 0x0100, 0x0200, 0x0000, 0x0300, 0x0400, 0x0500, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 1 */ 0x0600, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 2 */ 0x0700, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 3 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 4 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 5 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 6 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 7 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 8 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 9 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* A */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* B */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* C */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* D */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* E */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* F */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0900, 0x0A00,
+
+ // Table 1 (for high byte 0x00)
+
+ /* 0 */ 0xFFFF, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ /* 1 */ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ /* 2 */ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ /* 3 */ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ /* 4 */ 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ /* 5 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ /* 6 */ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ /* 7 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ /* 8 */ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
+ 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ /* 9 */ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
+ 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ /* A */ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+ 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+ /* B */ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+ 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+ /* C */ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00E6, 0x00C7,
+ 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ /* D */ 0x00F0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
+ 0x00F8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00FE, 0x00DF,
+ /* E */ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+ 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ /* F */ 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
+ 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF,
+
+ // Table 2 (for high byte 0x01)
+
+ /* 0 */ 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107,
+ 0x0108, 0x0109, 0x010A, 0x010B, 0x010C, 0x010D, 0x010E, 0x010F,
+ /* 1 */ 0x0111, 0x0111, 0x0112, 0x0113, 0x0114, 0x0115, 0x0116, 0x0117,
+ 0x0118, 0x0119, 0x011A, 0x011B, 0x011C, 0x011D, 0x011E, 0x011F,
+ /* 2 */ 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, 0x0125, 0x0127, 0x0127,
+ 0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, 0x012F,
+ /* 3 */ 0x0130, 0x0131, 0x0133, 0x0133, 0x0134, 0x0135, 0x0136, 0x0137,
+ 0x0138, 0x0139, 0x013A, 0x013B, 0x013C, 0x013D, 0x013E, 0x0140,
+ /* 4 */ 0x0140, 0x0142, 0x0142, 0x0143, 0x0144, 0x0145, 0x0146, 0x0147,
+ 0x0148, 0x0149, 0x014B, 0x014B, 0x014C, 0x014D, 0x014E, 0x014F,
+ /* 5 */ 0x0150, 0x0151, 0x0153, 0x0153, 0x0154, 0x0155, 0x0156, 0x0157,
+ 0x0158, 0x0159, 0x015A, 0x015B, 0x015C, 0x015D, 0x015E, 0x015F,
+ /* 6 */ 0x0160, 0x0161, 0x0162, 0x0163, 0x0164, 0x0165, 0x0167, 0x0167,
+ 0x0168, 0x0169, 0x016A, 0x016B, 0x016C, 0x016D, 0x016E, 0x016F,
+ /* 7 */ 0x0170, 0x0171, 0x0172, 0x0173, 0x0174, 0x0175, 0x0176, 0x0177,
+ 0x0178, 0x0179, 0x017A, 0x017B, 0x017C, 0x017D, 0x017E, 0x017F,
+ /* 8 */ 0x0180, 0x0253, 0x0183, 0x0183, 0x0185, 0x0185, 0x0254, 0x0188,
+ 0x0188, 0x0256, 0x0257, 0x018C, 0x018C, 0x018D, 0x01DD, 0x0259,
+ /* 9 */ 0x025B, 0x0192, 0x0192, 0x0260, 0x0263, 0x0195, 0x0269, 0x0268,
+ 0x0199, 0x0199, 0x019A, 0x019B, 0x026F, 0x0272, 0x019E, 0x0275,
+ /* A */ 0x01A0, 0x01A1, 0x01A3, 0x01A3, 0x01A5, 0x01A5, 0x01A6, 0x01A8,
+ 0x01A8, 0x0283, 0x01AA, 0x01AB, 0x01AD, 0x01AD, 0x0288, 0x01AF,
+ /* B */ 0x01B0, 0x028A, 0x028B, 0x01B4, 0x01B4, 0x01B6, 0x01B6, 0x0292,
+ 0x01B9, 0x01B9, 0x01BA, 0x01BB, 0x01BD, 0x01BD, 0x01BE, 0x01BF,
+ /* C */ 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C6, 0x01C6, 0x01C6, 0x01C9,
+ 0x01C9, 0x01C9, 0x01CC, 0x01CC, 0x01CC, 0x01CD, 0x01CE, 0x01CF,
+ /* D */ 0x01D0, 0x01D1, 0x01D2, 0x01D3, 0x01D4, 0x01D5, 0x01D6, 0x01D7,
+ 0x01D8, 0x01D9, 0x01DA, 0x01DB, 0x01DC, 0x01DD, 0x01DE, 0x01DF,
+ /* E */ 0x01E0, 0x01E1, 0x01E2, 0x01E3, 0x01E5, 0x01E5, 0x01E6, 0x01E7,
+ 0x01E8, 0x01E9, 0x01EA, 0x01EB, 0x01EC, 0x01ED, 0x01EE, 0x01EF,
+ /* F */ 0x01F0, 0x01F3, 0x01F3, 0x01F3, 0x01F4, 0x01F5, 0x01F6, 0x01F7,
+ 0x01F8, 0x01F9, 0x01FA, 0x01FB, 0x01FC, 0x01FD, 0x01FE, 0x01FF,
+
+ // Table 3 (for high byte 0x03)
+
+ /* 0 */ 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307,
+ 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F,
+ /* 1 */ 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317,
+ 0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F,
+ /* 2 */ 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327,
+ 0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F,
+ /* 3 */ 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337,
+ 0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F,
+ /* 4 */ 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347,
+ 0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F,
+ /* 5 */ 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357,
+ 0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F,
+ /* 6 */ 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367,
+ 0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F,
+ /* 7 */ 0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377,
+ 0x0378, 0x0379, 0x037A, 0x037B, 0x037C, 0x037D, 0x037E, 0x037F,
+ /* 8 */ 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0386, 0x0387,
+ 0x0388, 0x0389, 0x038A, 0x038B, 0x038C, 0x038D, 0x038E, 0x038F,
+ /* 9 */ 0x0390, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
+ 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
+ /* A */ 0x03C0, 0x03C1, 0x03A2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,
+ 0x03C8, 0x03C9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
+ /* B */ 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
+ 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
+ /* C */ 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,
+ 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x03CF,
+ /* D */ 0x03D0, 0x03D1, 0x03D2, 0x03D3, 0x03D4, 0x03D5, 0x03D6, 0x03D7,
+ 0x03D8, 0x03D9, 0x03DA, 0x03DB, 0x03DC, 0x03DD, 0x03DE, 0x03DF,
+ /* E */ 0x03E0, 0x03E1, 0x03E3, 0x03E3, 0x03E5, 0x03E5, 0x03E7, 0x03E7,
+ 0x03E9, 0x03E9, 0x03EB, 0x03EB, 0x03ED, 0x03ED, 0x03EF, 0x03EF,
+ /* F */ 0x03F0, 0x03F1, 0x03F2, 0x03F3, 0x03F4, 0x03F5, 0x03F6, 0x03F7,
+ 0x03F8, 0x03F9, 0x03FA, 0x03FB, 0x03FC, 0x03FD, 0x03FE, 0x03FF,
+
+ // Table 4 (for high byte 0x04)
+
+ /* 0 */ 0x0400, 0x0401, 0x0452, 0x0403, 0x0454, 0x0455, 0x0456, 0x0407,
+ 0x0458, 0x0459, 0x045A, 0x045B, 0x040C, 0x040D, 0x040E, 0x045F,
+ /* 1 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
+ 0x0438, 0x0419, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+ /* 2 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
+ 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+ /* 3 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
+ 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+ /* 4 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
+ 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+ /* 5 */ 0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457,
+ 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x045D, 0x045E, 0x045F,
+ /* 6 */ 0x0461, 0x0461, 0x0463, 0x0463, 0x0465, 0x0465, 0x0467, 0x0467,
+ 0x0469, 0x0469, 0x046B, 0x046B, 0x046D, 0x046D, 0x046F, 0x046F,
+ /* 7 */ 0x0471, 0x0471, 0x0473, 0x0473, 0x0475, 0x0475, 0x0476, 0x0477,
+ 0x0479, 0x0479, 0x047B, 0x047B, 0x047D, 0x047D, 0x047F, 0x047F,
+ /* 8 */ 0x0481, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487,
+ 0x0488, 0x0489, 0x048A, 0x048B, 0x048C, 0x048D, 0x048E, 0x048F,
+ /* 9 */ 0x0491, 0x0491, 0x0493, 0x0493, 0x0495, 0x0495, 0x0497, 0x0497,
+ 0x0499, 0x0499, 0x049B, 0x049B, 0x049D, 0x049D, 0x049F, 0x049F,
+ /* A */ 0x04A1, 0x04A1, 0x04A3, 0x04A3, 0x04A5, 0x04A5, 0x04A7, 0x04A7,
+ 0x04A9, 0x04A9, 0x04AB, 0x04AB, 0x04AD, 0x04AD, 0x04AF, 0x04AF,
+ /* B */ 0x04B1, 0x04B1, 0x04B3, 0x04B3, 0x04B5, 0x04B5, 0x04B7, 0x04B7,
+ 0x04B9, 0x04B9, 0x04BB, 0x04BB, 0x04BD, 0x04BD, 0x04BF, 0x04BF,
+ /* C */ 0x04C0, 0x04C1, 0x04C2, 0x04C4, 0x04C4, 0x04C5, 0x04C6, 0x04C8,
+ 0x04C8, 0x04C9, 0x04CA, 0x04CC, 0x04CC, 0x04CD, 0x04CE, 0x04CF,
+ /* D */ 0x04D0, 0x04D1, 0x04D2, 0x04D3, 0x04D4, 0x04D5, 0x04D6, 0x04D7,
+ 0x04D8, 0x04D9, 0x04DA, 0x04DB, 0x04DC, 0x04DD, 0x04DE, 0x04DF,
+ /* E */ 0x04E0, 0x04E1, 0x04E2, 0x04E3, 0x04E4, 0x04E5, 0x04E6, 0x04E7,
+ 0x04E8, 0x04E9, 0x04EA, 0x04EB, 0x04EC, 0x04ED, 0x04EE, 0x04EF,
+ /* F */ 0x04F0, 0x04F1, 0x04F2, 0x04F3, 0x04F4, 0x04F5, 0x04F6, 0x04F7,
+ 0x04F8, 0x04F9, 0x04FA, 0x04FB, 0x04FC, 0x04FD, 0x04FE, 0x04FF,
+
+ // Table 5 (for high byte 0x05)
+
+ /* 0 */ 0x0500, 0x0501, 0x0502, 0x0503, 0x0504, 0x0505, 0x0506, 0x0507,
+ 0x0508, 0x0509, 0x050A, 0x050B, 0x050C, 0x050D, 0x050E, 0x050F,
+ /* 1 */ 0x0510, 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0516, 0x0517,
+ 0x0518, 0x0519, 0x051A, 0x051B, 0x051C, 0x051D, 0x051E, 0x051F,
+ /* 2 */ 0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527,
+ 0x0528, 0x0529, 0x052A, 0x052B, 0x052C, 0x052D, 0x052E, 0x052F,
+ /* 3 */ 0x0530, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,
+ 0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F,
+ /* 4 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577,
+ 0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F,
+ /* 5 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0557,
+ 0x0558, 0x0559, 0x055A, 0x055B, 0x055C, 0x055D, 0x055E, 0x055F,
+ /* 6 */ 0x0560, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,
+ 0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F,
+ /* 7 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577,
+ 0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F,
+ /* 8 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0587,
+ 0x0588, 0x0589, 0x058A, 0x058B, 0x058C, 0x058D, 0x058E, 0x058F,
+ /* 9 */ 0x0590, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597,
+ 0x0598, 0x0599, 0x059A, 0x059B, 0x059C, 0x059D, 0x059E, 0x059F,
+ /* A */ 0x05A0, 0x05A1, 0x05A2, 0x05A3, 0x05A4, 0x05A5, 0x05A6, 0x05A7,
+ 0x05A8, 0x05A9, 0x05AA, 0x05AB, 0x05AC, 0x05AD, 0x05AE, 0x05AF,
+ /* B */ 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7,
+ 0x05B8, 0x05B9, 0x05BA, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF,
+ /* C */ 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05C4, 0x05C5, 0x05C6, 0x05C7,
+ 0x05C8, 0x05C9, 0x05CA, 0x05CB, 0x05CC, 0x05CD, 0x05CE, 0x05CF,
+ /* D */ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,
+ 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
+ /* E */ 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,
+ 0x05E8, 0x05E9, 0x05EA, 0x05EB, 0x05EC, 0x05ED, 0x05EE, 0x05EF,
+ /* F */ 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0x05F5, 0x05F6, 0x05F7,
+ 0x05F8, 0x05F9, 0x05FA, 0x05FB, 0x05FC, 0x05FD, 0x05FE, 0x05FF,
+
+ // Table 6 (for high byte 0x10)
+
+ /* 0 */ 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007,
+ 0x1008, 0x1009, 0x100A, 0x100B, 0x100C, 0x100D, 0x100E, 0x100F,
+ /* 1 */ 0x1010, 0x1011, 0x1012, 0x1013, 0x1014, 0x1015, 0x1016, 0x1017,
+ 0x1018, 0x1019, 0x101A, 0x101B, 0x101C, 0x101D, 0x101E, 0x101F,
+ /* 2 */ 0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027,
+ 0x1028, 0x1029, 0x102A, 0x102B, 0x102C, 0x102D, 0x102E, 0x102F,
+ /* 3 */ 0x1030, 0x1031, 0x1032, 0x1033, 0x1034, 0x1035, 0x1036, 0x1037,
+ 0x1038, 0x1039, 0x103A, 0x103B, 0x103C, 0x103D, 0x103E, 0x103F,
+ /* 4 */ 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047,
+ 0x1048, 0x1049, 0x104A, 0x104B, 0x104C, 0x104D, 0x104E, 0x104F,
+ /* 5 */ 0x1050, 0x1051, 0x1052, 0x1053, 0x1054, 0x1055, 0x1056, 0x1057,
+ 0x1058, 0x1059, 0x105A, 0x105B, 0x105C, 0x105D, 0x105E, 0x105F,
+ /* 6 */ 0x1060, 0x1061, 0x1062, 0x1063, 0x1064, 0x1065, 0x1066, 0x1067,
+ 0x1068, 0x1069, 0x106A, 0x106B, 0x106C, 0x106D, 0x106E, 0x106F,
+ /* 7 */ 0x1070, 0x1071, 0x1072, 0x1073, 0x1074, 0x1075, 0x1076, 0x1077,
+ 0x1078, 0x1079, 0x107A, 0x107B, 0x107C, 0x107D, 0x107E, 0x107F,
+ /* 8 */ 0x1080, 0x1081, 0x1082, 0x1083, 0x1084, 0x1085, 0x1086, 0x1087,
+ 0x1088, 0x1089, 0x108A, 0x108B, 0x108C, 0x108D, 0x108E, 0x108F,
+ /* 9 */ 0x1090, 0x1091, 0x1092, 0x1093, 0x1094, 0x1095, 0x1096, 0x1097,
+ 0x1098, 0x1099, 0x109A, 0x109B, 0x109C, 0x109D, 0x109E, 0x109F,
+ /* A */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7,
+ 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF,
+ /* B */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7,
+ 0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF,
+ /* C */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10C6, 0x10C7,
+ 0x10C8, 0x10C9, 0x10CA, 0x10CB, 0x10CC, 0x10CD, 0x10CE, 0x10CF,
+ /* D */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7,
+ 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF,
+ /* E */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7,
+ 0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF,
+ /* F */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10F6, 0x10F7,
+ 0x10F8, 0x10F9, 0x10FA, 0x10FB, 0x10FC, 0x10FD, 0x10FE, 0x10FF,
+
+ // Table 7 (for high byte 0x20)
+
+ /* 0 */ 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007,
+ 0x2008, 0x2009, 0x200A, 0x200B, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 1 */ 0x2010, 0x2011, 0x2012, 0x2013, 0x2014, 0x2015, 0x2016, 0x2017,
+ 0x2018, 0x2019, 0x201A, 0x201B, 0x201C, 0x201D, 0x201E, 0x201F,
+ /* 2 */ 0x2020, 0x2021, 0x2022, 0x2023, 0x2024, 0x2025, 0x2026, 0x2027,
+ 0x2028, 0x2029, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x202F,
+ /* 3 */ 0x2030, 0x2031, 0x2032, 0x2033, 0x2034, 0x2035, 0x2036, 0x2037,
+ 0x2038, 0x2039, 0x203A, 0x203B, 0x203C, 0x203D, 0x203E, 0x203F,
+ /* 4 */ 0x2040, 0x2041, 0x2042, 0x2043, 0x2044, 0x2045, 0x2046, 0x2047,
+ 0x2048, 0x2049, 0x204A, 0x204B, 0x204C, 0x204D, 0x204E, 0x204F,
+ /* 5 */ 0x2050, 0x2051, 0x2052, 0x2053, 0x2054, 0x2055, 0x2056, 0x2057,
+ 0x2058, 0x2059, 0x205A, 0x205B, 0x205C, 0x205D, 0x205E, 0x205F,
+ /* 6 */ 0x2060, 0x2061, 0x2062, 0x2063, 0x2064, 0x2065, 0x2066, 0x2067,
+ 0x2068, 0x2069, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 7 */ 0x2070, 0x2071, 0x2072, 0x2073, 0x2074, 0x2075, 0x2076, 0x2077,
+ 0x2078, 0x2079, 0x207A, 0x207B, 0x207C, 0x207D, 0x207E, 0x207F,
+ /* 8 */ 0x2080, 0x2081, 0x2082, 0x2083, 0x2084, 0x2085, 0x2086, 0x2087,
+ 0x2088, 0x2089, 0x208A, 0x208B, 0x208C, 0x208D, 0x208E, 0x208F,
+ /* 9 */ 0x2090, 0x2091, 0x2092, 0x2093, 0x2094, 0x2095, 0x2096, 0x2097,
+ 0x2098, 0x2099, 0x209A, 0x209B, 0x209C, 0x209D, 0x209E, 0x209F,
+ /* A */ 0x20A0, 0x20A1, 0x20A2, 0x20A3, 0x20A4, 0x20A5, 0x20A6, 0x20A7,
+ 0x20A8, 0x20A9, 0x20AA, 0x20AB, 0x20AC, 0x20AD, 0x20AE, 0x20AF,
+ /* B */ 0x20B0, 0x20B1, 0x20B2, 0x20B3, 0x20B4, 0x20B5, 0x20B6, 0x20B7,
+ 0x20B8, 0x20B9, 0x20BA, 0x20BB, 0x20BC, 0x20BD, 0x20BE, 0x20BF,
+ /* C */ 0x20C0, 0x20C1, 0x20C2, 0x20C3, 0x20C4, 0x20C5, 0x20C6, 0x20C7,
+ 0x20C8, 0x20C9, 0x20CA, 0x20CB, 0x20CC, 0x20CD, 0x20CE, 0x20CF,
+ /* D */ 0x20D0, 0x20D1, 0x20D2, 0x20D3, 0x20D4, 0x20D5, 0x20D6, 0x20D7,
+ 0x20D8, 0x20D9, 0x20DA, 0x20DB, 0x20DC, 0x20DD, 0x20DE, 0x20DF,
+ /* E */ 0x20E0, 0x20E1, 0x20E2, 0x20E3, 0x20E4, 0x20E5, 0x20E6, 0x20E7,
+ 0x20E8, 0x20E9, 0x20EA, 0x20EB, 0x20EC, 0x20ED, 0x20EE, 0x20EF,
+ /* F */ 0x20F0, 0x20F1, 0x20F2, 0x20F3, 0x20F4, 0x20F5, 0x20F6, 0x20F7,
+ 0x20F8, 0x20F9, 0x20FA, 0x20FB, 0x20FC, 0x20FD, 0x20FE, 0x20FF,
+
+ // Table 8 (for high byte 0x21)
+
+ /* 0 */ 0x2100, 0x2101, 0x2102, 0x2103, 0x2104, 0x2105, 0x2106, 0x2107,
+ 0x2108, 0x2109, 0x210A, 0x210B, 0x210C, 0x210D, 0x210E, 0x210F,
+ /* 1 */ 0x2110, 0x2111, 0x2112, 0x2113, 0x2114, 0x2115, 0x2116, 0x2117,
+ 0x2118, 0x2119, 0x211A, 0x211B, 0x211C, 0x211D, 0x211E, 0x211F,
+ /* 2 */ 0x2120, 0x2121, 0x2122, 0x2123, 0x2124, 0x2125, 0x2126, 0x2127,
+ 0x2128, 0x2129, 0x212A, 0x212B, 0x212C, 0x212D, 0x212E, 0x212F,
+ /* 3 */ 0x2130, 0x2131, 0x2132, 0x2133, 0x2134, 0x2135, 0x2136, 0x2137,
+ 0x2138, 0x2139, 0x213A, 0x213B, 0x213C, 0x213D, 0x213E, 0x213F,
+ /* 4 */ 0x2140, 0x2141, 0x2142, 0x2143, 0x2144, 0x2145, 0x2146, 0x2147,
+ 0x2148, 0x2149, 0x214A, 0x214B, 0x214C, 0x214D, 0x214E, 0x214F,
+ /* 5 */ 0x2150, 0x2151, 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157,
+ 0x2158, 0x2159, 0x215A, 0x215B, 0x215C, 0x215D, 0x215E, 0x215F,
+ /* 6 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177,
+ 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F,
+ /* 7 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177,
+ 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F,
+ /* 8 */ 0x2180, 0x2181, 0x2182, 0x2183, 0x2184, 0x2185, 0x2186, 0x2187,
+ 0x2188, 0x2189, 0x218A, 0x218B, 0x218C, 0x218D, 0x218E, 0x218F,
+ /* 9 */ 0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x2196, 0x2197,
+ 0x2198, 0x2199, 0x219A, 0x219B, 0x219C, 0x219D, 0x219E, 0x219F,
+ /* A */ 0x21A0, 0x21A1, 0x21A2, 0x21A3, 0x21A4, 0x21A5, 0x21A6, 0x21A7,
+ 0x21A8, 0x21A9, 0x21AA, 0x21AB, 0x21AC, 0x21AD, 0x21AE, 0x21AF,
+ /* B */ 0x21B0, 0x21B1, 0x21B2, 0x21B3, 0x21B4, 0x21B5, 0x21B6, 0x21B7,
+ 0x21B8, 0x21B9, 0x21BA, 0x21BB, 0x21BC, 0x21BD, 0x21BE, 0x21BF,
+ /* C */ 0x21C0, 0x21C1, 0x21C2, 0x21C3, 0x21C4, 0x21C5, 0x21C6, 0x21C7,
+ 0x21C8, 0x21C9, 0x21CA, 0x21CB, 0x21CC, 0x21CD, 0x21CE, 0x21CF,
+ /* D */ 0x21D0, 0x21D1, 0x21D2, 0x21D3, 0x21D4, 0x21D5, 0x21D6, 0x21D7,
+ 0x21D8, 0x21D9, 0x21DA, 0x21DB, 0x21DC, 0x21DD, 0x21DE, 0x21DF,
+ /* E */ 0x21E0, 0x21E1, 0x21E2, 0x21E3, 0x21E4, 0x21E5, 0x21E6, 0x21E7,
+ 0x21E8, 0x21E9, 0x21EA, 0x21EB, 0x21EC, 0x21ED, 0x21EE, 0x21EF,
+ /* F */ 0x21F0, 0x21F1, 0x21F2, 0x21F3, 0x21F4, 0x21F5, 0x21F6, 0x21F7,
+ 0x21F8, 0x21F9, 0x21FA, 0x21FB, 0x21FC, 0x21FD, 0x21FE, 0x21FF,
+
+ // Table 9 (for high byte 0xFE)
+
+ /* 0 */ 0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05, 0xFE06, 0xFE07,
+ 0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D, 0xFE0E, 0xFE0F,
+ /* 1 */ 0xFE10, 0xFE11, 0xFE12, 0xFE13, 0xFE14, 0xFE15, 0xFE16, 0xFE17,
+ 0xFE18, 0xFE19, 0xFE1A, 0xFE1B, 0xFE1C, 0xFE1D, 0xFE1E, 0xFE1F,
+ /* 2 */ 0xFE20, 0xFE21, 0xFE22, 0xFE23, 0xFE24, 0xFE25, 0xFE26, 0xFE27,
+ 0xFE28, 0xFE29, 0xFE2A, 0xFE2B, 0xFE2C, 0xFE2D, 0xFE2E, 0xFE2F,
+ /* 3 */ 0xFE30, 0xFE31, 0xFE32, 0xFE33, 0xFE34, 0xFE35, 0xFE36, 0xFE37,
+ 0xFE38, 0xFE39, 0xFE3A, 0xFE3B, 0xFE3C, 0xFE3D, 0xFE3E, 0xFE3F,
+ /* 4 */ 0xFE40, 0xFE41, 0xFE42, 0xFE43, 0xFE44, 0xFE45, 0xFE46, 0xFE47,
+ 0xFE48, 0xFE49, 0xFE4A, 0xFE4B, 0xFE4C, 0xFE4D, 0xFE4E, 0xFE4F,
+ /* 5 */ 0xFE50, 0xFE51, 0xFE52, 0xFE53, 0xFE54, 0xFE55, 0xFE56, 0xFE57,
+ 0xFE58, 0xFE59, 0xFE5A, 0xFE5B, 0xFE5C, 0xFE5D, 0xFE5E, 0xFE5F,
+ /* 6 */ 0xFE60, 0xFE61, 0xFE62, 0xFE63, 0xFE64, 0xFE65, 0xFE66, 0xFE67,
+ 0xFE68, 0xFE69, 0xFE6A, 0xFE6B, 0xFE6C, 0xFE6D, 0xFE6E, 0xFE6F,
+ /* 7 */ 0xFE70, 0xFE71, 0xFE72, 0xFE73, 0xFE74, 0xFE75, 0xFE76, 0xFE77,
+ 0xFE78, 0xFE79, 0xFE7A, 0xFE7B, 0xFE7C, 0xFE7D, 0xFE7E, 0xFE7F,
+ /* 8 */ 0xFE80, 0xFE81, 0xFE82, 0xFE83, 0xFE84, 0xFE85, 0xFE86, 0xFE87,
+ 0xFE88, 0xFE89, 0xFE8A, 0xFE8B, 0xFE8C, 0xFE8D, 0xFE8E, 0xFE8F,
+ /* 9 */ 0xFE90, 0xFE91, 0xFE92, 0xFE93, 0xFE94, 0xFE95, 0xFE96, 0xFE97,
+ 0xFE98, 0xFE99, 0xFE9A, 0xFE9B, 0xFE9C, 0xFE9D, 0xFE9E, 0xFE9F,
+ /* A */ 0xFEA0, 0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4, 0xFEA5, 0xFEA6, 0xFEA7,
+ 0xFEA8, 0xFEA9, 0xFEAA, 0xFEAB, 0xFEAC, 0xFEAD, 0xFEAE, 0xFEAF,
+ /* B */ 0xFEB0, 0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4, 0xFEB5, 0xFEB6, 0xFEB7,
+ 0xFEB8, 0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC, 0xFEBD, 0xFEBE, 0xFEBF,
+ /* C */ 0xFEC0, 0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4, 0xFEC5, 0xFEC6, 0xFEC7,
+ 0xFEC8, 0xFEC9, 0xFECA, 0xFECB, 0xFECC, 0xFECD, 0xFECE, 0xFECF,
+ /* D */ 0xFED0, 0xFED1, 0xFED2, 0xFED3, 0xFED4, 0xFED5, 0xFED6, 0xFED7,
+ 0xFED8, 0xFED9, 0xFEDA, 0xFEDB, 0xFEDC, 0xFEDD, 0xFEDE, 0xFEDF,
+ /* E */ 0xFEE0, 0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4, 0xFEE5, 0xFEE6, 0xFEE7,
+ 0xFEE8, 0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC, 0xFEED, 0xFEEE, 0xFEEF,
+ /* F */ 0xFEF0, 0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4, 0xFEF5, 0xFEF6, 0xFEF7,
+ 0xFEF8, 0xFEF9, 0xFEFA, 0xFEFB, 0xFEFC, 0xFEFD, 0xFEFE, 0x0000,
+
+ // Table 10 (for high byte 0xFF)
+
+ /* 0 */ 0xFF00, 0xFF01, 0xFF02, 0xFF03, 0xFF04, 0xFF05, 0xFF06, 0xFF07,
+ 0xFF08, 0xFF09, 0xFF0A, 0xFF0B, 0xFF0C, 0xFF0D, 0xFF0E, 0xFF0F,
+ /* 1 */ 0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17,
+ 0xFF18, 0xFF19, 0xFF1A, 0xFF1B, 0xFF1C, 0xFF1D, 0xFF1E, 0xFF1F,
+ /* 2 */ 0xFF20, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47,
+ 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F,
+ /* 3 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57,
+ 0xFF58, 0xFF59, 0xFF5A, 0xFF3B, 0xFF3C, 0xFF3D, 0xFF3E, 0xFF3F,
+ /* 4 */ 0xFF40, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47,
+ 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F,
+ /* 5 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57,
+ 0xFF58, 0xFF59, 0xFF5A, 0xFF5B, 0xFF5C, 0xFF5D, 0xFF5E, 0xFF5F,
+ /* 6 */ 0xFF60, 0xFF61, 0xFF62, 0xFF63, 0xFF64, 0xFF65, 0xFF66, 0xFF67,
+ 0xFF68, 0xFF69, 0xFF6A, 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F,
+ /* 7 */ 0xFF70, 0xFF71, 0xFF72, 0xFF73, 0xFF74, 0xFF75, 0xFF76, 0xFF77,
+ 0xFF78, 0xFF79, 0xFF7A, 0xFF7B, 0xFF7C, 0xFF7D, 0xFF7E, 0xFF7F,
+ /* 8 */ 0xFF80, 0xFF81, 0xFF82, 0xFF83, 0xFF84, 0xFF85, 0xFF86, 0xFF87,
+ 0xFF88, 0xFF89, 0xFF8A, 0xFF8B, 0xFF8C, 0xFF8D, 0xFF8E, 0xFF8F,
+ /* 9 */ 0xFF90, 0xFF91, 0xFF92, 0xFF93, 0xFF94, 0xFF95, 0xFF96, 0xFF97,
+ 0xFF98, 0xFF99, 0xFF9A, 0xFF9B, 0xFF9C, 0xFF9D, 0xFF9E, 0xFF9F,
+ /* A */ 0xFFA0, 0xFFA1, 0xFFA2, 0xFFA3, 0xFFA4, 0xFFA5, 0xFFA6, 0xFFA7,
+ 0xFFA8, 0xFFA9, 0xFFAA, 0xFFAB, 0xFFAC, 0xFFAD, 0xFFAE, 0xFFAF,
+ /* B */ 0xFFB0, 0xFFB1, 0xFFB2, 0xFFB3, 0xFFB4, 0xFFB5, 0xFFB6, 0xFFB7,
+ 0xFFB8, 0xFFB9, 0xFFBA, 0xFFBB, 0xFFBC, 0xFFBD, 0xFFBE, 0xFFBF,
+ /* C */ 0xFFC0, 0xFFC1, 0xFFC2, 0xFFC3, 0xFFC4, 0xFFC5, 0xFFC6, 0xFFC7,
+ 0xFFC8, 0xFFC9, 0xFFCA, 0xFFCB, 0xFFCC, 0xFFCD, 0xFFCE, 0xFFCF,
+ /* D */ 0xFFD0, 0xFFD1, 0xFFD2, 0xFFD3, 0xFFD4, 0xFFD5, 0xFFD6, 0xFFD7,
+ 0xFFD8, 0xFFD9, 0xFFDA, 0xFFDB, 0xFFDC, 0xFFDD, 0xFFDE, 0xFFDF,
+ /* E */ 0xFFE0, 0xFFE1, 0xFFE2, 0xFFE3, 0xFFE4, 0xFFE5, 0xFFE6, 0xFFE7,
+ 0xFFE8, 0xFFE9, 0xFFEA, 0xFFEB, 0xFFEC, 0xFFED, 0xFFEE, 0xFFEF,
+ /* F */ 0xFFF0, 0xFFF1, 0xFFF2, 0xFFF3, 0xFFF4, 0xFFF5, 0xFFF6, 0xFFF7,
+ 0xFFF8, 0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF,
+};
+
+// Returns the next non-ignorable codepoint within string starting from the
+// position indicated by index, or zero if there are no more.
+// The passed-in index is automatically advanced as the characters in the input
+// HFS-decomposed UTF-8 strings are read.
+inline int HFSReadNextNonIgnorableCodepoint(const char* string,
+ int length,
+ int* index) {
+ int codepoint = 0;
+ while (*index < length && codepoint == 0) {
+ // CBU8_NEXT returns a value < 0 in error cases. For purposes of string
+ // comparison, we just use that value and flag it with DCHECK.
+ CBU8_NEXT(string, *index, length, codepoint);
+ DCHECK_GT(codepoint, 0);
+ if (codepoint > 0) {
+ // Check if there is a subtable for this upper byte.
+ int lookup_offset = lower_case_table[codepoint >> 8];
+ if (lookup_offset != 0)
+ codepoint = lower_case_table[lookup_offset + (codepoint & 0x00FF)];
+ // Note: codepoint1 may be again 0 at this point if the character was
+ // an ignorable.
+ }
+ }
+ return codepoint;
+}
+
+} // anonymous namespace
+
+// Special UTF-8 version of FastUnicodeCompare. Cf:
+// http://developer.apple.com/mac/library/technotes/tn/tn1150.html#StringComparisonAlgorithm
+// The input strings must be in the special HFS decomposed form.
+int FilePath::HFSFastUnicodeCompare(const StringType& string1,
+ const StringType& string2) {
+ int length1 = string1.length();
+ int length2 = string2.length();
+ int index1 = 0;
+ int index2 = 0;
+
+ for (;;) {
+ int codepoint1 = HFSReadNextNonIgnorableCodepoint(string1.c_str(),
+ length1,
+ &index1);
+ int codepoint2 = HFSReadNextNonIgnorableCodepoint(string2.c_str(),
+ length2,
+ &index2);
+ if (codepoint1 != codepoint2)
+ return (codepoint1 < codepoint2) ? -1 : 1;
+ if (codepoint1 == 0) {
+ DCHECK_EQ(index1, length1);
+ DCHECK_EQ(index2, length2);
+ return 0;
+ }
+ }
+}
+
+StringType FilePath::GetHFSDecomposedForm(const StringType& string) {
+ base::mac::ScopedCFTypeRef<CFStringRef> cfstring(
+ CFStringCreateWithBytesNoCopy(
+ NULL,
+ reinterpret_cast<const UInt8*>(string.c_str()),
+ string.length(),
+ kCFStringEncodingUTF8,
+ false,
+ kCFAllocatorNull));
+ // Query the maximum length needed to store the result. In most cases this
+ // will overestimate the required space. The return value also already
+ // includes the space needed for a terminating 0.
+ CFIndex length = CFStringGetMaximumSizeOfFileSystemRepresentation(cfstring);
+ DCHECK_GT(length, 0); // should be at least 1 for the 0-terminator.
+ // Reserve enough space for CFStringGetFileSystemRepresentation to write into.
+ // Also set the length to the maximum so that we can shrink it later.
+ // (Increasing rather than decreasing it would clobber the string contents!)
+ StringType result;
+ result.reserve(length);
+ result.resize(length - 1);
+ Boolean success = CFStringGetFileSystemRepresentation(cfstring,
+ &result[0],
+ length);
+ if (success) {
+ // Reduce result.length() to actual string length.
+ result.resize(strlen(result.c_str()));
+ } else {
+ // An error occurred -> clear result.
+ result.clear();
+ }
+ return result;
+}
+
+int FilePath::CompareIgnoreCase(const StringType& string1,
+ const StringType& string2) {
+ // Quick checks for empty strings - these speed things up a bit and make the
+ // following code cleaner.
+ if (string1.empty())
+ return string2.empty() ? 0 : -1;
+ if (string2.empty())
+ return 1;
+
+ StringType hfs1 = GetHFSDecomposedForm(string1);
+ StringType hfs2 = GetHFSDecomposedForm(string2);
+
+ // GetHFSDecomposedForm() returns an empty string in an error case.
+ if (hfs1.empty() || hfs2.empty()) {
+ NOTREACHED();
+ base::mac::ScopedCFTypeRef<CFStringRef> cfstring1(
+ CFStringCreateWithBytesNoCopy(
+ NULL,
+ reinterpret_cast<const UInt8*>(string1.c_str()),
+ string1.length(),
+ kCFStringEncodingUTF8,
+ false,
+ kCFAllocatorNull));
+ base::mac::ScopedCFTypeRef<CFStringRef> cfstring2(
+ CFStringCreateWithBytesNoCopy(
+ NULL,
+ reinterpret_cast<const UInt8*>(string2.c_str()),
+ string2.length(),
+ kCFStringEncodingUTF8,
+ false,
+ kCFAllocatorNull));
+ return CFStringCompare(cfstring1,
+ cfstring2,
+ kCFCompareCaseInsensitive);
+ }
+
+ return HFSFastUnicodeCompare(hfs1, hfs2);
+}
+
+#else // << WIN. MACOSX | other (POSIX) >>
+
+// Generic (POSIX) implementation of file string comparison.
+// TODO(rolandsteiner) check if this is sufficient/correct.
+int FilePath::CompareIgnoreCase(const StringType& string1,
+ const StringType& string2) {
+ int comparison = strcasecmp(string1.c_str(), string2.c_str());
+ if (comparison < 0)
+ return -1;
+ if (comparison > 0)
+ return 1;
+ return 0;
+}
+
+#endif // OS versions of CompareIgnoreCase()
+
+
+void FilePath::StripTrailingSeparatorsInternal() {
+ // If there is no drive letter, start will be 1, which will prevent stripping
+ // the leading separator if there is only one separator. If there is a drive
+ // letter, start will be set appropriately to prevent stripping the first
+ // separator following the drive letter, if a separator immediately follows
+ // the drive letter.
+ StringType::size_type start = FindDriveLetter(path_) + 2;
+
+ StringType::size_type last_stripped = StringType::npos;
+ for (StringType::size_type pos = path_.length();
+ pos > start && IsSeparator(path_[pos - 1]);
+ --pos) {
+ // If the string only has two separators and they're at the beginning,
+ // don't strip them, unless the string began with more than two separators.
+ if (pos != start + 1 || last_stripped == start + 2 ||
+ !IsSeparator(path_[start - 1])) {
+ path_.resize(pos - 1);
+ last_stripped = pos;
+ }
+ }
+}
+
+FilePath FilePath::NormalizePathSeparators() const {
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ StringType copy = path_;
+ for (size_t i = 1; i < arraysize(kSeparators); ++i) {
+ std::replace(copy.begin(), copy.end(), kSeparators[i], kSeparators[0]);
+ }
+ return FilePath(copy);
+#else
+ return *this;
+#endif
+}
+
+void PrintTo(const FilePath& path, std::ostream* out) {
+ *out << path.value();
+}
diff --git a/src/base/file_path.h b/src/base/file_path.h
new file mode 100644
index 0000000..d1eb74c
--- /dev/null
+++ b/src/base/file_path.h
@@ -0,0 +1,437 @@
+// Copyright (c) 2012 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.
+
+// FilePath is a container for pathnames stored in a platform's native string
+// type, providing containers for manipulation in according with the
+// platform's conventions for pathnames. It supports the following path
+// types:
+//
+// POSIX Windows
+// --------------- ----------------------------------
+// Fundamental type char[] wchar_t[]
+// Encoding unspecified* UTF-16
+// Separator / \, tolerant of /
+// Drive letters no case-insensitive A-Z followed by :
+// Alternate root // (surprise!) \\, for UNC paths
+//
+// * The encoding need not be specified on POSIX systems, although some
+// POSIX-compliant systems do specify an encoding. Mac OS X uses UTF-8.
+// Chrome OS also uses UTF-8.
+// Linux does not specify an encoding, but in practice, the locale's
+// character set may be used.
+//
+// For more arcane bits of path trivia, see below.
+//
+// FilePath objects are intended to be used anywhere paths are. An
+// application may pass FilePath objects around internally, masking the
+// underlying differences between systems, only differing in implementation
+// where interfacing directly with the system. For example, a single
+// OpenFile(const FilePath &) function may be made available, allowing all
+// callers to operate without regard to the underlying implementation. On
+// POSIX-like platforms, OpenFile might wrap fopen, and on Windows, it might
+// wrap _wfopen_s, perhaps both by calling file_path.value().c_str(). This
+// allows each platform to pass pathnames around without requiring conversions
+// between encodings, which has an impact on performance, but more imporantly,
+// has an impact on correctness on platforms that do not have well-defined
+// encodings for pathnames.
+//
+// Several methods are available to perform common operations on a FilePath
+// object, such as determining the parent directory (DirName), isolating the
+// final path component (BaseName), and appending a relative pathname string
+// to an existing FilePath object (Append). These methods are highly
+// recommended over attempting to split and concatenate strings directly.
+// These methods are based purely on string manipulation and knowledge of
+// platform-specific pathname conventions, and do not consult the filesystem
+// at all, making them safe to use without fear of blocking on I/O operations.
+// These methods do not function as mutators but instead return distinct
+// instances of FilePath objects, and are therefore safe to use on const
+// objects. The objects themselves are safe to share between threads.
+//
+// To aid in initialization of FilePath objects from string literals, a
+// FILE_PATH_LITERAL macro is provided, which accounts for the difference
+// between char[]-based pathnames on POSIX systems and wchar_t[]-based
+// pathnames on Windows.
+//
+// Paths can't contain NULs as a precaution agaist premature truncation.
+//
+// Because a FilePath object should not be instantiated at the global scope,
+// instead, use a FilePath::CharType[] and initialize it with
+// FILE_PATH_LITERAL. At runtime, a FilePath object can be created from the
+// character array. Example:
+//
+// | const FilePath::CharType kLogFileName[] = FILE_PATH_LITERAL("log.txt");
+// |
+// | void Function() {
+// | FilePath log_file_path(kLogFileName);
+// | [...]
+// | }
+//
+// WARNING: FilePaths should ALWAYS be displayed with LTR directionality, even
+// when the UI language is RTL. This means you always need to pass filepaths
+// through base::i18n::WrapPathWithLTRFormatting() before displaying it in the
+// RTL UI.
+//
+// This is a very common source of bugs, please try to keep this in mind.
+//
+// ARCANE BITS OF PATH TRIVIA
+//
+// - A double leading slash is actually part of the POSIX standard. Systems
+// are allowed to treat // as an alternate root, as Windows does for UNC
+// (network share) paths. Most POSIX systems don't do anything special
+// with two leading slashes, but FilePath handles this case properly
+// in case it ever comes across such a system. FilePath needs this support
+// for Windows UNC paths, anyway.
+// References:
+// The Open Group Base Specifications Issue 7, sections 3.266 ("Pathname")
+// and 4.12 ("Pathname Resolution"), available at:
+// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_266
+// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_12
+//
+// - Windows treats c:\\ the same way it treats \\. This was intended to
+// allow older applications that require drive letters to support UNC paths
+// like \\server\share\path, by permitting c:\\server\share\path as an
+// equivalent. Since the OS treats these paths specially, FilePath needs
+// to do the same. Since Windows can use either / or \ as the separator,
+// FilePath treats c://, c:\\, //, and \\ all equivalently.
+// Reference:
+// The Old New Thing, "Why is a drive letter permitted in front of UNC
+// paths (sometimes)?", available at:
+// http://blogs.msdn.com/oldnewthing/archive/2005/11/22/495740.aspx
+
+#ifndef BASE_FILE_PATH_H_
+#define BASE_FILE_PATH_H_
+
+#include <stddef.h>
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/hash_tables.h"
+#include "base/string16.h"
+#include "base/string_piece.h" // For implicit conversions.
+#include "build/build_config.h"
+
+// Windows-style drive letter support and pathname separator characters can be
+// enabled and disabled independently, to aid testing. These #defines are
+// here so that the same setting can be used in both the implementation and
+// in the unit test.
+#if defined(OS_WIN) || defined(__LB_XB1__) || defined(__LB_XB360__)
+#define FILE_PATH_USES_DRIVE_LETTERS
+#define FILE_PATH_USES_WIN_SEPARATORS
+#endif // OS_WIN || __LB_XB1__ || __LB_XB360__
+
+class Pickle;
+class PickleIterator;
+
+// An abstraction to isolate users from the differences between native
+// pathnames on different platforms.
+class BASE_EXPORT FilePath {
+ public:
+#if defined(OS_POSIX) || defined(OS_STARBOARD)
+ // On most platforms, native pathnames are char arrays, and the encoding
+ // may or may not be specified. On Mac OS X, native pathnames are encoded
+ // in UTF-8.
+ typedef std::string StringType;
+#elif defined(OS_WIN)
+ // On Windows, for Unicode-aware applications, native pathnames are wchar_t
+ // arrays encoded in UTF-16.
+ typedef std::wstring StringType;
+#endif // OS_WIN
+
+ typedef StringType::value_type CharType;
+
+ // Null-terminated array of separators used to separate components in
+ // hierarchical paths. Each character in this array is a valid separator,
+ // but kSeparators[0] is treated as the canonical separator and will be used
+ // when composing pathnames.
+ static const CharType kSeparators[];
+
+ // A special path component meaning "this directory."
+ static const CharType kCurrentDirectory[];
+
+ // A special path component meaning "the parent directory."
+ static const CharType kParentDirectory[];
+
+ // The character used to identify a file extension.
+ static const CharType kExtensionSeparator;
+
+ FilePath();
+ FilePath(const FilePath& that);
+ explicit FilePath(const StringType& path);
+ ~FilePath();
+ FilePath& operator=(const FilePath& that);
+
+ bool operator==(const FilePath& that) const;
+
+ bool operator!=(const FilePath& that) const;
+
+ // Required for some STL containers and operations
+ bool operator<(const FilePath& that) const {
+ return path_ < that.path_;
+ }
+
+ const StringType& value() const { return path_; }
+
+ bool empty() const { return path_.empty(); }
+
+ void clear() { path_.clear(); }
+
+ // Returns true if |character| is in kSeparators.
+ static bool IsSeparator(CharType character);
+
+ // Returns a vector of all of the components of the provided path. It is
+ // equivalent to calling DirName().value() on the path's root component,
+ // and BaseName().value() on each child component.
+ void GetComponents(std::vector<FilePath::StringType>* components) const;
+
+ // Returns true if this FilePath is a strict parent of the |child|. Absolute
+ // and relative paths are accepted i.e. is /foo parent to /foo/bar and
+ // is foo parent to foo/bar. Does not convert paths to absolute, follow
+ // symlinks or directory navigation (e.g. ".."). A path is *NOT* its own
+ // parent.
+ bool IsParent(const FilePath& child) const;
+
+ // If IsParent(child) holds, appends to path (if non-NULL) the
+ // relative path to child and returns true. For example, if parent
+ // holds "/Users/johndoe/Library/Application Support", child holds
+ // "/Users/johndoe/Library/Application Support/Google/Chrome/Default", and
+ // *path holds "/Users/johndoe/Library/Caches", then after
+ // parent.AppendRelativePath(child, path) is called *path will hold
+ // "/Users/johndoe/Library/Caches/Google/Chrome/Default". Otherwise,
+ // returns false.
+ bool AppendRelativePath(const FilePath& child, FilePath* path) const;
+
+ // Returns a FilePath corresponding to the directory containing the path
+ // named by this object, stripping away the file component. If this object
+ // only contains one component, returns a FilePath identifying
+ // kCurrentDirectory. If this object already refers to the root directory,
+ // returns a FilePath identifying the root directory.
+ FilePath DirName() const WARN_UNUSED_RESULT;
+
+ // Returns a FilePath corresponding to the last path component of this
+ // object, either a file or a directory. If this object already refers to
+ // the root directory, returns a FilePath identifying the root directory;
+ // this is the only situation in which BaseName will return an absolute path.
+ FilePath BaseName() const WARN_UNUSED_RESULT;
+
+ // Returns ".jpg" for path "C:\pics\jojo.jpg", or an empty string if
+ // the file has no extension. If non-empty, Extension() will always start
+ // with precisely one ".". The following code should always work regardless
+ // of the value of path.
+ // new_path = path.RemoveExtension().value().append(path.Extension());
+ // ASSERT(new_path == path.value());
+ // NOTE: this is different from the original file_util implementation which
+ // returned the extension without a leading "." ("jpg" instead of ".jpg")
+ StringType Extension() const;
+
+ // Returns "C:\pics\jojo" for path "C:\pics\jojo.jpg"
+ // NOTE: this is slightly different from the similar file_util implementation
+ // which returned simply 'jojo'.
+ FilePath RemoveExtension() const WARN_UNUSED_RESULT;
+
+ // Inserts |suffix| after the file name portion of |path| but before the
+ // extension. Returns "" if BaseName() == "." or "..".
+ // Examples:
+ // path == "C:\pics\jojo.jpg" suffix == " (1)", returns "C:\pics\jojo (1).jpg"
+ // path == "jojo.jpg" suffix == " (1)", returns "jojo (1).jpg"
+ // path == "C:\pics\jojo" suffix == " (1)", returns "C:\pics\jojo (1)"
+ // path == "C:\pics.old\jojo" suffix == " (1)", returns "C:\pics.old\jojo (1)"
+ FilePath InsertBeforeExtension(
+ const StringType& suffix) const WARN_UNUSED_RESULT;
+ FilePath InsertBeforeExtensionASCII(
+ const base::StringPiece& suffix) const WARN_UNUSED_RESULT;
+
+ // Adds |extension| to |file_name|. Returns the current FilePath if
+ // |extension| is empty. Returns "" if BaseName() == "." or "..".
+ FilePath AddExtension(
+ const StringType& extension) const WARN_UNUSED_RESULT;
+
+ // Replaces the extension of |file_name| with |extension|. If |file_name|
+ // does not have an extension, then |extension| is added. If |extension| is
+ // empty, then the extension is removed from |file_name|.
+ // Returns "" if BaseName() == "." or "..".
+ FilePath ReplaceExtension(
+ const StringType& extension) const WARN_UNUSED_RESULT;
+
+ // Returns true if the file path matches the specified extension. The test is
+ // case insensitive. Don't forget the leading period if appropriate.
+ bool MatchesExtension(const StringType& extension) const;
+
+ // Returns a FilePath by appending a separator and the supplied path
+ // component to this object's path. Append takes care to avoid adding
+ // excessive separators if this object's path already ends with a separator.
+ // If this object's path is kCurrentDirectory, a new FilePath corresponding
+ // only to |component| is returned. |component| must be a relative path;
+ // it is an error to pass an absolute path.
+ FilePath Append(const StringType& component) const WARN_UNUSED_RESULT;
+ FilePath Append(const FilePath& component) const WARN_UNUSED_RESULT;
+
+ // Although Windows StringType is std::wstring, since the encoding it uses for
+ // paths is well defined, it can handle ASCII path components as well.
+ // Mac uses UTF8, and since ASCII is a subset of that, it works there as well.
+ // On Linux, although it can use any 8-bit encoding for paths, we assume that
+ // ASCII is a valid subset, regardless of the encoding, since many operating
+ // system paths will always be ASCII.
+ FilePath AppendASCII(const base::StringPiece& component)
+ const WARN_UNUSED_RESULT;
+
+ // Returns true if this FilePath contains an absolute path. On Windows, an
+ // absolute path begins with either a drive letter specification followed by
+ // a separator character, or with two separator characters. On POSIX
+ // platforms, an absolute path begins with a separator character.
+ bool IsAbsolute() const;
+
+ // Returns a copy of this FilePath that does not end with a trailing
+ // separator.
+ FilePath StripTrailingSeparators() const WARN_UNUSED_RESULT;
+
+ // Returns true if this FilePath contains any attempt to reference a parent
+ // directory (i.e. has a path component that is ".."
+ bool ReferencesParent() const;
+
+ // Return a Unicode human-readable version of this path.
+ // Warning: you can *not*, in general, go from a display name back to a real
+ // path. Only use this when displaying paths to users, not just when you
+ // want to stuff a string16 into some other API.
+ string16 LossyDisplayName() const;
+
+ // Return the path as ASCII, or the empty string if the path is not ASCII.
+ // This should only be used for cases where the FilePath is representing a
+ // known-ASCII filename.
+ std::string MaybeAsASCII() const;
+
+ // Return the path as UTF-8.
+ //
+ // This function is *unsafe* as there is no way to tell what encoding is
+ // used in file names on POSIX systems other than Mac and Chrome OS,
+ // although UTF-8 is practically used everywhere these days. To mitigate
+ // the encoding issue, this function internally calls
+ // SysNativeMBToWide() on POSIX systems other than Mac and Chrome OS,
+ // per assumption that the current locale's encoding is used in file
+ // names, but this isn't a perfect solution.
+ //
+ // Once it becomes safe to to stop caring about non-UTF-8 file names,
+ // the SysNativeMBToWide() hack will be removed from the code, along
+ // with "Unsafe" in the function name.
+ std::string AsUTF8Unsafe() const;
+
+ // Older Chromium code assumes that paths are always wstrings.
+ // This function converts wstrings to FilePaths, and is
+ // useful to smooth porting that old code to the FilePath API.
+ // It has "Hack" its name so people feel bad about using it.
+ // http://code.google.com/p/chromium/issues/detail?id=24672
+ //
+ // If you are trying to be a good citizen and remove these, ask yourself:
+ // - Am I interacting with other Chrome code that deals with files? Then
+ // try to convert the API into using FilePath.
+ // - Am I interacting with OS-native calls? Then use value() to get at an
+ // OS-native string format.
+ // - Am I using well-known file names, like "config.ini"? Then use the
+ // ASCII functions (we require paths to always be supersets of ASCII).
+ // - Am I displaying a string to the user in some UI? Then use the
+ // LossyDisplayName() function, but keep in mind that you can't
+ // ever use the result of that again as a path.
+ static FilePath FromWStringHack(const std::wstring& wstring);
+
+ // Returns a FilePath object from a path name in UTF-8. This function
+ // should only be used for cases where you are sure that the input
+ // string is UTF-8.
+ //
+ // Like AsUTF8Unsafe(), this function is unsafe. This function
+ // internally calls SysWideToNativeMB() on POSIX systems other than Mac
+ // and Chrome OS, to mitigate the encoding issue. See the comment at
+ // AsUTF8Unsafe() for details.
+ static FilePath FromUTF8Unsafe(const std::string& utf8);
+
+ void WriteToPickle(Pickle* pickle) const;
+ bool ReadFromPickle(PickleIterator* iter);
+
+ // Normalize all path separators to backslash on Windows
+ // (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems.
+ FilePath NormalizePathSeparators() const;
+
+ // Compare two strings in the same way the file system does.
+ // Note that these always ignore case, even on file systems that are case-
+ // sensitive. If case-sensitive comparison is ever needed, add corresponding
+ // methods here.
+ // The methods are written as a static method so that they can also be used
+ // on parts of a file path, e.g., just the extension.
+ // CompareIgnoreCase() returns -1, 0 or 1 for less-than, equal-to and
+ // greater-than respectively.
+ static int CompareIgnoreCase(const StringType& string1,
+ const StringType& string2);
+ static bool CompareEqualIgnoreCase(const StringType& string1,
+ const StringType& string2) {
+ return CompareIgnoreCase(string1, string2) == 0;
+ }
+ static bool CompareLessIgnoreCase(const StringType& string1,
+ const StringType& string2) {
+ return CompareIgnoreCase(string1, string2) < 0;
+ }
+
+#if defined(OS_MACOSX)
+ // Returns the string in the special canonical decomposed form as defined for
+ // HFS, which is close to, but not quite, decomposition form D. See
+ // http://developer.apple.com/mac/library/technotes/tn/tn1150.html#UnicodeSubtleties
+ // for further comments.
+ // Returns the epmty string if the conversion failed.
+ static StringType GetHFSDecomposedForm(const FilePath::StringType& string);
+
+ // Special UTF-8 version of FastUnicodeCompare. Cf:
+ // http://developer.apple.com/mac/library/technotes/tn/tn1150.html#StringComparisonAlgorithm
+ // IMPORTANT: The input strings must be in the special HFS decomposed form!
+ // (cf. above GetHFSDecomposedForm method)
+ static int HFSFastUnicodeCompare(const StringType& string1,
+ const StringType& string2);
+#endif
+
+ private:
+ // Remove trailing separators from this object. If the path is absolute, it
+ // will never be stripped any more than to refer to the absolute root
+ // directory, so "////" will become "/", not "". A leading pair of
+ // separators is never stripped, to support alternate roots. This is used to
+ // support UNC paths on Windows.
+ void StripTrailingSeparatorsInternal();
+
+ StringType path_;
+};
+
+// This is required by googletest to print a readable output on test failures.
+BASE_EXPORT extern void PrintTo(const FilePath& path, std::ostream* out);
+
+// Macros for string literal initialization of FilePath::CharType[], and for
+// using a FilePath::CharType[] in a printf-style format string.
+#if defined(OS_POSIX) || defined(OS_STARBOARD)
+#define FILE_PATH_LITERAL(x) x
+#define PRFilePath "s"
+#define PRFilePathLiteral "%s"
+#elif defined(OS_WIN)
+#define FILE_PATH_LITERAL(x) L ## x
+#define PRFilePath "ls"
+#define PRFilePathLiteral L"%ls"
+#endif // OS_WIN
+
+// Provide a hash function so that hash_sets and maps can contain FilePath
+// objects.
+namespace BASE_HASH_NAMESPACE {
+
+#if defined(BASE_HASH_USE_HASH_STRUCT)
+template<>
+struct hash<FilePath> {
+ size_t operator()(const FilePath& f) const {
+ return hash<FilePath::StringType>()(f.value());
+ }
+};
+
+#else
+inline size_t hash_value(const FilePath& f) {
+ return hash_value(f.value());
+}
+
+#endif // defined(BASE_HASH_USE_HASH_STRUCT)
+
+} // namespace BASE_HASH_NAMESPACE
+
+#endif // BASE_FILE_PATH_H_
diff --git a/src/base/file_path_unittest.cc b/src/base/file_path_unittest.cc
new file mode 100644
index 0000000..b61d5d2
--- /dev/null
+++ b/src/base/file_path_unittest.cc
@@ -0,0 +1,1213 @@
+// Copyright (c) 2012 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/basictypes.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+// This macro helps avoid wrapped lines in the test structs.
+#define FPL(x) FILE_PATH_LITERAL(x)
+
+// This macro constructs strings which can contain NULs.
+#define FPS(x) FilePath::StringType(FPL(x), arraysize(FPL(x)) - 1)
+
+struct UnaryTestData {
+ const FilePath::CharType* input;
+ const FilePath::CharType* expected;
+};
+
+struct UnaryBooleanTestData {
+ const FilePath::CharType* input;
+ bool expected;
+};
+
+struct BinaryTestData {
+ const FilePath::CharType* inputs[2];
+ const FilePath::CharType* expected;
+};
+
+struct BinaryBooleanTestData {
+ const FilePath::CharType* inputs[2];
+ bool expected;
+};
+
+struct BinaryIntTestData {
+ const FilePath::CharType* inputs[2];
+ int expected;
+};
+
+struct UTF8TestData {
+ const FilePath::CharType* native;
+ const char* utf8;
+};
+
+// file_util winds up using autoreleased objects on the Mac, so this needs
+// to be a PlatformTest
+class FilePathTest : public PlatformTest {
+ protected:
+ virtual void SetUp() OVERRIDE {
+ PlatformTest::SetUp();
+ }
+ virtual void TearDown() OVERRIDE {
+ PlatformTest::TearDown();
+ }
+};
+
+TEST_F(FilePathTest, DirName) {
+ const struct UnaryTestData cases[] = {
+ { FPL(""), FPL(".") },
+ { FPL("aa"), FPL(".") },
+ { FPL("/aa/bb"), FPL("/aa") },
+ { FPL("/aa/bb/"), FPL("/aa") },
+ { FPL("/aa/bb//"), FPL("/aa") },
+ { FPL("/aa/bb/ccc"), FPL("/aa/bb") },
+ { FPL("/aa"), FPL("/") },
+ { FPL("/aa/"), FPL("/") },
+ { FPL("/"), FPL("/") },
+ { FPL("//"), FPL("//") },
+ { FPL("///"), FPL("/") },
+ { FPL("aa/"), FPL(".") },
+ { FPL("aa/bb"), FPL("aa") },
+ { FPL("aa/bb/"), FPL("aa") },
+ { FPL("aa/bb//"), FPL("aa") },
+ { FPL("aa//bb//"), FPL("aa") },
+ { FPL("aa//bb/"), FPL("aa") },
+ { FPL("aa//bb"), FPL("aa") },
+ { FPL("//aa/bb"), FPL("//aa") },
+ { FPL("//aa/"), FPL("//") },
+ { FPL("//aa"), FPL("//") },
+ { FPL("0:"), FPL(".") },
+ { FPL("@:"), FPL(".") },
+ { FPL("[:"), FPL(".") },
+ { FPL("`:"), FPL(".") },
+ { FPL("{:"), FPL(".") },
+ { FPL("\xB3:"), FPL(".") },
+ { FPL("\xC5:"), FPL(".") },
+#if defined(OS_WIN)
+ { FPL("\x0143:"), FPL(".") },
+#endif // OS_WIN
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ { FPL("c:"), FPL("c:") },
+ { FPL("C:"), FPL("C:") },
+ { FPL("A:"), FPL("A:") },
+ { FPL("Z:"), FPL("Z:") },
+ { FPL("a:"), FPL("a:") },
+ { FPL("z:"), FPL("z:") },
+ { FPL("c:aa"), FPL("c:") },
+ { FPL("c:/"), FPL("c:/") },
+ { FPL("c://"), FPL("c://") },
+ { FPL("c:///"), FPL("c:/") },
+ { FPL("c:/aa"), FPL("c:/") },
+ { FPL("c:/aa/"), FPL("c:/") },
+ { FPL("c:/aa/bb"), FPL("c:/aa") },
+ { FPL("c:aa/bb"), FPL("c:aa") },
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ { FPL("\\aa\\bb"), FPL("\\aa") },
+ { FPL("\\aa\\bb\\"), FPL("\\aa") },
+ { FPL("\\aa\\bb\\\\"), FPL("\\aa") },
+ { FPL("\\aa\\bb\\ccc"), FPL("\\aa\\bb") },
+ { FPL("\\aa"), FPL("\\") },
+ { FPL("\\aa\\"), FPL("\\") },
+ { FPL("\\"), FPL("\\") },
+ { FPL("\\\\"), FPL("\\\\") },
+ { FPL("\\\\\\"), FPL("\\") },
+ { FPL("aa\\"), FPL(".") },
+ { FPL("aa\\bb"), FPL("aa") },
+ { FPL("aa\\bb\\"), FPL("aa") },
+ { FPL("aa\\bb\\\\"), FPL("aa") },
+ { FPL("aa\\\\bb\\\\"), FPL("aa") },
+ { FPL("aa\\\\bb\\"), FPL("aa") },
+ { FPL("aa\\\\bb"), FPL("aa") },
+ { FPL("\\\\aa\\bb"), FPL("\\\\aa") },
+ { FPL("\\\\aa\\"), FPL("\\\\") },
+ { FPL("\\\\aa"), FPL("\\\\") },
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ { FPL("c:\\"), FPL("c:\\") },
+ { FPL("c:\\\\"), FPL("c:\\\\") },
+ { FPL("c:\\\\\\"), FPL("c:\\") },
+ { FPL("c:\\aa"), FPL("c:\\") },
+ { FPL("c:\\aa\\"), FPL("c:\\") },
+ { FPL("c:\\aa\\bb"), FPL("c:\\aa") },
+ { FPL("c:aa\\bb"), FPL("c:aa") },
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+#endif // FILE_PATH_USES_WIN_SEPARATORS
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ FilePath input(cases[i].input);
+ FilePath observed = input.DirName();
+ EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) <<
+ "i: " << i << ", input: " << input.value();
+ }
+}
+
+TEST_F(FilePathTest, BaseName) {
+ const struct UnaryTestData cases[] = {
+ { FPL(""), FPL("") },
+ { FPL("aa"), FPL("aa") },
+ { FPL("/aa/bb"), FPL("bb") },
+ { FPL("/aa/bb/"), FPL("bb") },
+ { FPL("/aa/bb//"), FPL("bb") },
+ { FPL("/aa/bb/ccc"), FPL("ccc") },
+ { FPL("/aa"), FPL("aa") },
+ { FPL("/"), FPL("/") },
+ { FPL("//"), FPL("//") },
+ { FPL("///"), FPL("/") },
+ { FPL("aa/"), FPL("aa") },
+ { FPL("aa/bb"), FPL("bb") },
+ { FPL("aa/bb/"), FPL("bb") },
+ { FPL("aa/bb//"), FPL("bb") },
+ { FPL("aa//bb//"), FPL("bb") },
+ { FPL("aa//bb/"), FPL("bb") },
+ { FPL("aa//bb"), FPL("bb") },
+ { FPL("//aa/bb"), FPL("bb") },
+ { FPL("//aa/"), FPL("aa") },
+ { FPL("//aa"), FPL("aa") },
+ { FPL("0:"), FPL("0:") },
+ { FPL("@:"), FPL("@:") },
+ { FPL("[:"), FPL("[:") },
+ { FPL("`:"), FPL("`:") },
+ { FPL("{:"), FPL("{:") },
+ { FPL("\xB3:"), FPL("\xB3:") },
+ { FPL("\xC5:"), FPL("\xC5:") },
+#if defined(OS_WIN)
+ { FPL("\x0143:"), FPL("\x0143:") },
+#endif // OS_WIN
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ { FPL("c:"), FPL("") },
+ { FPL("C:"), FPL("") },
+ { FPL("A:"), FPL("") },
+ { FPL("Z:"), FPL("") },
+ { FPL("a:"), FPL("") },
+ { FPL("z:"), FPL("") },
+ { FPL("c:aa"), FPL("aa") },
+ { FPL("c:/"), FPL("/") },
+ { FPL("c://"), FPL("//") },
+ { FPL("c:///"), FPL("/") },
+ { FPL("c:/aa"), FPL("aa") },
+ { FPL("c:/aa/"), FPL("aa") },
+ { FPL("c:/aa/bb"), FPL("bb") },
+ { FPL("c:aa/bb"), FPL("bb") },
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ { FPL("\\aa\\bb"), FPL("bb") },
+ { FPL("\\aa\\bb\\"), FPL("bb") },
+ { FPL("\\aa\\bb\\\\"), FPL("bb") },
+ { FPL("\\aa\\bb\\ccc"), FPL("ccc") },
+ { FPL("\\aa"), FPL("aa") },
+ { FPL("\\"), FPL("\\") },
+ { FPL("\\\\"), FPL("\\\\") },
+ { FPL("\\\\\\"), FPL("\\") },
+ { FPL("aa\\"), FPL("aa") },
+ { FPL("aa\\bb"), FPL("bb") },
+ { FPL("aa\\bb\\"), FPL("bb") },
+ { FPL("aa\\bb\\\\"), FPL("bb") },
+ { FPL("aa\\\\bb\\\\"), FPL("bb") },
+ { FPL("aa\\\\bb\\"), FPL("bb") },
+ { FPL("aa\\\\bb"), FPL("bb") },
+ { FPL("\\\\aa\\bb"), FPL("bb") },
+ { FPL("\\\\aa\\"), FPL("aa") },
+ { FPL("\\\\aa"), FPL("aa") },
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ { FPL("c:\\"), FPL("\\") },
+ { FPL("c:\\\\"), FPL("\\\\") },
+ { FPL("c:\\\\\\"), FPL("\\") },
+ { FPL("c:\\aa"), FPL("aa") },
+ { FPL("c:\\aa\\"), FPL("aa") },
+ { FPL("c:\\aa\\bb"), FPL("bb") },
+ { FPL("c:aa\\bb"), FPL("bb") },
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+#endif // FILE_PATH_USES_WIN_SEPARATORS
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ FilePath input(cases[i].input);
+ FilePath observed = input.BaseName();
+ EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) <<
+ "i: " << i << ", input: " << input.value();
+ }
+}
+
+TEST_F(FilePathTest, Append) {
+ const struct BinaryTestData cases[] = {
+ { { FPL(""), FPL("cc") }, FPL("cc") },
+ { { FPL("."), FPL("ff") }, FPL("ff") },
+ { { FPL("/"), FPL("cc") }, FPL("/cc") },
+ { { FPL("/aa"), FPL("") }, FPL("/aa") },
+ { { FPL("/aa/"), FPL("") }, FPL("/aa") },
+ { { FPL("//aa"), FPL("") }, FPL("//aa") },
+ { { FPL("//aa/"), FPL("") }, FPL("//aa") },
+ { { FPL("//"), FPL("aa") }, FPL("//aa") },
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ { { FPL("c:"), FPL("a") }, FPL("c:a") },
+ { { FPL("c:"), FPL("") }, FPL("c:") },
+ { { FPL("c:/"), FPL("a") }, FPL("c:/a") },
+ { { FPL("c://"), FPL("a") }, FPL("c://a") },
+ { { FPL("c:///"), FPL("a") }, FPL("c:/a") },
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ // Append introduces the default separator character, so these test cases
+ // need to be defined with different expected results on platforms that use
+ // different default separator characters.
+ { { FPL("\\"), FPL("cc") }, FPL("\\cc") },
+ { { FPL("\\aa"), FPL("") }, FPL("\\aa") },
+ { { FPL("\\aa\\"), FPL("") }, FPL("\\aa") },
+ { { FPL("\\\\aa"), FPL("") }, FPL("\\\\aa") },
+ { { FPL("\\\\aa\\"), FPL("") }, FPL("\\\\aa") },
+ { { FPL("\\\\"), FPL("aa") }, FPL("\\\\aa") },
+ { { FPL("/aa/bb"), FPL("cc") }, FPL("/aa/bb\\cc") },
+ { { FPL("/aa/bb/"), FPL("cc") }, FPL("/aa/bb\\cc") },
+ { { FPL("aa/bb/"), FPL("cc") }, FPL("aa/bb\\cc") },
+ { { FPL("aa/bb"), FPL("cc") }, FPL("aa/bb\\cc") },
+ { { FPL("a/b"), FPL("c") }, FPL("a/b\\c") },
+ { { FPL("a/b/"), FPL("c") }, FPL("a/b\\c") },
+ { { FPL("//aa"), FPL("bb") }, FPL("//aa\\bb") },
+ { { FPL("//aa/"), FPL("bb") }, FPL("//aa\\bb") },
+ { { FPL("\\aa\\bb"), FPL("cc") }, FPL("\\aa\\bb\\cc") },
+ { { FPL("\\aa\\bb\\"), FPL("cc") }, FPL("\\aa\\bb\\cc") },
+ { { FPL("aa\\bb\\"), FPL("cc") }, FPL("aa\\bb\\cc") },
+ { { FPL("aa\\bb"), FPL("cc") }, FPL("aa\\bb\\cc") },
+ { { FPL("a\\b"), FPL("c") }, FPL("a\\b\\c") },
+ { { FPL("a\\b\\"), FPL("c") }, FPL("a\\b\\c") },
+ { { FPL("\\\\aa"), FPL("bb") }, FPL("\\\\aa\\bb") },
+ { { FPL("\\\\aa\\"), FPL("bb") }, FPL("\\\\aa\\bb") },
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ { { FPL("c:\\"), FPL("a") }, FPL("c:\\a") },
+ { { FPL("c:\\\\"), FPL("a") }, FPL("c:\\\\a") },
+ { { FPL("c:\\\\\\"), FPL("a") }, FPL("c:\\a") },
+ { { FPL("c:\\"), FPL("") }, FPL("c:\\") },
+ { { FPL("c:\\a"), FPL("b") }, FPL("c:\\a\\b") },
+ { { FPL("c:\\a\\"), FPL("b") }, FPL("c:\\a\\b") },
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+#else // FILE_PATH_USES_WIN_SEPARATORS
+ { { FPL("/aa/bb"), FPL("cc") }, FPL("/aa/bb/cc") },
+ { { FPL("/aa/bb/"), FPL("cc") }, FPL("/aa/bb/cc") },
+ { { FPL("aa/bb/"), FPL("cc") }, FPL("aa/bb/cc") },
+ { { FPL("aa/bb"), FPL("cc") }, FPL("aa/bb/cc") },
+ { { FPL("a/b"), FPL("c") }, FPL("a/b/c") },
+ { { FPL("a/b/"), FPL("c") }, FPL("a/b/c") },
+ { { FPL("//aa"), FPL("bb") }, FPL("//aa/bb") },
+ { { FPL("//aa/"), FPL("bb") }, FPL("//aa/bb") },
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ { { FPL("c:/"), FPL("a") }, FPL("c:/a") },
+ { { FPL("c:/"), FPL("") }, FPL("c:/") },
+ { { FPL("c:/a"), FPL("b") }, FPL("c:/a/b") },
+ { { FPL("c:/a/"), FPL("b") }, FPL("c:/a/b") },
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+#endif // FILE_PATH_USES_WIN_SEPARATORS
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ FilePath root(cases[i].inputs[0]);
+ FilePath::StringType leaf(cases[i].inputs[1]);
+ FilePath observed_str = root.Append(leaf);
+ EXPECT_EQ(FilePath::StringType(cases[i].expected), observed_str.value()) <<
+ "i: " << i << ", root: " << root.value() << ", leaf: " << leaf;
+ FilePath observed_path = root.Append(FilePath(leaf));
+ EXPECT_EQ(FilePath::StringType(cases[i].expected), observed_path.value()) <<
+ "i: " << i << ", root: " << root.value() << ", leaf: " << leaf;
+
+ // TODO(erikkay): It would be nice to have a unicode test append value to
+ // handle the case when AppendASCII is passed UTF8
+#if defined(OS_WIN)
+ std::string ascii = WideToUTF8(leaf);
+#elif defined(OS_POSIX) || defined(OS_STARBOARD)
+ std::string ascii = leaf;
+#endif
+ observed_str = root.AppendASCII(ascii);
+ EXPECT_EQ(FilePath::StringType(cases[i].expected), observed_str.value()) <<
+ "i: " << i << ", root: " << root.value() << ", leaf: " << leaf;
+ }
+}
+
+TEST_F(FilePathTest, StripTrailingSeparators) {
+ const struct UnaryTestData cases[] = {
+ { FPL(""), FPL("") },
+ { FPL("/"), FPL("/") },
+ { FPL("//"), FPL("//") },
+ { FPL("///"), FPL("/") },
+ { FPL("////"), FPL("/") },
+ { FPL("a/"), FPL("a") },
+ { FPL("a//"), FPL("a") },
+ { FPL("a///"), FPL("a") },
+ { FPL("a////"), FPL("a") },
+ { FPL("/a"), FPL("/a") },
+ { FPL("/a/"), FPL("/a") },
+ { FPL("/a//"), FPL("/a") },
+ { FPL("/a///"), FPL("/a") },
+ { FPL("/a////"), FPL("/a") },
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ { FPL("c:"), FPL("c:") },
+ { FPL("c:/"), FPL("c:/") },
+ { FPL("c://"), FPL("c://") },
+ { FPL("c:///"), FPL("c:/") },
+ { FPL("c:////"), FPL("c:/") },
+ { FPL("c:/a"), FPL("c:/a") },
+ { FPL("c:/a/"), FPL("c:/a") },
+ { FPL("c:/a//"), FPL("c:/a") },
+ { FPL("c:/a///"), FPL("c:/a") },
+ { FPL("c:/a////"), FPL("c:/a") },
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ { FPL("\\"), FPL("\\") },
+ { FPL("\\\\"), FPL("\\\\") },
+ { FPL("\\\\\\"), FPL("\\") },
+ { FPL("\\\\\\\\"), FPL("\\") },
+ { FPL("a\\"), FPL("a") },
+ { FPL("a\\\\"), FPL("a") },
+ { FPL("a\\\\\\"), FPL("a") },
+ { FPL("a\\\\\\\\"), FPL("a") },
+ { FPL("\\a"), FPL("\\a") },
+ { FPL("\\a\\"), FPL("\\a") },
+ { FPL("\\a\\\\"), FPL("\\a") },
+ { FPL("\\a\\\\\\"), FPL("\\a") },
+ { FPL("\\a\\\\\\\\"), FPL("\\a") },
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ { FPL("c:\\"), FPL("c:\\") },
+ { FPL("c:\\\\"), FPL("c:\\\\") },
+ { FPL("c:\\\\\\"), FPL("c:\\") },
+ { FPL("c:\\\\\\\\"), FPL("c:\\") },
+ { FPL("c:\\a"), FPL("c:\\a") },
+ { FPL("c:\\a\\"), FPL("c:\\a") },
+ { FPL("c:\\a\\\\"), FPL("c:\\a") },
+ { FPL("c:\\a\\\\\\"), FPL("c:\\a") },
+ { FPL("c:\\a\\\\\\\\"), FPL("c:\\a") },
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+#endif // FILE_PATH_USES_WIN_SEPARATORS
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ FilePath input(cases[i].input);
+ FilePath observed = input.StripTrailingSeparators();
+ EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) <<
+ "i: " << i << ", input: " << input.value();
+ }
+}
+
+TEST_F(FilePathTest, IsAbsolute) {
+ const struct UnaryBooleanTestData cases[] = {
+ { FPL(""), false },
+ { FPL("a"), false },
+ { FPL("c:"), false },
+ { FPL("c:a"), false },
+ { FPL("a/b"), false },
+ { FPL("//"), true },
+ { FPL("//a"), true },
+ { FPL("c:a/b"), false },
+ { FPL("?:/a"), false },
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ { FPL("/"), false },
+ { FPL("/a"), false },
+ { FPL("/."), false },
+ { FPL("/.."), false },
+ { FPL("c:/"), true },
+ { FPL("c:/a"), true },
+ { FPL("c:/."), true },
+ { FPL("c:/.."), true },
+ { FPL("C:/a"), true },
+ { FPL("d:/a"), true },
+#else // FILE_PATH_USES_DRIVE_LETTERS
+ { FPL("/"), true },
+ { FPL("/a"), true },
+ { FPL("/."), true },
+ { FPL("/.."), true },
+ { FPL("c:/"), false },
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ { FPL("a\\b"), false },
+ { FPL("\\\\"), true },
+ { FPL("\\\\a"), true },
+ { FPL("a\\b"), false },
+ { FPL("\\\\"), true },
+ { FPL("//a"), true },
+ { FPL("c:a\\b"), false },
+ { FPL("?:\\a"), false },
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ { FPL("\\"), false },
+ { FPL("\\a"), false },
+ { FPL("\\."), false },
+ { FPL("\\.."), false },
+ { FPL("c:\\"), true },
+ { FPL("c:\\"), true },
+ { FPL("c:\\a"), true },
+ { FPL("c:\\."), true },
+ { FPL("c:\\.."), true },
+ { FPL("C:\\a"), true },
+ { FPL("d:\\a"), true },
+#else // FILE_PATH_USES_DRIVE_LETTERS
+ { FPL("\\"), true },
+ { FPL("\\a"), true },
+ { FPL("\\."), true },
+ { FPL("\\.."), true },
+ { FPL("c:\\"), false },
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+#endif // FILE_PATH_USES_WIN_SEPARATORS
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ FilePath input(cases[i].input);
+ bool observed = input.IsAbsolute();
+ EXPECT_EQ(cases[i].expected, observed) <<
+ "i: " << i << ", input: " << input.value();
+ }
+}
+
+TEST_F(FilePathTest, PathComponentsTest) {
+ const struct UnaryTestData cases[] = {
+ { FPL("//foo/bar/baz/"), FPL("|//|foo|bar|baz")},
+ { FPL("///"), FPL("|/")},
+ { FPL("/foo//bar//baz/"), FPL("|/|foo|bar|baz")},
+ { FPL("/foo/bar/baz/"), FPL("|/|foo|bar|baz")},
+ { FPL("/foo/bar/baz//"), FPL("|/|foo|bar|baz")},
+ { FPL("/foo/bar/baz///"), FPL("|/|foo|bar|baz")},
+ { FPL("/foo/bar/baz"), FPL("|/|foo|bar|baz")},
+ { FPL("/foo/bar.bot/baz.txt"), FPL("|/|foo|bar.bot|baz.txt")},
+ { FPL("//foo//bar/baz"), FPL("|//|foo|bar|baz")},
+ { FPL("/"), FPL("|/")},
+ { FPL("foo"), FPL("|foo")},
+ { FPL(""), FPL("")},
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ { FPL("e:/foo"), FPL("|e:|/|foo")},
+ { FPL("e:/"), FPL("|e:|/")},
+ { FPL("e:"), FPL("|e:")},
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ { FPL("../foo"), FPL("|..|foo")},
+ { FPL("./foo"), FPL("|foo")},
+ { FPL("../foo/bar/"), FPL("|..|foo|bar") },
+ { FPL("\\\\foo\\bar\\baz\\"), FPL("|\\\\|foo|bar|baz")},
+ { FPL("\\\\\\"), FPL("|\\")},
+ { FPL("\\foo\\\\bar\\\\baz\\"), FPL("|\\|foo|bar|baz")},
+ { FPL("\\foo\\bar\\baz\\"), FPL("|\\|foo|bar|baz")},
+ { FPL("\\foo\\bar\\baz\\\\"), FPL("|\\|foo|bar|baz")},
+ { FPL("\\foo\\bar\\baz\\\\\\"), FPL("|\\|foo|bar|baz")},
+ { FPL("\\foo\\bar\\baz"), FPL("|\\|foo|bar|baz")},
+ { FPL("\\foo\\bar/baz\\\\\\"), FPL("|\\|foo|bar|baz")},
+ { FPL("/foo\\bar\\baz"), FPL("|/|foo|bar|baz")},
+ { FPL("\\foo\\bar.bot\\baz.txt"), FPL("|\\|foo|bar.bot|baz.txt")},
+ { FPL("\\\\foo\\\\bar\\baz"), FPL("|\\\\|foo|bar|baz")},
+ { FPL("\\"), FPL("|\\")},
+#endif // FILE_PATH_USES_WIN_SEPARATORS
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ FilePath input(cases[i].input);
+ std::vector<FilePath::StringType> comps;
+ input.GetComponents(&comps);
+
+ FilePath::StringType observed;
+ for (size_t j = 0; j < comps.size(); ++j) {
+ observed.append(FILE_PATH_LITERAL("|"), 1);
+ observed.append(comps[j]);
+ }
+ EXPECT_EQ(FilePath::StringType(cases[i].expected), observed) <<
+ "i: " << i << ", input: " << input.value();
+ }
+}
+
+TEST_F(FilePathTest, IsParentTest) {
+ const struct BinaryBooleanTestData cases[] = {
+ { { FPL("/"), FPL("/foo/bar/baz") }, true},
+ { { FPL("/foo/bar"), FPL("/foo/bar/baz") }, true},
+ { { FPL("/foo/bar/"), FPL("/foo/bar/baz") }, true},
+ { { FPL("//foo/bar/"), FPL("//foo/bar/baz") }, true},
+ { { FPL("/foo/bar"), FPL("/foo2/bar/baz") }, false},
+ { { FPL("/foo/bar.txt"), FPL("/foo/bar/baz") }, false},
+ { { FPL("/foo/bar"), FPL("/foo/bar2/baz") }, false},
+ { { FPL("/foo/bar"), FPL("/foo/bar") }, false},
+ { { FPL("/foo/bar/baz"), FPL("/foo/bar") }, false},
+ { { FPL("foo/bar"), FPL("foo/bar/baz") }, true},
+ { { FPL("foo/bar"), FPL("foo2/bar/baz") }, false},
+ { { FPL("foo/bar"), FPL("foo/bar2/baz") }, false},
+ { { FPL(""), FPL("foo") }, false},
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ { { FPL("c:/foo/bar"), FPL("c:/foo/bar/baz") }, true},
+ { { FPL("E:/foo/bar"), FPL("e:/foo/bar/baz") }, true},
+ { { FPL("f:/foo/bar"), FPL("F:/foo/bar/baz") }, true},
+ { { FPL("E:/Foo/bar"), FPL("e:/foo/bar/baz") }, false},
+ { { FPL("f:/foo/bar"), FPL("F:/foo/Bar/baz") }, false},
+ { { FPL("c:/"), FPL("c:/foo/bar/baz") }, true},
+ { { FPL("c:"), FPL("c:/foo/bar/baz") }, true},
+ { { FPL("c:/foo/bar"), FPL("d:/foo/bar/baz") }, false},
+ { { FPL("c:/foo/bar"), FPL("D:/foo/bar/baz") }, false},
+ { { FPL("C:/foo/bar"), FPL("d:/foo/bar/baz") }, false},
+ { { FPL("c:/foo/bar"), FPL("c:/foo2/bar/baz") }, false},
+ { { FPL("e:/foo/bar"), FPL("E:/foo2/bar/baz") }, false},
+ { { FPL("F:/foo/bar"), FPL("f:/foo2/bar/baz") }, false},
+ { { FPL("c:/foo/bar"), FPL("c:/foo/bar2/baz") }, false},
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ { { FPL("\\foo\\bar"), FPL("\\foo\\bar\\baz") }, true},
+ { { FPL("\\foo/bar"), FPL("\\foo\\bar\\baz") }, true},
+ { { FPL("\\foo/bar"), FPL("\\foo/bar/baz") }, true},
+ { { FPL("\\"), FPL("\\foo\\bar\\baz") }, true},
+ { { FPL(""), FPL("\\foo\\bar\\baz") }, false},
+ { { FPL("\\foo\\bar"), FPL("\\foo2\\bar\\baz") }, false},
+ { { FPL("\\foo\\bar"), FPL("\\foo\\bar2\\baz") }, false},
+#endif // FILE_PATH_USES_WIN_SEPARATORS
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ FilePath parent(cases[i].inputs[0]);
+ FilePath child(cases[i].inputs[1]);
+
+ EXPECT_EQ(parent.IsParent(child), cases[i].expected) <<
+ "i: " << i << ", parent: " << parent.value() << ", child: " <<
+ child.value();
+ }
+}
+
+TEST_F(FilePathTest, AppendRelativePathTest) {
+ const struct BinaryTestData cases[] = {
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ { { FPL("/"), FPL("/foo/bar/baz") }, FPL("foo\\bar\\baz")},
+#else // FILE_PATH_USES_WIN_SEPARATORS
+ { { FPL("/"), FPL("/foo/bar/baz") }, FPL("foo/bar/baz")},
+#endif // FILE_PATH_USES_WIN_SEPARATORS
+ { { FPL("/foo/bar"), FPL("/foo/bar/baz") }, FPL("baz")},
+ { { FPL("/foo/bar/"), FPL("/foo/bar/baz") }, FPL("baz")},
+ { { FPL("//foo/bar/"), FPL("//foo/bar/baz") }, FPL("baz")},
+ { { FPL("/foo/bar"), FPL("/foo2/bar/baz") }, FPL("")},
+ { { FPL("/foo/bar.txt"), FPL("/foo/bar/baz") }, FPL("")},
+ { { FPL("/foo/bar"), FPL("/foo/bar2/baz") }, FPL("")},
+ { { FPL("/foo/bar"), FPL("/foo/bar") }, FPL("")},
+ { { FPL("/foo/bar/baz"), FPL("/foo/bar") }, FPL("")},
+ { { FPL("foo/bar"), FPL("foo/bar/baz") }, FPL("baz")},
+ { { FPL("foo/bar"), FPL("foo2/bar/baz") }, FPL("")},
+ { { FPL("foo/bar"), FPL("foo/bar2/baz") }, FPL("")},
+ { { FPL(""), FPL("foo") }, FPL("")},
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ { { FPL("c:/foo/bar"), FPL("c:/foo/bar/baz") }, FPL("baz")},
+ { { FPL("E:/foo/bar"), FPL("e:/foo/bar/baz") }, FPL("baz")},
+ { { FPL("f:/foo/bar"), FPL("F:/foo/bar/baz") }, FPL("baz")},
+ { { FPL("E:/Foo/bar"), FPL("e:/foo/bar/baz") }, FPL("")},
+ { { FPL("f:/foo/bar"), FPL("F:/foo/Bar/baz") }, FPL("")},
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ { { FPL("c:/"), FPL("c:/foo/bar/baz") }, FPL("foo\\bar\\baz")},
+ // TODO(akalin): Figure out how to handle the corner case in the
+ // commented-out test case below. Appending to an empty path gives
+ // /foo\bar\baz but appending to a nonempty path "blah" gives
+ // blah\foo\bar\baz.
+ // { { FPL("c:"), FPL("c:/foo/bar/baz") }, FPL("foo\\bar\\baz")},
+#endif // FILE_PATH_USES_WIN_SEPARATORS
+ { { FPL("c:/foo/bar"), FPL("d:/foo/bar/baz") }, FPL("")},
+ { { FPL("c:/foo/bar"), FPL("D:/foo/bar/baz") }, FPL("")},
+ { { FPL("C:/foo/bar"), FPL("d:/foo/bar/baz") }, FPL("")},
+ { { FPL("c:/foo/bar"), FPL("c:/foo2/bar/baz") }, FPL("")},
+ { { FPL("e:/foo/bar"), FPL("E:/foo2/bar/baz") }, FPL("")},
+ { { FPL("F:/foo/bar"), FPL("f:/foo2/bar/baz") }, FPL("")},
+ { { FPL("c:/foo/bar"), FPL("c:/foo/bar2/baz") }, FPL("")},
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ { { FPL("\\foo\\bar"), FPL("\\foo\\bar\\baz") }, FPL("baz")},
+ { { FPL("\\foo/bar"), FPL("\\foo\\bar\\baz") }, FPL("baz")},
+ { { FPL("\\foo/bar"), FPL("\\foo/bar/baz") }, FPL("baz")},
+ { { FPL("\\"), FPL("\\foo\\bar\\baz") }, FPL("foo\\bar\\baz")},
+ { { FPL(""), FPL("\\foo\\bar\\baz") }, FPL("")},
+ { { FPL("\\foo\\bar"), FPL("\\foo2\\bar\\baz") }, FPL("")},
+ { { FPL("\\foo\\bar"), FPL("\\foo\\bar2\\baz") }, FPL("")},
+#endif // FILE_PATH_USES_WIN_SEPARATORS
+ };
+
+ const FilePath base(FPL("blah"));
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ FilePath parent(cases[i].inputs[0]);
+ FilePath child(cases[i].inputs[1]);
+ {
+ FilePath result;
+ bool success = parent.AppendRelativePath(child, &result);
+ EXPECT_EQ(cases[i].expected[0] != '\0', success) <<
+ "i: " << i << ", parent: " << parent.value() << ", child: " <<
+ child.value();
+ EXPECT_STREQ(cases[i].expected, result.value().c_str()) <<
+ "i: " << i << ", parent: " << parent.value() << ", child: " <<
+ child.value();
+ }
+ {
+ FilePath result(base);
+ bool success = parent.AppendRelativePath(child, &result);
+ EXPECT_EQ(cases[i].expected[0] != '\0', success) <<
+ "i: " << i << ", parent: " << parent.value() << ", child: " <<
+ child.value();
+ EXPECT_EQ(base.Append(cases[i].expected).value(), result.value()) <<
+ "i: " << i << ", parent: " << parent.value() << ", child: " <<
+ child.value();
+ }
+ }
+}
+
+TEST_F(FilePathTest, EqualityTest) {
+ const struct BinaryBooleanTestData cases[] = {
+ { { FPL("/foo/bar/baz"), FPL("/foo/bar/baz") }, true},
+ { { FPL("/foo/bar"), FPL("/foo/bar/baz") }, false},
+ { { FPL("/foo/bar/baz"), FPL("/foo/bar") }, false},
+ { { FPL("//foo/bar/"), FPL("//foo/bar/") }, true},
+ { { FPL("/foo/bar"), FPL("/foo2/bar") }, false},
+ { { FPL("/foo/bar.txt"), FPL("/foo/bar") }, false},
+ { { FPL("foo/bar"), FPL("foo/bar") }, true},
+ { { FPL("foo/bar"), FPL("foo/bar/baz") }, false},
+ { { FPL(""), FPL("foo") }, false},
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ { { FPL("c:/foo/bar"), FPL("c:/foo/bar") }, true},
+ { { FPL("E:/foo/bar"), FPL("e:/foo/bar") }, true},
+ { { FPL("f:/foo/bar"), FPL("F:/foo/bar") }, true},
+ { { FPL("E:/Foo/bar"), FPL("e:/foo/bar") }, false},
+ { { FPL("f:/foo/bar"), FPL("F:/foo/Bar") }, false},
+ { { FPL("c:/"), FPL("c:/") }, true},
+ { { FPL("c:"), FPL("c:") }, true},
+ { { FPL("c:/foo/bar"), FPL("d:/foo/bar") }, false},
+ { { FPL("c:/foo/bar"), FPL("D:/foo/bar") }, false},
+ { { FPL("C:/foo/bar"), FPL("d:/foo/bar") }, false},
+ { { FPL("c:/foo/bar"), FPL("c:/foo2/bar") }, false},
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ { { FPL("\\foo\\bar"), FPL("\\foo\\bar") }, true},
+ { { FPL("\\foo/bar"), FPL("\\foo/bar") }, true},
+ { { FPL("\\foo/bar"), FPL("\\foo\\bar") }, false},
+ { { FPL("\\"), FPL("\\") }, true},
+ { { FPL("\\"), FPL("/") }, false},
+ { { FPL(""), FPL("\\") }, false},
+ { { FPL("\\foo\\bar"), FPL("\\foo2\\bar") }, false},
+ { { FPL("\\foo\\bar"), FPL("\\foo\\bar2") }, false},
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ { { FPL("c:\\foo\\bar"), FPL("c:\\foo\\bar") }, true},
+ { { FPL("E:\\foo\\bar"), FPL("e:\\foo\\bar") }, true},
+ { { FPL("f:\\foo\\bar"), FPL("F:\\foo/bar") }, false},
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+#endif // FILE_PATH_USES_WIN_SEPARATORS
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ FilePath a(cases[i].inputs[0]);
+ FilePath b(cases[i].inputs[1]);
+
+ EXPECT_EQ(a == b, cases[i].expected) <<
+ "equality i: " << i << ", a: " << a.value() << ", b: " <<
+ b.value();
+ }
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ FilePath a(cases[i].inputs[0]);
+ FilePath b(cases[i].inputs[1]);
+
+ EXPECT_EQ(a != b, !cases[i].expected) <<
+ "inequality i: " << i << ", a: " << a.value() << ", b: " <<
+ b.value();
+ }
+}
+
+TEST_F(FilePathTest, Extension) {
+ FilePath base_dir(FILE_PATH_LITERAL("base_dir"));
+
+ FilePath jpg = base_dir.Append(FILE_PATH_LITERAL("foo.jpg"));
+ EXPECT_EQ(FILE_PATH_LITERAL(".jpg"), jpg.Extension());
+
+ FilePath base = jpg.BaseName().RemoveExtension();
+ EXPECT_EQ(FILE_PATH_LITERAL("foo"), base.value());
+
+ FilePath path_no_ext = base_dir.Append(base);
+ EXPECT_EQ(path_no_ext.value(), jpg.RemoveExtension().value());
+
+ EXPECT_EQ(path_no_ext.value(), path_no_ext.RemoveExtension().value());
+ EXPECT_EQ(FILE_PATH_LITERAL(""), path_no_ext.Extension());
+}
+
+TEST_F(FilePathTest, Extension2) {
+ const struct UnaryTestData cases[] = {
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ { FPL("C:\\a\\b\\c.ext"), FPL(".ext") },
+ { FPL("C:\\a\\b\\c."), FPL(".") },
+ { FPL("C:\\a\\b\\c"), FPL("") },
+ { FPL("C:\\a\\b\\"), FPL("") },
+ { FPL("C:\\a\\b.\\"), FPL(".") },
+ { FPL("C:\\a\\b\\c.ext1.ext2"), FPL(".ext2") },
+ { FPL("C:\\foo.bar\\\\\\"), FPL(".bar") },
+ { FPL("C:\\foo.bar\\.."), FPL("") },
+ { FPL("C:\\foo.bar\\..\\\\"), FPL("") },
+#endif
+ { FPL("/foo/bar/baz.ext"), FPL(".ext") },
+ { FPL("/foo/bar/baz."), FPL(".") },
+ { FPL("/foo/bar/baz.."), FPL(".") },
+ { FPL("/foo/bar/baz"), FPL("") },
+ { FPL("/foo/bar/"), FPL("") },
+ { FPL("/foo/bar./"), FPL(".") },
+ { FPL("/foo/bar/baz.ext1.ext2"), FPL(".ext2") },
+ { FPL("/foo.tar.gz"), FPL(".tar.gz") },
+ { FPL("/foo.tar.Z"), FPL(".tar.Z") },
+ { FPL("/foo.tar.bz2"), FPL(".tar.bz2") },
+ { FPL("/subversion-1.6.12.zip"), FPL(".zip") },
+ { FPL("/foo.1234.gz"), FPL(".1234.gz") },
+ { FPL("/foo.12345.gz"), FPL(".gz") },
+ { FPL("/foo..gz"), FPL(".gz") },
+ { FPL("/foo.1234.tar.gz"), FPL(".tar.gz") },
+ { FPL("/foo.tar.tar.gz"), FPL(".tar.gz") },
+ { FPL("/foo.tar.gz.gz"), FPL(".gz.gz") },
+ { FPL("."), FPL("") },
+ { FPL(".."), FPL("") },
+ { FPL("./foo"), FPL("") },
+ { FPL("./foo.ext"), FPL(".ext") },
+ { FPL("/foo.ext1/bar.ext2"), FPL(".ext2") },
+ { FPL("/foo.bar////"), FPL(".bar") },
+ { FPL("/foo.bar/.."), FPL("") },
+ { FPL("/foo.bar/..////"), FPL("") },
+ { FPL("/foo.1234.user.js"), FPL(".user.js") },
+ { FPL("foo.user.js"), FPL(".user.js") },
+ { FPL("/foo.1234.luser.js"), FPL(".js") },
+ { FPL("/user.js"), FPL(".js") },
+ };
+ for (unsigned int i = 0; i < arraysize(cases); ++i) {
+ FilePath path(cases[i].input);
+ FilePath::StringType extension = path.Extension();
+ EXPECT_STREQ(cases[i].expected, extension.c_str()) << "i: " << i <<
+ ", path: " << path.value();
+ }
+}
+
+TEST_F(FilePathTest, InsertBeforeExtension) {
+ const struct BinaryTestData cases[] = {
+ { { FPL(""), FPL("") }, FPL("") },
+ { { FPL(""), FPL("txt") }, FPL("") },
+ { { FPL("."), FPL("txt") }, FPL("") },
+ { { FPL(".."), FPL("txt") }, FPL("") },
+ { { FPL("foo.dll"), FPL("txt") }, FPL("footxt.dll") },
+ { { FPL("."), FPL("") }, FPL(".") },
+ { { FPL("foo.dll"), FPL(".txt") }, FPL("foo.txt.dll") },
+ { { FPL("foo"), FPL("txt") }, FPL("footxt") },
+ { { FPL("foo"), FPL(".txt") }, FPL("foo.txt") },
+ { { FPL("foo.baz.dll"), FPL("txt") }, FPL("foo.baztxt.dll") },
+ { { FPL("foo.baz.dll"), FPL(".txt") }, FPL("foo.baz.txt.dll") },
+ { { FPL("foo.dll"), FPL("") }, FPL("foo.dll") },
+ { { FPL("foo.dll"), FPL(".") }, FPL("foo..dll") },
+ { { FPL("foo"), FPL("") }, FPL("foo") },
+ { { FPL("foo"), FPL(".") }, FPL("foo.") },
+ { { FPL("foo.baz.dll"), FPL("") }, FPL("foo.baz.dll") },
+ { { FPL("foo.baz.dll"), FPL(".") }, FPL("foo.baz..dll") },
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ { { FPL("\\"), FPL("") }, FPL("\\") },
+ { { FPL("\\"), FPL("txt") }, FPL("\\txt") },
+ { { FPL("\\."), FPL("txt") }, FPL("") },
+ { { FPL("\\.."), FPL("txt") }, FPL("") },
+ { { FPL("\\."), FPL("") }, FPL("\\.") },
+ { { FPL("C:\\bar\\foo.dll"), FPL("txt") },
+ FPL("C:\\bar\\footxt.dll") },
+ { { FPL("C:\\bar.baz\\foodll"), FPL("txt") },
+ FPL("C:\\bar.baz\\foodlltxt") },
+ { { FPL("C:\\bar.baz\\foo.dll"), FPL("txt") },
+ FPL("C:\\bar.baz\\footxt.dll") },
+ { { FPL("C:\\bar.baz\\foo.dll.exe"), FPL("txt") },
+ FPL("C:\\bar.baz\\foo.dlltxt.exe") },
+ { { FPL("C:\\bar.baz\\foo"), FPL("") },
+ FPL("C:\\bar.baz\\foo") },
+ { { FPL("C:\\bar.baz\\foo.exe"), FPL("") },
+ FPL("C:\\bar.baz\\foo.exe") },
+ { { FPL("C:\\bar.baz\\foo.dll.exe"), FPL("") },
+ FPL("C:\\bar.baz\\foo.dll.exe") },
+ { { FPL("C:\\bar\\baz\\foo.exe"), FPL(" (1)") },
+ FPL("C:\\bar\\baz\\foo (1).exe") },
+ { { FPL("C:\\foo.baz\\\\"), FPL(" (1)") }, FPL("C:\\foo (1).baz") },
+ { { FPL("C:\\foo.baz\\..\\"), FPL(" (1)") }, FPL("") },
+#endif
+ { { FPL("/"), FPL("") }, FPL("/") },
+ { { FPL("/"), FPL("txt") }, FPL("/txt") },
+ { { FPL("/."), FPL("txt") }, FPL("") },
+ { { FPL("/.."), FPL("txt") }, FPL("") },
+ { { FPL("/."), FPL("") }, FPL("/.") },
+ { { FPL("/bar/foo.dll"), FPL("txt") }, FPL("/bar/footxt.dll") },
+ { { FPL("/bar.baz/foodll"), FPL("txt") }, FPL("/bar.baz/foodlltxt") },
+ { { FPL("/bar.baz/foo.dll"), FPL("txt") }, FPL("/bar.baz/footxt.dll") },
+ { { FPL("/bar.baz/foo.dll.exe"), FPL("txt") },
+ FPL("/bar.baz/foo.dlltxt.exe") },
+ { { FPL("/bar.baz/foo"), FPL("") }, FPL("/bar.baz/foo") },
+ { { FPL("/bar.baz/foo.exe"), FPL("") }, FPL("/bar.baz/foo.exe") },
+ { { FPL("/bar.baz/foo.dll.exe"), FPL("") }, FPL("/bar.baz/foo.dll.exe") },
+ { { FPL("/bar/baz/foo.exe"), FPL(" (1)") }, FPL("/bar/baz/foo (1).exe") },
+ { { FPL("/bar/baz/..////"), FPL(" (1)") }, FPL("") },
+ };
+ for (unsigned int i = 0; i < arraysize(cases); ++i) {
+ FilePath path(cases[i].inputs[0]);
+ FilePath result = path.InsertBeforeExtension(cases[i].inputs[1]);
+ EXPECT_EQ(cases[i].expected, result.value()) << "i: " << i <<
+ ", path: " << path.value() << ", insert: " << cases[i].inputs[1];
+ }
+}
+
+TEST_F(FilePathTest, RemoveExtension) {
+ const struct UnaryTestData cases[] = {
+ { FPL(""), FPL("") },
+ { FPL("."), FPL(".") },
+ { FPL(".."), FPL("..") },
+ { FPL("foo.dll"), FPL("foo") },
+ { FPL("./foo.dll"), FPL("./foo") },
+ { FPL("foo..dll"), FPL("foo.") },
+ { FPL("foo"), FPL("foo") },
+ { FPL("foo."), FPL("foo") },
+ { FPL("foo.."), FPL("foo.") },
+ { FPL("foo.baz.dll"), FPL("foo.baz") },
+ { FPL("foo.tar.gz"), FPL("foo") },
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ { FPL("C:\\foo.bar\\foo"), FPL("C:\\foo.bar\\foo") },
+ { FPL("C:\\foo.bar\\..\\\\"), FPL("C:\\foo.bar\\..\\\\") },
+#endif
+ { FPL("/foo.bar/foo"), FPL("/foo.bar/foo") },
+ { FPL("/foo.bar/..////"), FPL("/foo.bar/..////") },
+ };
+ for (unsigned int i = 0; i < arraysize(cases); ++i) {
+ FilePath path(cases[i].input);
+ FilePath removed = path.RemoveExtension();
+ EXPECT_EQ(cases[i].expected, removed.value()) << "i: " << i <<
+ ", path: " << path.value();
+ }
+}
+
+TEST_F(FilePathTest, ReplaceExtension) {
+ const struct BinaryTestData cases[] = {
+ { { FPL(""), FPL("") }, FPL("") },
+ { { FPL(""), FPL("txt") }, FPL("") },
+ { { FPL("."), FPL("txt") }, FPL("") },
+ { { FPL(".."), FPL("txt") }, FPL("") },
+ { { FPL("."), FPL("") }, FPL("") },
+ { { FPL("foo.dll"), FPL("txt") }, FPL("foo.txt") },
+ { { FPL("./foo.dll"), FPL("txt") }, FPL("./foo.txt") },
+ { { FPL("foo..dll"), FPL("txt") }, FPL("foo..txt") },
+ { { FPL("foo.dll"), FPL(".txt") }, FPL("foo.txt") },
+ { { FPL("foo"), FPL("txt") }, FPL("foo.txt") },
+ { { FPL("foo."), FPL("txt") }, FPL("foo.txt") },
+ { { FPL("foo.."), FPL("txt") }, FPL("foo..txt") },
+ { { FPL("foo"), FPL(".txt") }, FPL("foo.txt") },
+ { { FPL("foo.baz.dll"), FPL("txt") }, FPL("foo.baz.txt") },
+ { { FPL("foo.baz.dll"), FPL(".txt") }, FPL("foo.baz.txt") },
+ { { FPL("foo.dll"), FPL("") }, FPL("foo") },
+ { { FPL("foo.dll"), FPL(".") }, FPL("foo") },
+ { { FPL("foo"), FPL("") }, FPL("foo") },
+ { { FPL("foo"), FPL(".") }, FPL("foo") },
+ { { FPL("foo.baz.dll"), FPL("") }, FPL("foo.baz") },
+ { { FPL("foo.baz.dll"), FPL(".") }, FPL("foo.baz") },
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ { { FPL("C:\\foo.bar\\foo"), FPL("baz") }, FPL("C:\\foo.bar\\foo.baz") },
+ { { FPL("C:\\foo.bar\\..\\\\"), FPL("baz") }, FPL("") },
+#endif
+ { { FPL("/foo.bar/foo"), FPL("baz") }, FPL("/foo.bar/foo.baz") },
+ { { FPL("/foo.bar/..////"), FPL("baz") }, FPL("") },
+ };
+ for (unsigned int i = 0; i < arraysize(cases); ++i) {
+ FilePath path(cases[i].inputs[0]);
+ FilePath replaced = path.ReplaceExtension(cases[i].inputs[1]);
+ EXPECT_EQ(cases[i].expected, replaced.value()) << "i: " << i <<
+ ", path: " << path.value() << ", replace: " << cases[i].inputs[1];
+ }
+}
+
+TEST_F(FilePathTest, AddExtension) {
+ const struct BinaryTestData cases[] = {
+ { { FPL(""), FPL("") }, FPL("") },
+ { { FPL(""), FPL("txt") }, FPL("") },
+ { { FPL("."), FPL("txt") }, FPL("") },
+ { { FPL(".."), FPL("txt") }, FPL("") },
+ { { FPL("."), FPL("") }, FPL("") },
+ { { FPL("foo.dll"), FPL("txt") }, FPL("foo.dll.txt") },
+ { { FPL("./foo.dll"), FPL("txt") }, FPL("./foo.dll.txt") },
+ { { FPL("foo..dll"), FPL("txt") }, FPL("foo..dll.txt") },
+ { { FPL("foo.dll"), FPL(".txt") }, FPL("foo.dll.txt") },
+ { { FPL("foo"), FPL("txt") }, FPL("foo.txt") },
+ { { FPL("foo."), FPL("txt") }, FPL("foo.txt") },
+ { { FPL("foo.."), FPL("txt") }, FPL("foo..txt") },
+ { { FPL("foo"), FPL(".txt") }, FPL("foo.txt") },
+ { { FPL("foo.baz.dll"), FPL("txt") }, FPL("foo.baz.dll.txt") },
+ { { FPL("foo.baz.dll"), FPL(".txt") }, FPL("foo.baz.dll.txt") },
+ { { FPL("foo.dll"), FPL("") }, FPL("foo.dll") },
+ { { FPL("foo.dll"), FPL(".") }, FPL("foo.dll") },
+ { { FPL("foo"), FPL("") }, FPL("foo") },
+ { { FPL("foo"), FPL(".") }, FPL("foo") },
+ { { FPL("foo.baz.dll"), FPL("") }, FPL("foo.baz.dll") },
+ { { FPL("foo.baz.dll"), FPL(".") }, FPL("foo.baz.dll") },
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ { { FPL("C:\\foo.bar\\foo"), FPL("baz") }, FPL("C:\\foo.bar\\foo.baz") },
+ { { FPL("C:\\foo.bar\\..\\\\"), FPL("baz") }, FPL("") },
+#endif
+ { { FPL("/foo.bar/foo"), FPL("baz") }, FPL("/foo.bar/foo.baz") },
+ { { FPL("/foo.bar/..////"), FPL("baz") }, FPL("") },
+ };
+ for (unsigned int i = 0; i < arraysize(cases); ++i) {
+ FilePath path(cases[i].inputs[0]);
+ FilePath added = path.AddExtension(cases[i].inputs[1]);
+ EXPECT_EQ(cases[i].expected, added.value()) << "i: " << i <<
+ ", path: " << path.value() << ", add: " << cases[i].inputs[1];
+ }
+}
+
+TEST_F(FilePathTest, MatchesExtension) {
+ const struct BinaryBooleanTestData cases[] = {
+ { { FPL("foo"), FPL("") }, true},
+ { { FPL("foo"), FPL(".") }, false},
+ { { FPL("foo."), FPL("") }, false},
+ { { FPL("foo."), FPL(".") }, true},
+ { { FPL("foo.txt"), FPL(".dll") }, false},
+ { { FPL("foo.txt"), FPL(".txt") }, true},
+ { { FPL("foo.txt.dll"), FPL(".txt") }, false},
+ { { FPL("foo.txt.dll"), FPL(".dll") }, true},
+ { { FPL("foo.TXT"), FPL(".txt") }, true},
+ { { FPL("foo.txt"), FPL(".TXT") }, true},
+ { { FPL("foo.tXt"), FPL(".txt") }, true},
+ { { FPL("foo.txt"), FPL(".tXt") }, true},
+ { { FPL("foo.tXt"), FPL(".TXT") }, true},
+ { { FPL("foo.tXt"), FPL(".tXt") }, true},
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ { { FPL("c:/foo.txt.dll"), FPL(".txt") }, false},
+ { { FPL("c:/foo.txt"), FPL(".txt") }, true},
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ { { FPL("c:\\bar\\foo.txt.dll"), FPL(".txt") }, false},
+ { { FPL("c:\\bar\\foo.txt"), FPL(".txt") }, true},
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+ { { FPL("/bar/foo.txt.dll"), FPL(".txt") }, false},
+ { { FPL("/bar/foo.txt"), FPL(".txt") }, true},
+#if defined(OS_WIN) || defined(OS_MACOSX)
+ // Umlauts A, O, U: direct comparison, and upper case vs. lower case
+ { { FPL("foo.\u00E4\u00F6\u00FC"), FPL(".\u00E4\u00F6\u00FC") }, true},
+ { { FPL("foo.\u00C4\u00D6\u00DC"), FPL(".\u00E4\u00F6\u00FC") }, true},
+ // C with circumflex: direct comparison, and upper case vs. lower case
+ { { FPL("foo.\u0109"), FPL(".\u0109") }, true},
+ { { FPL("foo.\u0108"), FPL(".\u0109") }, true},
+#endif
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ FilePath path(cases[i].inputs[0]);
+ FilePath::StringType ext(cases[i].inputs[1]);
+
+ EXPECT_EQ(cases[i].expected, path.MatchesExtension(ext)) <<
+ "i: " << i << ", path: " << path.value() << ", ext: " << ext;
+ }
+}
+
+TEST_F(FilePathTest, CompareIgnoreCase) {
+ const struct BinaryIntTestData cases[] = {
+ { { FPL("foo"), FPL("foo") }, 0},
+ { { FPL("FOO"), FPL("foo") }, 0},
+ { { FPL("foo.ext"), FPL("foo.ext") }, 0},
+ { { FPL("FOO.EXT"), FPL("foo.ext") }, 0},
+ { { FPL("Foo.Ext"), FPL("foo.ext") }, 0},
+ { { FPL("foO"), FPL("foo") }, 0},
+ { { FPL("foo"), FPL("foO") }, 0},
+ { { FPL("fOo"), FPL("foo") }, 0},
+ { { FPL("foo"), FPL("fOo") }, 0},
+ { { FPL("bar"), FPL("foo") }, -1},
+ { { FPL("foo"), FPL("bar") }, 1},
+ { { FPL("BAR"), FPL("foo") }, -1},
+ { { FPL("FOO"), FPL("bar") }, 1},
+ { { FPL("bar"), FPL("FOO") }, -1},
+ { { FPL("foo"), FPL("BAR") }, 1},
+ { { FPL("BAR"), FPL("FOO") }, -1},
+ { { FPL("FOO"), FPL("BAR") }, 1},
+ // German "Eszett" (lower case and the new-fangled upper case)
+ // Note that uc(<lowercase eszett>) => "SS", NOT <uppercase eszett>!
+ // However, neither Windows nor Mac OSX converts these.
+ // (or even have glyphs for <uppercase eszett>)
+ { { FPL("\u00DF"), FPL("\u00DF") }, 0},
+// TODO: Since Cobalt on Windows is compiled in POSIX emulation mode, strings
+// are single-byte. Unicode characters cannot be represented in
+// single-byte encoding, so tests below are doomed to fail. Re-enable
+// tests after getting rid of POSIX emulation.
+// TODO: Starboard, the replacement for POSIX emulation, would like path
+// encoding to be consistent across platforms in common code. Lowest
+// common denominator suggests we should still use single-byte paths. The
+// TODO is to discuss and act on these TODOs.
+#if !defined(COBALT_WIN) && !defined(OS_STARBOARD)
+ { { FPL("\u1E9E"), FPL("\u1E9E") }, 0},
+#endif
+// CompareIgnoreCase is tertiary. These glyphs don't exist.
+#if !defined(COBALT) && !defined(OS_STARBOARD)
+ { { FPL("\u00DF"), FPL("\u1E9E") }, 1},
+ { { FPL("SS"), FPL("\u00DF") }, -1},
+ { { FPL("SS"), FPL("\u1E9E") }, 1},
+#endif
+#if defined(OS_WIN) || defined(OS_MACOSX)
+ // Umlauts A, O, U: direct comparison, and upper case vs. lower case
+ { { FPL("\u00E4\u00F6\u00FC"), FPL("\u00E4\u00F6\u00FC") }, 0},
+ { { FPL("\u00C4\u00D6\u00DC"), FPL("\u00E4\u00F6\u00FC") }, 0},
+ // C with circumflex: direct comparison, and upper case vs. lower case
+ { { FPL("\u0109"), FPL("\u0109") }, 0},
+ { { FPL("\u0108"), FPL("\u0109") }, 0},
+ // Cyrillic letter SHA: direct comparison, and upper case vs. lower case
+ { { FPL("\u0428"), FPL("\u0428") }, 0},
+ { { FPL("\u0428"), FPL("\u0448") }, 0},
+ // Greek letter DELTA: direct comparison, and upper case vs. lower case
+ { { FPL("\u0394"), FPL("\u0394") }, 0},
+ { { FPL("\u0394"), FPL("\u03B4") }, 0},
+ // Japanese full-width A: direct comparison, and upper case vs. lower case
+ // Note that full-width and standard characters are considered different.
+ { { FPL("\uFF21"), FPL("\uFF21") }, 0},
+ { { FPL("\uFF21"), FPL("\uFF41") }, 0},
+ { { FPL("A"), FPL("\uFF21") }, -1},
+ { { FPL("A"), FPL("\uFF41") }, -1},
+ { { FPL("a"), FPL("\uFF21") }, -1},
+ { { FPL("a"), FPL("\uFF41") }, -1},
+#endif
+#if defined(OS_MACOSX)
+ // Codepoints > 0x1000
+ // Georgian letter DON: direct comparison, and upper case vs. lower case
+ { { FPL("\u10A3"), FPL("\u10A3") }, 0},
+ { { FPL("\u10A3"), FPL("\u10D3") }, 0},
+ // Combining characters vs. pre-composed characters, upper and lower case
+ { { FPL("k\u0301u\u032Do\u0304\u0301n"), FPL("\u1E31\u1E77\u1E53n") }, 0},
+ { { FPL("k\u0301u\u032Do\u0304\u0301n"), FPL("kuon") }, 1},
+ { { FPL("kuon"), FPL("k\u0301u\u032Do\u0304\u0301n") }, -1},
+ { { FPL("K\u0301U\u032DO\u0304\u0301N"), FPL("KUON") }, 1},
+ { { FPL("KUON"), FPL("K\u0301U\u032DO\u0304\u0301N") }, -1},
+ { { FPL("k\u0301u\u032Do\u0304\u0301n"), FPL("KUON") }, 1},
+ { { FPL("K\u0301U\u032DO\u0304\u0301N"), FPL("\u1E31\u1E77\u1E53n") }, 0},
+ { { FPL("k\u0301u\u032Do\u0304\u0301n"), FPL("\u1E30\u1E76\u1E52n") }, 0},
+ { { FPL("k\u0301u\u032Do\u0304\u0302n"), FPL("\u1E30\u1E76\u1E52n") }, 1},
+#endif
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ FilePath::StringType s1(cases[i].inputs[0]);
+ FilePath::StringType s2(cases[i].inputs[1]);
+ int result = FilePath::CompareIgnoreCase(s1, s2);
+ EXPECT_EQ(cases[i].expected, result) <<
+ "i: " << i << ", s1: " << s1 << ", s2: " << s2;
+ }
+}
+
+TEST_F(FilePathTest, ReferencesParent) {
+ const struct UnaryBooleanTestData cases[] = {
+ { FPL("."), false },
+ { FPL(".."), true },
+ { FPL("a.."), false },
+ { FPL("..a"), false },
+ { FPL("../"), true },
+ { FPL("/.."), true },
+ { FPL("/../"), true },
+ { FPL("/a../"), false },
+ { FPL("/..a/"), false },
+ { FPL("//.."), true },
+ { FPL("..//"), true },
+ { FPL("//..//"), true },
+ { FPL("a//..//c"), true },
+ { FPL("../b/c"), true },
+ { FPL("/../b/c"), true },
+ { FPL("a/b/.."), true },
+ { FPL("a/b/../"), true },
+ { FPL("a/../c"), true },
+ { FPL("a/b/c"), false },
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ FilePath input(cases[i].input);
+ bool observed = input.ReferencesParent();
+ EXPECT_EQ(cases[i].expected, observed) <<
+ "i: " << i << ", input: " << input.value();
+ }
+}
+
+TEST_F(FilePathTest, FromUTF8Unsafe_And_AsUTF8Unsafe) {
+ const struct UTF8TestData cases[] = {
+ { FPL("foo.txt"), "foo.txt" },
+#if !defined(__LB_SHELL__)
+ // "aeo" with accents. Use http://0xcc.net/jsescape/ to decode them.
+ { FPL("\u00E0\u00E8\u00F2.txt"), "\xC3\xA0\xC3\xA8\xC3\xB2.txt" },
+ // Full-width "ABC".
+ { FPL("\uFF21\uFF22\uFF23.txt"),
+ "\xEF\xBC\xA1\xEF\xBC\xA2\xEF\xBC\xA3.txt" },
+#endif
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ // Test FromUTF8Unsafe() works.
+ FilePath from_utf8 = FilePath::FromUTF8Unsafe(cases[i].utf8);
+ EXPECT_EQ(cases[i].native, from_utf8.value())
+ << "i: " << i << ", input: " << cases[i].native;
+ // Test AsUTF8Unsafe() works.
+ FilePath from_native = FilePath(cases[i].native);
+ EXPECT_EQ(cases[i].utf8, from_native.AsUTF8Unsafe())
+ << "i: " << i << ", input: " << cases[i].native;
+ // Test the two file paths are identical.
+ EXPECT_EQ(from_utf8.value(), from_native.value());
+ }
+}
+
+TEST_F(FilePathTest, ConstructWithNUL) {
+ // Assert FPS() works.
+ ASSERT_TRUE(FPS("a\0b").length() == 3);
+
+ // Test constructor strips '\0'
+ FilePath path(FPS("a\0b"));
+ EXPECT_TRUE(path.value().length() == 1);
+ EXPECT_EQ(path.value(), FPL("a"));
+}
+
+TEST_F(FilePathTest, AppendWithNUL) {
+ // Assert FPS() works.
+ ASSERT_TRUE(FPS("b\0b").length() == 3);
+
+ // Test Append() strips '\0'
+ FilePath path(FPL("a"));
+ path = path.Append(FPS("b\0b"));
+ EXPECT_TRUE(path.value().length() == 3);
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ EXPECT_EQ(path.value(), FPL("a\\b"));
+#else
+ EXPECT_EQ(path.value(), FPL("a/b"));
+#endif
+}
+
+TEST_F(FilePathTest, ReferencesParentWithNUL) {
+ // Assert FPS() works.
+ ASSERT_TRUE(FPS("..\0").length() == 3);
+
+ // Test ReferencesParent() doesn't break with "..\0"
+ FilePath path(FPS("..\0"));
+ EXPECT_TRUE(path.ReferencesParent());
+}
+
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+TEST_F(FilePathTest, NormalizePathSeparators) {
+ const struct UnaryTestData cases[] = {
+ { FPL("foo/bar"), FPL("foo\\bar") },
+ { FPL("foo/bar\\betz"), FPL("foo\\bar\\betz") },
+ { FPL("foo\\bar"), FPL("foo\\bar") },
+ { FPL("foo\\bar/betz"), FPL("foo\\bar\\betz") },
+ { FPL("foo"), FPL("foo") },
+ // Trailing slashes don't automatically get stripped. That's what
+ // StripTrailingSeparators() is for.
+ { FPL("foo\\"), FPL("foo\\") },
+ { FPL("foo/"), FPL("foo\\") },
+ { FPL("foo/bar\\"), FPL("foo\\bar\\") },
+ { FPL("foo\\bar/"), FPL("foo\\bar\\") },
+ { FPL("foo/bar/"), FPL("foo\\bar\\") },
+ { FPL("foo\\bar\\"), FPL("foo\\bar\\") },
+ { FPL("\\foo/bar"), FPL("\\foo\\bar") },
+ { FPL("/foo\\bar"), FPL("\\foo\\bar") },
+ { FPL("c:/foo/bar/"), FPL("c:\\foo\\bar\\") },
+ { FPL("/foo/bar/"), FPL("\\foo\\bar\\") },
+ { FPL("\\foo\\bar\\"), FPL("\\foo\\bar\\") },
+ { FPL("c:\\foo/bar"), FPL("c:\\foo\\bar") },
+ { FPL("//foo\\bar\\"), FPL("\\\\foo\\bar\\") },
+ { FPL("\\\\foo\\bar\\"), FPL("\\\\foo\\bar\\") },
+ { FPL("//foo\\bar\\"), FPL("\\\\foo\\bar\\") },
+ // This method does not normalize the number of path separators.
+ { FPL("foo\\\\bar"), FPL("foo\\\\bar") },
+ { FPL("foo//bar"), FPL("foo\\\\bar") },
+ { FPL("foo/\\bar"), FPL("foo\\\\bar") },
+ { FPL("foo\\/bar"), FPL("foo\\\\bar") },
+ { FPL("///foo\\\\bar"), FPL("\\\\\\foo\\\\bar") },
+ { FPL("foo//bar///"), FPL("foo\\\\bar\\\\\\") },
+ { FPL("foo/\\bar/\\"), FPL("foo\\\\bar\\\\") },
+ { FPL("/\\foo\\/bar"), FPL("\\\\foo\\\\bar") },
+ };
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ FilePath input(cases[i].input);
+ FilePath observed = input.NormalizePathSeparators();
+ EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) <<
+ "i: " << i << ", input: " << input.value();
+ }
+}
+
+#endif
diff --git a/src/base/file_util.cc b/src/base/file_util.cc
new file mode 100644
index 0000000..cf12128
--- /dev/null
+++ b/src/base/file_util.cc
@@ -0,0 +1,444 @@
+// Copyright (c) 2012 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/file_util.h"
+
+#if defined(OS_WIN)
+#include <io.h>
+#endif
+#include <stdio.h>
+
+#include <fstream>
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "base/string_piece.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+
+namespace {
+
+const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.');
+
+// The maximum number of 'uniquified' files we will try to create.
+// This is used when the filename we're trying to download is already in use,
+// so we create a new unique filename by appending " (nnn)" before the
+// extension, where 1 <= nnn <= kMaxUniqueFiles.
+// Also used by code that cleans up said files.
+static const int kMaxUniqueFiles = 100;
+
+} // namespace
+
+namespace file_util {
+
+bool g_bug108724_debug = false;
+
+bool EndsWithSeparator(const FilePath& path) {
+ FilePath::StringType value = path.value();
+ if (value.empty())
+ return false;
+
+ return FilePath::IsSeparator(value[value.size() - 1]);
+}
+
+bool EnsureEndsWithSeparator(FilePath* path) {
+ if (!DirectoryExists(*path))
+ return false;
+
+ if (EndsWithSeparator(*path))
+ return true;
+
+ FilePath::StringType& path_str =
+ const_cast<FilePath::StringType&>(path->value());
+ path_str.append(&FilePath::kSeparators[0], 1);
+
+ return true;
+}
+
+void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix) {
+ FilePath::StringType& value =
+ const_cast<FilePath::StringType&>(path->value());
+
+ const FilePath::StringType::size_type last_dot =
+ value.rfind(kExtensionSeparator);
+ const FilePath::StringType::size_type last_separator =
+ value.find_last_of(FilePath::StringType(FilePath::kSeparators));
+
+ if (last_dot == FilePath::StringType::npos ||
+ (last_separator != std::wstring::npos && last_dot < last_separator)) {
+ // The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo".
+ // We should just append the suffix to the entire path.
+ value.append(suffix);
+ return;
+ }
+
+ value.insert(last_dot, suffix);
+}
+
+bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
+ // We open the file in binary format even if they are text files because
+ // we are just comparing that bytes are exactly same in both files and not
+ // doing anything smart with text formatting.
+ std::ifstream file1(filename1.value().c_str(),
+ std::ios::in | std::ios::binary);
+ std::ifstream file2(filename2.value().c_str(),
+ std::ios::in | std::ios::binary);
+
+ // Even if both files aren't openable (and thus, in some sense, "equal"),
+ // any unusable file yields a result of "false".
+ if (!file1.is_open() || !file2.is_open())
+ return false;
+
+ const int BUFFER_SIZE = 2056;
+ char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
+ do {
+ file1.read(buffer1, BUFFER_SIZE);
+ file2.read(buffer2, BUFFER_SIZE);
+
+ if ((file1.eof() != file2.eof()) ||
+ (file1.gcount() != file2.gcount()) ||
+ (memcmp(buffer1, buffer2, file1.gcount()))) {
+ file1.close();
+ file2.close();
+ return false;
+ }
+ } while (!file1.eof() || !file2.eof());
+
+ file1.close();
+ file2.close();
+ return true;
+}
+
+#if !defined(__LB_SHELL__) && !defined(OS_STARBOARD)
+bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
+ std::ifstream file1(filename1.value().c_str(), std::ios::in);
+ std::ifstream file2(filename2.value().c_str(), std::ios::in);
+
+ // Even if both files aren't openable (and thus, in some sense, "equal"),
+ // any unusable file yields a result of "false".
+ if (!file1.is_open() || !file2.is_open())
+ return false;
+
+ do {
+ std::string line1, line2;
+ getline(file1, line1);
+ getline(file2, line2);
+
+ // Check for mismatched EOF states, or any error state.
+ if ((file1.eof() != file2.eof()) ||
+ file1.bad() || file2.bad()) {
+ return false;
+ }
+
+ // Trim all '\r' and '\n' characters from the end of the line.
+ std::string::size_type end1 = line1.find_last_not_of("\r\n");
+ if (end1 == std::string::npos)
+ line1.clear();
+ else if (end1 + 1 < line1.length())
+ line1.erase(end1 + 1);
+
+ std::string::size_type end2 = line2.find_last_not_of("\r\n");
+ if (end2 == std::string::npos)
+ line2.clear();
+ else if (end2 + 1 < line2.length())
+ line2.erase(end2 + 1);
+
+ if (line1 != line2)
+ return false;
+ } while (!file1.eof() || !file2.eof());
+
+ return true;
+}
+#endif
+
+bool ReadFileToString(const FilePath& path, std::string* contents) {
+ if (path.ReferencesParent())
+ return false;
+
+#if defined(COBALT)
+ // Use a smaller buffer so we don't run out of stack space.
+ const size_t kReadBufferSize = 1 << 12;
+#else
+ const size_t kReadBufferSize = 1 << 16;
+#endif
+
+#if defined(OS_STARBOARD)
+ base::PlatformFile file = base::CreatePlatformFile(
+ path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, NULL, NULL);
+ if (file == base::kInvalidPlatformFileValue) {
+ return false;
+ }
+
+ char buf[kReadBufferSize];
+ size_t len;
+ while ((len = base::ReadPlatformFileAtCurrentPos(file, buf, sizeof(buf))) >
+ 0) {
+ if (contents) {
+ contents->append(buf, len);
+ }
+ }
+ base::ClosePlatformFile(file);
+
+ return true;
+#else
+ FILE* file = OpenFile(path, "rb");
+ if (!file) {
+ return false;
+ }
+
+ char buf[kReadBufferSize];
+ size_t len;
+ while ((len = fread(buf, 1, sizeof(buf), file)) > 0) {
+ if (contents)
+ contents->append(buf, len);
+ }
+ CloseFile(file);
+
+ return true;
+#endif
+}
+
+bool IsDirectoryEmpty(const FilePath& dir_path) {
+ FileEnumerator files(dir_path, false,
+ FileEnumerator::FILES | FileEnumerator::DIRECTORIES);
+ if (files.Next().value().empty())
+ return true;
+ return false;
+}
+
+#if !defined(OS_STARBOARD)
+FILE* CreateAndOpenTemporaryFile(FilePath* path) {
+ FilePath directory;
+ if (!GetTempDir(&directory))
+ return NULL;
+
+ return CreateAndOpenTemporaryFileInDir(directory, path);
+}
+#endif // !defined(OS_STARBOARD)
+
+bool GetFileSize(const FilePath& file_path, int64* file_size) {
+ base::PlatformFileInfo info;
+ if (!GetFileInfo(file_path, &info))
+ return false;
+ *file_size = info.size;
+ return true;
+}
+
+bool IsDot(const FilePath& path) {
+ return FILE_PATH_LITERAL(".") == path.BaseName().value();
+}
+
+bool IsDotDot(const FilePath& path) {
+ return FILE_PATH_LITERAL("..") == path.BaseName().value();
+}
+
+#if !defined(OS_STARBOARD)
+bool TouchFile(const FilePath& path,
+ const base::Time& last_accessed,
+ const base::Time& last_modified) {
+ int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE_ATTRIBUTES;
+
+#if defined(COBALT_WIN)
+ // Ensure that this makes it through to the CRT.
+ flags |= base::PLATFORM_FILE_WRITE;
+#endif // COBALT_WIN
+
+#if defined(OS_WIN)
+ // On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory.
+ if (DirectoryExists(path))
+ flags |= base::PLATFORM_FILE_BACKUP_SEMANTICS;
+#endif // OS_WIN
+
+ const base::PlatformFile file =
+ base::CreatePlatformFile(path, flags, NULL, NULL);
+ if (file != base::kInvalidPlatformFileValue) {
+ bool result = base::TouchPlatformFile(file, last_accessed, last_modified);
+ base::ClosePlatformFile(file);
+ return result;
+ }
+
+ return false;
+}
+
+bool SetLastModifiedTime(const FilePath& path,
+ const base::Time& last_modified) {
+ return TouchFile(path, last_modified, last_modified);
+}
+
+bool CloseFile(FILE* file) {
+ if (file == NULL)
+ return true;
+ return fclose(file) == 0;
+}
+
+bool TruncateFile(FILE* file) {
+ if (file == NULL)
+ return false;
+ long current_offset = ftell(file);
+ if (current_offset == -1)
+ return false;
+#if defined(OS_WIN)
+ int fd = _fileno(file);
+ if (_chsize(fd, current_offset) != 0)
+ return false;
+#else
+ int fd = fileno(file);
+ if (ftruncate(fd, current_offset) != 0)
+ return false;
+#endif
+ return true;
+}
+#endif // !defined(OS_STARBOARD)
+
+int GetUniquePathNumber(
+ const FilePath& path,
+ const FilePath::StringType& suffix) {
+ bool have_suffix = !suffix.empty();
+ if (!PathExists(path) &&
+ (!have_suffix || !PathExists(FilePath(path.value() + suffix)))) {
+ return 0;
+ }
+
+ FilePath new_path;
+ for (int count = 1; count <= kMaxUniqueFiles; ++count) {
+ new_path = path.InsertBeforeExtensionASCII(StringPrintf(" (%d)", count));
+ if (!PathExists(new_path) &&
+ (!have_suffix || !PathExists(FilePath(new_path.value() + suffix)))) {
+ return count;
+ }
+ }
+
+ return -1;
+}
+
+bool ContainsPath(const FilePath &parent, const FilePath& child) {
+ FilePath abs_parent = FilePath(parent);
+ FilePath abs_child = FilePath(child);
+
+ if (!file_util::AbsolutePath(&abs_parent) ||
+ !file_util::AbsolutePath(&abs_child))
+ return false;
+
+#if defined(OS_WIN)
+ // file_util::AbsolutePath() does not flatten case on Windows, so we must do
+ // a case-insensitive compare.
+ if (!StartsWith(abs_child.value(), abs_parent.value(), false))
+#else
+ if (!StartsWithASCII(abs_child.value(), abs_parent.value(), true))
+#endif
+ return false;
+
+ // file_util::AbsolutePath() normalizes '/' to '\' on Windows, so we only need
+ // to check kSeparators[0].
+ if (abs_child.value().length() <= abs_parent.value().length() ||
+ abs_child.value()[abs_parent.value().length()] !=
+ FilePath::kSeparators[0])
+ return false;
+
+ return true;
+}
+
+int64 ComputeDirectorySize(const FilePath& root_path) {
+ int64 running_size = 0;
+ FileEnumerator file_iter(root_path, true, FileEnumerator::FILES);
+ for (FilePath current = file_iter.Next(); !current.empty();
+ current = file_iter.Next()) {
+ FileEnumerator::FindInfo info;
+ file_iter.GetFindInfo(&info);
+#if defined(OS_WIN)
+ LARGE_INTEGER li = { info.nFileSizeLow, info.nFileSizeHigh };
+ running_size += li.QuadPart;
+#elif defined(OS_STARBOARD)
+ running_size += info.sb_info.size;
+#else
+ running_size += info.stat.st_size;
+#endif
+ }
+ return running_size;
+}
+
+int64 ComputeFilesSize(const FilePath& directory,
+ const FilePath::StringType& pattern) {
+ int64 running_size = 0;
+ FileEnumerator file_iter(directory, false, FileEnumerator::FILES, pattern);
+ for (FilePath current = file_iter.Next(); !current.empty();
+ current = file_iter.Next()) {
+ FileEnumerator::FindInfo info;
+ file_iter.GetFindInfo(&info);
+#if defined(OS_WIN)
+ LARGE_INTEGER li = { info.nFileSizeLow, info.nFileSizeHigh };
+ running_size += li.QuadPart;
+#elif defined(OS_STARBOARD)
+ running_size += info.sb_info.size;
+#else
+ running_size += info.stat.st_size;
+#endif
+ }
+ return running_size;
+}
+
+#if !defined(__LB_SHELL__) && !defined(OS_STARBOARD)
+///////////////////////////////////////////////
+// MemoryMappedFile
+
+MemoryMappedFile::~MemoryMappedFile() {
+ CloseHandles();
+}
+
+bool MemoryMappedFile::Initialize(const FilePath& file_name) {
+ if (IsValid())
+ return false;
+
+ if (!MapFileToMemory(file_name)) {
+ CloseHandles();
+ return false;
+ }
+
+ return true;
+}
+
+bool MemoryMappedFile::Initialize(base::PlatformFile file) {
+ if (IsValid())
+ return false;
+
+ file_ = file;
+
+ if (!MapFileToMemoryInternal()) {
+ CloseHandles();
+ return false;
+ }
+
+ return true;
+}
+
+bool MemoryMappedFile::IsValid() const {
+ return data_ != NULL;
+}
+
+bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
+ file_ = base::CreatePlatformFile(
+ file_name, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
+ NULL, NULL);
+
+ if (file_ == base::kInvalidPlatformFileValue) {
+ DLOG(ERROR) << "Couldn't open " << file_name.value();
+ return false;
+ }
+
+ return MapFileToMemoryInternal();
+}
+#endif
+
+///////////////////////////////////////////////
+// FileEnumerator
+//
+// Note: the main logic is in file_util_<platform>.cc
+
+bool FileEnumerator::ShouldSkip(const FilePath& path) {
+ FilePath::StringType basename = path.BaseName().value();
+ return IsDot(path) || (IsDotDot(path) && !(INCLUDE_DOT_DOT & file_type_));
+}
+
+} // namespace
diff --git a/src/base/file_util.h b/src/base/file_util.h
new file mode 100644
index 0000000..4392627
--- /dev/null
+++ b/src/base/file_util.h
@@ -0,0 +1,679 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains utility functions for dealing with the local
+// filesystem.
+
+#ifndef BASE_FILE_UTIL_H_
+#define BASE_FILE_UTIL_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_POSIX)
+#include <sys/stat.h>
+#include <unistd.h>
+#elif defined(OS_STARBOARD)
+#include "starboard/file.h"
+#endif
+
+#include <stdio.h>
+
+#include <set>
+#include <stack>
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/platform_file.h"
+#include "base/string16.h"
+
+#if defined(OS_POSIX)
+#include "base/file_descriptor_posix.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#endif
+
+namespace base {
+class Time;
+}
+
+namespace file_util {
+
+extern bool g_bug108724_debug;
+
+//-----------------------------------------------------------------------------
+// Functions that operate purely on a path string w/o touching the filesystem:
+
+// Returns true if the given path ends with a path separator character.
+BASE_EXPORT bool EndsWithSeparator(const FilePath& path);
+
+// Makes sure that |path| ends with a separator IFF path is a directory that
+// exists. Returns true if |path| is an existing directory, false otherwise.
+BASE_EXPORT bool EnsureEndsWithSeparator(FilePath* path);
+
+// Convert provided relative path into an absolute path. Returns false on
+// error. On POSIX, this function fails if the path does not exist.
+BASE_EXPORT bool AbsolutePath(FilePath* path);
+
+// Returns true if |parent| contains |child|. Both paths are converted to
+// absolute paths before doing the comparison.
+BASE_EXPORT bool ContainsPath(const FilePath& parent, const FilePath& child);
+
+//-----------------------------------------------------------------------------
+// Functions that involve filesystem access or modification:
+
+#if !defined(OS_STARBOARD)
+// Returns the number of files matching the current path that were
+// created on or after the given |file_time|. Doesn't count ".." or ".".
+//
+// Note for POSIX environments: a file created before |file_time|
+// can be mis-detected as a newer file due to low precision of
+// timestmap of file creation time. If you need to avoid such
+// mis-detection perfectly, you should wait one second before
+// obtaining |file_time|.
+BASE_EXPORT int CountFilesCreatedAfter(const FilePath& path,
+ const base::Time& file_time);
+#endif
+
+// Returns the total number of bytes used by all the files under |root_path|.
+// If the path does not exist the function returns 0.
+//
+// This function is implemented using the FileEnumerator class so it is not
+// particularly speedy in any platform.
+BASE_EXPORT int64 ComputeDirectorySize(const FilePath& root_path);
+
+// Returns the total number of bytes used by all files matching the provided
+// |pattern|, on this |directory| (without recursion). If the path does not
+// exist the function returns 0.
+//
+// This function is implemented using the FileEnumerator class so it is not
+// particularly speedy in any platform.
+BASE_EXPORT int64 ComputeFilesSize(const FilePath& directory,
+ const FilePath::StringType& pattern);
+
+// Deletes the given path, whether it's a file or a directory.
+// If it's a directory, it's perfectly happy to delete all of the
+// directory's contents. Passing true to recursive deletes
+// subdirectories and their contents as well.
+// Returns true if successful, false otherwise.
+//
+// In posix environment and if |path| is a symbolic link, this deletes only
+// the symlink. (even if the symlink points to a non-existent file)
+//
+// WARNING: USING THIS WITH recursive==true IS EQUIVALENT
+// TO "rm -rf", SO USE WITH CAUTION.
+BASE_EXPORT bool Delete(const FilePath& path, bool recursive);
+
+#if defined(OS_WIN)
+// Schedules to delete the given path, whether it's a file or a directory, until
+// the operating system is restarted.
+// Note:
+// 1) The file/directory to be deleted should exist in a temp folder.
+// 2) The directory to be deleted must be empty.
+BASE_EXPORT bool DeleteAfterReboot(const FilePath& path);
+#endif
+
+#if !defined(OS_STARBOARD)
+// NOTE: file_util::Move is used in a small number of places. We won't
+// implement it in Starboard for the time being, but we may bring it back if it
+// is is deemed necessary.
+
+// Moves the given path, whether it's a file or a directory.
+// If a simple rename is not possible, such as in the case where the paths are
+// on different volumes, this will attempt to copy and delete. Returns
+// true for success.
+BASE_EXPORT bool Move(const FilePath& from_path, const FilePath& to_path);
+
+// Renames file |from_path| to |to_path|. Both paths must be on the same
+// volume, or the function will fail. Destination file will be created
+// if it doesn't exist. Prefer this function over Move when dealing with
+// temporary files. On Windows it preserves attributes of the target file.
+// Returns true on success.
+BASE_EXPORT bool ReplaceFile(const FilePath& from_path,
+ const FilePath& to_path);
+#endif
+
+// Copies a single file. Use CopyDirectory to copy directories.
+BASE_EXPORT bool CopyFile(const FilePath& from_path, const FilePath& to_path);
+
+#if !defined(OS_STARBOARD)
+// NOTE: file_util::CopyDirectory is only referenced by disabled tests.
+
+// Copies the given path, and optionally all subdirectories and their contents
+// as well.
+// If there are files existing under to_path, always overwrite.
+// Returns true if successful, false otherwise.
+// Don't use wildcards on the names, it may stop working without notice.
+//
+// If you only need to copy a file use CopyFile, it's faster.
+BASE_EXPORT bool CopyDirectory(const FilePath& from_path,
+ const FilePath& to_path,
+ bool recursive);
+#endif
+
+// Returns true if the given path exists on the local filesystem,
+// false otherwise.
+BASE_EXPORT bool PathExists(const FilePath& path);
+
+// Returns true if the given path is writable by the user, false otherwise.
+BASE_EXPORT bool PathIsWritable(const FilePath& path);
+
+// Returns true if the given path exists and is a directory, false otherwise.
+BASE_EXPORT bool DirectoryExists(const FilePath& path);
+
+#if defined(OS_WIN)
+// Gets the creation time of the given file (expressed in the local timezone),
+// and returns it via the creation_time parameter. Returns true if successful,
+// false otherwise.
+BASE_EXPORT bool GetFileCreationLocalTime(const std::wstring& filename,
+ LPSYSTEMTIME creation_time);
+
+// Same as above, but takes a previously-opened file handle instead of a name.
+BASE_EXPORT bool GetFileCreationLocalTimeFromHandle(HANDLE file_handle,
+ LPSYSTEMTIME creation_time);
+#endif // defined(OS_WIN)
+
+// Returns true if the contents of the two files given are equal, false
+// otherwise. If either file can't be read, returns false.
+BASE_EXPORT bool ContentsEqual(const FilePath& filename1,
+ const FilePath& filename2);
+
+#if !defined(__LB_SHELL__) && !defined(COBALT)
+// Returns true if the contents of the two text files given are equal, false
+// otherwise. This routine treats "\r\n" and "\n" as equivalent.
+BASE_EXPORT bool TextContentsEqual(const FilePath& filename1,
+ const FilePath& filename2);
+#endif
+
+// Read the file at |path| into |contents|, returning true on success.
+// This function fails if the |path| contains path traversal components ('..').
+// |contents| may be NULL, in which case this function is useful for its
+// side effect of priming the disk cache.
+// Useful for unit tests.
+BASE_EXPORT bool ReadFileToString(const FilePath& path, std::string* contents);
+
+#if defined(OS_POSIX)
+// Read exactly |bytes| bytes from file descriptor |fd|, storing the result
+// in |buffer|. This function is protected against EINTR and partial reads.
+// Returns true iff |bytes| bytes have been successfuly read from |fd|.
+BASE_EXPORT bool ReadFromFD(int fd, char* buffer, size_t bytes);
+
+// Creates a symbolic link at |symlink| pointing to |target|. Returns
+// false on failure.
+BASE_EXPORT bool CreateSymbolicLink(const FilePath& target,
+ const FilePath& symlink);
+
+// Reads the given |symlink| and returns where it points to in |target|.
+// Returns false upon failure.
+BASE_EXPORT bool ReadSymbolicLink(const FilePath& symlink, FilePath* target);
+
+// Bits ans masks of the file permission.
+enum FilePermissionBits {
+ FILE_PERMISSION_MASK = S_IRWXU | S_IRWXG | S_IRWXO,
+ FILE_PERMISSION_USER_MASK = S_IRWXU,
+ FILE_PERMISSION_GROUP_MASK = S_IRWXG,
+ FILE_PERMISSION_OTHERS_MASK = S_IRWXO,
+
+ FILE_PERMISSION_READ_BY_USER = S_IRUSR,
+ FILE_PERMISSION_WRITE_BY_USER = S_IWUSR,
+ FILE_PERMISSION_EXECUTE_BY_USER = S_IXUSR,
+ FILE_PERMISSION_READ_BY_GROUP = S_IRGRP,
+ FILE_PERMISSION_WRITE_BY_GROUP = S_IWGRP,
+ FILE_PERMISSION_EXECUTE_BY_GROUP = S_IXGRP,
+ FILE_PERMISSION_READ_BY_OTHERS = S_IROTH,
+ FILE_PERMISSION_WRITE_BY_OTHERS = S_IWOTH,
+ FILE_PERMISSION_EXECUTE_BY_OTHERS = S_IXOTH,
+};
+
+// Reads the permission of the given |path|, storing the file permission
+// bits in |mode|. If |path| is symbolic link, |mode| is the permission of
+// a file which the symlink points to.
+BASE_EXPORT bool GetPosixFilePermissions(const FilePath& path,
+ int* mode);
+// Sets the permission of the given |path|. If |path| is symbolic link, sets
+// the permission of a file which the symlink points to.
+BASE_EXPORT bool SetPosixFilePermissions(const FilePath& path,
+ int mode);
+#endif // defined(OS_POSIX)
+
+#if defined(OS_WIN)
+// Copy from_path to to_path recursively and then delete from_path recursively.
+// Returns true if all operations succeed.
+// This function simulates Move(), but unlike Move() it works across volumes.
+// This fuction is not transactional.
+BASE_EXPORT bool CopyAndDeleteDirectory(const FilePath& from_path,
+ const FilePath& to_path);
+#endif // defined(OS_WIN)
+
+// Return true if the given directory is empty
+BASE_EXPORT bool IsDirectoryEmpty(const FilePath& dir_path);
+
+// Get the temporary directory provided by the system.
+// WARNING: DON'T USE THIS. If you want to create a temporary file, use one of
+// the functions below.
+BASE_EXPORT bool GetTempDir(FilePath* path);
+// Get a temporary directory for shared memory files.
+// Only useful on POSIX; redirects to GetTempDir() on Windows.
+BASE_EXPORT bool GetShmemTempDir(FilePath* path, bool executable);
+
+// Get the home directory. This is more complicated than just getenv("HOME")
+// as it knows to fall back on getpwent() etc.
+BASE_EXPORT FilePath GetHomeDir();
+
+// Creates a temporary file. The full path is placed in |path|, and the
+// function returns true if was successful in creating the file. The file will
+// be empty and all handles closed after this function returns.
+BASE_EXPORT bool CreateTemporaryFile(FilePath* path);
+
+// Same as CreateTemporaryFile but the file is created in |dir|.
+BASE_EXPORT bool CreateTemporaryFileInDir(const FilePath& dir,
+ FilePath* temp_file);
+
+#if !defined(OS_STARBOARD)
+// Create and open a temporary file. File is opened for read/write.
+// The full path is placed in |path|.
+// Returns a handle to the opened file or NULL if an error occured.
+BASE_EXPORT FILE* CreateAndOpenTemporaryFile(FilePath* path);
+// Like above but for shmem files. Only useful for POSIX.
+// The executable flag says the file needs to support using
+// mprotect with PROT_EXEC after mapping.
+BASE_EXPORT FILE* CreateAndOpenTemporaryShmemFile(FilePath* path,
+ bool executable);
+// Similar to CreateAndOpenTemporaryFile, but the file is created in |dir|.
+BASE_EXPORT FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir,
+ FilePath* path);
+#endif
+
+// Create a new directory. If prefix is provided, the new directory name is in
+// the format of prefixyyyy.
+// NOTE: prefix is ignored in the POSIX implementation.
+// If success, return true and output the full path of the directory created.
+BASE_EXPORT bool CreateNewTempDirectory(const FilePath::StringType& prefix,
+ FilePath* new_temp_path);
+
+// Create a directory within another directory.
+// Extra characters will be appended to |prefix| to ensure that the
+// new directory does not have the same name as an existing directory.
+BASE_EXPORT bool CreateTemporaryDirInDir(const FilePath& base_dir,
+ const FilePath::StringType& prefix,
+ FilePath* new_dir);
+
+// Creates a directory, as well as creating any parent directories, if they
+// don't exist. Returns 'true' on successful creation, or if the directory
+// already exists. The directory is only readable by the current user.
+BASE_EXPORT bool CreateDirectory(const FilePath& full_path);
+
+// Returns the file size. Returns true on success.
+BASE_EXPORT bool GetFileSize(const FilePath& file_path, int64* file_size);
+
+// Returns true if the given path's base name is ".".
+BASE_EXPORT bool IsDot(const FilePath& path);
+
+// Returns true if the given path's base name is "..".
+BASE_EXPORT bool IsDotDot(const FilePath& path);
+
+#if !defined(OS_STARBOARD)
+// Sets |real_path| to |path| with symbolic links and junctions expanded.
+// On windows, make sure the path starts with a lettered drive.
+// |path| must reference a file. Function will fail if |path| points to
+// a directory or to a nonexistent path. On windows, this function will
+// fail if |path| is a junction or symlink that points to an empty file,
+// or if |real_path| would be longer than MAX_PATH characters.
+BASE_EXPORT bool NormalizeFilePath(const FilePath& path, FilePath* real_path);
+#endif
+
+#if defined(OS_WIN)
+
+// Given a path in NT native form ("\Device\HarddiskVolumeXX\..."),
+// return in |drive_letter_path| the equivalent path that starts with
+// a drive letter ("C:\..."). Return false if no such path exists.
+BASE_EXPORT bool DevicePathToDriveLetterPath(const FilePath& device_path,
+ FilePath* drive_letter_path);
+
+// Given an existing file in |path|, set |real_path| to the path
+// in native NT format, of the form "\Device\HarddiskVolumeXX\..".
+// Returns false if the path can not be found. Empty files cannot
+// be resolved with this function.
+BASE_EXPORT bool NormalizeToNativeFilePath(const FilePath& path,
+ FilePath* nt_path);
+#endif
+
+// This function will return if the given file is a symlink or not.
+BASE_EXPORT bool IsLink(const FilePath& file_path);
+
+// Returns information about the given file path.
+BASE_EXPORT bool GetFileInfo(const FilePath& file_path,
+ base::PlatformFileInfo* info);
+
+#if !defined(OS_STARBOARD)
+// Sets the time of the last access and the time of the last modification.
+BASE_EXPORT bool TouchFile(const FilePath& path,
+ const base::Time& last_accessed,
+ const base::Time& last_modified);
+
+// Set the time of the last modification. Useful for unit tests.
+BASE_EXPORT bool SetLastModifiedTime(const FilePath& path,
+ const base::Time& last_modified);
+
+#if defined(OS_POSIX) && !defined(__LB_WIIU__)
+// Store inode number of |path| in |inode|. Return true on success.
+BASE_EXPORT bool GetInode(const FilePath& path, ino_t* inode);
+#endif
+
+// Wrapper for fopen-like calls. Returns non-NULL FILE* on success.
+BASE_EXPORT FILE* OpenFile(const FilePath& filename, const char* mode);
+
+// Closes file opened by OpenFile. Returns true on success.
+BASE_EXPORT bool CloseFile(FILE* file);
+
+// Truncates an open file to end at the location of the current file pointer.
+// This is a cross-platform analog to Windows' SetEndOfFile() function.
+BASE_EXPORT bool TruncateFile(FILE* file);
+#endif
+
+// Reads the given number of bytes from the file into the buffer. Returns
+// the number of read bytes, or -1 on error.
+BASE_EXPORT int ReadFile(const FilePath& filename, char* data, int size);
+
+// Writes the given buffer into the file, overwriting any data that was
+// previously there. Returns the number of bytes written, or -1 on error.
+BASE_EXPORT int WriteFile(const FilePath& filename, const char* data, int size);
+
+#if defined(OS_POSIX)
+// Append the data to |fd|. Does not close |fd| when done.
+BASE_EXPORT int WriteFileDescriptor(const int fd, const char* data, int size);
+#endif
+
+// Append the given buffer into the file. Returns the number of bytes written,
+// or -1 on error.
+BASE_EXPORT int AppendToFile(const FilePath& filename,
+ const char* data, int size);
+
+#if !defined(OS_STARBOARD)
+// Gets the current working directory for the process.
+BASE_EXPORT bool GetCurrentDirectory(FilePath* path);
+
+// Sets the current working directory for the process.
+BASE_EXPORT bool SetCurrentDirectory(const FilePath& path);
+#endif
+
+// Attempts to find a number that can be appended to the |path| to make it
+// unique. If |path| does not exist, 0 is returned. If it fails to find such
+// a number, -1 is returned. If |suffix| is not empty, also checks the
+// existence of it with the given suffix.
+BASE_EXPORT int GetUniquePathNumber(const FilePath& path,
+ const FilePath::StringType& suffix);
+
+#if defined(OS_POSIX)
+// Test that |path| can only be changed by a given user and members of
+// a given set of groups.
+// Specifically, test that all parts of |path| under (and including) |base|:
+// * Exist.
+// * Are owned by a specific user.
+// * Are not writable by all users.
+// * Are owned by a memeber of a given set of groups, or are not writable by
+// their group.
+// * Are not symbolic links.
+// This is useful for checking that a config file is administrator-controlled.
+// |base| must contain |path|.
+BASE_EXPORT bool VerifyPathControlledByUser(const FilePath& base,
+ const FilePath& path,
+ uid_t owner_uid,
+ const std::set<gid_t>& group_gids);
+#endif // defined(OS_POSIX)
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+// Is |path| writable only by a user with administrator privileges?
+// This function uses Mac OS conventions. The super user is assumed to have
+// uid 0, and the administrator group is assumed to be named "admin".
+// Testing that |path|, and every parent directory including the root of
+// the filesystem, are owned by the superuser, controlled by the group
+// "admin", are not writable by all users, and contain no symbolic links.
+// Will return false if |path| does not exist.
+BASE_EXPORT bool VerifyPathControlledByAdmin(const FilePath& path);
+#endif // defined(OS_MACOSX) && !defined(OS_IOS)
+
+#if !defined(OS_STARBOARD)
+// A class to handle auto-closing of FILE*'s.
+class ScopedFILEClose {
+ public:
+ inline void operator()(FILE* x) const {
+ if (x) {
+ fclose(x);
+ }
+ }
+};
+
+typedef scoped_ptr_malloc<FILE, ScopedFILEClose> ScopedFILE;
+#endif
+
+#if defined(OS_POSIX)
+// A class to handle auto-closing of FDs.
+class ScopedFDClose {
+ public:
+ inline void operator()(int* x) const {
+ if (x && *x >= 0) {
+ if (HANDLE_EINTR(close(*x)) < 0)
+ DPLOG(ERROR) << "close";
+ }
+ }
+};
+
+typedef scoped_ptr_malloc<int, ScopedFDClose> ScopedFD;
+#endif // OS_POSIX
+
+// A class for enumerating the files in a provided path. The order of the
+// results is not guaranteed.
+//
+// DO NOT USE FROM THE MAIN THREAD of your application unless it is a test
+// program where latency does not matter. This class is blocking.
+class BASE_EXPORT FileEnumerator {
+ public:
+#if defined(OS_WIN)
+ typedef WIN32_FIND_DATA FindInfo;
+#elif defined(OS_POSIX)
+ typedef struct {
+ struct stat stat;
+ std::string filename;
+ } FindInfo;
+#elif defined(OS_STARBOARD)
+ typedef struct {
+ SbFileInfo sb_info;
+ std::string filename;
+ } FindInfo;
+#endif
+
+ enum FileType {
+ FILES = 1 << 0,
+ DIRECTORIES = 1 << 1,
+ INCLUDE_DOT_DOT = 1 << 2,
+#if defined(OS_POSIX)
+ SHOW_SYM_LINKS = 1 << 4,
+#endif
+ };
+
+ // |root_path| is the starting directory to search for. It may or may not end
+ // in a slash.
+ //
+ // If |recursive| is true, this will enumerate all matches in any
+ // subdirectories matched as well. It does a breadth-first search, so all
+ // files in one directory will be returned before any files in a
+ // subdirectory.
+ //
+ // |file_type|, a bit mask of FileType, specifies whether the enumerator
+ // should match files, directories, or both.
+ //
+ // |pattern| is an optional pattern for which files to match. This
+ // works like shell globbing. For example, "*.txt" or "Foo???.doc".
+ // However, be careful in specifying patterns that aren't cross platform
+ // since the underlying code uses OS-specific matching routines. In general,
+ // Windows matching is less featureful than others, so test there first.
+ // If unspecified, this will match all files.
+ // NOTE: the pattern only matches the contents of root_path, not files in
+ // recursive subdirectories.
+ // TODO(erikkay): Fix the pattern matching to work at all levels.
+ FileEnumerator(const FilePath& root_path,
+ bool recursive,
+ int file_type);
+ FileEnumerator(const FilePath& root_path,
+ bool recursive,
+ int file_type,
+ const FilePath::StringType& pattern);
+ ~FileEnumerator();
+
+ // Returns an empty string if there are no more results.
+ FilePath Next();
+
+ // Write the file info into |info|.
+ void GetFindInfo(FindInfo* info);
+
+ // Looks inside a FindInfo and determines if it's a directory.
+ static bool IsDirectory(const FindInfo& info);
+
+ static FilePath GetFilename(const FindInfo& find_info);
+ static int64 GetFilesize(const FindInfo& find_info);
+ static base::Time GetLastModifiedTime(const FindInfo& find_info);
+
+ private:
+ // Returns true if the given path should be skipped in enumeration.
+ bool ShouldSkip(const FilePath& path);
+
+
+#if defined(OS_WIN)
+ // True when find_data_ is valid.
+ bool has_find_data_;
+ WIN32_FIND_DATA find_data_;
+ HANDLE find_handle_;
+#elif defined(OS_POSIX)
+ struct DirectoryEntryInfo {
+ FilePath filename;
+ struct stat stat;
+ };
+
+ // Read the filenames in source into the vector of DirectoryEntryInfo's
+ static bool ReadDirectory(std::vector<DirectoryEntryInfo>* entries,
+ const FilePath& source, bool show_links);
+
+ // The files in the current directory
+ std::vector<DirectoryEntryInfo> directory_entries_;
+
+ // The next entry to use from the directory_entries_ vector
+ size_t current_directory_entry_;
+#elif defined(OS_STARBOARD)
+ struct DirectoryEntryInfo {
+ FilePath filename;
+ SbFileInfo sb_info;
+ };
+
+ // Read the filenames in source into the vector of DirectoryEntryInfo's
+ static bool ReadDirectory(std::vector<DirectoryEntryInfo>* entries,
+ const FilePath& source);
+
+ // The files in the current directory
+ std::vector<DirectoryEntryInfo> directory_entries_;
+
+ // The next entry to use from the directory_entries_ vector
+ size_t current_directory_entry_;
+#endif
+
+ FilePath root_path_;
+ bool recursive_;
+ int file_type_;
+ FilePath::StringType pattern_; // Empty when we want to find everything.
+
+ // A stack that keeps track of which subdirectories we still need to
+ // enumerate in the breadth-first search.
+ std::stack<FilePath> pending_paths_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileEnumerator);
+};
+
+#if !defined(__LB_SHELL__) && !defined(COBALT)
+class BASE_EXPORT MemoryMappedFile {
+ public:
+ // The default constructor sets all members to invalid/null values.
+ MemoryMappedFile();
+ ~MemoryMappedFile();
+
+ // Opens an existing file and maps it into memory. Access is restricted to
+ // read only. If this object already points to a valid memory mapped file
+ // then this method will fail and return false. If it cannot open the file,
+ // the file does not exist, or the memory mapping fails, it will return false.
+ // Later we may want to allow the user to specify access.
+ bool Initialize(const FilePath& file_name);
+ // As above, but works with an already-opened file. MemoryMappedFile will take
+ // ownership of |file| and close it when done.
+ bool Initialize(base::PlatformFile file);
+
+#if defined(OS_WIN)
+ // Opens an existing file and maps it as an image section. Please refer to
+ // the Initialize function above for additional information.
+ bool InitializeAsImageSection(const FilePath& file_name);
+#endif // OS_WIN
+
+ const uint8* data() const { return data_; }
+ size_t length() const { return length_; }
+
+ // Is file_ a valid file handle that points to an open, memory mapped file?
+ bool IsValid() const;
+
+ private:
+ // Open the given file and pass it to MapFileToMemoryInternal().
+ bool MapFileToMemory(const FilePath& file_name);
+
+ // Map the file to memory, set data_ to that memory address. Return true on
+ // success, false on any kind of failure. This is a helper for Initialize().
+ bool MapFileToMemoryInternal();
+
+ // Closes all open handles. Later we may want to make this public.
+ void CloseHandles();
+
+#if defined(OS_WIN)
+ // MapFileToMemoryInternal calls this function. It provides the ability to
+ // pass in flags which control the mapped section.
+ bool MapFileToMemoryInternalEx(int flags);
+
+ HANDLE file_mapping_;
+#endif
+ base::PlatformFile file_;
+ uint8* data_;
+ size_t length_;
+
+ DISALLOW_COPY_AND_ASSIGN(MemoryMappedFile);
+};
+#endif
+
+// Returns whether the file has been modified since a particular date.
+BASE_EXPORT bool HasFileBeenModifiedSince(
+ const FileEnumerator::FindInfo& find_info,
+ const base::Time& cutoff_time);
+
+#if defined(OS_LINUX)
+// Broad categories of file systems as returned by statfs() on Linux.
+enum FileSystemType {
+ FILE_SYSTEM_UNKNOWN, // statfs failed.
+ FILE_SYSTEM_0, // statfs.f_type == 0 means unknown, may indicate AFS.
+ FILE_SYSTEM_ORDINARY, // on-disk filesystem like ext2
+ FILE_SYSTEM_NFS,
+ FILE_SYSTEM_SMB,
+ FILE_SYSTEM_CODA,
+ FILE_SYSTEM_MEMORY, // in-memory file system
+ FILE_SYSTEM_CGROUP, // cgroup control.
+ FILE_SYSTEM_OTHER, // any other value.
+ FILE_SYSTEM_TYPE_COUNT
+};
+
+// Attempts determine the FileSystemType for |path|.
+// Returns false if |path| doesn't exist.
+BASE_EXPORT bool GetFileSystemType(const FilePath& path, FileSystemType* type);
+#endif
+
+} // namespace file_util
+
+#endif // BASE_FILE_UTIL_H_
diff --git a/src/base/file_util_android.cc b/src/base/file_util_android.cc
new file mode 100644
index 0000000..79db279
--- /dev/null
+++ b/src/base/file_util_android.cc
@@ -0,0 +1,16 @@
+// Copyright (c) 2012 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/file_util.h"
+
+#include "base/file_path.h"
+#include "base/path_service.h"
+
+namespace file_util {
+
+bool GetShmemTempDir(FilePath* path, bool executable) {
+ return PathService::Get(base::DIR_CACHE, path);
+}
+
+} // namespace file_util
diff --git a/src/base/file_util_linux.cc b/src/base/file_util_linux.cc
new file mode 100644
index 0000000..f7d4f6e
--- /dev/null
+++ b/src/base/file_util_linux.cc
@@ -0,0 +1,62 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/file_util.h"
+
+#include "base/file_path.h"
+
+#include <errno.h>
+#include <sys/vfs.h>
+
+namespace file_util {
+
+bool GetFileSystemType(const FilePath& path, FileSystemType* type) {
+ struct statfs statfs_buf;
+ if (statfs(path.value().c_str(), &statfs_buf) < 0) {
+ if (errno == ENOENT)
+ return false;
+ *type = FILE_SYSTEM_UNKNOWN;
+ return true;
+ }
+
+ // While you would think the possible values of f_type would be available
+ // in a header somewhere, it appears that is not the case. These values
+ // are copied from the statfs man page.
+ switch (statfs_buf.f_type) {
+ case 0:
+ *type = FILE_SYSTEM_0;
+ break;
+ case 0xEF53: // ext2, ext3.
+ case 0x4D44: // dos
+ case 0x5346544E: // NFTS
+ case 0x52654973: // reiser
+ case 0x58465342: // XFS
+ case 0x9123683E: // btrfs
+ case 0x3153464A: // JFS
+ *type = FILE_SYSTEM_ORDINARY;
+ break;
+ case 0x6969: // NFS
+ *type = FILE_SYSTEM_NFS;
+ break;
+ case 0xFF534D42: // CIFS
+ case 0x517B: // SMB
+ *type = FILE_SYSTEM_SMB;
+ break;
+ case 0x73757245: // Coda
+ *type = FILE_SYSTEM_CODA;
+ break;
+ case 0x858458f6: // ramfs
+ case 0x01021994: // tmpfs
+ *type = FILE_SYSTEM_MEMORY;
+ break;
+ case 0x27e0eb: // CGROUP
+ *type = FILE_SYSTEM_CGROUP;
+ break;
+ default:
+ *type = FILE_SYSTEM_OTHER;
+ }
+ return true;
+}
+
+} // namespace
diff --git a/src/base/file_util_mac.mm b/src/base/file_util_mac.mm
new file mode 100644
index 0000000..0638167
--- /dev/null
+++ b/src/base/file_util_mac.mm
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 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/file_util.h"
+
+#import <Foundation/Foundation.h>
+#include <copyfile.h>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/mac/foundation_util.h"
+#include "base/string_util.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace file_util {
+
+bool GetTempDir(FilePath* path) {
+ NSString* tmp = NSTemporaryDirectory();
+ if (tmp == nil)
+ return false;
+ *path = base::mac::NSStringToFilePath(tmp);
+ return true;
+}
+
+bool GetShmemTempDir(FilePath* path, bool executable) {
+ return GetTempDir(path);
+}
+
+bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return (copyfile(from_path.value().c_str(),
+ to_path.value().c_str(), NULL, COPYFILE_ALL) == 0);
+}
+
+} // namespace
diff --git a/src/base/file_util_posix.cc b/src/base/file_util_posix.cc
new file mode 100644
index 0000000..6a4a309
--- /dev/null
+++ b/src/base/file_util_posix.cc
@@ -0,0 +1,1177 @@
+// Copyright (c) 2012 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/file_util.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#if defined(OS_MACOSX)
+#include <AvailabilityMacros.h>
+#include "base/mac/foundation_util.h"
+#elif !defined(OS_ANDROID) && !defined(__LB_SHELL__)
+#include <glib.h>
+#endif
+
+#include <fstream>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/path_service.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/stl_util.h"
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "base/sys_string_conversions.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+
+#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
+#include "base/os_compat_android.h"
+#endif
+
+#if !defined(OS_IOS) && !defined(__LB_SHELL__)
+#include <grp.h>
+#endif
+
+#if defined(OS_CHROMEOS)
+#include "base/chromeos/chromeos_version.h"
+#endif
+
+namespace file_util {
+
+namespace {
+
+#if defined(OS_BSD) || defined(OS_MACOSX) || defined(__LB_SHELL__)
+typedef struct stat stat_wrapper_t;
+static int CallStat(const char *path, stat_wrapper_t *sb) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return stat(path, sb);
+}
+static int CallLstat(const char *path, stat_wrapper_t *sb) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return lstat(path, sb);
+}
+#else
+typedef struct stat64 stat_wrapper_t;
+static int CallStat(const char *path, stat_wrapper_t *sb) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return stat64(path, sb);
+}
+static int CallLstat(const char *path, stat_wrapper_t *sb) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return lstat64(path, sb);
+}
+#endif
+
+#if !defined(__LB_PS4__) && !defined(__LB_ANDROID__)
+// Helper for NormalizeFilePath(), defined below.
+bool RealPath(const FilePath& path, FilePath* real_path) {
+ base::ThreadRestrictions::AssertIOAllowed(); // For realpath().
+ FilePath::CharType buf[PATH_MAX];
+ if (!realpath(path.value().c_str(), buf))
+ return false;
+
+ *real_path = FilePath(buf);
+ return true;
+}
+#endif
+
+// Helper for VerifyPathControlledByUser.
+bool VerifySpecificPathControlledByUser(const FilePath& path,
+ uid_t owner_uid,
+ const std::set<gid_t>& group_gids) {
+ stat_wrapper_t stat_info;
+ if (CallLstat(path.value().c_str(), &stat_info) != 0) {
+ DPLOG(ERROR) << "Failed to get information on path "
+ << path.value();
+ return false;
+ }
+
+ if (S_ISLNK(stat_info.st_mode)) {
+ DLOG(ERROR) << "Path " << path.value()
+ << " is a symbolic link.";
+ return false;
+ }
+
+ if (stat_info.st_uid != owner_uid) {
+ DLOG(ERROR) << "Path " << path.value()
+ << " is owned by the wrong user.";
+ return false;
+ }
+
+ if ((stat_info.st_mode & S_IWGRP) &&
+ !ContainsKey(group_gids, stat_info.st_gid)) {
+ DLOG(ERROR) << "Path " << path.value()
+ << " is writable by an unprivileged group.";
+ return false;
+ }
+
+ if (stat_info.st_mode & S_IWOTH) {
+ DLOG(ERROR) << "Path " << path.value()
+ << " is writable by any user.";
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+static std::string TempFileName() {
+#if defined(OS_MACOSX)
+ return StringPrintf(".%s.XXXXXX", base::mac::BaseBundleID());
+#endif
+
+#if defined(GOOGLE_CHROME_BUILD)
+ return std::string(".com.google.Chrome.XXXXXX");
+#else
+ return std::string(".org.chromium.Chromium.XXXXXX");
+#endif
+}
+
+#if !defined(__LB_PS4__) && !defined(__LB_ANDROID__)
+bool AbsolutePath(FilePath* path) {
+ base::ThreadRestrictions::AssertIOAllowed(); // For realpath().
+ char full_path[PATH_MAX];
+ if (realpath(path->value().c_str(), full_path) == NULL)
+ return false;
+ *path = FilePath(full_path);
+ return true;
+}
+#endif
+
+int CountFilesCreatedAfter(const FilePath& path,
+ const base::Time& comparison_time) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ int file_count = 0;
+
+ DIR* dir = opendir(path.value().c_str());
+ if (dir) {
+#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_BSD) && \
+ !defined(OS_SOLARIS) && !defined(OS_ANDROID) && !defined(__LB_SHELL__)
+ #error Port warning: depending on the definition of struct dirent, \
+ additional space for pathname may be needed
+#endif
+ struct dirent ent_buf;
+ struct dirent* ent;
+ while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) {
+ if ((strcmp(ent->d_name, ".") == 0) ||
+ (strcmp(ent->d_name, "..") == 0))
+ continue;
+
+ stat_wrapper_t st;
+ int test = CallStat(path.Append(ent->d_name).value().c_str(), &st);
+ if (test != 0) {
+ DPLOG(ERROR) << "stat64 failed";
+ continue;
+ }
+ // Here, we use Time::TimeT(), which discards microseconds. This
+ // means that files which are newer than |comparison_time| may
+ // be considered older. If we don't discard microseconds, it
+ // introduces another issue. Suppose the following case:
+ //
+ // 1. Get |comparison_time| by Time::Now() and the value is 10.1 (secs).
+ // 2. Create a file and the current time is 10.3 (secs).
+ //
+ // As POSIX doesn't have microsecond precision for |st_ctime|,
+ // the creation time of the file created in the step 2 is 10 and
+ // the file is considered older than |comparison_time|. After
+ // all, we may have to accept either of the two issues: 1. files
+ // which are older than |comparison_time| are considered newer
+ // (current implementation) 2. files newer than
+ // |comparison_time| are considered older.
+ if (static_cast<time_t>(st.st_ctime) >= comparison_time.ToTimeT())
+ ++file_count;
+ }
+ closedir(dir);
+ }
+ return file_count;
+}
+
+// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
+// which works both with and without the recursive flag. I'm not sure we need
+// that functionality. If not, remove from file_util_win.cc, otherwise add it
+// here.
+bool Delete(const FilePath& path, bool recursive) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ const char* path_str = path.value().c_str();
+ stat_wrapper_t file_info;
+ int test = CallLstat(path_str, &file_info);
+ if (test != 0) {
+ // The Windows version defines this condition as success.
+ bool ret = (errno == ENOENT || errno == ENOTDIR);
+ return ret;
+ }
+ if (!S_ISDIR(file_info.st_mode))
+ return (unlink(path_str) == 0);
+ if (!recursive)
+ return (rmdir(path_str) == 0);
+
+ bool success = true;
+ std::stack<std::string> directories;
+ directories.push(path.value());
+ FileEnumerator traversal(path, true,
+ FileEnumerator::FILES | FileEnumerator::DIRECTORIES |
+ FileEnumerator::SHOW_SYM_LINKS);
+ for (FilePath current = traversal.Next(); success && !current.empty();
+ current = traversal.Next()) {
+ FileEnumerator::FindInfo info;
+ traversal.GetFindInfo(&info);
+
+ if (S_ISDIR(info.stat.st_mode))
+ directories.push(current.value());
+ else
+ success = (unlink(current.value().c_str()) == 0);
+ }
+
+ while (success && !directories.empty()) {
+ FilePath dir = FilePath(directories.top());
+ directories.pop();
+ success = (rmdir(dir.value().c_str()) == 0);
+ }
+ return success;
+}
+
+bool Move(const FilePath& from_path, const FilePath& to_path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ // Windows compatibility: if to_path exists, from_path and to_path
+ // must be the same type, either both files, or both directories.
+ stat_wrapper_t to_file_info;
+ if (CallStat(to_path.value().c_str(), &to_file_info) == 0) {
+ stat_wrapper_t from_file_info;
+ if (CallStat(from_path.value().c_str(), &from_file_info) == 0) {
+ if (S_ISDIR(to_file_info.st_mode) != S_ISDIR(from_file_info.st_mode))
+ return false;
+ } else {
+ return false;
+ }
+ }
+
+ if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
+ return true;
+
+ if (!CopyDirectory(from_path, to_path, true))
+ return false;
+
+ Delete(from_path, true);
+ return true;
+}
+
+bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return (rename(from_path.value().c_str(), to_path.value().c_str()) == 0);
+}
+
+bool CopyDirectory(const FilePath& from_path,
+ const FilePath& to_path,
+ bool recursive) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ // Some old callers of CopyDirectory want it to support wildcards.
+ // After some discussion, we decided to fix those callers.
+ // Break loudly here if anyone tries to do this.
+ // TODO(evanm): remove this once we're sure it's ok.
+ DCHECK(to_path.value().find('*') == std::string::npos);
+ DCHECK(from_path.value().find('*') == std::string::npos);
+
+ char top_dir[PATH_MAX];
+ if (base::strlcpy(top_dir, from_path.value().c_str(),
+ arraysize(top_dir)) >= arraysize(top_dir)) {
+ return false;
+ }
+
+ // This function does not properly handle destinations within the source
+ FilePath real_to_path = to_path;
+ if (PathExists(real_to_path)) {
+ if (!AbsolutePath(&real_to_path))
+ return false;
+ } else {
+ real_to_path = real_to_path.DirName();
+ if (!AbsolutePath(&real_to_path))
+ return false;
+ }
+ FilePath real_from_path = from_path;
+ if (!AbsolutePath(&real_from_path))
+ return false;
+ if (real_to_path.value().size() >= real_from_path.value().size() &&
+ real_to_path.value().compare(0, real_from_path.value().size(),
+ real_from_path.value()) == 0)
+ return false;
+
+ bool success = true;
+ int traverse_type = FileEnumerator::FILES | FileEnumerator::SHOW_SYM_LINKS;
+ if (recursive)
+ traverse_type |= FileEnumerator::DIRECTORIES;
+ FileEnumerator traversal(from_path, recursive, traverse_type);
+
+ // We have to mimic windows behavior here. |to_path| may not exist yet,
+ // start the loop with |to_path|.
+ FileEnumerator::FindInfo info;
+ FilePath current = from_path;
+ if (stat(from_path.value().c_str(), &info.stat) < 0) {
+ DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: "
+ << from_path.value() << " errno = " << errno;
+ success = false;
+ }
+ struct stat to_path_stat;
+ FilePath from_path_base = from_path;
+ if (recursive && stat(to_path.value().c_str(), &to_path_stat) == 0 &&
+ S_ISDIR(to_path_stat.st_mode)) {
+ // If the destination already exists and is a directory, then the
+ // top level of source needs to be copied.
+ from_path_base = from_path.DirName();
+ }
+
+ // The Windows version of this function assumes that non-recursive calls
+ // will always have a directory for from_path.
+ DCHECK(recursive || S_ISDIR(info.stat.st_mode));
+
+ while (success && !current.empty()) {
+ // current is the source path, including from_path, so append
+ // the suffix after from_path to to_path to create the target_path.
+ FilePath target_path(to_path);
+ if (from_path_base != current) {
+ if (!from_path_base.AppendRelativePath(current, &target_path)) {
+ success = false;
+ break;
+ }
+ }
+
+ if (S_ISDIR(info.stat.st_mode)) {
+ if (mkdir(target_path.value().c_str(), info.stat.st_mode & 01777) != 0 &&
+ errno != EEXIST) {
+ DLOG(ERROR) << "CopyDirectory() couldn't create directory: "
+ << target_path.value() << " errno = " << errno;
+ success = false;
+ }
+ } else if (S_ISREG(info.stat.st_mode)) {
+ if (!CopyFile(current, target_path)) {
+ DLOG(ERROR) << "CopyDirectory() couldn't create file: "
+ << target_path.value();
+ success = false;
+ }
+ } else {
+ DLOG(WARNING) << "CopyDirectory() skipping non-regular file: "
+ << current.value();
+ }
+
+ current = traversal.Next();
+ traversal.GetFindInfo(&info);
+ }
+
+ return success;
+}
+
+bool PathExists(const FilePath& path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return access(path.value().c_str(), F_OK) == 0;
+}
+
+bool PathIsWritable(const FilePath& path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return access(path.value().c_str(), W_OK) == 0;
+}
+
+#if !defined(__LB_XB1__) && !defined(__LB_XB360__)
+bool DirectoryExists(const FilePath& path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ stat_wrapper_t file_info;
+ if (CallStat(path.value().c_str(), &file_info) == 0)
+ return S_ISDIR(file_info.st_mode);
+ return false;
+}
+#endif
+
+// TODO(erikkay): implement
+#if 0
+bool GetFileCreationLocalTimeFromHandle(int fd,
+ LPSYSTEMTIME creation_time) {
+ if (!file_handle)
+ return false;
+
+ FILETIME utc_filetime;
+ if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
+ return false;
+
+ FILETIME local_filetime;
+ if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
+ return false;
+
+ return !!FileTimeToSystemTime(&local_filetime, creation_time);
+}
+
+bool GetFileCreationLocalTime(const std::string& filename,
+ LPSYSTEMTIME creation_time) {
+ ScopedHandle file_handle(
+ CreateFile(filename.c_str(), GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
+ return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
+}
+#endif
+
+bool ReadFromFD(int fd, char* buffer, size_t bytes) {
+ size_t total_read = 0;
+ while (total_read < bytes) {
+ ssize_t bytes_read =
+ HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read));
+ if (bytes_read <= 0)
+ break;
+ total_read += bytes_read;
+ }
+ return total_read == bytes;
+}
+
+bool CreateSymbolicLink(const FilePath& target_path,
+ const FilePath& symlink_path) {
+ DCHECK(!symlink_path.empty());
+ DCHECK(!target_path.empty());
+ return ::symlink(target_path.value().c_str(),
+ symlink_path.value().c_str()) != -1;
+}
+
+bool ReadSymbolicLink(const FilePath& symlink_path,
+ FilePath* target_path) {
+ DCHECK(!symlink_path.empty());
+ DCHECK(target_path);
+ char buf[PATH_MAX];
+ ssize_t count = ::readlink(symlink_path.value().c_str(), buf, arraysize(buf));
+
+ if (count <= 0) {
+ target_path->clear();
+ return false;
+ }
+
+ *target_path = FilePath(FilePath::StringType(buf, count));
+ return true;
+}
+
+bool GetPosixFilePermissions(const FilePath& path, int* mode) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(mode);
+
+ stat_wrapper_t file_info;
+ // Uses stat(), because on symbolic link, lstat() does not return valid
+ // permission bits in st_mode
+ if (CallStat(path.value().c_str(), &file_info) != 0)
+ return false;
+
+ *mode = file_info.st_mode & FILE_PERMISSION_MASK;
+ return true;
+}
+
+bool SetPosixFilePermissions(const FilePath& path,
+ int mode) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK((mode & ~FILE_PERMISSION_MASK) == 0);
+
+ // Calls stat() so that we can preserve the higher bits like S_ISGID.
+ stat_wrapper_t stat_buf;
+ if (CallStat(path.value().c_str(), &stat_buf) != 0)
+ return false;
+
+ // Clears the existing permission bits, and adds the new ones.
+ mode_t updated_mode_bits = stat_buf.st_mode & ~FILE_PERMISSION_MASK;
+ updated_mode_bits |= mode & FILE_PERMISSION_MASK;
+
+ if (HANDLE_EINTR(chmod(path.value().c_str(), updated_mode_bits)) != 0)
+ return false;
+
+ return true;
+}
+
+// Creates and opens a temporary file in |directory|, returning the
+// file descriptor. |path| is set to the temporary file path.
+// This function does NOT unlink() the file.
+int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
+ base::ThreadRestrictions::AssertIOAllowed(); // For call to mkstemp().
+ *path = directory.Append(TempFileName());
+ const std::string& tmpdir_string = path->value();
+ // this should be OK since mkstemp just replaces characters in place
+ char* buffer = const_cast<char*>(tmpdir_string.c_str());
+
+ return HANDLE_EINTR(mkstemp(buffer));
+}
+
+bool CreateTemporaryFile(FilePath* path) {
+ base::ThreadRestrictions::AssertIOAllowed(); // For call to close().
+ FilePath directory;
+ if (!GetTempDir(&directory))
+ return false;
+ int fd = CreateAndOpenFdForTemporaryFile(directory, path);
+ if (fd < 0)
+ return false;
+ ignore_result(HANDLE_EINTR(close(fd)));
+ return true;
+}
+
+#if !defined(__LB_SHELL__)
+FILE* CreateAndOpenTemporaryShmemFile(FilePath* path, bool executable) {
+ FilePath directory;
+ if (!GetShmemTempDir(&directory, executable))
+ return NULL;
+
+ return CreateAndOpenTemporaryFileInDir(directory, path);
+}
+#endif
+
+FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
+ int fd = CreateAndOpenFdForTemporaryFile(dir, path);
+ if (fd < 0)
+ return NULL;
+
+ FILE* file = fdopen(fd, "a+");
+ if (!file)
+ ignore_result(HANDLE_EINTR(close(fd)));
+ return file;
+}
+
+bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
+ base::ThreadRestrictions::AssertIOAllowed(); // For call to close().
+ int fd = CreateAndOpenFdForTemporaryFile(dir, temp_file);
+ return ((fd >= 0) && !HANDLE_EINTR(close(fd)));
+}
+
+static bool CreateTemporaryDirInDirImpl(const FilePath& base_dir,
+ const FilePath::StringType& name_tmpl,
+ FilePath* new_dir) {
+ base::ThreadRestrictions::AssertIOAllowed(); // For call to mkdtemp().
+ DCHECK(name_tmpl.find("XXXXXX") != FilePath::StringType::npos)
+ << "Directory name template must contain \"XXXXXX\".";
+
+ FilePath sub_dir = base_dir.Append(name_tmpl);
+ std::string sub_dir_string = sub_dir.value();
+
+ // this should be OK since mkdtemp just replaces characters in place
+ char* buffer = const_cast<char*>(sub_dir_string.c_str());
+ char* dtemp = mkdtemp(buffer);
+ if (!dtemp) {
+ DPLOG(ERROR) << "mkdtemp";
+ return false;
+ }
+ *new_dir = FilePath(dtemp);
+ return true;
+}
+
+bool CreateTemporaryDirInDir(const FilePath& base_dir,
+ const FilePath::StringType& prefix,
+ FilePath* new_dir) {
+ FilePath::StringType mkdtemp_template = prefix;
+ mkdtemp_template.append(FILE_PATH_LITERAL("XXXXXX"));
+ return CreateTemporaryDirInDirImpl(base_dir, mkdtemp_template, new_dir);
+}
+
+bool CreateNewTempDirectory(const FilePath::StringType& prefix,
+ FilePath* new_temp_path) {
+ FilePath tmpdir;
+ if (!GetTempDir(&tmpdir))
+ return false;
+
+ return CreateTemporaryDirInDirImpl(tmpdir, TempFileName(), new_temp_path);
+}
+
+#if !defined(__LB_XB1__) && !defined(__LB_XB360__)
+bool CreateDirectory(const FilePath& full_path) {
+ base::ThreadRestrictions::AssertIOAllowed(); // For call to mkdir().
+ std::vector<FilePath> subpaths;
+
+ // Collect a list of all parent directories.
+ FilePath last_path = full_path;
+ subpaths.push_back(full_path);
+ for (FilePath path = full_path.DirName();
+ path.value() != last_path.value(); path = path.DirName()) {
+ subpaths.push_back(path);
+ last_path = path;
+ }
+
+ // Iterate through the parents and create the missing ones.
+ for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
+ i != subpaths.rend(); ++i) {
+ if (DirectoryExists(*i))
+ continue;
+ if (mkdir(i->value().c_str(), 0700) == 0)
+ continue;
+ // Mkdir failed, but it might have failed with EEXIST, or some other error
+ // due to the the directory appearing out of thin air. This can occur if
+ // two processes are trying to create the same file system tree at the same
+ // time. Check to see if it exists and make sure it is a directory.
+ if (!DirectoryExists(*i))
+ return false;
+ }
+ return true;
+}
+#endif
+
+// TODO(rkc): Refactor GetFileInfo and FileEnumerator to handle symlinks
+// correctly. http://code.google.com/p/chromium-os/issues/detail?id=15948
+bool IsLink(const FilePath& file_path) {
+ stat_wrapper_t st;
+ // If we can't lstat the file, it's safe to assume that the file won't at
+ // least be a 'followable' link.
+ if (CallLstat(file_path.value().c_str(), &st) != 0)
+ return false;
+
+ if (S_ISLNK(st.st_mode))
+ return true;
+ else
+ return false;
+}
+
+bool GetFileInfo(const FilePath& file_path, base::PlatformFileInfo* results) {
+ stat_wrapper_t file_info;
+ if (CallStat(file_path.value().c_str(), &file_info) != 0)
+ return false;
+ results->is_directory = S_ISDIR(file_info.st_mode);
+ results->size = file_info.st_size;
+ results->last_modified = base::Time::FromTimeT(file_info.st_mtime);
+ results->last_accessed = base::Time::FromTimeT(file_info.st_atime);
+ results->creation_time = base::Time::FromTimeT(file_info.st_ctime);
+ return true;
+}
+
+#if !defined(__LB_SHELL__)
+bool GetInode(const FilePath& path, ino_t* inode) {
+ base::ThreadRestrictions::AssertIOAllowed(); // For call to stat().
+ struct stat buffer;
+ int result = stat(path.value().c_str(), &buffer);
+ if (result < 0)
+ return false;
+
+ *inode = buffer.st_ino;
+ return true;
+}
+#endif
+
+FILE* OpenFile(const std::string& filename, const char* mode) {
+ return OpenFile(FilePath(filename), mode);
+}
+
+FILE* OpenFile(const FilePath& filename, const char* mode) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ FILE* result = NULL;
+ do {
+ result = fopen(filename.value().c_str(), mode);
+ } while (!result && errno == EINTR);
+ return result;
+}
+
+int ReadFile(const FilePath& filename, char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ int fd = HANDLE_EINTR(open(filename.value().c_str(), O_RDONLY));
+ if (fd < 0)
+ return -1;
+
+ ssize_t bytes_read = HANDLE_EINTR(read(fd, data, size));
+ if (int ret = HANDLE_EINTR(close(fd)) < 0)
+ return ret;
+ return bytes_read;
+}
+
+int WriteFile(const FilePath& filename, const char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ int fd = HANDLE_EINTR(creat(filename.value().c_str(), 0666));
+ if (fd < 0)
+ return -1;
+
+ int bytes_written = WriteFileDescriptor(fd, data, size);
+ if (int ret = HANDLE_EINTR(close(fd)) < 0)
+ return ret;
+ return bytes_written;
+}
+
+int WriteFileDescriptor(const int fd, const char* data, int size) {
+ // Allow for partial writes.
+ ssize_t bytes_written_total = 0;
+ for (ssize_t bytes_written_partial = 0; bytes_written_total < size;
+ bytes_written_total += bytes_written_partial) {
+ bytes_written_partial =
+ HANDLE_EINTR(write(fd, data + bytes_written_total,
+ size - bytes_written_total));
+ if (bytes_written_partial < 0)
+ return -1;
+ }
+
+ return bytes_written_total;
+}
+
+int AppendToFile(const FilePath& filename, const char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ int fd = HANDLE_EINTR(open(filename.value().c_str(), O_WRONLY | O_APPEND));
+ if (fd < 0)
+ return -1;
+
+ int bytes_written = WriteFileDescriptor(fd, data, size);
+ if (int ret = HANDLE_EINTR(close(fd)) < 0)
+ return ret;
+ return bytes_written;
+}
+
+// Gets the current working directory for the process.
+bool GetCurrentDirectory(FilePath* dir) {
+ // getcwd can return ENOENT, which implies it checks against the disk.
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ char system_buffer[PATH_MAX] = "";
+ if (!getcwd(system_buffer, sizeof(system_buffer))) {
+ NOTREACHED();
+ return false;
+ }
+ *dir = FilePath(system_buffer);
+ return true;
+}
+
+// Sets the current working directory for the process.
+bool SetCurrentDirectory(const FilePath& path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ int ret = chdir(path.value().c_str());
+ return !ret;
+}
+
+///////////////////////////////////////////////
+// FileEnumerator
+
+FileEnumerator::FileEnumerator(const FilePath& root_path,
+ bool recursive,
+ int file_type)
+ : current_directory_entry_(0),
+ root_path_(root_path),
+ recursive_(recursive),
+ file_type_(file_type) {
+ // INCLUDE_DOT_DOT must not be specified if recursive.
+ DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
+ pending_paths_.push(root_path);
+}
+
+FileEnumerator::FileEnumerator(const FilePath& root_path,
+ bool recursive,
+ int file_type,
+ const FilePath::StringType& pattern)
+ : current_directory_entry_(0),
+ root_path_(root_path),
+ recursive_(recursive),
+ file_type_(file_type),
+ pattern_(root_path.Append(pattern).value()) {
+ // INCLUDE_DOT_DOT must not be specified if recursive.
+ DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
+ // The Windows version of this code appends the pattern to the root_path,
+ // potentially only matching against items in the top-most directory.
+ // Do the same here.
+ if (pattern.empty())
+ pattern_ = FilePath::StringType();
+ pending_paths_.push(root_path);
+}
+
+FileEnumerator::~FileEnumerator() {
+}
+
+FilePath FileEnumerator::Next() {
+ ++current_directory_entry_;
+
+ // While we've exhausted the entries in the current directory, do the next
+ while (current_directory_entry_ >= directory_entries_.size()) {
+ if (pending_paths_.empty())
+ return FilePath();
+
+ root_path_ = pending_paths_.top();
+ root_path_ = root_path_.StripTrailingSeparators();
+ pending_paths_.pop();
+
+ std::vector<DirectoryEntryInfo> entries;
+ if (!ReadDirectory(
+ &entries, root_path_, (file_type_ & SHOW_SYM_LINKS) != 0))
+ continue;
+
+ directory_entries_.clear();
+ current_directory_entry_ = 0;
+ for (std::vector<DirectoryEntryInfo>::const_iterator
+ i = entries.begin(); i != entries.end(); ++i) {
+ FilePath full_path = root_path_.Append(i->filename);
+ if (ShouldSkip(full_path))
+ continue;
+
+ if (pattern_.size() &&
+ fnmatch(pattern_.c_str(), full_path.value().c_str(), FNM_NOESCAPE))
+ continue;
+
+ if (recursive_ && S_ISDIR(i->stat.st_mode))
+ pending_paths_.push(full_path);
+
+ if ((S_ISDIR(i->stat.st_mode) && (file_type_ & DIRECTORIES)) ||
+ (!S_ISDIR(i->stat.st_mode) && (file_type_ & FILES)))
+ directory_entries_.push_back(*i);
+ }
+ }
+
+ return root_path_.Append(directory_entries_[current_directory_entry_
+ ].filename);
+}
+
+void FileEnumerator::GetFindInfo(FindInfo* info) {
+ DCHECK(info);
+
+ if (current_directory_entry_ >= directory_entries_.size())
+ return;
+
+ DirectoryEntryInfo* cur_entry = &directory_entries_[current_directory_entry_];
+ memcpy(&(info->stat), &(cur_entry->stat), sizeof(info->stat));
+ info->filename.assign(cur_entry->filename.value());
+}
+
+// static
+bool FileEnumerator::IsDirectory(const FindInfo& info) {
+ return S_ISDIR(info.stat.st_mode);
+}
+
+// static
+FilePath FileEnumerator::GetFilename(const FindInfo& find_info) {
+ return FilePath(find_info.filename);
+}
+
+// static
+int64 FileEnumerator::GetFilesize(const FindInfo& find_info) {
+ return find_info.stat.st_size;
+}
+
+// static
+base::Time FileEnumerator::GetLastModifiedTime(const FindInfo& find_info) {
+ return base::Time::FromTimeT(find_info.stat.st_mtime);
+}
+
+bool FileEnumerator::ReadDirectory(std::vector<DirectoryEntryInfo>* entries,
+ const FilePath& source, bool show_links) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DIR* dir = opendir(source.value().c_str());
+ if (!dir)
+ return false;
+
+#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_BSD) && \
+ !defined(OS_SOLARIS) && !defined(OS_ANDROID) && !defined(__LB_SHELL__)
+ #error Port warning: depending on the definition of struct dirent, \
+ additional space for pathname may be needed
+#endif
+
+ struct dirent dent_buf;
+ struct dirent* dent;
+ while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) {
+ DirectoryEntryInfo info;
+ info.filename = FilePath(dent->d_name);
+
+ FilePath full_name = source.Append(dent->d_name);
+ int ret;
+ if (show_links)
+ ret = lstat(full_name.value().c_str(), &info.stat);
+ else
+ ret = stat(full_name.value().c_str(), &info.stat);
+ if (ret < 0) {
+ // Print the stat() error message unless it was ENOENT and we're
+ // following symlinks.
+ if (!(errno == ENOENT && !show_links)) {
+ DPLOG(ERROR) << "Couldn't stat "
+ << source.Append(dent->d_name).value();
+ }
+ memset(&info.stat, 0, sizeof(info.stat));
+ }
+ entries->push_back(info);
+ }
+
+ closedir(dir);
+ return true;
+}
+
+///////////////////////////////////////////////
+// MemoryMappedFile
+#if !defined(__LB_SHELL__)
+MemoryMappedFile::MemoryMappedFile()
+ : file_(base::kInvalidPlatformFileValue),
+ data_(NULL),
+ length_(0) {
+}
+
+bool MemoryMappedFile::MapFileToMemoryInternal() {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ struct stat file_stat;
+ if (fstat(file_, &file_stat) == base::kInvalidPlatformFileValue) {
+ DLOG(ERROR) << "Couldn't fstat " << file_ << ", errno " << errno;
+ return false;
+ }
+ length_ = file_stat.st_size;
+
+ data_ = static_cast<uint8*>(
+ mmap(NULL, length_, PROT_READ, MAP_SHARED, file_, 0));
+ if (data_ == MAP_FAILED)
+ DLOG(ERROR) << "Couldn't mmap " << file_ << ", errno " << errno;
+
+ return data_ != MAP_FAILED;
+}
+
+void MemoryMappedFile::CloseHandles() {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ if (data_ != NULL)
+ munmap(data_, length_);
+ if (file_ != base::kInvalidPlatformFileValue)
+ ignore_result(HANDLE_EINTR(close(file_)));
+
+ data_ = NULL;
+ length_ = 0;
+ file_ = base::kInvalidPlatformFileValue;
+}
+#endif
+
+bool HasFileBeenModifiedSince(const FileEnumerator::FindInfo& find_info,
+ const base::Time& cutoff_time) {
+ return static_cast<time_t>(find_info.stat.st_mtime) >= cutoff_time.ToTimeT();
+}
+
+bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) {
+ FilePath real_path_result;
+ if (!RealPath(path, &real_path_result))
+ return false;
+
+ // To be consistant with windows, fail if |real_path_result| is a
+ // directory.
+ stat_wrapper_t file_info;
+ if (CallStat(real_path_result.value().c_str(), &file_info) != 0 ||
+ S_ISDIR(file_info.st_mode))
+ return false;
+
+ *normalized_path = real_path_result;
+ return true;
+}
+
+#if !defined(OS_MACOSX)
+#if !defined(__LB_SHELL__)
+bool GetTempDir(FilePath* path) {
+ const char* tmp = getenv("TMPDIR");
+ if (tmp)
+ *path = FilePath(tmp);
+ else
+#if defined(OS_ANDROID)
+ return PathService::Get(base::DIR_CACHE, path);
+#else
+ *path = FilePath("/tmp");
+#endif
+ return true;
+}
+
+#if !defined(OS_ANDROID)
+
+#if defined(OS_LINUX)
+// Determine if /dev/shm files can be mapped and then mprotect'd PROT_EXEC.
+// This depends on the mount options used for /dev/shm, which vary among
+// different Linux distributions and possibly local configuration. It also
+// depends on details of kernel--ChromeOS uses the noexec option for /dev/shm
+// but its kernel allows mprotect with PROT_EXEC anyway.
+
+namespace {
+
+bool DetermineDevShmExecutable() {
+ bool result = false;
+ FilePath path;
+ int fd = CreateAndOpenFdForTemporaryFile(FilePath("/dev/shm"), &path);
+ if (fd >= 0) {
+ ScopedFD shm_fd_closer(&fd);
+ Delete(path, false);
+ long sysconf_result = sysconf(_SC_PAGESIZE);
+ CHECK_GE(sysconf_result, 0);
+ size_t pagesize = static_cast<size_t>(sysconf_result);
+ CHECK_GE(sizeof(pagesize), sizeof(sysconf_result));
+ void *mapping = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd, 0);
+ if (mapping != MAP_FAILED) {
+ if (mprotect(mapping, pagesize, PROT_READ | PROT_EXEC) == 0)
+ result = true;
+ munmap(mapping, pagesize);
+ }
+ }
+ return result;
+}
+
+}; // namespace
+#endif // defined(OS_LINUX)
+
+bool GetShmemTempDir(FilePath* path, bool executable) {
+#if defined(OS_LINUX)
+ bool use_dev_shm = true;
+ if (executable) {
+ static const bool s_dev_shm_executable = DetermineDevShmExecutable();
+ use_dev_shm = s_dev_shm_executable;
+ }
+ if (use_dev_shm) {
+ *path = FilePath("/dev/shm");
+ return true;
+ }
+#endif
+ return GetTempDir(path);
+}
+#endif // !defined(OS_ANDROID)
+
+FilePath GetHomeDir() {
+#if defined(OS_CHROMEOS)
+ if (base::chromeos::IsRunningOnChromeOS())
+ return FilePath("/home/chronos/user");
+#endif
+
+ const char* home_dir = getenv("HOME");
+ if (home_dir && home_dir[0])
+ return FilePath(home_dir);
+
+#if defined(OS_ANDROID)
+ DLOG(WARNING) << "OS_ANDROID: Home directory lookup not yet implemented.";
+#else
+ // g_get_home_dir calls getpwent, which can fall through to LDAP calls.
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ home_dir = g_get_home_dir();
+ if (home_dir && home_dir[0])
+ return FilePath(home_dir);
+#endif
+
+ FilePath rv;
+ if (file_util::GetTempDir(&rv))
+ return rv;
+
+ // Last resort.
+ return FilePath("/tmp");
+}
+#endif // !defined(__LB_SHELL__)
+
+bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ int infile = HANDLE_EINTR(open(from_path.value().c_str(), O_RDONLY));
+ if (infile < 0)
+ return false;
+
+ int outfile = HANDLE_EINTR(creat(to_path.value().c_str(), 0666));
+ if (outfile < 0) {
+ ignore_result(HANDLE_EINTR(close(infile)));
+ return false;
+ }
+
+ const size_t kBufferSize = 32768;
+ std::vector<char> buffer(kBufferSize);
+ bool result = true;
+
+ while (result) {
+ ssize_t bytes_read = HANDLE_EINTR(read(infile, &buffer[0], buffer.size()));
+ if (bytes_read < 0) {
+ result = false;
+ break;
+ }
+ if (bytes_read == 0)
+ break;
+ // Allow for partial writes
+ ssize_t bytes_written_per_read = 0;
+ do {
+ ssize_t bytes_written_partial = HANDLE_EINTR(write(
+ outfile,
+ &buffer[bytes_written_per_read],
+ bytes_read - bytes_written_per_read));
+ if (bytes_written_partial < 0) {
+ result = false;
+ break;
+ }
+ bytes_written_per_read += bytes_written_partial;
+ } while (bytes_written_per_read < bytes_read);
+ }
+
+ if (HANDLE_EINTR(close(infile)) < 0)
+ result = false;
+ if (HANDLE_EINTR(close(outfile)) < 0)
+ result = false;
+
+ return result;
+}
+#endif // !defined(OS_MACOSX)
+
+bool VerifyPathControlledByUser(const FilePath& base,
+ const FilePath& path,
+ uid_t owner_uid,
+ const std::set<gid_t>& group_gids) {
+ if (base != path && !base.IsParent(path)) {
+ DLOG(ERROR) << "|base| must be a subdirectory of |path|. base = \""
+ << base.value() << "\", path = \"" << path.value() << "\"";
+ return false;
+ }
+
+ std::vector<FilePath::StringType> base_components;
+ std::vector<FilePath::StringType> path_components;
+
+ base.GetComponents(&base_components);
+ path.GetComponents(&path_components);
+
+ std::vector<FilePath::StringType>::const_iterator ib, ip;
+ for (ib = base_components.begin(), ip = path_components.begin();
+ ib != base_components.end(); ++ib, ++ip) {
+ // |base| must be a subpath of |path|, so all components should match.
+ // If these CHECKs fail, look at the test that base is a parent of
+ // path at the top of this function.
+ DCHECK(ip != path_components.end());
+ DCHECK(*ip == *ib);
+ }
+
+ FilePath current_path = base;
+ if (!VerifySpecificPathControlledByUser(current_path, owner_uid, group_gids))
+ return false;
+
+ for (; ip != path_components.end(); ++ip) {
+ current_path = current_path.Append(*ip);
+ if (!VerifySpecificPathControlledByUser(
+ current_path, owner_uid, group_gids))
+ return false;
+ }
+ return true;
+}
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+bool VerifyPathControlledByAdmin(const FilePath& path) {
+ const unsigned kRootUid = 0;
+ const FilePath kFileSystemRoot("/");
+
+ // The name of the administrator group on mac os.
+ const char* const kAdminGroupNames[] = {
+ "admin",
+ "wheel"
+ };
+
+ // Reading the groups database may touch the file system.
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ std::set<gid_t> allowed_group_ids;
+ for (int i = 0, ie = arraysize(kAdminGroupNames); i < ie; ++i) {
+ struct group *group_record = getgrnam(kAdminGroupNames[i]);
+ if (!group_record) {
+ DPLOG(ERROR) << "Could not get the group ID of group \""
+ << kAdminGroupNames[i] << "\".";
+ continue;
+ }
+
+ allowed_group_ids.insert(group_record->gr_gid);
+ }
+
+ return VerifyPathControlledByUser(
+ kFileSystemRoot, path, kRootUid, allowed_group_ids);
+}
+#endif // defined(OS_MACOSX) && !defined(OS_IOS)
+
+} // namespace file_util
diff --git a/src/base/file_util_proxy.cc b/src/base/file_util_proxy.cc
new file mode 100644
index 0000000..2b075b0
--- /dev/null
+++ b/src/base/file_util_proxy.cc
@@ -0,0 +1,449 @@
+// Copyright (c) 2012 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/file_util_proxy.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/file_util.h"
+#include "base/location.h"
+#include "base/message_loop_proxy.h"
+#include "base/task_runner.h"
+#include "base/task_runner_util.h"
+
+namespace base {
+
+namespace {
+
+void CallWithTranslatedParameter(const FileUtilProxy::StatusCallback& callback,
+ bool value) {
+ DCHECK(!callback.is_null());
+ callback.Run(value ? PLATFORM_FILE_OK : PLATFORM_FILE_ERROR_FAILED);
+}
+
+// Helper classes or routines for individual methods.
+class CreateOrOpenHelper {
+ public:
+ CreateOrOpenHelper(TaskRunner* task_runner,
+ const FileUtilProxy::CloseTask& close_task)
+ : task_runner_(task_runner),
+ close_task_(close_task),
+ file_handle_(kInvalidPlatformFileValue),
+ created_(false),
+ error_(PLATFORM_FILE_OK) {}
+
+ ~CreateOrOpenHelper() {
+ if (file_handle_ != kInvalidPlatformFileValue) {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(base::IgnoreResult(close_task_), file_handle_));
+ }
+ }
+
+ void RunWork(const FileUtilProxy::CreateOrOpenTask& task) {
+ error_ = task.Run(&file_handle_, &created_);
+ }
+
+ void Reply(const FileUtilProxy::CreateOrOpenCallback& callback,
+ const FileUtilProxy::CreateOrOpenTask& open_task) {
+ DCHECK(!callback.is_null());
+ if (error_ != PLATFORM_FILE_ERROR_TOO_MANY_OPENED) {
+ callback.Run(error_, PassPlatformFile(&file_handle_), created_);
+ return;
+ }
+ DLOG(WARNING) << "Too many files are opened, retrying ...";
+ CreateOrOpenHelper* helper =
+ new CreateOrOpenHelper(task_runner_, close_task_);
+ task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&CreateOrOpenHelper::RunWork, Unretained(helper), open_task),
+ Bind(&CreateOrOpenHelper::Reply, Owned(helper), callback, open_task));
+ }
+
+ private:
+ scoped_refptr<TaskRunner> task_runner_;
+ FileUtilProxy::CloseTask close_task_;
+ PlatformFile file_handle_;
+ bool created_;
+ PlatformFileError error_;
+ DISALLOW_COPY_AND_ASSIGN(CreateOrOpenHelper);
+};
+
+class CreateTemporaryHelper {
+ public:
+ explicit CreateTemporaryHelper(TaskRunner* task_runner)
+ : task_runner_(task_runner),
+ file_handle_(kInvalidPlatformFileValue),
+ error_(PLATFORM_FILE_OK) {}
+
+ ~CreateTemporaryHelper() {
+ if (file_handle_ != kInvalidPlatformFileValue) {
+ FileUtilProxy::Close(task_runner_, file_handle_,
+ FileUtilProxy::StatusCallback());
+ }
+ }
+
+ void RunWork(int additional_file_flags) {
+ // TODO(darin): file_util should have a variant of CreateTemporaryFile
+ // that returns a FilePath and a PlatformFile.
+ file_util::CreateTemporaryFile(&file_path_);
+
+ int file_flags =
+ PLATFORM_FILE_WRITE |
+ PLATFORM_FILE_TEMPORARY |
+ PLATFORM_FILE_CREATE_ALWAYS |
+ additional_file_flags;
+
+ error_ = PLATFORM_FILE_OK;
+ file_handle_ = CreatePlatformFile(file_path_, file_flags, NULL, &error_);
+ }
+
+ void Reply(const FileUtilProxy::CreateTemporaryCallback& callback) {
+ DCHECK(!callback.is_null());
+ callback.Run(error_, PassPlatformFile(&file_handle_), file_path_);
+ }
+
+ private:
+ scoped_refptr<TaskRunner> task_runner_;
+ PlatformFile file_handle_;
+ FilePath file_path_;
+ PlatformFileError error_;
+ DISALLOW_COPY_AND_ASSIGN(CreateTemporaryHelper);
+};
+
+class GetFileInfoHelper {
+ public:
+ GetFileInfoHelper()
+ : error_(PLATFORM_FILE_OK) {}
+
+ void RunWorkForFilePath(const FilePath& file_path) {
+ if (!file_util::PathExists(file_path)) {
+ error_ = PLATFORM_FILE_ERROR_NOT_FOUND;
+ return;
+ }
+ if (!file_util::GetFileInfo(file_path, &file_info_))
+ error_ = PLATFORM_FILE_ERROR_FAILED;
+ }
+
+ void RunWorkForPlatformFile(PlatformFile file) {
+ if (!GetPlatformFileInfo(file, &file_info_))
+ error_ = PLATFORM_FILE_ERROR_FAILED;
+ }
+
+ void Reply(const FileUtilProxy::GetFileInfoCallback& callback) {
+ if (!callback.is_null()) {
+ callback.Run(error_, file_info_);
+ }
+ }
+
+ private:
+ PlatformFileError error_;
+ PlatformFileInfo file_info_;
+ DISALLOW_COPY_AND_ASSIGN(GetFileInfoHelper);
+};
+
+class ReadHelper {
+ public:
+ explicit ReadHelper(int bytes_to_read)
+ : buffer_(new char[bytes_to_read]),
+ bytes_to_read_(bytes_to_read),
+ bytes_read_(0) {}
+
+ void RunWork(PlatformFile file, int64 offset) {
+ bytes_read_ = ReadPlatformFile(file, offset, buffer_.get(), bytes_to_read_);
+ }
+
+ void Reply(const FileUtilProxy::ReadCallback& callback) {
+ if (!callback.is_null()) {
+ PlatformFileError error =
+ (bytes_read_ < 0) ? PLATFORM_FILE_ERROR_FAILED : PLATFORM_FILE_OK;
+ callback.Run(error, buffer_.get(), bytes_read_);
+ }
+ }
+
+ private:
+ scoped_array<char> buffer_;
+ int bytes_to_read_;
+ int bytes_read_;
+ DISALLOW_COPY_AND_ASSIGN(ReadHelper);
+};
+
+class WriteHelper {
+ public:
+ WriteHelper(const char* buffer, int bytes_to_write)
+ : buffer_(new char[bytes_to_write]),
+ bytes_to_write_(bytes_to_write),
+ bytes_written_(0) {
+ memcpy(buffer_.get(), buffer, bytes_to_write);
+ }
+
+ void RunWork(PlatformFile file, int64 offset) {
+ bytes_written_ = WritePlatformFile(file, offset, buffer_.get(),
+ bytes_to_write_);
+ }
+
+ void Reply(const FileUtilProxy::WriteCallback& callback) {
+ if (!callback.is_null()) {
+ PlatformFileError error =
+ (bytes_written_ < 0) ? PLATFORM_FILE_ERROR_FAILED : PLATFORM_FILE_OK;
+ callback.Run(error, bytes_written_);
+ }
+ }
+
+ private:
+ scoped_array<char> buffer_;
+ int bytes_to_write_;
+ int bytes_written_;
+ DISALLOW_COPY_AND_ASSIGN(WriteHelper);
+};
+
+
+PlatformFileError CreateOrOpenAdapter(
+ const FilePath& file_path, int file_flags,
+ PlatformFile* file_handle, bool* created) {
+ DCHECK(file_handle);
+ DCHECK(created);
+ if (!file_util::DirectoryExists(file_path.DirName())) {
+ // If its parent does not exist, should return NOT_FOUND error.
+ return PLATFORM_FILE_ERROR_NOT_FOUND;
+ }
+ PlatformFileError error = PLATFORM_FILE_OK;
+ *file_handle = CreatePlatformFile(file_path, file_flags, created, &error);
+ return error;
+}
+
+PlatformFileError CloseAdapter(PlatformFile file_handle) {
+ if (!ClosePlatformFile(file_handle)) {
+ return PLATFORM_FILE_ERROR_FAILED;
+ }
+ return PLATFORM_FILE_OK;
+}
+
+PlatformFileError DeleteAdapter(const FilePath& file_path, bool recursive) {
+ if (!file_util::PathExists(file_path)) {
+ return PLATFORM_FILE_ERROR_NOT_FOUND;
+ }
+ if (!file_util::Delete(file_path, recursive)) {
+ if (!recursive && !file_util::IsDirectoryEmpty(file_path)) {
+ return PLATFORM_FILE_ERROR_NOT_EMPTY;
+ }
+ return PLATFORM_FILE_ERROR_FAILED;
+ }
+ return PLATFORM_FILE_OK;
+}
+
+} // namespace
+
+// static
+bool FileUtilProxy::CreateOrOpen(
+ TaskRunner* task_runner,
+ const FilePath& file_path, int file_flags,
+ const CreateOrOpenCallback& callback) {
+ return RelayCreateOrOpen(
+ task_runner,
+ base::Bind(&CreateOrOpenAdapter, file_path, file_flags),
+ base::Bind(&CloseAdapter),
+ callback);
+}
+
+// static
+bool FileUtilProxy::CreateTemporary(
+ TaskRunner* task_runner,
+ int additional_file_flags,
+ const CreateTemporaryCallback& callback) {
+ CreateTemporaryHelper* helper = new CreateTemporaryHelper(task_runner);
+ return task_runner->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&CreateTemporaryHelper::RunWork, Unretained(helper),
+ additional_file_flags),
+ Bind(&CreateTemporaryHelper::Reply, Owned(helper), callback));
+}
+
+// static
+bool FileUtilProxy::Close(
+ TaskRunner* task_runner,
+ base::PlatformFile file_handle,
+ const StatusCallback& callback) {
+ return RelayClose(
+ task_runner,
+ base::Bind(&CloseAdapter),
+ file_handle, callback);
+}
+
+// Retrieves the information about a file. It is invalid to pass NULL for the
+// callback.
+bool FileUtilProxy::GetFileInfo(
+ TaskRunner* task_runner,
+ const FilePath& file_path,
+ const GetFileInfoCallback& callback) {
+ GetFileInfoHelper* helper = new GetFileInfoHelper;
+ return task_runner->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&GetFileInfoHelper::RunWorkForFilePath,
+ Unretained(helper), file_path),
+ Bind(&GetFileInfoHelper::Reply, Owned(helper), callback));
+}
+
+// static
+bool FileUtilProxy::GetFileInfoFromPlatformFile(
+ TaskRunner* task_runner,
+ PlatformFile file,
+ const GetFileInfoCallback& callback) {
+ GetFileInfoHelper* helper = new GetFileInfoHelper;
+ return task_runner->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&GetFileInfoHelper::RunWorkForPlatformFile,
+ Unretained(helper), file),
+ Bind(&GetFileInfoHelper::Reply, Owned(helper), callback));
+}
+
+// static
+bool FileUtilProxy::Delete(TaskRunner* task_runner,
+ const FilePath& file_path,
+ bool recursive,
+ const StatusCallback& callback) {
+ return RelayFileTask(
+ task_runner, FROM_HERE,
+ Bind(&DeleteAdapter, file_path, recursive),
+ callback);
+}
+
+// static
+bool FileUtilProxy::RecursiveDelete(
+ TaskRunner* task_runner,
+ const FilePath& file_path,
+ const StatusCallback& callback) {
+ return RelayFileTask(
+ task_runner, FROM_HERE,
+ Bind(&DeleteAdapter, file_path, true /* recursive */),
+ callback);
+}
+
+// static
+bool FileUtilProxy::Read(
+ TaskRunner* task_runner,
+ PlatformFile file,
+ int64 offset,
+ int bytes_to_read,
+ const ReadCallback& callback) {
+ if (bytes_to_read < 0) {
+ return false;
+ }
+ ReadHelper* helper = new ReadHelper(bytes_to_read);
+ return task_runner->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&ReadHelper::RunWork, Unretained(helper), file, offset),
+ Bind(&ReadHelper::Reply, Owned(helper), callback));
+}
+
+// static
+bool FileUtilProxy::Write(
+ TaskRunner* task_runner,
+ PlatformFile file,
+ int64 offset,
+ const char* buffer,
+ int bytes_to_write,
+ const WriteCallback& callback) {
+ if (bytes_to_write <= 0 || buffer == NULL) {
+ return false;
+ }
+ WriteHelper* helper = new WriteHelper(buffer, bytes_to_write);
+ return task_runner->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&WriteHelper::RunWork, Unretained(helper), file, offset),
+ Bind(&WriteHelper::Reply, Owned(helper), callback));
+}
+
+#if !defined(OS_STARBOARD)
+// static
+bool FileUtilProxy::Touch(
+ TaskRunner* task_runner,
+ PlatformFile file,
+ const Time& last_access_time,
+ const Time& last_modified_time,
+ const StatusCallback& callback) {
+ return base::PostTaskAndReplyWithResult(
+ task_runner,
+ FROM_HERE,
+ Bind(&TouchPlatformFile, file,
+ last_access_time, last_modified_time),
+ Bind(&CallWithTranslatedParameter, callback));
+}
+
+// static
+bool FileUtilProxy::Touch(
+ TaskRunner* task_runner,
+ const FilePath& file_path,
+ const Time& last_access_time,
+ const Time& last_modified_time,
+ const StatusCallback& callback) {
+ return base::PostTaskAndReplyWithResult(
+ task_runner,
+ FROM_HERE,
+ Bind(&file_util::TouchFile, file_path,
+ last_access_time, last_modified_time),
+ Bind(&CallWithTranslatedParameter, callback));
+}
+#endif
+
+// static
+bool FileUtilProxy::Truncate(
+ TaskRunner* task_runner,
+ PlatformFile file,
+ int64 length,
+ const StatusCallback& callback) {
+ return base::PostTaskAndReplyWithResult(
+ task_runner,
+ FROM_HERE,
+ Bind(&TruncatePlatformFile, file, length),
+ Bind(&CallWithTranslatedParameter, callback));
+}
+
+// static
+bool FileUtilProxy::Flush(
+ TaskRunner* task_runner,
+ PlatformFile file,
+ const StatusCallback& callback) {
+ return base::PostTaskAndReplyWithResult(
+ task_runner,
+ FROM_HERE,
+ Bind(&FlushPlatformFile, file),
+ Bind(&CallWithTranslatedParameter, callback));
+}
+
+// static
+bool FileUtilProxy::RelayFileTask(
+ TaskRunner* task_runner,
+ const tracked_objects::Location& from_here,
+ const FileTask& file_task,
+ const StatusCallback& callback) {
+ return base::PostTaskAndReplyWithResult(
+ task_runner, from_here, file_task, callback);
+}
+
+// static
+bool FileUtilProxy::RelayCreateOrOpen(
+ TaskRunner* task_runner,
+ const CreateOrOpenTask& open_task,
+ const CloseTask& close_task,
+ const CreateOrOpenCallback& callback) {
+ CreateOrOpenHelper* helper = new CreateOrOpenHelper(
+ task_runner, close_task);
+ return task_runner->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&CreateOrOpenHelper::RunWork, Unretained(helper), open_task),
+ Bind(&CreateOrOpenHelper::Reply, Owned(helper), callback, open_task));
+}
+
+// static
+bool FileUtilProxy::RelayClose(
+ TaskRunner* task_runner,
+ const CloseTask& close_task,
+ PlatformFile file_handle,
+ const StatusCallback& callback) {
+ return base::PostTaskAndReplyWithResult(
+ task_runner, FROM_HERE, Bind(close_task, file_handle), callback);
+}
+
+} // namespace base
diff --git a/src/base/file_util_proxy.h b/src/base/file_util_proxy.h
new file mode 100644
index 0000000..f6ecbad
--- /dev/null
+++ b/src/base/file_util_proxy.h
@@ -0,0 +1,198 @@
+// Copyright (c) 2012 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_FILE_UTIL_PROXY_H_
+#define BASE_FILE_UTIL_PROXY_H_
+
+#include "base/base_export.h"
+#include "base/callback_forward.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/platform_file.h"
+
+namespace tracked_objects {
+class Location;
+};
+
+namespace base {
+
+class TaskRunner;
+class Time;
+
+// This class provides asynchronous access to common file routines.
+class BASE_EXPORT FileUtilProxy {
+ public:
+ // Holds metadata for file or directory entry.
+ struct Entry {
+ FilePath::StringType name;
+ bool is_directory;
+ int64 size;
+ base::Time last_modified_time;
+ };
+
+ // This callback is used by methods that report only an error code. It is
+ // valid to pass a null callback to any function that takes a StatusCallback,
+ // in which case the operation will complete silently.
+ typedef Callback<void(PlatformFileError)> StatusCallback;
+
+ typedef Callback<void(PlatformFileError,
+ PassPlatformFile,
+ bool /* created */)> CreateOrOpenCallback;
+ typedef Callback<void(PlatformFileError,
+ PassPlatformFile,
+ const FilePath&)> CreateTemporaryCallback;
+ typedef Callback<void(PlatformFileError,
+ const PlatformFileInfo&)> GetFileInfoCallback;
+ typedef Callback<void(PlatformFileError,
+ const char* /* data */,
+ int /* bytes read */)> ReadCallback;
+ typedef Callback<void(PlatformFileError,
+ int /* bytes written */)> WriteCallback;
+
+ typedef Callback<PlatformFileError(PlatformFile*, bool*)> CreateOrOpenTask;
+ typedef Callback<PlatformFileError(PlatformFile)> CloseTask;
+ typedef Callback<PlatformFileError(void)> FileTask;
+
+ // Creates or opens a file with the given flags. It is invalid to pass a null
+ // callback. If PLATFORM_FILE_CREATE is set in |file_flags| it always tries to
+ // create a new file at the given |file_path| and calls back with
+ // PLATFORM_FILE_ERROR_FILE_EXISTS if the |file_path| already exists.
+ static bool CreateOrOpen(TaskRunner* task_runner,
+ const FilePath& file_path,
+ int file_flags,
+ const CreateOrOpenCallback& callback);
+
+ // Creates a temporary file for writing. The path and an open file handle are
+ // returned. It is invalid to pass a null callback. The additional file flags
+ // will be added on top of the default file flags which are:
+ // base::PLATFORM_FILE_CREATE_ALWAYS
+ // base::PLATFORM_FILE_WRITE
+ // base::PLATFORM_FILE_TEMPORARY.
+ // Set |additional_file_flags| to 0 for synchronous writes and set to
+ // base::PLATFORM_FILE_ASYNC to support asynchronous file operations.
+ static bool CreateTemporary(
+ TaskRunner* task_runner,
+ int additional_file_flags,
+ const CreateTemporaryCallback& callback);
+
+ // Close the given file handle.
+ static bool Close(TaskRunner* task_runner,
+ PlatformFile,
+ const StatusCallback& callback);
+
+ // Retrieves the information about a file. It is invalid to pass a null
+ // callback.
+ static bool GetFileInfo(
+ TaskRunner* task_runner,
+ const FilePath& file_path,
+ const GetFileInfoCallback& callback);
+
+ static bool GetFileInfoFromPlatformFile(
+ TaskRunner* task_runner,
+ PlatformFile file,
+ const GetFileInfoCallback& callback);
+
+ // Deletes a file or a directory.
+ // It is an error to delete a non-empty directory with recursive=false.
+ static bool Delete(TaskRunner* task_runner,
+ const FilePath& file_path,
+ bool recursive,
+ const StatusCallback& callback);
+
+ // Deletes a directory and all of its contents.
+ static bool RecursiveDelete(
+ TaskRunner* task_runner,
+ const FilePath& file_path,
+ const StatusCallback& callback);
+
+ // Reads from a file. On success, the file pointer is moved to position
+ // |offset + bytes_to_read| in the file. The callback can be null.
+ static bool Read(
+ TaskRunner* task_runner,
+ PlatformFile file,
+ int64 offset,
+ int bytes_to_read,
+ const ReadCallback& callback);
+
+ // Writes to a file. If |offset| is greater than the length of the file,
+ // |false| is returned. On success, the file pointer is moved to position
+ // |offset + bytes_to_write| in the file. The callback can be null.
+ // |bytes_to_write| must be greater than zero.
+ static bool Write(
+ TaskRunner* task_runner,
+ PlatformFile file,
+ int64 offset,
+ const char* buffer,
+ int bytes_to_write,
+ const WriteCallback& callback);
+
+#if !defined(OS_STARBOARD)
+ // Touches a file. The callback can be null.
+ static bool Touch(
+ TaskRunner* task_runner,
+ PlatformFile file,
+ const Time& last_access_time,
+ const Time& last_modified_time,
+ const StatusCallback& callback);
+
+ // Touches a file. The callback can be null.
+ static bool Touch(
+ TaskRunner* task_runner,
+ const FilePath& file_path,
+ const Time& last_access_time,
+ const Time& last_modified_time,
+ const StatusCallback& callback);
+#endif
+
+ // Truncates a file to the given length. If |length| is greater than the
+ // current length of the file, the file will be extended with zeroes.
+ // The callback can be null.
+ static bool Truncate(
+ TaskRunner* task_runner,
+ PlatformFile file,
+ int64 length,
+ const StatusCallback& callback);
+
+ // Truncates a file to the given length. If |length| is greater than the
+ // current length of the file, the file will be extended with zeroes.
+ // The callback can be null.
+ static bool Truncate(
+ TaskRunner* task_runner,
+ const FilePath& path,
+ int64 length,
+ const StatusCallback& callback);
+
+ // Flushes a file. The callback can be null.
+ static bool Flush(
+ TaskRunner* task_runner,
+ PlatformFile file,
+ const StatusCallback& callback);
+
+ // Relay helpers.
+ static bool RelayFileTask(
+ TaskRunner* task_runner,
+ const tracked_objects::Location& from_here,
+ const FileTask& task,
+ const StatusCallback& callback);
+
+ static bool RelayCreateOrOpen(
+ TaskRunner* task_runner,
+ const CreateOrOpenTask& open_task,
+ const CloseTask& close_task,
+ const CreateOrOpenCallback& callback);
+
+ static bool RelayClose(
+ TaskRunner* task_runner,
+ const CloseTask& close_task,
+ PlatformFile,
+ const StatusCallback& callback);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(FileUtilProxy);
+};
+
+} // namespace base
+
+#endif // BASE_FILE_UTIL_PROXY_H_
diff --git a/src/base/file_util_proxy_unittest.cc b/src/base/file_util_proxy_unittest.cc
new file mode 100644
index 0000000..e238b32
--- /dev/null
+++ b/src/base/file_util_proxy_unittest.cc
@@ -0,0 +1,438 @@
+// Copyright (c) 2012 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/file_util_proxy.h"
+
+#include <map>
+
+#include "base/bind.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop.h"
+#include "base/platform_file.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+class FileUtilProxyTest : public testing::Test {
+ public:
+ FileUtilProxyTest()
+ : message_loop_(MessageLoop::TYPE_IO),
+ file_thread_("FileUtilProxyTestFileThread"),
+ error_(PLATFORM_FILE_OK),
+ created_(false),
+ file_(kInvalidPlatformFileValue),
+ bytes_written_(-1),
+ weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {}
+
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(dir_.CreateUniqueTempDir());
+ ASSERT_TRUE(file_thread_.Start());
+ }
+
+ virtual void TearDown() OVERRIDE {
+ if (file_ != kInvalidPlatformFileValue)
+ ClosePlatformFile(file_);
+ }
+
+ void DidFinish(PlatformFileError error) {
+ error_ = error;
+ MessageLoop::current()->Quit();
+ }
+
+ void DidCreateOrOpen(PlatformFileError error,
+ PassPlatformFile file,
+ bool created) {
+ error_ = error;
+ file_ = file.ReleaseValue();
+ created_ = created;
+ MessageLoop::current()->Quit();
+ }
+
+ void DidCreateTemporary(PlatformFileError error,
+ PassPlatformFile file,
+ const FilePath& path) {
+ error_ = error;
+ file_ = file.ReleaseValue();
+ path_ = path;
+ MessageLoop::current()->Quit();
+ }
+
+ void DidGetFileInfo(PlatformFileError error,
+ const PlatformFileInfo& file_info) {
+ error_ = error;
+ file_info_ = file_info;
+ MessageLoop::current()->Quit();
+ }
+
+ void DidRead(PlatformFileError error,
+ const char* data,
+ int bytes_read) {
+ error_ = error;
+ buffer_.resize(bytes_read);
+ memcpy(&buffer_[0], data, bytes_read);
+ MessageLoop::current()->Quit();
+ }
+
+ void DidWrite(PlatformFileError error,
+ int bytes_written) {
+ error_ = error;
+ bytes_written_ = bytes_written;
+ MessageLoop::current()->Quit();
+ }
+
+ protected:
+ PlatformFile GetTestPlatformFile(int flags) {
+ if (file_ != kInvalidPlatformFileValue)
+ return file_;
+ bool created;
+ PlatformFileError error;
+ file_ = CreatePlatformFile(test_path(), flags, &created, &error);
+ EXPECT_EQ(PLATFORM_FILE_OK, error);
+ EXPECT_NE(kInvalidPlatformFileValue, file_);
+ return file_;
+ }
+
+ TaskRunner* file_task_runner() const {
+ return file_thread_.message_loop_proxy().get();
+ }
+ const FilePath& test_dir_path() const { return dir_.path(); }
+ const FilePath test_path() const { return dir_.path().AppendASCII("test"); }
+
+ MessageLoop message_loop_;
+ Thread file_thread_;
+
+ ScopedTempDir dir_;
+ PlatformFileError error_;
+ bool created_;
+ PlatformFile file_;
+ FilePath path_;
+ PlatformFileInfo file_info_;
+ std::vector<char> buffer_;
+ int bytes_written_;
+ WeakPtrFactory<FileUtilProxyTest> weak_factory_;
+};
+
+TEST_F(FileUtilProxyTest, CreateOrOpen_Create) {
+ FileUtilProxy::CreateOrOpen(
+ file_task_runner(),
+ test_path(),
+ PLATFORM_FILE_CREATE | PLATFORM_FILE_READ,
+ Bind(&FileUtilProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+
+ EXPECT_EQ(PLATFORM_FILE_OK, error_);
+ EXPECT_TRUE(created_);
+ EXPECT_NE(kInvalidPlatformFileValue, file_);
+ EXPECT_TRUE(file_util::PathExists(test_path()));
+}
+
+TEST_F(FileUtilProxyTest, CreateOrOpen_Open) {
+ // Creates a file.
+ file_util::WriteFile(test_path(), NULL, 0);
+ ASSERT_TRUE(file_util::PathExists(test_path()));
+
+ // Opens the created file.
+ FileUtilProxy::CreateOrOpen(
+ file_task_runner(),
+ test_path(),
+ PLATFORM_FILE_OPEN | PLATFORM_FILE_READ,
+ Bind(&FileUtilProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+
+ EXPECT_EQ(PLATFORM_FILE_OK, error_);
+ EXPECT_FALSE(created_);
+ EXPECT_NE(kInvalidPlatformFileValue, file_);
+}
+
+TEST_F(FileUtilProxyTest, CreateOrOpen_OpenNonExistent) {
+ FileUtilProxy::CreateOrOpen(
+ file_task_runner(),
+ test_path(),
+ PLATFORM_FILE_OPEN | PLATFORM_FILE_READ,
+ Bind(&FileUtilProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+ EXPECT_EQ(PLATFORM_FILE_ERROR_NOT_FOUND, error_);
+ EXPECT_FALSE(created_);
+ EXPECT_EQ(kInvalidPlatformFileValue, file_);
+ EXPECT_FALSE(file_util::PathExists(test_path()));
+}
+
+TEST_F(FileUtilProxyTest, Close) {
+ // Creates a file.
+ PlatformFile file = GetTestPlatformFile(
+ PLATFORM_FILE_CREATE | PLATFORM_FILE_WRITE);
+
+#if defined(OS_WIN)
+ // This fails on Windows if the file is not closed.
+ EXPECT_FALSE(file_util::Move(test_path(),
+ test_dir_path().AppendASCII("new")));
+#endif
+
+ FileUtilProxy::Close(
+ file_task_runner(),
+ file,
+ Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+ ASSERT_EQ(PLATFORM_FILE_OK, error_);
+
+ // file_ has been closed by FileUtilProxy::Close
+ file_ = kInvalidPlatformFileValue;
+
+#if !defined(OS_STARBOARD)
+ // Now it should pass on all platforms.
+ EXPECT_TRUE(file_util::Move(test_path(), test_dir_path().AppendASCII("new")));
+#endif
+}
+
+TEST_F(FileUtilProxyTest, CreateTemporary) {
+ FileUtilProxy::CreateTemporary(
+ file_task_runner(), 0 /* additional_file_flags */,
+ Bind(&FileUtilProxyTest::DidCreateTemporary, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+ EXPECT_EQ(PLATFORM_FILE_OK, error_);
+ EXPECT_TRUE(file_util::PathExists(path_));
+ EXPECT_NE(kInvalidPlatformFileValue, file_);
+
+ // The file should be writable.
+#if defined(OS_WIN)
+ HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ OVERLAPPED overlapped = {0};
+ overlapped.hEvent = hEvent;
+ DWORD bytes_written;
+ if (!::WriteFile(file_, "test", 4, &bytes_written, &overlapped)) {
+ // Temporary file is created with ASYNC flag, so WriteFile may return 0
+ // with ERROR_IO_PENDING.
+ EXPECT_EQ(ERROR_IO_PENDING, GetLastError());
+ GetOverlappedResult(file_, &overlapped, &bytes_written, TRUE);
+ }
+ EXPECT_EQ(4, bytes_written);
+#else
+ // On POSIX ASYNC flag does not affect synchronous read/write behavior.
+ EXPECT_EQ(4, WritePlatformFile(file_, 0, "test", 4));
+#endif
+ EXPECT_TRUE(ClosePlatformFile(file_));
+ file_ = kInvalidPlatformFileValue;
+
+ // Make sure the written data can be read from the returned path.
+ std::string data;
+ EXPECT_TRUE(file_util::ReadFileToString(path_, &data));
+ EXPECT_EQ("test", data);
+
+ // Make sure we can & do delete the created file to prevent leaks on the bots.
+ EXPECT_TRUE(file_util::Delete(path_, false));
+}
+
+TEST_F(FileUtilProxyTest, GetFileInfo_File) {
+ // Setup.
+ ASSERT_EQ(4, file_util::WriteFile(test_path(), "test", 4));
+ PlatformFileInfo expected_info;
+ file_util::GetFileInfo(test_path(), &expected_info);
+
+ // Run.
+ FileUtilProxy::GetFileInfo(
+ file_task_runner(),
+ test_path(),
+ Bind(&FileUtilProxyTest::DidGetFileInfo, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+
+ // Verify.
+ EXPECT_EQ(PLATFORM_FILE_OK, error_);
+ EXPECT_EQ(expected_info.size, file_info_.size);
+ EXPECT_EQ(expected_info.is_directory, file_info_.is_directory);
+ EXPECT_EQ(expected_info.is_symbolic_link, file_info_.is_symbolic_link);
+ EXPECT_EQ(expected_info.last_modified, file_info_.last_modified);
+ EXPECT_EQ(expected_info.last_accessed, file_info_.last_accessed);
+ EXPECT_EQ(expected_info.creation_time, file_info_.creation_time);
+}
+
+TEST_F(FileUtilProxyTest, GetFileInfo_Directory) {
+ // Setup.
+ ASSERT_TRUE(file_util::CreateDirectory(test_path()));
+ PlatformFileInfo expected_info;
+ file_util::GetFileInfo(test_path(), &expected_info);
+
+ // Run.
+ FileUtilProxy::GetFileInfo(
+ file_task_runner(),
+ test_path(),
+ Bind(&FileUtilProxyTest::DidGetFileInfo, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+
+ // Verify.
+ EXPECT_EQ(PLATFORM_FILE_OK, error_);
+ EXPECT_EQ(expected_info.size, file_info_.size);
+ EXPECT_EQ(expected_info.is_directory, file_info_.is_directory);
+ EXPECT_EQ(expected_info.is_symbolic_link, file_info_.is_symbolic_link);
+ EXPECT_EQ(expected_info.last_modified, file_info_.last_modified);
+ EXPECT_EQ(expected_info.last_accessed, file_info_.last_accessed);
+ EXPECT_EQ(expected_info.creation_time, file_info_.creation_time);
+}
+
+TEST_F(FileUtilProxyTest, Read) {
+ // Setup.
+ const char expected_data[] = "bleh";
+ int expected_bytes = arraysize(expected_data);
+ ASSERT_EQ(expected_bytes,
+ file_util::WriteFile(test_path(), expected_data, expected_bytes));
+
+ // Run.
+ FileUtilProxy::Read(
+ file_task_runner(),
+ GetTestPlatformFile(PLATFORM_FILE_OPEN | PLATFORM_FILE_READ),
+ 0, // offset
+ 128,
+ Bind(&FileUtilProxyTest::DidRead, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+
+ // Verify.
+ EXPECT_EQ(PLATFORM_FILE_OK, error_);
+ EXPECT_EQ(expected_bytes, static_cast<int>(buffer_.size()));
+ for (size_t i = 0; i < buffer_.size(); ++i) {
+ EXPECT_EQ(expected_data[i], buffer_[i]);
+ }
+}
+
+TEST_F(FileUtilProxyTest, WriteAndFlush) {
+ const char data[] = "foo!";
+ int data_bytes = ARRAYSIZE_UNSAFE(data);
+ PlatformFile file = GetTestPlatformFile(
+ PLATFORM_FILE_CREATE | PLATFORM_FILE_WRITE);
+
+ FileUtilProxy::Write(
+ file_task_runner(),
+ file,
+ 0, // offset
+ data,
+ data_bytes,
+ Bind(&FileUtilProxyTest::DidWrite, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+ EXPECT_EQ(PLATFORM_FILE_OK, error_);
+ EXPECT_EQ(data_bytes, bytes_written_);
+
+ // Flush the written data. (So that the following read should always
+ // succeed. On some platforms it may work with or without this flush.)
+ FileUtilProxy::Flush(
+ file_task_runner(),
+ file,
+ Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+ EXPECT_EQ(PLATFORM_FILE_OK, error_);
+
+#if defined(__LB_WIIU__)
+ // Don't open a handle for reading if one is already open for writing
+ ClosePlatformFile(file);
+#endif
+
+ // Verify the written data.
+ char buffer[10];
+ EXPECT_EQ(data_bytes, file_util::ReadFile(test_path(), buffer, data_bytes));
+ for (int i = 0; i < data_bytes; ++i) {
+ EXPECT_EQ(data[i], buffer[i]);
+ }
+}
+
+#if !defined(__LB_PS3__) && !defined(__LB_WIIU__) && !defined(OS_STARBOARD)
+// FileUtilProxy::Touch creates a platform file and passes its descriptor to
+// base::TouchPlatformFile().
+// This requires OS support for futimes.
+
+TEST_F(FileUtilProxyTest, Touch) {
+ Time last_accessed_time = Time::Now() - TimeDelta::FromDays(12345);
+ Time last_modified_time = Time::Now() - TimeDelta::FromHours(98765);
+
+ FileUtilProxy::Touch(
+ file_task_runner(),
+ GetTestPlatformFile(PLATFORM_FILE_CREATE |
+ PLATFORM_FILE_WRITE |
+ PLATFORM_FILE_WRITE_ATTRIBUTES),
+ last_accessed_time,
+ last_modified_time,
+ Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+ EXPECT_EQ(PLATFORM_FILE_OK, error_);
+
+ PlatformFileInfo info;
+ file_util::GetFileInfo(test_path(), &info);
+
+ // The returned values may only have the seconds precision, so we cast
+ // the double values to int here.
+ EXPECT_EQ(static_cast<int>(last_modified_time.ToDoubleT()),
+ static_cast<int>(info.last_modified.ToDoubleT()));
+ EXPECT_EQ(static_cast<int>(last_accessed_time.ToDoubleT()),
+ static_cast<int>(info.last_accessed.ToDoubleT()));
+}
+#endif
+
+TEST_F(FileUtilProxyTest, Truncate_Shrink) {
+ // Setup.
+ const char kTestData[] = "0123456789";
+ ASSERT_EQ(10, file_util::WriteFile(test_path(), kTestData, 10));
+ PlatformFileInfo info;
+ file_util::GetFileInfo(test_path(), &info);
+ ASSERT_EQ(10, info.size);
+
+ // Run.
+ FileUtilProxy::Truncate(
+ file_task_runner(),
+ GetTestPlatformFile(PLATFORM_FILE_OPEN | PLATFORM_FILE_WRITE),
+ 7,
+ Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+
+ // Verify.
+ file_util::GetFileInfo(test_path(), &info);
+ ASSERT_EQ(7, info.size);
+
+#if defined(__LB_WIIU__)
+ // If the platform file is already created, GetTestPlatformFile will return
+ // the platform file, so the flags are irrelevant.
+ ClosePlatformFile(GetTestPlatformFile(0));
+#endif
+
+ char buffer[7];
+ EXPECT_EQ(7, file_util::ReadFile(test_path(), buffer, 7));
+ int i = 0;
+ for (; i < 7; ++i)
+ EXPECT_EQ(kTestData[i], buffer[i]);
+}
+
+TEST_F(FileUtilProxyTest, Truncate_Expand) {
+ // Setup.
+ const char kTestData[] = "9876543210";
+ ASSERT_EQ(10, file_util::WriteFile(test_path(), kTestData, 10));
+ PlatformFileInfo info;
+ file_util::GetFileInfo(test_path(), &info);
+ ASSERT_EQ(10, info.size);
+
+ // Run.
+ FileUtilProxy::Truncate(
+ file_task_runner(),
+ GetTestPlatformFile(PLATFORM_FILE_OPEN | PLATFORM_FILE_WRITE),
+ 53,
+ Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+
+ // Verify.
+ file_util::GetFileInfo(test_path(), &info);
+ ASSERT_EQ(53, info.size);
+
+#if defined(__LB_WIIU__)
+ // If the platform file is already created, GetTestPlatformFile will return
+ // the platform file, so the flags are irrelevant.
+ ClosePlatformFile(GetTestPlatformFile(0));
+#endif
+
+ char buffer[53];
+ EXPECT_EQ(53, file_util::ReadFile(test_path(), buffer, 53));
+ int i = 0;
+ for (; i < 10; ++i)
+ EXPECT_EQ(kTestData[i], buffer[i]);
+ for (; i < 53; ++i)
+ EXPECT_EQ(0, buffer[i]);
+}
+
+} // namespace base
diff --git a/src/base/file_util_starboard.cc b/src/base/file_util_starboard.cc
new file mode 100644
index 0000000..0c6511a
--- /dev/null
+++ b/src/base/file_util_starboard.cc
@@ -0,0 +1,602 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Adapted from file_util_posix.cc.
+
+#include "base/file_util.h"
+
+#include <stack>
+#include <string>
+
+#include "base/base_paths.h"
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/platform_file.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time.h"
+#include "starboard/directory.h"
+#include "starboard/file.h"
+#include "starboard/system.h"
+
+namespace {
+
+// The list of portable filename characters as per POSIX. This should be the
+// lowest-common-denominator of acceptable filename characters.
+const char kPortableFilenameCharacters[] = {
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "._-"
+};
+const int kPortableFilenameCharactersLength =
+ SB_ARRAY_SIZE_INT(kPortableFilenameCharacters) - 1;
+
+// 8 characters = 65 ^ 8 possible filenames, which gives a nice wide space for
+// avoiding collisions.
+const char kTempSubstitution[] = "XXXXXXXX";
+const int kTempSubstitutionLength = SB_ARRAY_SIZE_INT(kTempSubstitution) - 1;
+
+std::string TempFileName() {
+ return std::string(".com.youtube.Cobalt.XXXXXXXX");
+}
+
+// Takes the template defined in |in_out_template| and destructively replaces
+// any 'X' characters at the end with randomized portable filename characters.
+void GenerateTempFileName(FilePath::StringType *in_out_template) {
+ size_t template_start = in_out_template->find(kTempSubstitution);
+ if (template_start == FilePath::StringType::npos) {
+ // No pattern.
+ return;
+ }
+
+ for (int i = 0; i < kTempSubstitutionLength; ++i) {
+ uint64_t random = SbSystemGetRandomUInt64();
+ int index = random % kPortableFilenameCharactersLength;
+ (*in_out_template)[template_start + i] = kPortableFilenameCharacters[index];
+ }
+}
+
+// Creates a random filename based on the TempFileName() pattern, creates the
+// file, leaving it open, placing the path in |out_path| and returning the open
+// SbFile.
+SbFile CreateAndOpenTemporaryFile(FilePath directory, FilePath *out_path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(out_path);
+ FilePath path = directory.Append(TempFileName());
+ FilePath::StringType tmpdir_string = path.value();
+ GenerateTempFileName(&tmpdir_string);
+ *out_path = FilePath(tmpdir_string);
+ return SbFileOpen(tmpdir_string.c_str(), kSbFileCreateOnly | kSbFileWrite,
+ NULL, NULL);
+}
+
+// Retries creating a temporary file until it can win the race to create a
+// unique one.
+SbFile CreateAndOpenTemporaryFileSafely(FilePath directory,
+ FilePath *out_path) {
+ SbFile file = kSbFileInvalid;
+ while (!SbFileIsValid(file)) {
+ file = CreateAndOpenTemporaryFile(directory, out_path);
+ }
+ return file;
+}
+
+bool CreateTemporaryDirInDirImpl(const FilePath &base_dir,
+ const FilePath::StringType &name_tmpl,
+ FilePath *new_dir) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(name_tmpl.find(kTempSubstitution) != FilePath::StringType::npos)
+ << "Directory name template must contain \"XXXXXXXX\".";
+
+ FilePath dir_template = base_dir.Append(name_tmpl);
+ std::string dir_template_string = dir_template.value();
+ FilePath sub_dir;
+ while (true) {
+ std::string sub_dir_string = dir_template_string;
+ GenerateTempFileName(&sub_dir_string);
+ sub_dir = FilePath(sub_dir_string);
+ if (!file_util::DirectoryExists(sub_dir)) {
+ break;
+ }
+ }
+
+ // NOTE: This is not as secure as mkdtemp, because it doesn't atomically
+ // guarantee that there is no collision. But, with 8 X's it should be good
+ // enough for our purposes.
+ if (!file_util::CreateDirectory(sub_dir)) {
+ DPLOG(ERROR) << "CreateDirectory";
+ return false;
+ }
+
+ *new_dir = sub_dir;
+ return true;
+}
+
+} // namespace
+
+namespace file_util {
+
+bool AbsolutePath(FilePath* path) {
+ // We don't have cross-platform tools for this, so we will just return true if
+ // the path is already absolute.
+ return path->IsAbsolute();
+}
+
+bool Delete(const FilePath &path, bool recursive) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ const char *path_str = path.value().c_str();
+
+ bool directory = SbDirectoryCanOpen(path_str);
+ if (!recursive || !directory) {
+ return SbFileDelete(path_str);
+ }
+
+ bool success = true;
+ std::stack<std::string> directories;
+ directories.push(path.value());
+
+ // NOTE: Right now, for Linux, SbFileGetInfo does not follow
+ // symlinks. This is good for avoiding deleting through symlinks, but makes it
+ // hard to use symlinks on platforms where they are supported. This seems
+ // safest for the lowest-common-denominator approach of pretending symlinks
+ // don't exist.
+ FileEnumerator traversal(
+ path, true, FileEnumerator::FILES | FileEnumerator::DIRECTORIES);
+
+ // Delete all files and push all directories in depth order onto the stack.
+ for (FilePath current = traversal.Next();
+ success && !current.empty();
+ current = traversal.Next()) {
+ FileEnumerator::FindInfo info;
+ traversal.GetFindInfo(&info);
+
+ if (FileEnumerator::IsDirectory(info)) {
+ directories.push(current.value());
+ } else {
+ success = SbFileDelete(current.value().c_str());
+ }
+ }
+
+ // Delete all directories in reverse-depth order, now that they have no more
+ // regular files.
+ while (success && !directories.empty()) {
+ success = SbFileDelete(directories.top().c_str());
+ directories.pop();
+ }
+
+ return success;
+}
+
+bool CopyFile(const FilePath &from_path, const FilePath &to_path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ base::PlatformFile source_file = base::CreatePlatformFile(
+ from_path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, NULL,
+ NULL);
+ if (source_file == base::kInvalidPlatformFileValue) {
+ DPLOG(ERROR) << "CopyFile(): Unable to open source file: "
+ << from_path.value();
+ return false;
+ }
+
+ base::PlatformFile destination_file = base::CreatePlatformFileUnsafe(
+ to_path, base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE, NULL,
+ NULL);
+ if (destination_file == base::kInvalidPlatformFileValue) {
+ DPLOG(ERROR) << "CopyFile(): Unable to open destination file: "
+ << to_path.value();
+ ignore_result(base::ClosePlatformFile(destination_file));
+ return false;
+ }
+
+ const size_t kBufferSize = 32768;
+ std::vector<char> buffer(kBufferSize);
+ bool result = true;
+
+ while (result) {
+ int bytes_read = base::ReadPlatformFileAtCurrentPos(source_file, &buffer[0],
+ buffer.size());
+ if (bytes_read < 0) {
+ result = false;
+ break;
+ }
+
+ if (bytes_read == 0) {
+ break;
+ }
+
+ int bytes_written = base::WritePlatformFileAtCurrentPos(
+ destination_file, &buffer[0], bytes_read);
+ if (bytes_written < bytes_read) {
+ DLOG(ERROR) << "CopyFile(): bytes_read (" << bytes_read
+ << ") > bytes_written (" << bytes_written << ")";
+ // Because we use a best-effort write, if we wrote less than what was
+ // available, something went wrong.
+ result = false;
+ break;
+ }
+ }
+
+ if (!base::ClosePlatformFile(source_file)) {
+ result = false;
+ }
+
+ if (!base::ClosePlatformFile(destination_file)) {
+ result = false;
+ }
+
+ return result;
+}
+
+bool PathExists(const FilePath &path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return SbFileExists(path.value().c_str());
+}
+
+bool PathIsWritable(const FilePath &path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return SbFileCanOpen(path.value().c_str(), kSbFileOpenAlways | kSbFileWrite);
+}
+
+bool DirectoryExists(const FilePath& path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ SbFileInfo info;
+ if (!SbFileGetPathInfo(path.value().c_str(), &info)) {
+ return false;
+ }
+
+ return info.is_directory;
+}
+
+bool GetTempDir(FilePath *path) {
+ char buffer[SB_FILE_MAX_PATH + 1] = {0};
+ bool result = SbSystemGetPath(kSbSystemPathTempDirectory, buffer,
+ SB_ARRAY_SIZE_INT(buffer));
+ if (!result) {
+ return false;
+ }
+
+ *path = FilePath(buffer);
+ if (DirectoryExists(*path)) {
+ return true;
+ }
+
+ return CreateDirectory(*path);
+}
+
+bool GetShmemTempDir(FilePath *path, bool executable) {
+ return GetTempDir(path);
+}
+
+FilePath GetHomeDir() {
+ FilePath path;
+ bool result = PathService::Get(base::DIR_HOME, &path);
+ DCHECK(result);
+ return path;
+}
+
+bool CreateTemporaryFile(FilePath *path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(path);
+
+ FilePath directory;
+ if (!GetTempDir(&directory)) {
+ return false;
+ }
+
+ return CreateTemporaryFileInDir(directory, path);
+}
+
+bool CreateTemporaryFileInDir(const FilePath &dir, FilePath *temp_file) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(temp_file);
+ SbFile file = CreateAndOpenTemporaryFileSafely(dir, temp_file);
+ return (SbFileIsValid(file) && SbFileClose(file));
+}
+
+bool CreateTemporaryDirInDir(const FilePath &base_dir,
+ const FilePath::StringType &prefix,
+ FilePath *new_dir) {
+ FilePath::StringType mkdtemp_template = prefix + kTempSubstitution;
+ return CreateTemporaryDirInDirImpl(base_dir, mkdtemp_template, new_dir);
+}
+
+bool CreateNewTempDirectory(const FilePath::StringType &prefix,
+ FilePath *new_temp_path) {
+ FilePath tmpdir;
+ if (!GetTempDir(&tmpdir)) {
+ return false;
+ }
+
+ return CreateTemporaryDirInDirImpl(tmpdir, TempFileName(), new_temp_path);
+}
+
+bool CreateDirectory(const FilePath &full_path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ std::vector<FilePath> subpaths;
+
+ // Collect a list of all parent directories.
+ FilePath last_path = full_path;
+ subpaths.push_back(full_path);
+ for (FilePath path = full_path.DirName();
+ path.value() != last_path.value();
+ path = path.DirName()) {
+ subpaths.push_back(path);
+ last_path = path;
+ }
+
+ // Iterate through the parents and create the missing ones.
+ for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
+ i != subpaths.rend(); ++i) {
+ if (DirectoryExists(*i)) {
+ continue;
+ }
+
+ if (!SbDirectoryCreate(i->value().c_str())) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool IsLink(const FilePath &file_path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ SbFileInfo info;
+
+ // If we can't SbfileGetPathInfo on the file, it's safe to assume that the
+ // file won't at least be a 'followable' link.
+ if (!SbFileGetPathInfo(file_path.value().c_str(), &info)) {
+ return false;
+ }
+
+ return info.is_symbolic_link;
+}
+
+bool GetFileInfo(const FilePath &file_path, base::PlatformFileInfo *results) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ SbFileInfo info;
+ if (!SbFileGetPathInfo(file_path.value().c_str(), &info)) {
+ return false;
+ }
+
+ results->is_directory = info.is_directory;
+ results->size = info.size;
+ results->last_modified = base::Time::FromSbTime(info.last_modified);
+ results->last_accessed = base::Time::FromSbTime(info.last_accessed);
+ results->creation_time = base::Time::FromSbTime(info.creation_time);
+ return true;
+}
+
+int ReadFile(const FilePath &filename, char *data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ base::PlatformFile file = base::CreatePlatformFile(
+ filename, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, NULL,
+ NULL);
+ if (file == base::kInvalidPlatformFileValue) {
+ DLOG(ERROR) << "ReadFile(" << filename.value() << "): Unable to open.";
+ return -1;
+ }
+
+ // We use a best-effort read here.
+ int bytes_read = base::ReadPlatformFileAtCurrentPos(file, data, size);
+ if (!base::ClosePlatformFile(file)) {
+ DLOG(ERROR) << "ReadFile(" << filename.value() << "): Unable to close.";
+ return -1;
+ }
+
+ return bytes_read;
+}
+
+int WriteFile(const FilePath &filename, const char *data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ base::PlatformFile file = base::CreatePlatformFile(
+ filename, base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE,
+ NULL, NULL);
+ if (file == base::kInvalidPlatformFileValue) {
+ DLOG(ERROR) << "WriteFile(" << filename.value() << "): Unable to open.";
+ return -1;
+ }
+
+ // We use a best-effort write here.
+ int bytes_written = base::WritePlatformFileAtCurrentPos(file, data, size);
+ if (!base::ClosePlatformFile(file)) {
+ DLOG(ERROR) << "WriteFile(" << filename.value() << "): Unable to close.";
+ return -1;
+ }
+
+ return bytes_written;
+}
+
+int AppendToFile(const FilePath &filename, const char *data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ base::PlatformFile file = base::CreatePlatformFile(
+ filename, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE, NULL,
+ NULL);
+ if (file == base::kInvalidPlatformFileValue) {
+ DLOG(ERROR) << "AppendToFile(" << filename.value() << "): Unable to open.";
+ return -1;
+ }
+
+ if (!base::SeekPlatformFile(file, base::PLATFORM_FILE_FROM_END, 0)) {
+ DLOG(ERROR) << "AppendToFile(" << filename.value()
+ << "): Unable to truncate.";
+ return -1;
+ }
+
+ int bytes_written = base::WritePlatformFileAtCurrentPos(file, data, size);
+ if (!base::ClosePlatformFile(file)) {
+ DLOG(ERROR) << "AppendToFile(" << filename.value() << "): Unable to close.";
+ return -1;
+ }
+
+ return bytes_written;
+}
+
+bool HasFileBeenModifiedSince(const FileEnumerator::FindInfo &find_info,
+ const base::Time &cutoff_time) {
+ return FileEnumerator::GetLastModifiedTime(find_info) >= cutoff_time;
+}
+
+
+///////////////////////////////////////////////
+// FileEnumerator
+
+FileEnumerator::FileEnumerator(const FilePath &root_path,
+ bool recursive,
+ int file_type)
+ : current_directory_entry_(0),
+ root_path_(root_path),
+ recursive_(recursive),
+ file_type_(file_type) {
+ // INCLUDE_DOT_DOT must not be specified if recursive.
+ DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
+ pending_paths_.push(root_path);
+}
+
+FileEnumerator::FileEnumerator(const FilePath &root_path,
+ bool recursive,
+ int file_type,
+ const FilePath::StringType &pattern)
+ : current_directory_entry_(0),
+ root_path_(root_path),
+ recursive_(recursive),
+ file_type_(file_type),
+ pattern_(root_path.Append(pattern).value()) {
+ // INCLUDE_DOT_DOT must not be specified if recursive.
+ DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
+ // The Windows version of this code appends the pattern to the root_path,
+ // potentially only matching against items in the top-most directory.
+ // Do the same here.
+ if (pattern.empty()) {
+ pattern_ = FilePath::StringType();
+ }
+ pending_paths_.push(root_path);
+}
+
+FileEnumerator::~FileEnumerator() {
+}
+
+FilePath FileEnumerator::Next() {
+ ++current_directory_entry_;
+
+ // While we've exhausted the entries in the current directory, do the next
+ while (current_directory_entry_ >= directory_entries_.size()) {
+ if (pending_paths_.empty()) {
+ return FilePath();
+ }
+
+ root_path_ = pending_paths_.top();
+ root_path_ = root_path_.StripTrailingSeparators();
+ pending_paths_.pop();
+
+ std::vector<DirectoryEntryInfo> entries;
+ if (!ReadDirectory(&entries, root_path_)) {
+ continue;
+ }
+
+ directory_entries_.clear();
+ current_directory_entry_ = 0;
+ for (std::vector<DirectoryEntryInfo>::const_iterator i = entries.begin();
+ i != entries.end(); ++i) {
+ FilePath full_path = root_path_.Append(i->filename);
+ if (ShouldSkip(full_path)) {
+ continue;
+ }
+
+ if (pattern_.size()) {
+ NOTREACHED() << "Patterns not supported in Starboard.";
+ continue;
+ }
+
+ if (recursive_ && i->sb_info.is_directory) {
+ pending_paths_.push(full_path);
+ }
+
+ if ((i->sb_info.is_directory && (file_type_ & DIRECTORIES)) ||
+ (!i->sb_info.is_directory && (file_type_ & FILES))) {
+ directory_entries_.push_back(*i);
+ }
+ }
+ }
+
+ return
+ root_path_.Append(directory_entries_[current_directory_entry_].filename);
+}
+
+void FileEnumerator::GetFindInfo(FindInfo *info) {
+ DCHECK(info);
+
+ if (current_directory_entry_ >= directory_entries_.size()) {
+ return;
+ }
+
+ DirectoryEntryInfo *cur_entry = &directory_entries_[current_directory_entry_];
+ memcpy(&(info->sb_info), &(cur_entry->sb_info), sizeof(info->sb_info));
+ info->filename.assign(cur_entry->filename.value());
+}
+
+// static
+bool FileEnumerator::IsDirectory(const FindInfo &info) {
+ return info.sb_info.is_directory;
+}
+
+// static
+FilePath FileEnumerator::GetFilename(const FindInfo &find_info) {
+ return FilePath(find_info.filename);
+}
+
+// static
+int64 FileEnumerator::GetFilesize(const FindInfo &find_info) {
+ return find_info.sb_info.size;
+}
+
+// static
+base::Time FileEnumerator::GetLastModifiedTime(const FindInfo &find_info) {
+ return base::Time::FromSbTime(find_info.sb_info.last_modified);
+}
+
+// static
+bool FileEnumerator::ReadDirectory(std::vector<DirectoryEntryInfo> *entries,
+ const FilePath &source) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ SbDirectory dir = SbDirectoryOpen(source.value().c_str(), NULL);
+ if (!SbDirectoryIsValid(dir)) {
+ return false;
+ }
+
+ SbDirectoryEntry entry;
+ while (SbDirectoryGetNext(dir, &entry)) {
+ DirectoryEntryInfo info;
+ info.filename = FilePath(entry.name);
+
+ FilePath full_name = source.Append(entry.name);
+ // TODO: Make sure this follows symlinks on relevant platforms.
+ if (!SbFileGetPathInfo(full_name.value().c_str(), &info.sb_info)) {
+ DPLOG(ERROR) << "Couldn't SbFileGetInfo on " << full_name.value();
+ memset(&info.sb_info, 0, sizeof(info.sb_info));
+ }
+
+ entries->push_back(info);
+ }
+
+ ignore_result(SbDirectoryClose(dir));
+ return true;
+}
+
+} // namespace file_util
diff --git a/src/base/file_util_unittest.cc b/src/base/file_util_unittest.cc
new file mode 100644
index 0000000..6f4e111
--- /dev/null
+++ b/src/base/file_util_unittest.cc
@@ -0,0 +1,2493 @@
+// Copyright (c) 2012 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"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#include <tchar.h>
+#include <winioctl.h>
+#endif
+
+#if !defined(__LB_PS3__)
+// These headers are not available on the PS3
+#include <algorithm>
+#include <fstream>
+#endif
+#include <set>
+
+#include "base/base_paths.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/path_service.h"
+#include "base/test/test_file_util.h"
+#include "base/threading/platform_thread.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+#if defined(OS_WIN)
+#include "base/win/scoped_handle.h"
+#endif
+
+// This macro helps avoid wrapped lines in the test structs.
+#define FPL(x) FILE_PATH_LITERAL(x)
+
+#if defined(COBALT) || defined(OS_STARBOARD)
+#define MAYBE_CopyDirectoryRecursivelyNew DISABLED_CopyDirectoryRecursivelyNew
+#define MAYBE_CopyDirectoryRecursivelyExists \
+ DISABLED_CopyDirectoryRecursivelyExists
+#define MAYBE_CopyDirectoryNew DISABLED_CopyDirectoryNew
+#define MAYBE_CopyDirectoryExists DISABLED_CopyDirectoryExists
+#define MAYBE_CopyFileWithCopyDirectoryRecursiveToNew \
+ DISABLED_CopyFileWithCopyDirectoryRecursiveToNew
+#define MAYBE_CopyFileWithCopyDirectoryRecursiveToExisting \
+ DISABLED_CopyFileWithCopyDirectoryRecursiveToExisting
+#define MAYBE_CopyFileWithCopyDirectoryRecursiveToExistingDirectory \
+ DISABLED_CopyFileWithCopyDirectoryRecursiveToExistingDirectory
+#define MAYBE_CopyDirectoryWithTrailingSeparators \
+ DISABLED_CopyDirectoryWithTrailingSeparators
+#else
+#define MAYBE_CopyDirectoryRecursivelyNew CopyDirectoryRecursivelyNew
+#define MAYBE_CopyDirectoryRecursivelyExists CopyDirectoryRecursivelyExists
+#define MAYBE_CopyDirectoryNew CopyDirectoryNew
+#define MAYBE_CopyDirectoryExists CopyDirectoryExists
+#define MAYBE_CopyFileWithCopyDirectoryRecursiveToNew \
+ CopyFileWithCopyDirectoryRecursiveToNew
+#define MAYBE_CopyFileWithCopyDirectoryRecursiveToExisting \
+ CopyFileWithCopyDirectoryRecursiveToExisting
+#define MAYBE_CopyFileWithCopyDirectoryRecursiveToExistingDirectory \
+ CopyFileWithCopyDirectoryRecursiveToExistingDirectory
+#define MAYBE_CopyDirectoryWithTrailingSeparators \
+ CopyDirectoryWithTrailingSeparators
+#endif
+
+namespace {
+
+// To test that file_util::Normalize FilePath() deals with NTFS reparse points
+// correctly, we need functions to create and delete reparse points.
+#if defined(OS_WIN)
+typedef struct _REPARSE_DATA_BUFFER {
+ ULONG ReparseTag;
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ union {
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+ WCHAR PathBuffer[1];
+ } SymbolicLinkReparseBuffer;
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPointReparseBuffer;
+ struct {
+ UCHAR DataBuffer[1];
+ } GenericReparseBuffer;
+ };
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+
+// Sets a reparse point. |source| will now point to |target|. Returns true if
+// the call succeeds, false otherwise.
+bool SetReparsePoint(HANDLE source, const FilePath& target_path) {
+ std::wstring kPathPrefix = L"\\??\\";
+ std::wstring target_str;
+ // The juction will not work if the target path does not start with \??\ .
+ if (kPathPrefix != target_path.value().substr(0, kPathPrefix.size()))
+ target_str += kPathPrefix;
+ target_str += target_path.value();
+ const wchar_t* target = target_str.c_str();
+ USHORT size_target = static_cast<USHORT>(wcslen(target)) * sizeof(target[0]);
+ char buffer[2000] = {0};
+ DWORD returned;
+
+ REPARSE_DATA_BUFFER* data = reinterpret_cast<REPARSE_DATA_BUFFER*>(buffer);
+
+ data->ReparseTag = 0xa0000003;
+ memcpy(data->MountPointReparseBuffer.PathBuffer, target, size_target + 2);
+
+ data->MountPointReparseBuffer.SubstituteNameLength = size_target;
+ data->MountPointReparseBuffer.PrintNameOffset = size_target + 2;
+ data->ReparseDataLength = size_target + 4 + 8;
+
+ int data_size = data->ReparseDataLength + 8;
+
+ if (!DeviceIoControl(source, FSCTL_SET_REPARSE_POINT, &buffer, data_size,
+ NULL, 0, &returned, NULL)) {
+ return false;
+ }
+ return true;
+}
+
+// Delete the reparse point referenced by |source|. Returns true if the call
+// succeeds, false otherwise.
+bool DeleteReparsePoint(HANDLE source) {
+ DWORD returned;
+ REPARSE_DATA_BUFFER data = {0};
+ data.ReparseTag = 0xa0000003;
+ if (!DeviceIoControl(source, FSCTL_DELETE_REPARSE_POINT, &data, 8, NULL, 0,
+ &returned, NULL)) {
+ return false;
+ }
+ return true;
+}
+#endif
+
+#if defined(OS_POSIX)
+// Provide a simple way to change the permissions bits on |path| in tests.
+// ASSERT failures will return, but not stop the test. Caller should wrap
+// calls to this function in ASSERT_NO_FATAL_FAILURE().
+void ChangePosixFilePermissions(const FilePath& path,
+ int mode_bits_to_set,
+ int mode_bits_to_clear) {
+ ASSERT_FALSE(mode_bits_to_set & mode_bits_to_clear)
+ << "Can't set and clear the same bits.";
+
+ int mode = 0;
+ ASSERT_TRUE(file_util::GetPosixFilePermissions(path, &mode));
+ mode |= mode_bits_to_set;
+ mode &= ~mode_bits_to_clear;
+ ASSERT_TRUE(file_util::SetPosixFilePermissions(path, mode));
+}
+#endif // defined(OS_POSIX)
+
+const wchar_t bogus_content[] = L"I'm cannon fodder.";
+
+const int FILES_AND_DIRECTORIES =
+ file_util::FileEnumerator::FILES | file_util::FileEnumerator::DIRECTORIES;
+
+// file_util winds up using autoreleased objects on the Mac, so this needs
+// to be a PlatformTest
+class FileUtilTest : public PlatformTest {
+ protected:
+ virtual void SetUp() OVERRIDE {
+ PlatformTest::SetUp();
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ }
+
+ base::ScopedTempDir temp_dir_;
+};
+
+// Collects all the results from the given file enumerator, and provides an
+// interface to query whether a given file is present.
+class FindResultCollector {
+ public:
+ explicit FindResultCollector(file_util::FileEnumerator& enumerator) {
+ FilePath cur_file;
+ while (!(cur_file = enumerator.Next()).value().empty()) {
+ FilePath::StringType path = cur_file.value();
+ // The file should not be returned twice.
+ EXPECT_TRUE(files_.end() == files_.find(path))
+ << "Same file returned twice";
+
+ // Save for later.
+ files_.insert(path);
+ }
+ }
+
+ // Returns true if the enumerator found the file.
+ bool HasFile(const FilePath& file) const {
+ return files_.find(file.value()) != files_.end();
+ }
+
+ int size() {
+ return static_cast<int>(files_.size());
+ }
+
+ private:
+ std::set<FilePath::StringType> files_;
+};
+
+// Simple function to dump some text into a new file.
+void CreateTextFile(const FilePath& filename,
+ const std::wstring& contents) {
+#if defined(__LB_PS3__)
+ FILE *file = fopen(filename.value().c_str(), "w");
+ ASSERT_TRUE(file != NULL);
+ fputws(contents.c_str(), file);
+ fclose(file);
+#elif defined(__LB_WIIU__)
+ int fd = open(filename.value().c_str(), O_WRONLY | O_CREAT | O_TRUNC);
+ ASSERT_TRUE(fd >= 0);
+ char mb_sequence[64];
+ const wchar_t* wc_string = contents.c_str();
+ size_t nbytes = wcsrtombs(mb_sequence, &wc_string, arraysize(mb_sequence), NULL);
+ ssize_t bytes_written = write(fd, mb_sequence, nbytes);
+ ASSERT_TRUE(bytes_written == nbytes);
+ ASSERT_TRUE(wc_string == NULL);
+ close(fd);
+#else
+ std::wofstream file;
+ file.open(filename.value().c_str());
+ ASSERT_TRUE(file.is_open());
+ file << contents;
+ file.close();
+#endif
+}
+
+// Simple function to take out some text from a file.
+std::wstring ReadTextFile(const FilePath& filename) {
+ wchar_t contents[64];
+#if defined(__LB_PS3__)
+ FILE *file = fopen(filename.value().c_str(), "r");
+ EXPECT_TRUE(file != NULL);
+ fgetws(contents, arraysize(contents), file);
+ fclose(file);
+#elif defined(__LB_WIIU__)
+ char mb_sequence_buffer[64];
+ int fd = open(filename.value().c_str(), O_RDONLY);
+ EXPECT_TRUE(fd >= 0);
+ ssize_t bytes_read = read(fd, mb_sequence_buffer, arraysize(mb_sequence_buffer));
+ EXPECT_TRUE(bytes_read >= 0);
+ close(fd);
+ const char * mb_sequence = mb_sequence_buffer;
+ size_t nbytes = mbsrtowcs(contents, &mb_sequence, sizeof(contents), NULL);
+ EXPECT_TRUE(mb_sequence == NULL);
+#else
+ std::wifstream file;
+ file.open(filename.value().c_str());
+ EXPECT_TRUE(file.is_open());
+ file.getline(contents, arraysize(contents));
+ file.close();
+#endif
+ return std::wstring(contents);
+}
+
+#if defined(OS_WIN)
+uint64 FileTimeAsUint64(const FILETIME& ft) {
+ ULARGE_INTEGER u;
+ u.LowPart = ft.dwLowDateTime;
+ u.HighPart = ft.dwHighDateTime;
+ return u.QuadPart;
+}
+#endif
+
+
+TEST_F(FileUtilTest, FileAndDirectorySize) {
+ // Create three files of 20, 30 and 3 chars (utf8). ComputeDirectorySize
+ // should return 53 bytes.
+ FilePath file_01 = temp_dir_.path().Append(FPL("The file 01.txt"));
+ CreateTextFile(file_01, L"12345678901234567890");
+ int64 size_f1 = 0;
+ ASSERT_TRUE(file_util::GetFileSize(file_01, &size_f1));
+ EXPECT_EQ(20ll, size_f1);
+
+ FilePath subdir_path = temp_dir_.path().Append(FPL("Level2"));
+ file_util::CreateDirectory(subdir_path);
+
+ FilePath file_02 = subdir_path.Append(FPL("The file 02.txt"));
+ CreateTextFile(file_02, L"123456789012345678901234567890");
+ int64 size_f2 = 0;
+ ASSERT_TRUE(file_util::GetFileSize(file_02, &size_f2));
+ EXPECT_EQ(30ll, size_f2);
+
+ FilePath subsubdir_path = subdir_path.Append(FPL("Level3"));
+ file_util::CreateDirectory(subsubdir_path);
+
+ FilePath file_03 = subsubdir_path.Append(FPL("The file 03.txt"));
+ CreateTextFile(file_03, L"123");
+
+ int64 computed_size = file_util::ComputeDirectorySize(temp_dir_.path());
+ EXPECT_EQ(size_f1 + size_f2 + 3, computed_size);
+
+#if !defined(OS_STARBOARD)
+ // No pattern support in Starboard.
+ computed_size =
+ file_util::ComputeFilesSize(temp_dir_.path(), FPL("The file*"));
+ EXPECT_EQ(size_f1, computed_size);
+
+ computed_size = file_util::ComputeFilesSize(temp_dir_.path(), FPL("bla*"));
+ EXPECT_EQ(0, computed_size);
+#endif
+}
+
+#if !defined(OS_STARBOARD)
+TEST_F(FileUtilTest, NormalizeFilePathBasic) {
+ // Create a directory under the test dir. Because we create it,
+ // we know it is not a link.
+ FilePath file_a_path = temp_dir_.path().Append(FPL("file_a"));
+ FilePath dir_path = temp_dir_.path().Append(FPL("dir"));
+ FilePath file_b_path = dir_path.Append(FPL("file_b"));
+ file_util::CreateDirectory(dir_path);
+
+ FilePath normalized_file_a_path, normalized_file_b_path;
+ ASSERT_FALSE(file_util::PathExists(file_a_path));
+ ASSERT_FALSE(file_util::NormalizeFilePath(file_a_path,
+ &normalized_file_a_path))
+ << "NormalizeFilePath() should fail on nonexistent paths.";
+
+ CreateTextFile(file_a_path, bogus_content);
+ ASSERT_TRUE(file_util::PathExists(file_a_path));
+ ASSERT_TRUE(file_util::NormalizeFilePath(file_a_path,
+ &normalized_file_a_path));
+
+ CreateTextFile(file_b_path, bogus_content);
+ ASSERT_TRUE(file_util::PathExists(file_b_path));
+ ASSERT_TRUE(file_util::NormalizeFilePath(file_b_path,
+ &normalized_file_b_path));
+
+ // Beacuse this test created |dir_path|, we know it is not a link
+ // or junction. So, the real path of the directory holding file a
+ // must be the parent of the path holding file b.
+ ASSERT_TRUE(normalized_file_a_path.DirName()
+ .IsParent(normalized_file_b_path.DirName()));
+}
+#endif
+
+#if defined(OS_WIN)
+
+TEST_F(FileUtilTest, NormalizeFilePathReparsePoints) {
+ // Build the following directory structure:
+ //
+ // temp_dir
+ // |-> base_a
+ // | |-> sub_a
+ // | |-> file.txt
+ // | |-> long_name___... (Very long name.)
+ // | |-> sub_long
+ // | |-> deep.txt
+ // |-> base_b
+ // |-> to_sub_a (reparse point to temp_dir\base_a\sub_a)
+ // |-> to_base_b (reparse point to temp_dir\base_b)
+ // |-> to_sub_long (reparse point to temp_dir\sub_a\long_name_\sub_long)
+
+ FilePath base_a = temp_dir_.path().Append(FPL("base_a"));
+ ASSERT_TRUE(file_util::CreateDirectory(base_a));
+
+ FilePath sub_a = base_a.Append(FPL("sub_a"));
+ ASSERT_TRUE(file_util::CreateDirectory(sub_a));
+
+ FilePath file_txt = sub_a.Append(FPL("file.txt"));
+ CreateTextFile(file_txt, bogus_content);
+
+ // Want a directory whose name is long enough to make the path to the file
+ // inside just under MAX_PATH chars. This will be used to test that when
+ // a junction expands to a path over MAX_PATH chars in length,
+ // NormalizeFilePath() fails without crashing.
+ FilePath sub_long_rel(FPL("sub_long"));
+ FilePath deep_txt(FPL("deep.txt"));
+
+ int target_length = MAX_PATH;
+ target_length -= (sub_a.value().length() + 1); // +1 for the sepperator '\'.
+ target_length -= (sub_long_rel.Append(deep_txt).value().length() + 1);
+ // Without making the path a bit shorter, CreateDirectory() fails.
+ // the resulting path is still long enough to hit the failing case in
+ // NormalizePath().
+ const int kCreateDirLimit = 4;
+ target_length -= kCreateDirLimit;
+ FilePath::StringType long_name_str = FPL("long_name_");
+ long_name_str.resize(target_length, '_');
+
+ FilePath long_name = sub_a.Append(FilePath(long_name_str));
+ FilePath deep_file = long_name.Append(sub_long_rel).Append(deep_txt);
+ ASSERT_EQ(MAX_PATH - kCreateDirLimit, deep_file.value().length());
+
+ FilePath sub_long = deep_file.DirName();
+ ASSERT_TRUE(file_util::CreateDirectory(sub_long));
+ CreateTextFile(deep_file, bogus_content);
+
+ FilePath base_b = temp_dir_.path().Append(FPL("base_b"));
+ ASSERT_TRUE(file_util::CreateDirectory(base_b));
+
+ FilePath to_sub_a = base_b.Append(FPL("to_sub_a"));
+ ASSERT_TRUE(file_util::CreateDirectory(to_sub_a));
+ base::win::ScopedHandle reparse_to_sub_a(
+ ::CreateFile(to_sub_a.value().c_str(),
+ FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, // Needed to open a directory.
+ NULL));
+ ASSERT_TRUE(reparse_to_sub_a.IsValid());
+ ASSERT_TRUE(SetReparsePoint(reparse_to_sub_a, sub_a));
+
+ FilePath to_base_b = base_b.Append(FPL("to_base_b"));
+ ASSERT_TRUE(file_util::CreateDirectory(to_base_b));
+ base::win::ScopedHandle reparse_to_base_b(
+ ::CreateFile(to_base_b.value().c_str(),
+ FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, // Needed to open a directory.
+ NULL));
+ ASSERT_TRUE(reparse_to_base_b.IsValid());
+ ASSERT_TRUE(SetReparsePoint(reparse_to_base_b, base_b));
+
+ FilePath to_sub_long = base_b.Append(FPL("to_sub_long"));
+ ASSERT_TRUE(file_util::CreateDirectory(to_sub_long));
+ base::win::ScopedHandle reparse_to_sub_long(
+ ::CreateFile(to_sub_long.value().c_str(),
+ FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, // Needed to open a directory.
+ NULL));
+ ASSERT_TRUE(reparse_to_sub_long.IsValid());
+ ASSERT_TRUE(SetReparsePoint(reparse_to_sub_long, sub_long));
+
+ // Normalize a junction free path: base_a\sub_a\file.txt .
+ FilePath normalized_path;
+ ASSERT_TRUE(file_util::NormalizeFilePath(file_txt, &normalized_path));
+ ASSERT_STREQ(file_txt.value().c_str(), normalized_path.value().c_str());
+
+ // Check that the path base_b\to_sub_a\file.txt can be normalized to exclude
+ // the junction to_sub_a.
+ ASSERT_TRUE(file_util::NormalizeFilePath(to_sub_a.Append(FPL("file.txt")),
+ &normalized_path));
+ ASSERT_STREQ(file_txt.value().c_str(), normalized_path.value().c_str());
+
+ // Check that the path base_b\to_base_b\to_base_b\to_sub_a\file.txt can be
+ // normalized to exclude junctions to_base_b and to_sub_a .
+ ASSERT_TRUE(file_util::NormalizeFilePath(base_b.Append(FPL("to_base_b"))
+ .Append(FPL("to_base_b"))
+ .Append(FPL("to_sub_a"))
+ .Append(FPL("file.txt")),
+ &normalized_path));
+ ASSERT_STREQ(file_txt.value().c_str(), normalized_path.value().c_str());
+
+ // A long enough path will cause NormalizeFilePath() to fail. Make a long
+ // path using to_base_b many times, and check that paths long enough to fail
+ // do not cause a crash.
+ FilePath long_path = base_b;
+ const int kLengthLimit = MAX_PATH + 200;
+ while (long_path.value().length() <= kLengthLimit) {
+ long_path = long_path.Append(FPL("to_base_b"));
+ }
+ long_path = long_path.Append(FPL("to_sub_a"))
+ .Append(FPL("file.txt"));
+
+ ASSERT_FALSE(file_util::NormalizeFilePath(long_path, &normalized_path));
+
+ // Normalizing the junction to deep.txt should fail, because the expanded
+ // path to deep.txt is longer than MAX_PATH.
+ ASSERT_FALSE(file_util::NormalizeFilePath(to_sub_long.Append(deep_txt),
+ &normalized_path));
+
+ // Delete the reparse points, and see that NormalizeFilePath() fails
+ // to traverse them.
+ ASSERT_TRUE(DeleteReparsePoint(reparse_to_sub_a));
+ ASSERT_TRUE(DeleteReparsePoint(reparse_to_base_b));
+ ASSERT_TRUE(DeleteReparsePoint(reparse_to_sub_long));
+
+ ASSERT_FALSE(file_util::NormalizeFilePath(to_sub_a.Append(FPL("file.txt")),
+ &normalized_path));
+}
+
+TEST_F(FileUtilTest, DevicePathToDriveLetter) {
+ // Get a drive letter.
+ std::wstring real_drive_letter = temp_dir_.path().value().substr(0, 2);
+ if (!isalpha(real_drive_letter[0]) || ':' != real_drive_letter[1]) {
+ LOG(ERROR) << "Can't get a drive letter to test with.";
+ return;
+ }
+
+ // Get the NT style path to that drive.
+ wchar_t device_path[MAX_PATH] = {'\0'};
+ ASSERT_TRUE(
+ ::QueryDosDevice(real_drive_letter.c_str(), device_path, MAX_PATH));
+ FilePath actual_device_path(device_path);
+ FilePath win32_path;
+
+ // Run DevicePathToDriveLetterPath() on the NT style path we got from
+ // QueryDosDevice(). Expect the drive letter we started with.
+ ASSERT_TRUE(file_util::DevicePathToDriveLetterPath(actual_device_path,
+ &win32_path));
+ ASSERT_EQ(real_drive_letter, win32_path.value());
+
+ // Add some directories to the path. Expect those extra path componenets
+ // to be preserved.
+ FilePath kRelativePath(FPL("dir1\\dir2\\file.txt"));
+ ASSERT_TRUE(file_util::DevicePathToDriveLetterPath(
+ actual_device_path.Append(kRelativePath),
+ &win32_path));
+ EXPECT_EQ(FilePath(real_drive_letter + L"\\").Append(kRelativePath).value(),
+ win32_path.value());
+
+ // Deform the real path so that it is invalid by removing the last four
+ // characters. The way windows names devices that are hard disks
+ // (\Device\HardDiskVolume${NUMBER}) guarantees that the string is longer
+ // than three characters. The only way the truncated string could be a
+ // real drive is if more than 10^3 disks are mounted:
+ // \Device\HardDiskVolume10000 would be truncated to \Device\HardDiskVolume1
+ // Check that DevicePathToDriveLetterPath fails.
+ int path_length = actual_device_path.value().length();
+ int new_length = path_length - 4;
+ ASSERT_LT(0, new_length);
+ FilePath prefix_of_real_device_path(
+ actual_device_path.value().substr(0, new_length));
+ ASSERT_FALSE(file_util::DevicePathToDriveLetterPath(
+ prefix_of_real_device_path,
+ &win32_path));
+
+ ASSERT_FALSE(file_util::DevicePathToDriveLetterPath(
+ prefix_of_real_device_path.Append(kRelativePath),
+ &win32_path));
+
+ // Deform the real path so that it is invalid by adding some characters. For
+ // example, if C: maps to \Device\HardDiskVolume8, then we simulate a
+ // request for the drive letter whose native path is
+ // \Device\HardDiskVolume812345 . We assume such a device does not exist,
+ // because drives are numbered in order and mounting 112345 hard disks will
+ // never happen.
+ const FilePath::StringType kExtraChars = FPL("12345");
+
+ FilePath real_device_path_plus_numbers(
+ actual_device_path.value() + kExtraChars);
+
+ ASSERT_FALSE(file_util::DevicePathToDriveLetterPath(
+ real_device_path_plus_numbers,
+ &win32_path));
+
+ ASSERT_FALSE(file_util::DevicePathToDriveLetterPath(
+ real_device_path_plus_numbers.Append(kRelativePath),
+ &win32_path));
+}
+
+TEST_F(FileUtilTest, GetPlatformFileInfoForDirectory) {
+ FilePath empty_dir = temp_dir_.path().Append(FPL("gpfi_test"));
+ ASSERT_TRUE(file_util::CreateDirectory(empty_dir));
+ base::win::ScopedHandle dir(
+ ::CreateFile(empty_dir.value().c_str(),
+ FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, // Needed to open a directory.
+ NULL));
+ ASSERT_TRUE(dir.IsValid());
+ base::PlatformFileInfo info;
+ EXPECT_TRUE(base::GetPlatformFileInfo(dir.Get(), &info));
+ EXPECT_TRUE(info.is_directory);
+ EXPECT_FALSE(info.is_symbolic_link);
+ EXPECT_EQ(0, info.size);
+}
+
+TEST_F(FileUtilTest, CreateTemporaryFileInDirLongPathTest) {
+ // Test that CreateTemporaryFileInDir() creates a path and returns a long path
+ // if it is available. This test requires that:
+ // - the filesystem at |temp_dir_| supports long filenames.
+ // - the account has FILE_LIST_DIRECTORY permission for all ancestor
+ // directories of |temp_dir_|.
+ const FilePath::CharType kLongDirName[] = FPL("A long path");
+ const FilePath::CharType kTestSubDirName[] = FPL("test");
+ FilePath long_test_dir = temp_dir_.path().Append(kLongDirName);
+ ASSERT_TRUE(file_util::CreateDirectory(long_test_dir));
+
+ // kLongDirName is not a 8.3 component. So GetShortName() should give us a
+ // different short name.
+ WCHAR path_buffer[MAX_PATH];
+ DWORD path_buffer_length = GetShortPathName(long_test_dir.value().c_str(),
+ path_buffer, MAX_PATH);
+ ASSERT_LT(path_buffer_length, DWORD(MAX_PATH));
+ ASSERT_NE(DWORD(0), path_buffer_length);
+ FilePath short_test_dir(path_buffer);
+ ASSERT_STRNE(kLongDirName, short_test_dir.BaseName().value().c_str());
+
+ FilePath temp_file;
+ ASSERT_TRUE(file_util::CreateTemporaryFileInDir(short_test_dir, &temp_file));
+ EXPECT_STREQ(kLongDirName, temp_file.DirName().BaseName().value().c_str());
+ EXPECT_TRUE(file_util::PathExists(temp_file));
+
+ // Create a subdirectory of |long_test_dir| and make |long_test_dir|
+ // unreadable. We should still be able to create a temp file in the
+ // subdirectory, but we won't be able to determine the long path for it. This
+ // mimics the environment that some users run where their user profiles reside
+ // in a location where the don't have full access to the higher level
+ // directories. (Note that this assumption is true for NTFS, but not for some
+ // network file systems. E.g. AFS).
+ FilePath access_test_dir = long_test_dir.Append(kTestSubDirName);
+ ASSERT_TRUE(file_util::CreateDirectory(access_test_dir));
+ file_util::PermissionRestorer long_test_dir_restorer(long_test_dir);
+ ASSERT_TRUE(file_util::MakeFileUnreadable(long_test_dir));
+
+ // Use the short form of the directory to create a temporary filename.
+ ASSERT_TRUE(file_util::CreateTemporaryFileInDir(
+ short_test_dir.Append(kTestSubDirName), &temp_file));
+ EXPECT_TRUE(file_util::PathExists(temp_file));
+ EXPECT_TRUE(short_test_dir.IsParent(temp_file.DirName()));
+
+ // Check that the long path can't be determined for |temp_file|.
+ path_buffer_length = GetLongPathName(temp_file.value().c_str(),
+ path_buffer, MAX_PATH);
+ EXPECT_EQ(DWORD(0), path_buffer_length);
+}
+
+#endif // defined(OS_WIN)
+
+#if defined(OS_POSIX) && !defined(__LB_SHELL__)
+// Symbolic links not supported in lbshell
+
+TEST_F(FileUtilTest, CreateAndReadSymlinks) {
+ FilePath link_from = temp_dir_.path().Append(FPL("from_file"));
+ FilePath link_to = temp_dir_.path().Append(FPL("to_file"));
+ CreateTextFile(link_to, bogus_content);
+
+ ASSERT_TRUE(file_util::CreateSymbolicLink(link_to, link_from))
+ << "Failed to create file symlink.";
+
+ // If we created the link properly, we should be able to read the
+ // contents through it.
+ std::wstring contents = ReadTextFile(link_from);
+ ASSERT_EQ(contents, bogus_content);
+
+ FilePath result;
+ ASSERT_TRUE(file_util::ReadSymbolicLink(link_from, &result));
+ ASSERT_EQ(link_to.value(), result.value());
+
+ // Link to a directory.
+ link_from = temp_dir_.path().Append(FPL("from_dir"));
+ link_to = temp_dir_.path().Append(FPL("to_dir"));
+ file_util::CreateDirectory(link_to);
+
+ ASSERT_TRUE(file_util::CreateSymbolicLink(link_to, link_from))
+ << "Failed to create directory symlink.";
+
+ // Test failures.
+ ASSERT_FALSE(file_util::CreateSymbolicLink(link_to, link_to));
+ ASSERT_FALSE(file_util::ReadSymbolicLink(link_to, &result));
+ FilePath missing = temp_dir_.path().Append(FPL("missing"));
+ ASSERT_FALSE(file_util::ReadSymbolicLink(missing, &result));
+}
+
+// The following test of NormalizeFilePath() require that we create a symlink.
+// This can not be done on Windows before Vista. On Vista, creating a symlink
+// requires privilege "SeCreateSymbolicLinkPrivilege".
+// TODO(skerner): Investigate the possibility of giving base_unittests the
+// privileges required to create a symlink.
+TEST_F(FileUtilTest, NormalizeFilePathSymlinks) {
+ FilePath normalized_path;
+
+ // Link one file to another.
+ FilePath link_from = temp_dir_.path().Append(FPL("from_file"));
+ FilePath link_to = temp_dir_.path().Append(FPL("to_file"));
+ CreateTextFile(link_to, bogus_content);
+
+ ASSERT_TRUE(file_util::CreateSymbolicLink(link_to, link_from))
+ << "Failed to create file symlink.";
+
+ // Check that NormalizeFilePath sees the link.
+ ASSERT_TRUE(file_util::NormalizeFilePath(link_from, &normalized_path));
+ ASSERT_TRUE(link_to != link_from);
+ ASSERT_EQ(link_to.BaseName().value(), normalized_path.BaseName().value());
+ ASSERT_EQ(link_to.BaseName().value(), normalized_path.BaseName().value());
+
+ // Link to a directory.
+ link_from = temp_dir_.path().Append(FPL("from_dir"));
+ link_to = temp_dir_.path().Append(FPL("to_dir"));
+ file_util::CreateDirectory(link_to);
+
+ ASSERT_TRUE(file_util::CreateSymbolicLink(link_to, link_from))
+ << "Failed to create directory symlink.";
+
+ ASSERT_FALSE(file_util::NormalizeFilePath(link_from, &normalized_path))
+ << "Links to directories should return false.";
+
+ // Test that a loop in the links causes NormalizeFilePath() to return false.
+ link_from = temp_dir_.path().Append(FPL("link_a"));
+ link_to = temp_dir_.path().Append(FPL("link_b"));
+ ASSERT_TRUE(file_util::CreateSymbolicLink(link_to, link_from))
+ << "Failed to create loop symlink a.";
+ ASSERT_TRUE(file_util::CreateSymbolicLink(link_from, link_to))
+ << "Failed to create loop symlink b.";
+
+ // Infinite loop!
+ ASSERT_FALSE(file_util::NormalizeFilePath(link_from, &normalized_path));
+}
+#endif // defined(OS_POSIX)
+
+TEST_F(FileUtilTest, DeleteNonExistent) {
+ FilePath non_existent = temp_dir_.path().AppendASCII("bogus_file_dne.foobar");
+ ASSERT_FALSE(file_util::PathExists(non_existent));
+
+ EXPECT_TRUE(file_util::Delete(non_existent, false));
+ ASSERT_FALSE(file_util::PathExists(non_existent));
+ EXPECT_TRUE(file_util::Delete(non_existent, true));
+ ASSERT_FALSE(file_util::PathExists(non_existent));
+}
+
+TEST_F(FileUtilTest, DeleteFile) {
+ // Create a file
+ FilePath file_name = temp_dir_.path().Append(FPL("Test DeleteFile 1.txt"));
+ CreateTextFile(file_name, bogus_content);
+ ASSERT_TRUE(file_util::PathExists(file_name));
+
+ // Make sure it's deleted
+ EXPECT_TRUE(file_util::Delete(file_name, false));
+ EXPECT_FALSE(file_util::PathExists(file_name));
+
+ // Test recursive case, create a new file
+ file_name = temp_dir_.path().Append(FPL("Test DeleteFile 2.txt"));
+ CreateTextFile(file_name, bogus_content);
+ ASSERT_TRUE(file_util::PathExists(file_name));
+
+ // Make sure it's deleted
+ EXPECT_TRUE(file_util::Delete(file_name, true));
+ EXPECT_FALSE(file_util::PathExists(file_name));
+}
+
+#if defined(OS_POSIX) && !defined(__LB_SHELL__)
+// Symbolic links not supported in lbshell
+
+TEST_F(FileUtilTest, DeleteSymlinkToExistentFile) {
+ // Create a file.
+ FilePath file_name = temp_dir_.path().Append(FPL("Test DeleteFile 2.txt"));
+ CreateTextFile(file_name, bogus_content);
+ ASSERT_TRUE(file_util::PathExists(file_name));
+
+ // Create a symlink to the file.
+ FilePath file_link = temp_dir_.path().Append("file_link_2");
+ ASSERT_TRUE(file_util::CreateSymbolicLink(file_name, file_link))
+ << "Failed to create symlink.";
+
+ // Delete the symbolic link.
+ EXPECT_TRUE(file_util::Delete(file_link, false));
+
+ // Make sure original file is not deleted.
+ EXPECT_FALSE(file_util::PathExists(file_link));
+ EXPECT_TRUE(file_util::PathExists(file_name));
+}
+
+TEST_F(FileUtilTest, DeleteSymlinkToNonExistentFile) {
+ // Create a non-existent file path.
+ FilePath non_existent = temp_dir_.path().Append(FPL("Test DeleteFile 3.txt"));
+ EXPECT_FALSE(file_util::PathExists(non_existent));
+
+ // Create a symlink to the non-existent file.
+ FilePath file_link = temp_dir_.path().Append("file_link_3");
+ ASSERT_TRUE(file_util::CreateSymbolicLink(non_existent, file_link))
+ << "Failed to create symlink.";
+
+ // Make sure the symbolic link is exist.
+ EXPECT_TRUE(file_util::IsLink(file_link));
+ EXPECT_FALSE(file_util::PathExists(file_link));
+
+ // Delete the symbolic link.
+ EXPECT_TRUE(file_util::Delete(file_link, false));
+
+ // Make sure the symbolic link is deleted.
+ EXPECT_FALSE(file_util::IsLink(file_link));
+}
+
+TEST_F(FileUtilTest, ChangeFilePermissionsAndRead) {
+ // Create a file path.
+ FilePath file_name = temp_dir_.path().Append(FPL("Test Readable File.txt"));
+ EXPECT_FALSE(file_util::PathExists(file_name));
+
+ const std::string kData("hello");
+
+ int buffer_size = kData.length();
+ char* buffer = new char[buffer_size];
+
+ // Write file.
+ EXPECT_EQ(static_cast<int>(kData.length()),
+ file_util::WriteFile(file_name, kData.data(), kData.length()));
+ EXPECT_TRUE(file_util::PathExists(file_name));
+
+ // Make sure the file is readable.
+ int32 mode = 0;
+ EXPECT_TRUE(file_util::GetPosixFilePermissions(file_name, &mode));
+ EXPECT_TRUE(mode & file_util::FILE_PERMISSION_READ_BY_USER);
+
+ // Get rid of the read permission.
+ EXPECT_TRUE(file_util::SetPosixFilePermissions(file_name, 0u));
+ EXPECT_TRUE(file_util::GetPosixFilePermissions(file_name, &mode));
+ EXPECT_FALSE(mode & file_util::FILE_PERMISSION_READ_BY_USER);
+ // Make sure the file can't be read.
+ EXPECT_EQ(-1, file_util::ReadFile(file_name, buffer, buffer_size));
+
+ // Give the read permission.
+ EXPECT_TRUE(file_util::SetPosixFilePermissions(
+ file_name,
+ file_util::FILE_PERMISSION_READ_BY_USER));
+ EXPECT_TRUE(file_util::GetPosixFilePermissions(file_name, &mode));
+ EXPECT_TRUE(mode & file_util::FILE_PERMISSION_READ_BY_USER);
+ // Make sure the file can be read.
+ EXPECT_EQ(static_cast<int>(kData.length()),
+ file_util::ReadFile(file_name, buffer, buffer_size));
+
+ // Delete the file.
+ EXPECT_TRUE(file_util::Delete(file_name, false));
+ EXPECT_FALSE(file_util::PathExists(file_name));
+
+ delete[] buffer;
+}
+
+TEST_F(FileUtilTest, ChangeFilePermissionsAndWrite) {
+ // Create a file path.
+ FilePath file_name = temp_dir_.path().Append(FPL("Test Readable File.txt"));
+ EXPECT_FALSE(file_util::PathExists(file_name));
+
+ const std::string kData("hello");
+
+ // Write file.
+ EXPECT_EQ(static_cast<int>(kData.length()),
+ file_util::WriteFile(file_name, kData.data(), kData.length()));
+ EXPECT_TRUE(file_util::PathExists(file_name));
+
+ // Make sure the file is writable.
+ int mode = 0;
+ EXPECT_TRUE(file_util::GetPosixFilePermissions(file_name, &mode));
+ EXPECT_TRUE(mode & file_util::FILE_PERMISSION_WRITE_BY_USER);
+ EXPECT_TRUE(file_util::PathIsWritable(file_name));
+
+ // Get rid of the write permission.
+ EXPECT_TRUE(file_util::SetPosixFilePermissions(file_name, 0u));
+ EXPECT_TRUE(file_util::GetPosixFilePermissions(file_name, &mode));
+ EXPECT_FALSE(mode & file_util::FILE_PERMISSION_WRITE_BY_USER);
+ // Make sure the file can't be write.
+ EXPECT_EQ(-1,
+ file_util::WriteFile(file_name, kData.data(), kData.length()));
+ EXPECT_FALSE(file_util::PathIsWritable(file_name));
+
+ // Give read permission.
+ EXPECT_TRUE(file_util::SetPosixFilePermissions(
+ file_name,
+ file_util::FILE_PERMISSION_WRITE_BY_USER));
+ EXPECT_TRUE(file_util::GetPosixFilePermissions(file_name, &mode));
+ EXPECT_TRUE(mode & file_util::FILE_PERMISSION_WRITE_BY_USER);
+ // Make sure the file can be write.
+ EXPECT_EQ(static_cast<int>(kData.length()),
+ file_util::WriteFile(file_name, kData.data(), kData.length()));
+ EXPECT_TRUE(file_util::PathIsWritable(file_name));
+
+ // Delete the file.
+ EXPECT_TRUE(file_util::Delete(file_name, false));
+ EXPECT_FALSE(file_util::PathExists(file_name));
+}
+
+TEST_F(FileUtilTest, ChangeDirectoryPermissionsAndEnumerate) {
+ // Create a directory path.
+ FilePath subdir_path =
+ temp_dir_.path().Append(FPL("PermissionTest1"));
+ file_util::CreateDirectory(subdir_path);
+ ASSERT_TRUE(file_util::PathExists(subdir_path));
+
+ // Create a dummy file to enumerate.
+ FilePath file_name = subdir_path.Append(FPL("Test Readable File.txt"));
+ EXPECT_FALSE(file_util::PathExists(file_name));
+ const std::string kData("hello");
+ EXPECT_EQ(static_cast<int>(kData.length()),
+ file_util::WriteFile(file_name, kData.data(), kData.length()));
+ EXPECT_TRUE(file_util::PathExists(file_name));
+
+ // Make sure the directory has the all permissions.
+ int mode = 0;
+ EXPECT_TRUE(file_util::GetPosixFilePermissions(subdir_path, &mode));
+ EXPECT_EQ(file_util::FILE_PERMISSION_USER_MASK,
+ mode & file_util::FILE_PERMISSION_USER_MASK);
+
+ // Get rid of the permissions from the directory.
+ EXPECT_TRUE(file_util::SetPosixFilePermissions(subdir_path, 0u));
+ EXPECT_TRUE(file_util::GetPosixFilePermissions(subdir_path, &mode));
+ EXPECT_FALSE(mode & file_util::FILE_PERMISSION_USER_MASK);
+
+ // Make sure the file in the directory can't be enumerated.
+ file_util::FileEnumerator f1(subdir_path, true,
+ file_util::FileEnumerator::FILES);
+ EXPECT_TRUE(file_util::PathExists(subdir_path));
+ FindResultCollector c1(f1);
+ EXPECT_EQ(c1.size(), 0);
+ EXPECT_FALSE(file_util::GetPosixFilePermissions(file_name, &mode));
+
+ // Give the permissions to the directory.
+ EXPECT_TRUE(file_util::SetPosixFilePermissions(
+ subdir_path,
+ file_util::FILE_PERMISSION_USER_MASK));
+ EXPECT_TRUE(file_util::GetPosixFilePermissions(subdir_path, &mode));
+ EXPECT_EQ(file_util::FILE_PERMISSION_USER_MASK,
+ mode & file_util::FILE_PERMISSION_USER_MASK);
+
+ // Make sure the file in the directory can be enumerated.
+ file_util::FileEnumerator f2(subdir_path, true,
+ file_util::FileEnumerator::FILES);
+ FindResultCollector c2(f2);
+ EXPECT_TRUE(c2.HasFile(file_name));
+ EXPECT_EQ(c2.size(), 1);
+
+ // Delete the file.
+ EXPECT_TRUE(file_util::Delete(subdir_path, true));
+ EXPECT_FALSE(file_util::PathExists(subdir_path));
+}
+
+#endif // defined(OS_POSIX)
+
+#if defined(OS_WIN)
+// Tests that the Delete function works for wild cards, especially
+// with the recursion flag. Also coincidentally tests PathExists.
+// TODO(erikkay): see if anyone's actually using this feature of the API
+TEST_F(FileUtilTest, DeleteWildCard) {
+ // Create a file and a directory
+ FilePath file_name = temp_dir_.path().Append(FPL("Test DeleteWildCard.txt"));
+ CreateTextFile(file_name, bogus_content);
+ ASSERT_TRUE(file_util::PathExists(file_name));
+
+ FilePath subdir_path = temp_dir_.path().Append(FPL("DeleteWildCardDir"));
+ file_util::CreateDirectory(subdir_path);
+ ASSERT_TRUE(file_util::PathExists(subdir_path));
+
+ // Create the wildcard path
+ FilePath directory_contents = temp_dir_.path();
+ directory_contents = directory_contents.Append(FPL("*"));
+
+ // Delete non-recursively and check that only the file is deleted
+ EXPECT_TRUE(file_util::Delete(directory_contents, false));
+ EXPECT_FALSE(file_util::PathExists(file_name));
+ EXPECT_TRUE(file_util::PathExists(subdir_path));
+
+ // Delete recursively and make sure all contents are deleted
+ EXPECT_TRUE(file_util::Delete(directory_contents, true));
+ EXPECT_FALSE(file_util::PathExists(file_name));
+ EXPECT_FALSE(file_util::PathExists(subdir_path));
+}
+
+// TODO(erikkay): see if anyone's actually using this feature of the API
+TEST_F(FileUtilTest, DeleteNonExistantWildCard) {
+ // Create a file and a directory
+ FilePath subdir_path =
+ temp_dir_.path().Append(FPL("DeleteNonExistantWildCard"));
+ file_util::CreateDirectory(subdir_path);
+ ASSERT_TRUE(file_util::PathExists(subdir_path));
+
+ // Create the wildcard path
+ FilePath directory_contents = subdir_path;
+ directory_contents = directory_contents.Append(FPL("*"));
+
+ // Delete non-recursively and check nothing got deleted
+ EXPECT_TRUE(file_util::Delete(directory_contents, false));
+ EXPECT_TRUE(file_util::PathExists(subdir_path));
+
+ // Delete recursively and check nothing got deleted
+ EXPECT_TRUE(file_util::Delete(directory_contents, true));
+ EXPECT_TRUE(file_util::PathExists(subdir_path));
+}
+#endif
+
+// Tests non-recursive Delete() for a directory.
+TEST_F(FileUtilTest, DeleteDirNonRecursive) {
+ // Create a subdirectory and put a file and two directories inside.
+ FilePath test_subdir = temp_dir_.path().Append(FPL("DeleteDirNonRecursive"));
+ file_util::CreateDirectory(test_subdir);
+ ASSERT_TRUE(file_util::PathExists(test_subdir));
+
+ FilePath file_name = test_subdir.Append(FPL("Test DeleteDir.txt"));
+ CreateTextFile(file_name, bogus_content);
+ ASSERT_TRUE(file_util::PathExists(file_name));
+
+ FilePath subdir_path1 = test_subdir.Append(FPL("TestSubDir1"));
+ file_util::CreateDirectory(subdir_path1);
+ ASSERT_TRUE(file_util::PathExists(subdir_path1));
+
+ FilePath subdir_path2 = test_subdir.Append(FPL("TestSubDir2"));
+ file_util::CreateDirectory(subdir_path2);
+ ASSERT_TRUE(file_util::PathExists(subdir_path2));
+
+ // Delete non-recursively and check that the empty dir got deleted
+ EXPECT_TRUE(file_util::Delete(subdir_path2, false));
+ EXPECT_FALSE(file_util::PathExists(subdir_path2));
+
+ // Delete non-recursively and check that nothing got deleted
+ EXPECT_FALSE(file_util::Delete(test_subdir, false));
+ EXPECT_TRUE(file_util::PathExists(test_subdir));
+ EXPECT_TRUE(file_util::PathExists(file_name));
+ EXPECT_TRUE(file_util::PathExists(subdir_path1));
+}
+
+// Tests recursive Delete() for a directory.
+TEST_F(FileUtilTest, DeleteDirRecursive) {
+ // Create a subdirectory and put a file and two directories inside.
+ FilePath test_subdir = temp_dir_.path().Append(FPL("DeleteDirRecursive"));
+ file_util::CreateDirectory(test_subdir);
+ ASSERT_TRUE(file_util::PathExists(test_subdir));
+
+ FilePath file_name = test_subdir.Append(FPL("Test DeleteDirRecursive.txt"));
+ CreateTextFile(file_name, bogus_content);
+ ASSERT_TRUE(file_util::PathExists(file_name));
+
+ FilePath subdir_path1 = test_subdir.Append(FPL("TestSubDir1"));
+ file_util::CreateDirectory(subdir_path1);
+ ASSERT_TRUE(file_util::PathExists(subdir_path1));
+
+ FilePath subdir_path2 = test_subdir.Append(FPL("TestSubDir2"));
+ file_util::CreateDirectory(subdir_path2);
+ ASSERT_TRUE(file_util::PathExists(subdir_path2));
+
+ // Delete recursively and check that the empty dir got deleted
+ EXPECT_TRUE(file_util::Delete(subdir_path2, true));
+ EXPECT_FALSE(file_util::PathExists(subdir_path2));
+
+ // Delete recursively and check that everything got deleted
+ EXPECT_TRUE(file_util::Delete(test_subdir, true));
+ EXPECT_FALSE(file_util::PathExists(file_name));
+ EXPECT_FALSE(file_util::PathExists(subdir_path1));
+ EXPECT_FALSE(file_util::PathExists(test_subdir));
+}
+
+#if !defined(OS_STARBOARD)
+TEST_F(FileUtilTest, MoveFileNew) {
+ // Create a file
+ FilePath file_name_from =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Move_Test_File.txt"));
+ CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // The destination.
+ FilePath file_name_to = temp_dir_.path().Append(
+ FILE_PATH_LITERAL("Move_Test_File_Destination.txt"));
+ ASSERT_FALSE(file_util::PathExists(file_name_to));
+
+ EXPECT_TRUE(file_util::Move(file_name_from, file_name_to));
+
+ // Check everything has been moved.
+ EXPECT_FALSE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+}
+
+TEST_F(FileUtilTest, MoveFileExists) {
+ // Create a file
+ FilePath file_name_from =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Move_Test_File.txt"));
+ CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // The destination name.
+ FilePath file_name_to = temp_dir_.path().Append(
+ FILE_PATH_LITERAL("Move_Test_File_Destination.txt"));
+ CreateTextFile(file_name_to, L"Old file content");
+ ASSERT_TRUE(file_util::PathExists(file_name_to));
+
+ EXPECT_TRUE(file_util::Move(file_name_from, file_name_to));
+
+ // Check everything has been moved.
+ EXPECT_FALSE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_TRUE(L"Gooooooooooooooooooooogle" == ReadTextFile(file_name_to));
+}
+
+TEST_F(FileUtilTest, MoveFileDirExists) {
+ // Create a file
+ FilePath file_name_from =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Move_Test_File.txt"));
+ CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // The destination directory
+ FilePath dir_name_to =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Destination"));
+ file_util::CreateDirectory(dir_name_to);
+ ASSERT_TRUE(file_util::PathExists(dir_name_to));
+
+ EXPECT_FALSE(file_util::Move(file_name_from, dir_name_to));
+}
+
+
+TEST_F(FileUtilTest, MoveNew) {
+ // Create a directory
+ FilePath dir_name_from =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Move_From_Subdir"));
+ file_util::CreateDirectory(dir_name_from);
+ ASSERT_TRUE(file_util::PathExists(dir_name_from));
+
+ // Create a file under the directory
+ FilePath file_name_from =
+ dir_name_from.Append(FILE_PATH_LITERAL("Move_Test_File.txt"));
+ CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Move the directory.
+ FilePath dir_name_to =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Move_To_Subdir"));
+ FilePath file_name_to =
+ dir_name_to.Append(FILE_PATH_LITERAL("Move_Test_File.txt"));
+
+ ASSERT_FALSE(file_util::PathExists(dir_name_to));
+
+ EXPECT_TRUE(file_util::Move(dir_name_from, dir_name_to));
+
+ // Check everything has been moved.
+ EXPECT_FALSE(file_util::PathExists(dir_name_from));
+ EXPECT_FALSE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(dir_name_to));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+}
+
+TEST_F(FileUtilTest, MoveExist) {
+ // Create a directory
+ FilePath dir_name_from =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Move_From_Subdir"));
+ file_util::CreateDirectory(dir_name_from);
+ ASSERT_TRUE(file_util::PathExists(dir_name_from));
+
+ // Create a file under the directory
+ FilePath file_name_from =
+ dir_name_from.Append(FILE_PATH_LITERAL("Move_Test_File.txt"));
+ CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Move the directory
+ FilePath dir_name_exists =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Destination"));
+
+ FilePath dir_name_to =
+ dir_name_exists.Append(FILE_PATH_LITERAL("Move_To_Subdir"));
+ FilePath file_name_to =
+ dir_name_to.Append(FILE_PATH_LITERAL("Move_Test_File.txt"));
+
+ // Create the destination directory.
+ file_util::CreateDirectory(dir_name_exists);
+ ASSERT_TRUE(file_util::PathExists(dir_name_exists));
+
+ EXPECT_TRUE(file_util::Move(dir_name_from, dir_name_to));
+
+ // Check everything has been moved.
+ EXPECT_FALSE(file_util::PathExists(dir_name_from));
+ EXPECT_FALSE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(dir_name_to));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+}
+#endif
+
+#if !defined(OS_STARBOARD)
+TEST_F(FileUtilTest, MAYBE_CopyDirectoryRecursivelyNew) {
+ // Create a directory.
+ FilePath dir_name_from =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+ file_util::CreateDirectory(dir_name_from);
+ ASSERT_TRUE(file_util::PathExists(dir_name_from));
+
+ // Create a file under the directory.
+ FilePath file_name_from =
+ dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+ CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Create a subdirectory.
+ FilePath subdir_name_from =
+ dir_name_from.Append(FILE_PATH_LITERAL("Subdir"));
+ file_util::CreateDirectory(subdir_name_from);
+ ASSERT_TRUE(file_util::PathExists(subdir_name_from));
+
+ // Create a file under the subdirectory.
+ FilePath file_name2_from =
+ subdir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+ CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name2_from));
+
+ // Copy the directory recursively.
+ FilePath dir_name_to =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_To_Subdir"));
+ FilePath file_name_to =
+ dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+ FilePath subdir_name_to =
+ dir_name_to.Append(FILE_PATH_LITERAL("Subdir"));
+ FilePath file_name2_to =
+ subdir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+
+ ASSERT_FALSE(file_util::PathExists(dir_name_to));
+
+ EXPECT_TRUE(file_util::CopyDirectory(dir_name_from, dir_name_to, true));
+
+ // Check everything has been copied.
+ EXPECT_TRUE(file_util::PathExists(dir_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(subdir_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name2_from));
+ EXPECT_TRUE(file_util::PathExists(dir_name_to));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_TRUE(file_util::PathExists(subdir_name_to));
+ EXPECT_TRUE(file_util::PathExists(file_name2_to));
+}
+
+TEST_F(FileUtilTest, MAYBE_CopyDirectoryRecursivelyExists) {
+ // Create a directory.
+ FilePath dir_name_from =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+ file_util::CreateDirectory(dir_name_from);
+ ASSERT_TRUE(file_util::PathExists(dir_name_from));
+
+ // Create a file under the directory.
+ FilePath file_name_from =
+ dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+ CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Create a subdirectory.
+ FilePath subdir_name_from =
+ dir_name_from.Append(FILE_PATH_LITERAL("Subdir"));
+ file_util::CreateDirectory(subdir_name_from);
+ ASSERT_TRUE(file_util::PathExists(subdir_name_from));
+
+ // Create a file under the subdirectory.
+ FilePath file_name2_from =
+ subdir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+ CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name2_from));
+
+ // Copy the directory recursively.
+ FilePath dir_name_exists =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Destination"));
+
+ FilePath dir_name_to =
+ dir_name_exists.Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+ FilePath file_name_to =
+ dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+ FilePath subdir_name_to =
+ dir_name_to.Append(FILE_PATH_LITERAL("Subdir"));
+ FilePath file_name2_to =
+ subdir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+
+ // Create the destination directory.
+ file_util::CreateDirectory(dir_name_exists);
+ ASSERT_TRUE(file_util::PathExists(dir_name_exists));
+
+ EXPECT_TRUE(file_util::CopyDirectory(dir_name_from, dir_name_exists, true));
+
+ // Check everything has been copied.
+ EXPECT_TRUE(file_util::PathExists(dir_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(subdir_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name2_from));
+ EXPECT_TRUE(file_util::PathExists(dir_name_to));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_TRUE(file_util::PathExists(subdir_name_to));
+ EXPECT_TRUE(file_util::PathExists(file_name2_to));
+}
+
+TEST_F(FileUtilTest, MAYBE_CopyDirectoryNew) {
+ // Create a directory.
+ FilePath dir_name_from =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+ file_util::CreateDirectory(dir_name_from);
+ ASSERT_TRUE(file_util::PathExists(dir_name_from));
+
+ // Create a file under the directory.
+ FilePath file_name_from =
+ dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+ CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Create a subdirectory.
+ FilePath subdir_name_from =
+ dir_name_from.Append(FILE_PATH_LITERAL("Subdir"));
+ file_util::CreateDirectory(subdir_name_from);
+ ASSERT_TRUE(file_util::PathExists(subdir_name_from));
+
+ // Create a file under the subdirectory.
+ FilePath file_name2_from =
+ subdir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+ CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name2_from));
+
+ // Copy the directory not recursively.
+ FilePath dir_name_to =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_To_Subdir"));
+ FilePath file_name_to =
+ dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+ FilePath subdir_name_to =
+ dir_name_to.Append(FILE_PATH_LITERAL("Subdir"));
+
+ ASSERT_FALSE(file_util::PathExists(dir_name_to));
+
+ EXPECT_TRUE(file_util::CopyDirectory(dir_name_from, dir_name_to, false));
+
+ // Check everything has been copied.
+ EXPECT_TRUE(file_util::PathExists(dir_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(subdir_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name2_from));
+ EXPECT_TRUE(file_util::PathExists(dir_name_to));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_FALSE(file_util::PathExists(subdir_name_to));
+}
+
+TEST_F(FileUtilTest, MAYBE_CopyDirectoryExists) {
+ // Create a directory.
+ FilePath dir_name_from =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+ file_util::CreateDirectory(dir_name_from);
+ ASSERT_TRUE(file_util::PathExists(dir_name_from));
+
+ // Create a file under the directory.
+ FilePath file_name_from =
+ dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+ CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Create a subdirectory.
+ FilePath subdir_name_from =
+ dir_name_from.Append(FILE_PATH_LITERAL("Subdir"));
+ file_util::CreateDirectory(subdir_name_from);
+ ASSERT_TRUE(file_util::PathExists(subdir_name_from));
+
+ // Create a file under the subdirectory.
+ FilePath file_name2_from =
+ subdir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+ CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name2_from));
+
+ // Copy the directory not recursively.
+ FilePath dir_name_to =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_To_Subdir"));
+ FilePath file_name_to =
+ dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+ FilePath subdir_name_to =
+ dir_name_to.Append(FILE_PATH_LITERAL("Subdir"));
+
+ // Create the destination directory.
+ file_util::CreateDirectory(dir_name_to);
+ ASSERT_TRUE(file_util::PathExists(dir_name_to));
+
+ EXPECT_TRUE(file_util::CopyDirectory(dir_name_from, dir_name_to, false));
+
+ // Check everything has been copied.
+ EXPECT_TRUE(file_util::PathExists(dir_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(subdir_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name2_from));
+ EXPECT_TRUE(file_util::PathExists(dir_name_to));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_FALSE(file_util::PathExists(subdir_name_to));
+}
+
+TEST_F(FileUtilTest, MAYBE_CopyFileWithCopyDirectoryRecursiveToNew) {
+ // Create a file
+ FilePath file_name_from =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+ CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // The destination name
+ FilePath file_name_to = temp_dir_.path().Append(
+ FILE_PATH_LITERAL("Copy_Test_File_Destination.txt"));
+ ASSERT_FALSE(file_util::PathExists(file_name_to));
+
+ EXPECT_TRUE(file_util::CopyDirectory(file_name_from, file_name_to, true));
+
+ // Check the has been copied
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+}
+
+TEST_F(FileUtilTest, MAYBE_CopyFileWithCopyDirectoryRecursiveToExisting) {
+ // Create a file
+ FilePath file_name_from =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+ CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // The destination name
+ FilePath file_name_to = temp_dir_.path().Append(
+ FILE_PATH_LITERAL("Copy_Test_File_Destination.txt"));
+ CreateTextFile(file_name_to, L"Old file content");
+ ASSERT_TRUE(file_util::PathExists(file_name_to));
+
+ EXPECT_TRUE(file_util::CopyDirectory(file_name_from, file_name_to, true));
+
+ // Check the has been copied
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_TRUE(L"Gooooooooooooooooooooogle" == ReadTextFile(file_name_to));
+}
+
+TEST_F(FileUtilTest,
+ MAYBE_CopyFileWithCopyDirectoryRecursiveToExistingDirectory) {
+ // Create a file
+ FilePath file_name_from =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+ CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // The destination
+ FilePath dir_name_to =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Destination"));
+ file_util::CreateDirectory(dir_name_to);
+ ASSERT_TRUE(file_util::PathExists(dir_name_to));
+ FilePath file_name_to =
+ dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+
+ EXPECT_TRUE(file_util::CopyDirectory(file_name_from, dir_name_to, true));
+
+ // Check the has been copied
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+}
+
+TEST_F(FileUtilTest, MAYBE_CopyDirectoryWithTrailingSeparators) {
+ // Create a directory.
+ FilePath dir_name_from =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+ file_util::CreateDirectory(dir_name_from);
+ ASSERT_TRUE(file_util::PathExists(dir_name_from));
+
+ // Create a file under the directory.
+ FilePath file_name_from =
+ dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+ CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Copy the directory recursively.
+ FilePath dir_name_to =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_To_Subdir"));
+ FilePath file_name_to =
+ dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+
+ // Create from path with trailing separators.
+#if defined(OS_WIN)
+ FilePath from_path =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_From_Subdir\\\\\\"));
+#elif defined (OS_POSIX)
+ FilePath from_path =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_From_Subdir///"));
+#endif
+
+ EXPECT_TRUE(file_util::CopyDirectory(from_path, dir_name_to, true));
+
+ // Check everything has been copied.
+ EXPECT_TRUE(file_util::PathExists(dir_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(dir_name_to));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+}
+#endif // defined(OS_STARBOARD)
+
+TEST_F(FileUtilTest, CopyFile) {
+ // Create a directory
+ FilePath dir_name_from =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
+ file_util::CreateDirectory(dir_name_from);
+ ASSERT_TRUE(file_util::PathExists(dir_name_from));
+
+ // Create a file under the directory
+ FilePath file_name_from =
+ dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
+ const std::wstring file_contents(L"Gooooooooooooooooooooogle");
+ CreateTextFile(file_name_from, file_contents);
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Copy the file.
+ FilePath dest_file = dir_name_from.Append(FILE_PATH_LITERAL("DestFile.txt"));
+ ASSERT_TRUE(file_util::CopyFile(file_name_from, dest_file));
+
+#if defined(OS_STARBOARD)
+ // Why should we ensure CopyFile is unsafe? On Starboard, it won't open files
+ // with a ".." in the path.
+ // Copy the file to another location using '..' in the path.
+ FilePath dest_file2(temp_dir_.path());
+ dest_file2 = dest_file2.AppendASCII("DestFile.txt");
+ ASSERT_TRUE(file_util::CopyFile(file_name_from, dest_file2));
+#else
+ // Copy the file to another location using '..' in the path.
+ FilePath dest_file2(dir_name_from);
+ dest_file2 = dest_file2.AppendASCII("..");
+ dest_file2 = dest_file2.AppendASCII("DestFile.txt");
+ ASSERT_TRUE(file_util::CopyFile(file_name_from, dest_file2));
+#endif
+
+ FilePath dest_file2_test(dir_name_from);
+ dest_file2_test = dest_file2_test.DirName();
+ dest_file2_test = dest_file2_test.AppendASCII("DestFile.txt");
+
+ // Check everything has been copied.
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(dest_file));
+ const std::wstring read_contents = ReadTextFile(dest_file);
+ EXPECT_EQ(file_contents, read_contents);
+ EXPECT_TRUE(file_util::PathExists(dest_file2_test));
+ EXPECT_TRUE(file_util::PathExists(dest_file2));
+}
+
+// TODO(erikkay): implement
+#if defined(OS_WIN)
+TEST_F(FileUtilTest, GetFileCreationLocalTime) {
+ FilePath file_name = temp_dir_.path().Append(L"Test File.txt");
+
+ SYSTEMTIME start_time;
+ GetLocalTime(&start_time);
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
+ CreateTextFile(file_name, L"New file!");
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
+ SYSTEMTIME end_time;
+ GetLocalTime(&end_time);
+
+ SYSTEMTIME file_creation_time;
+ file_util::GetFileCreationLocalTime(file_name.value(), &file_creation_time);
+
+ FILETIME start_filetime;
+ SystemTimeToFileTime(&start_time, &start_filetime);
+ FILETIME end_filetime;
+ SystemTimeToFileTime(&end_time, &end_filetime);
+ FILETIME file_creation_filetime;
+ SystemTimeToFileTime(&file_creation_time, &file_creation_filetime);
+
+ EXPECT_EQ(-1, CompareFileTime(&start_filetime, &file_creation_filetime)) <<
+ "start time: " << FileTimeAsUint64(start_filetime) << ", " <<
+ "creation time: " << FileTimeAsUint64(file_creation_filetime);
+
+ EXPECT_EQ(-1, CompareFileTime(&file_creation_filetime, &end_filetime)) <<
+ "creation time: " << FileTimeAsUint64(file_creation_filetime) << ", " <<
+ "end time: " << FileTimeAsUint64(end_filetime);
+
+ ASSERT_TRUE(DeleteFile(file_name.value().c_str()));
+}
+#endif
+
+// file_util winds up using autoreleased objects on the Mac, so this needs
+// to be a PlatformTest.
+typedef PlatformTest ReadOnlyFileUtilTest;
+
+#if !defined(__LB_WIIU__) && !defined(OS_STARBOARD)
+TEST_F(ReadOnlyFileUtilTest, ContentsEqual) {
+ FilePath data_dir;
+ ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir));
+ data_dir = data_dir.Append(FILE_PATH_LITERAL("base"))
+ .Append(FILE_PATH_LITERAL("data"))
+ .Append(FILE_PATH_LITERAL("file_util_unittest"));
+ ASSERT_TRUE(file_util::PathExists(data_dir));
+
+ FilePath original_file =
+ data_dir.Append(FILE_PATH_LITERAL("original.txt"));
+ FilePath same_file =
+ data_dir.Append(FILE_PATH_LITERAL("same.txt"));
+ FilePath same_length_file =
+ data_dir.Append(FILE_PATH_LITERAL("same_length.txt"));
+ FilePath different_file =
+ data_dir.Append(FILE_PATH_LITERAL("different.txt"));
+ FilePath different_first_file =
+ data_dir.Append(FILE_PATH_LITERAL("different_first.txt"));
+ FilePath different_last_file =
+ data_dir.Append(FILE_PATH_LITERAL("different_last.txt"));
+ FilePath empty1_file =
+ data_dir.Append(FILE_PATH_LITERAL("empty1.txt"));
+ FilePath empty2_file =
+ data_dir.Append(FILE_PATH_LITERAL("empty2.txt"));
+ FilePath shortened_file =
+ data_dir.Append(FILE_PATH_LITERAL("shortened.txt"));
+ FilePath binary_file =
+ data_dir.Append(FILE_PATH_LITERAL("binary_file.bin"));
+ FilePath binary_file_same =
+ data_dir.Append(FILE_PATH_LITERAL("binary_file_same.bin"));
+ FilePath binary_file_diff =
+ data_dir.Append(FILE_PATH_LITERAL("binary_file_diff.bin"));
+
+ EXPECT_TRUE(file_util::ContentsEqual(original_file, original_file));
+ EXPECT_TRUE(file_util::ContentsEqual(original_file, same_file));
+ EXPECT_FALSE(file_util::ContentsEqual(original_file, same_length_file));
+ EXPECT_FALSE(file_util::ContentsEqual(original_file, different_file));
+ EXPECT_FALSE(file_util::ContentsEqual(
+ FilePath(FILE_PATH_LITERAL("bogusname")),
+ FilePath(FILE_PATH_LITERAL("bogusname"))));
+ EXPECT_FALSE(file_util::ContentsEqual(original_file, different_first_file));
+ EXPECT_FALSE(file_util::ContentsEqual(original_file, different_last_file));
+ EXPECT_TRUE(file_util::ContentsEqual(empty1_file, empty2_file));
+ EXPECT_FALSE(file_util::ContentsEqual(original_file, shortened_file));
+ EXPECT_FALSE(file_util::ContentsEqual(shortened_file, original_file));
+ EXPECT_TRUE(file_util::ContentsEqual(binary_file, binary_file_same));
+ EXPECT_FALSE(file_util::ContentsEqual(binary_file, binary_file_diff));
+}
+#endif
+
+#if !defined(__LB_SHELL__) && !defined(OS_STARBOARD)
+TEST_F(ReadOnlyFileUtilTest, TextContentsEqual) {
+ FilePath data_dir;
+ ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir));
+ data_dir = data_dir.Append(FILE_PATH_LITERAL("base"))
+ .Append(FILE_PATH_LITERAL("data"))
+ .Append(FILE_PATH_LITERAL("file_util_unittest"));
+ ASSERT_TRUE(file_util::PathExists(data_dir));
+
+ FilePath original_file =
+ data_dir.Append(FILE_PATH_LITERAL("original.txt"));
+ FilePath same_file =
+ data_dir.Append(FILE_PATH_LITERAL("same.txt"));
+ FilePath crlf_file =
+ data_dir.Append(FILE_PATH_LITERAL("crlf.txt"));
+ FilePath shortened_file =
+ data_dir.Append(FILE_PATH_LITERAL("shortened.txt"));
+ FilePath different_file =
+ data_dir.Append(FILE_PATH_LITERAL("different.txt"));
+ FilePath different_first_file =
+ data_dir.Append(FILE_PATH_LITERAL("different_first.txt"));
+ FilePath different_last_file =
+ data_dir.Append(FILE_PATH_LITERAL("different_last.txt"));
+ FilePath first1_file =
+ data_dir.Append(FILE_PATH_LITERAL("first1.txt"));
+ FilePath first2_file =
+ data_dir.Append(FILE_PATH_LITERAL("first2.txt"));
+ FilePath empty1_file =
+ data_dir.Append(FILE_PATH_LITERAL("empty1.txt"));
+ FilePath empty2_file =
+ data_dir.Append(FILE_PATH_LITERAL("empty2.txt"));
+ FilePath blank_line_file =
+ data_dir.Append(FILE_PATH_LITERAL("blank_line.txt"));
+ FilePath blank_line_crlf_file =
+ data_dir.Append(FILE_PATH_LITERAL("blank_line_crlf.txt"));
+
+ EXPECT_TRUE(file_util::TextContentsEqual(original_file, same_file));
+ EXPECT_TRUE(file_util::TextContentsEqual(original_file, crlf_file));
+ EXPECT_FALSE(file_util::TextContentsEqual(original_file, shortened_file));
+ EXPECT_FALSE(file_util::TextContentsEqual(original_file, different_file));
+ EXPECT_FALSE(file_util::TextContentsEqual(original_file,
+ different_first_file));
+ EXPECT_FALSE(file_util::TextContentsEqual(original_file,
+ different_last_file));
+ EXPECT_FALSE(file_util::TextContentsEqual(first1_file, first2_file));
+ EXPECT_TRUE(file_util::TextContentsEqual(empty1_file, empty2_file));
+ EXPECT_FALSE(file_util::TextContentsEqual(original_file, empty1_file));
+ EXPECT_TRUE(file_util::TextContentsEqual(blank_line_file,
+ blank_line_crlf_file));
+}
+#endif // __LB_SHELL__
+
+// We don't need equivalent functionality outside of Windows.
+#if defined(OS_WIN)
+TEST_F(FileUtilTest, CopyAndDeleteDirectoryTest) {
+ // Create a directory
+ FilePath dir_name_from =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("CopyAndDelete_From_Subdir"));
+ file_util::CreateDirectory(dir_name_from);
+ ASSERT_TRUE(file_util::PathExists(dir_name_from));
+
+ // Create a file under the directory
+ FilePath file_name_from =
+ dir_name_from.Append(FILE_PATH_LITERAL("CopyAndDelete_Test_File.txt"));
+ CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Move the directory by using CopyAndDeleteDirectory
+ FilePath dir_name_to = temp_dir_.path().Append(
+ FILE_PATH_LITERAL("CopyAndDelete_To_Subdir"));
+ FilePath file_name_to =
+ dir_name_to.Append(FILE_PATH_LITERAL("CopyAndDelete_Test_File.txt"));
+
+ ASSERT_FALSE(file_util::PathExists(dir_name_to));
+
+ EXPECT_TRUE(file_util::CopyAndDeleteDirectory(dir_name_from, dir_name_to));
+
+ // Check everything has been moved.
+ EXPECT_FALSE(file_util::PathExists(dir_name_from));
+ EXPECT_FALSE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(dir_name_to));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+}
+
+TEST_F(FileUtilTest, GetTempDirTest) {
+ static const TCHAR* kTmpKey = _T("TMP");
+ static const TCHAR* kTmpValues[] = {
+ _T(""), _T("C:"), _T("C:\\"), _T("C:\\tmp"), _T("C:\\tmp\\")
+ };
+ // Save the original $TMP.
+ size_t original_tmp_size;
+ TCHAR* original_tmp;
+ ASSERT_EQ(0, ::_tdupenv_s(&original_tmp, &original_tmp_size, kTmpKey));
+ // original_tmp may be NULL.
+
+ for (unsigned int i = 0; i < arraysize(kTmpValues); ++i) {
+ FilePath path;
+ ::_tputenv_s(kTmpKey, kTmpValues[i]);
+ file_util::GetTempDir(&path);
+ EXPECT_TRUE(path.IsAbsolute()) << "$TMP=" << kTmpValues[i] <<
+ " result=" << path.value();
+ }
+
+ // Restore the original $TMP.
+ if (original_tmp) {
+ ::_tputenv_s(kTmpKey, original_tmp);
+ free(original_tmp);
+ } else {
+ ::_tputenv_s(kTmpKey, _T(""));
+ }
+}
+#endif // OS_WIN
+
+TEST_F(FileUtilTest, CreateTemporaryFileTest) {
+ FilePath temp_files[3];
+ for (int i = 0; i < 3; i++) {
+ ASSERT_TRUE(file_util::CreateTemporaryFile(&(temp_files[i])));
+ EXPECT_TRUE(file_util::PathExists(temp_files[i]));
+ EXPECT_FALSE(file_util::DirectoryExists(temp_files[i]));
+ }
+ for (int i = 0; i < 3; i++)
+ EXPECT_FALSE(temp_files[i] == temp_files[(i+1)%3]);
+ for (int i = 0; i < 3; i++)
+ EXPECT_TRUE(file_util::Delete(temp_files[i], false));
+}
+
+#if !defined(OS_STARBOARD)
+TEST_F(FileUtilTest, CreateAndOpenTemporaryFileTest) {
+ FilePath names[3];
+ FILE* fps[3];
+ int i;
+
+ // Create; make sure they are open and exist.
+ for (i = 0; i < 3; ++i) {
+ fps[i] = file_util::CreateAndOpenTemporaryFile(&(names[i]));
+ ASSERT_TRUE(fps[i]);
+ EXPECT_TRUE(file_util::PathExists(names[i]));
+ }
+
+ // Make sure all names are unique.
+ for (i = 0; i < 3; ++i) {
+ EXPECT_FALSE(names[i] == names[(i+1)%3]);
+ }
+
+ // Close and delete.
+ for (i = 0; i < 3; ++i) {
+ EXPECT_TRUE(file_util::CloseFile(fps[i]));
+ EXPECT_TRUE(file_util::Delete(names[i], false));
+ }
+}
+#endif
+
+TEST_F(FileUtilTest, CreateNewTempDirectoryTest) {
+ FilePath temp_dir;
+ ASSERT_TRUE(file_util::CreateNewTempDirectory(FilePath::StringType(),
+ &temp_dir));
+ EXPECT_TRUE(file_util::PathExists(temp_dir));
+ EXPECT_TRUE(file_util::Delete(temp_dir, false));
+}
+
+TEST_F(FileUtilTest, CreateNewTemporaryDirInDirTest) {
+ FilePath new_dir;
+ ASSERT_TRUE(file_util::CreateTemporaryDirInDir(
+ temp_dir_.path(),
+ FILE_PATH_LITERAL("CreateNewTemporaryDirInDirTest"),
+ &new_dir));
+ EXPECT_TRUE(file_util::PathExists(new_dir));
+ EXPECT_TRUE(temp_dir_.path().IsParent(new_dir));
+ EXPECT_TRUE(file_util::Delete(new_dir, false));
+}
+
+#if !defined(__LB_SHELL__)
+TEST_F(FileUtilTest, GetShmemTempDirTest) {
+ FilePath dir;
+ EXPECT_TRUE(file_util::GetShmemTempDir(&dir, false));
+ EXPECT_TRUE(file_util::DirectoryExists(dir));
+}
+#endif
+
+TEST_F(FileUtilTest, CreateDirectoryTest) {
+ FilePath test_root =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("create_directory_test"));
+#if defined(OS_WIN)
+ FilePath test_path =
+ test_root.Append(FILE_PATH_LITERAL("dir\\tree\\likely\\doesnt\\exist\\"));
+#elif defined(OS_POSIX)
+ FilePath test_path =
+ test_root.Append(FILE_PATH_LITERAL("dir/tree/likely/doesnt/exist/"));
+#elif defined(OS_STARBOARD)
+ // Directory depth is limited on certain platforms.
+ FilePath test_path =
+ test_root.Append(FILE_PATH_LITERAL("tree/likely/doesnt/exist/"));
+#endif
+
+ EXPECT_FALSE(file_util::PathExists(test_path));
+ EXPECT_TRUE(file_util::CreateDirectory(test_path));
+ EXPECT_TRUE(file_util::PathExists(test_path));
+ // CreateDirectory returns true if the DirectoryExists returns true.
+ EXPECT_TRUE(file_util::CreateDirectory(test_path));
+
+ // Doesn't work to create it on top of a non-dir
+ test_path = test_path.Append(FILE_PATH_LITERAL("foobar.txt"));
+ EXPECT_FALSE(file_util::PathExists(test_path));
+ CreateTextFile(test_path, L"test file");
+ EXPECT_TRUE(file_util::PathExists(test_path));
+ EXPECT_FALSE(file_util::CreateDirectory(test_path));
+
+ EXPECT_TRUE(file_util::Delete(test_root, true));
+ EXPECT_FALSE(file_util::PathExists(test_root));
+ EXPECT_FALSE(file_util::PathExists(test_path));
+
+#if !defined(__LB_PS3__) && !defined(OS_STARBOARD)
+ // Starboard doesn't support relative paths.
+
+ // Verify assumptions made by the Windows implementation:
+ // 1. The current directory always exists.
+ // 2. The root directory always exists.
+ ASSERT_TRUE(file_util::DirectoryExists(
+ FilePath(FilePath::kCurrentDirectory)));
+#endif
+
+ FilePath top_level = test_root;
+ while (top_level != top_level.DirName()) {
+ top_level = top_level.DirName();
+ }
+ ASSERT_TRUE(file_util::DirectoryExists(top_level));
+
+ // Given these assumptions hold, it should be safe to
+ // test that "creating" these directories succeeds.
+#if !defined(OS_STARBOARD)
+ EXPECT_TRUE(file_util::CreateDirectory(
+ FilePath(FilePath::kCurrentDirectory)));
+#endif
+
+ EXPECT_TRUE(file_util::CreateDirectory(top_level));
+
+#if defined(OS_WIN)
+ FilePath invalid_drive(FILE_PATH_LITERAL("o:\\"));
+ FilePath invalid_path =
+ invalid_drive.Append(FILE_PATH_LITERAL("some\\inaccessible\\dir"));
+ if (!file_util::PathExists(invalid_drive)) {
+ EXPECT_FALSE(file_util::CreateDirectory(invalid_path));
+ }
+#endif
+}
+
+TEST_F(FileUtilTest, DetectDirectoryTest) {
+ // Check a directory
+ FilePath test_root =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("detect_directory_test"));
+ EXPECT_FALSE(file_util::PathExists(test_root));
+ EXPECT_TRUE(file_util::CreateDirectory(test_root));
+ EXPECT_TRUE(file_util::PathExists(test_root));
+ EXPECT_TRUE(file_util::DirectoryExists(test_root));
+ // Check a file
+ FilePath test_path =
+ test_root.Append(FILE_PATH_LITERAL("foobar.txt"));
+ EXPECT_FALSE(file_util::PathExists(test_path));
+ CreateTextFile(test_path, L"test file");
+ EXPECT_TRUE(file_util::PathExists(test_path));
+ EXPECT_FALSE(file_util::DirectoryExists(test_path));
+ EXPECT_TRUE(file_util::Delete(test_path, false));
+
+ EXPECT_TRUE(file_util::Delete(test_root, true));
+}
+
+TEST_F(FileUtilTest, FileEnumeratorTest) {
+ // Test an empty directory.
+ file_util::FileEnumerator f0(temp_dir_.path(), true, FILES_AND_DIRECTORIES);
+ EXPECT_EQ(f0.Next().value(), FILE_PATH_LITERAL(""));
+ EXPECT_EQ(f0.Next().value(), FILE_PATH_LITERAL(""));
+
+ // Test an empty directory, non-recursively, including "..".
+#if !defined(OS_STARBOARD)
+ // Starboard doesn't support relative paths.
+ file_util::FileEnumerator f0_dotdot(temp_dir_.path(), false,
+ FILES_AND_DIRECTORIES | file_util::FileEnumerator::INCLUDE_DOT_DOT);
+ EXPECT_EQ(temp_dir_.path().Append(FILE_PATH_LITERAL("..")).value(),
+ f0_dotdot.Next().value());
+ EXPECT_EQ(FILE_PATH_LITERAL(""),
+ f0_dotdot.Next().value());
+#endif
+
+ // create the directories
+ FilePath dir1 = temp_dir_.path().Append(FILE_PATH_LITERAL("dir1"));
+ EXPECT_TRUE(file_util::CreateDirectory(dir1));
+ FilePath dir2 = temp_dir_.path().Append(FILE_PATH_LITERAL("dir2"));
+ EXPECT_TRUE(file_util::CreateDirectory(dir2));
+ FilePath dir2inner = dir2.Append(FILE_PATH_LITERAL("inner"));
+ EXPECT_TRUE(file_util::CreateDirectory(dir2inner));
+
+ // create the files
+ FilePath dir2file = dir2.Append(FILE_PATH_LITERAL("dir2file.txt"));
+ CreateTextFile(dir2file, L"");
+ FilePath dir2innerfile = dir2inner.Append(FILE_PATH_LITERAL("innerfile.txt"));
+ CreateTextFile(dir2innerfile, L"");
+ FilePath file1 = temp_dir_.path().Append(FILE_PATH_LITERAL("file1.txt"));
+ CreateTextFile(file1, L"");
+ FilePath file2_rel =
+ dir2.Append(FilePath::kParentDirectory)
+ .Append(FILE_PATH_LITERAL("file2.txt"));
+ CreateTextFile(file2_rel, L"");
+ FilePath file2_abs = temp_dir_.path().Append(FILE_PATH_LITERAL("file2.txt"));
+
+ // Only enumerate files.
+ file_util::FileEnumerator f1(temp_dir_.path(), true,
+ file_util::FileEnumerator::FILES);
+ FindResultCollector c1(f1);
+ EXPECT_TRUE(c1.HasFile(file1));
+ EXPECT_TRUE(c1.HasFile(file2_abs));
+ EXPECT_TRUE(c1.HasFile(dir2file));
+ EXPECT_TRUE(c1.HasFile(dir2innerfile));
+ EXPECT_EQ(c1.size(), 4);
+
+ // Only enumerate directories.
+ file_util::FileEnumerator f2(temp_dir_.path(), true,
+ file_util::FileEnumerator::DIRECTORIES);
+ FindResultCollector c2(f2);
+ EXPECT_TRUE(c2.HasFile(dir1));
+ EXPECT_TRUE(c2.HasFile(dir2));
+ EXPECT_TRUE(c2.HasFile(dir2inner));
+ EXPECT_EQ(c2.size(), 3);
+
+ // Only enumerate directories non-recursively.
+ file_util::FileEnumerator f2_non_recursive(
+ temp_dir_.path(), false, file_util::FileEnumerator::DIRECTORIES);
+ FindResultCollector c2_non_recursive(f2_non_recursive);
+ EXPECT_TRUE(c2_non_recursive.HasFile(dir1));
+ EXPECT_TRUE(c2_non_recursive.HasFile(dir2));
+ EXPECT_EQ(c2_non_recursive.size(), 2);
+
+#if !defined(__LB_XB1__) && !defined(OS_STARBOARD)
+// As all file access is absolute, this is not an issue
+ // Only enumerate directories, non-recursively, including "..".
+ file_util::FileEnumerator f2_dotdot(temp_dir_.path(), false,
+ file_util::FileEnumerator::DIRECTORIES |
+ file_util::FileEnumerator::INCLUDE_DOT_DOT);
+ FindResultCollector c2_dotdot(f2_dotdot);
+ EXPECT_TRUE(c2_dotdot.HasFile(dir1));
+ EXPECT_TRUE(c2_dotdot.HasFile(dir2));
+ EXPECT_TRUE(c2_dotdot.HasFile(
+ temp_dir_.path().Append(FILE_PATH_LITERAL(".."))));
+ EXPECT_EQ(c2_dotdot.size(), 3);
+#endif
+
+ // Enumerate files and directories.
+ file_util::FileEnumerator f3(temp_dir_.path(), true, FILES_AND_DIRECTORIES);
+ FindResultCollector c3(f3);
+ EXPECT_TRUE(c3.HasFile(dir1));
+ EXPECT_TRUE(c3.HasFile(dir2));
+ EXPECT_TRUE(c3.HasFile(file1));
+ EXPECT_TRUE(c3.HasFile(file2_abs));
+ EXPECT_TRUE(c3.HasFile(dir2file));
+ EXPECT_TRUE(c3.HasFile(dir2inner));
+ EXPECT_TRUE(c3.HasFile(dir2innerfile));
+ EXPECT_EQ(c3.size(), 7);
+
+ // Non-recursive operation.
+ file_util::FileEnumerator f4(temp_dir_.path(), false, FILES_AND_DIRECTORIES);
+ FindResultCollector c4(f4);
+ EXPECT_TRUE(c4.HasFile(dir2));
+ EXPECT_TRUE(c4.HasFile(dir2));
+ EXPECT_TRUE(c4.HasFile(file1));
+ EXPECT_TRUE(c4.HasFile(file2_abs));
+ EXPECT_EQ(c4.size(), 4);
+
+#if !defined(OS_STARBOARD)
+ // Enumerate with a pattern.
+ file_util::FileEnumerator f5(temp_dir_.path(), true, FILES_AND_DIRECTORIES,
+ FILE_PATH_LITERAL("dir*"));
+ FindResultCollector c5(f5);
+ EXPECT_TRUE(c5.HasFile(dir1));
+ EXPECT_TRUE(c5.HasFile(dir2));
+ EXPECT_TRUE(c5.HasFile(dir2file));
+ EXPECT_TRUE(c5.HasFile(dir2inner));
+ EXPECT_TRUE(c5.HasFile(dir2innerfile));
+ EXPECT_EQ(c5.size(), 5);
+#endif
+
+ // Make sure the destructor closes the find handle while in the middle of a
+ // query to allow TearDown to delete the directory.
+ file_util::FileEnumerator f6(temp_dir_.path(), true, FILES_AND_DIRECTORIES);
+ EXPECT_FALSE(f6.Next().value().empty()); // Should have found something
+ // (we don't care what).
+}
+
+TEST_F(FileUtilTest, AppendToFile) {
+ FilePath data_dir =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("FilePathTest"));
+
+ // Create a fresh, empty copy of this directory.
+ if (file_util::PathExists(data_dir)) {
+ ASSERT_TRUE(file_util::Delete(data_dir, true));
+ }
+ ASSERT_TRUE(file_util::CreateDirectory(data_dir));
+
+ // Create a fresh, empty copy of this directory.
+ if (file_util::PathExists(data_dir)) {
+ ASSERT_TRUE(file_util::Delete(data_dir, true));
+ }
+ ASSERT_TRUE(file_util::CreateDirectory(data_dir));
+ FilePath foobar(data_dir.Append(FILE_PATH_LITERAL("foobar.txt")));
+
+ std::string data("hello");
+ EXPECT_EQ(-1, file_util::AppendToFile(foobar, data.c_str(), data.length()));
+ EXPECT_EQ(static_cast<int>(data.length()),
+ file_util::WriteFile(foobar, data.c_str(), data.length()));
+ EXPECT_EQ(static_cast<int>(data.length()),
+ file_util::AppendToFile(foobar, data.c_str(), data.length()));
+
+ const std::wstring read_content = ReadTextFile(foobar);
+ EXPECT_EQ(L"hellohello", read_content);
+}
+
+TEST_F(FileUtilTest, Contains) {
+ FilePath data_dir =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("FilePathTest"));
+
+ // Create a fresh, empty copy of this directory.
+ if (file_util::PathExists(data_dir)) {
+ ASSERT_TRUE(file_util::Delete(data_dir, true));
+ }
+ ASSERT_TRUE(file_util::CreateDirectory(data_dir));
+
+ FilePath foo(data_dir.Append(FILE_PATH_LITERAL("foo")));
+ FilePath bar(foo.Append(FILE_PATH_LITERAL("bar.txt")));
+ FilePath baz(data_dir.Append(FILE_PATH_LITERAL("baz.txt")));
+ FilePath foobar(data_dir.Append(FILE_PATH_LITERAL("foobar.txt")));
+
+ // Annoyingly, the directories must actually exist in order for realpath(),
+ // which Contains() relies on in posix, to work.
+ ASSERT_TRUE(file_util::CreateDirectory(foo));
+ std::string data("hello");
+ ASSERT_TRUE(file_util::WriteFile(bar, data.c_str(), data.length()));
+ ASSERT_TRUE(file_util::WriteFile(baz, data.c_str(), data.length()));
+ ASSERT_TRUE(file_util::WriteFile(foobar, data.c_str(), data.length()));
+
+ EXPECT_TRUE(file_util::ContainsPath(foo, bar));
+ EXPECT_FALSE(file_util::ContainsPath(foo, baz));
+ EXPECT_FALSE(file_util::ContainsPath(foo, foobar));
+ EXPECT_FALSE(file_util::ContainsPath(foo, foo));
+
+ // Platform-specific concerns.
+ FilePath foo_caps(data_dir.Append(FILE_PATH_LITERAL("FOO")));
+#if defined(OS_WIN)
+ EXPECT_TRUE(file_util::ContainsPath(foo,
+ foo_caps.Append(FILE_PATH_LITERAL("bar.txt"))));
+ EXPECT_TRUE(file_util::ContainsPath(foo,
+ FilePath(foo.value() + FILE_PATH_LITERAL("/bar.txt"))));
+#elif defined(OS_MACOSX)
+ // We can't really do this test on OS X since the case-sensitivity of the
+ // filesystem is configurable.
+#elif defined(OS_POSIX)
+ EXPECT_FALSE(file_util::ContainsPath(foo,
+ foo_caps.Append(FILE_PATH_LITERAL("bar.txt"))));
+#endif
+}
+
+#if !defined(OS_STARBOARD)
+TEST_F(FileUtilTest, TouchFile) {
+ FilePath data_dir =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("FilePathTest"));
+
+ // Create a fresh, empty copy of this directory.
+ if (file_util::PathExists(data_dir)) {
+ ASSERT_TRUE(file_util::Delete(data_dir, true));
+ }
+ ASSERT_TRUE(file_util::CreateDirectory(data_dir));
+
+ FilePath foobar(data_dir.Append(FILE_PATH_LITERAL("foobar.txt")));
+ std::string data("hello");
+ ASSERT_TRUE(file_util::WriteFile(foobar, data.c_str(), data.length()));
+
+ base::Time access_time;
+ // This timestamp is divisible by one day (in local timezone),
+ // to make it work on FAT too.
+ ASSERT_TRUE(base::Time::FromString("Wed, 16 Nov 1994, 00:00:00",
+ &access_time));
+
+ base::Time modification_time;
+ // Note that this timestamp is divisible by two (seconds) - FAT stores
+ // modification times with 2s resolution.
+ ASSERT_TRUE(base::Time::FromString("Tue, 15 Nov 1994, 12:45:26 GMT",
+ &modification_time));
+
+#if !defined(__LB_PS3__)
+ // file_util::TouchFile creates a platform file and passes its descriptor to
+ // base::TouchPlatformFile().
+ // Unfortunately, PS3 only implements utime, not futime, which means it
+ // can only touch a file path, not a file descriptor.
+
+ ASSERT_TRUE(file_util::TouchFile(foobar, access_time, modification_time));
+ base::PlatformFileInfo file_info;
+ ASSERT_TRUE(file_util::GetFileInfo(foobar, &file_info));
+ EXPECT_EQ(file_info.last_accessed.ToInternalValue(),
+ access_time.ToInternalValue());
+ EXPECT_EQ(file_info.last_modified.ToInternalValue(),
+ modification_time.ToInternalValue());
+#endif
+}
+#endif
+
+TEST_F(FileUtilTest, IsDirectoryEmpty) {
+ FilePath empty_dir = temp_dir_.path().Append(FILE_PATH_LITERAL("EmptyDir"));
+
+ ASSERT_FALSE(file_util::PathExists(empty_dir));
+
+ ASSERT_TRUE(file_util::CreateDirectory(empty_dir));
+
+ EXPECT_TRUE(file_util::IsDirectoryEmpty(empty_dir));
+
+ FilePath foo(empty_dir.Append(FILE_PATH_LITERAL("foo.txt")));
+ std::string bar("baz");
+ ASSERT_TRUE(file_util::WriteFile(foo, bar.c_str(), bar.length()));
+
+ EXPECT_FALSE(file_util::IsDirectoryEmpty(empty_dir));
+}
+
+#if defined(OS_POSIX) && !defined(__LB_SHELL__)
+// We don't support uids or symlinks in lbshell
+
+// Testing VerifyPathControlledByAdmin() is hard, because there is no
+// way a test can make a file owned by root, or change file paths
+// at the root of the file system. VerifyPathControlledByAdmin()
+// is implemented as a call to VerifyPathControlledByUser, which gives
+// us the ability to test with paths under the test's temp directory,
+// using a user id we control.
+// Pull tests of VerifyPathControlledByUserTest() into a separate test class
+// with a common SetUp() method.
+class VerifyPathControlledByUserTest : public FileUtilTest {
+ protected:
+ virtual void SetUp() OVERRIDE {
+ FileUtilTest::SetUp();
+
+ // Create a basic structure used by each test.
+ // base_dir_
+ // |-> sub_dir_
+ // |-> text_file_
+
+ base_dir_ = temp_dir_.path().AppendASCII("base_dir");
+ ASSERT_TRUE(file_util::CreateDirectory(base_dir_));
+
+ sub_dir_ = base_dir_.AppendASCII("sub_dir");
+ ASSERT_TRUE(file_util::CreateDirectory(sub_dir_));
+
+ text_file_ = sub_dir_.AppendASCII("file.txt");
+ CreateTextFile(text_file_, L"This text file has some text in it.");
+
+ // Get the user and group files are created with from |base_dir_|.
+ struct stat stat_buf;
+ ASSERT_EQ(0, stat(base_dir_.value().c_str(), &stat_buf));
+ uid_ = stat_buf.st_uid;
+ ok_gids_.insert(stat_buf.st_gid);
+ bad_gids_.insert(stat_buf.st_gid + 1);
+
+ ASSERT_EQ(uid_, getuid()); // This process should be the owner.
+
+ // To ensure that umask settings do not cause the initial state
+ // of permissions to be different from what we expect, explicitly
+ // set permissions on the directories we create.
+ // Make all files and directories non-world-writable.
+
+ // Users and group can read, write, traverse
+ int enabled_permissions =
+ file_util::FILE_PERMISSION_USER_MASK |
+ file_util::FILE_PERMISSION_GROUP_MASK;
+ // Other users can't read, write, traverse
+ int disabled_permissions =
+ file_util::FILE_PERMISSION_OTHERS_MASK;
+
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(
+ base_dir_, enabled_permissions, disabled_permissions));
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(
+ sub_dir_, enabled_permissions, disabled_permissions));
+ }
+
+ FilePath base_dir_;
+ FilePath sub_dir_;
+ FilePath text_file_;
+ uid_t uid_;
+
+ std::set<gid_t> ok_gids_;
+ std::set<gid_t> bad_gids_;
+};
+
+TEST_F(VerifyPathControlledByUserTest, BadPaths) {
+ // File does not exist.
+ FilePath does_not_exist = base_dir_.AppendASCII("does")
+ .AppendASCII("not")
+ .AppendASCII("exist");
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, does_not_exist, uid_, ok_gids_));
+
+ // |base| not a subpath of |path|.
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ sub_dir_, base_dir_, uid_, ok_gids_));
+
+ // An empty base path will fail to be a prefix for any path.
+ FilePath empty;
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ empty, base_dir_, uid_, ok_gids_));
+
+ // Finding that a bad call fails proves nothing unless a good call succeeds.
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, sub_dir_, uid_, ok_gids_));
+}
+
+#if !defined(__LB_SHELL__)
+// We don't support uids or symlinks in lbshell
+
+TEST_F(VerifyPathControlledByUserTest, Symlinks) {
+ // Symlinks in the path should cause failure.
+
+ // Symlink to the file at the end of the path.
+ FilePath file_link = base_dir_.AppendASCII("file_link");
+ ASSERT_TRUE(file_util::CreateSymbolicLink(text_file_, file_link))
+ << "Failed to create symlink.";
+
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, file_link, uid_, ok_gids_));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ file_link, file_link, uid_, ok_gids_));
+
+ // Symlink from one directory to another within the path.
+ FilePath link_to_sub_dir = base_dir_.AppendASCII("link_to_sub_dir");
+ ASSERT_TRUE(file_util::CreateSymbolicLink(sub_dir_, link_to_sub_dir))
+ << "Failed to create symlink.";
+
+ FilePath file_path_with_link = link_to_sub_dir.AppendASCII("file.txt");
+ ASSERT_TRUE(file_util::PathExists(file_path_with_link));
+
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, file_path_with_link, uid_, ok_gids_));
+
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ link_to_sub_dir, file_path_with_link, uid_, ok_gids_));
+
+ // Symlinks in parents of base path are allowed.
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ file_path_with_link, file_path_with_link, uid_, ok_gids_));
+}
+
+TEST_F(VerifyPathControlledByUserTest, OwnershipChecks) {
+ // Get a uid that is not the uid of files we create.
+ uid_t bad_uid = uid_ + 1;
+
+ // Make all files and directories non-world-writable.
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(base_dir_, 0u, S_IWOTH));
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(sub_dir_, 0u, S_IWOTH));
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(text_file_, 0u, S_IWOTH));
+
+ // We control these paths.
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, sub_dir_, uid_, ok_gids_));
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, text_file_, uid_, ok_gids_));
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ sub_dir_, text_file_, uid_, ok_gids_));
+
+ // Another user does not control these paths.
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, sub_dir_, bad_uid, ok_gids_));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, text_file_, bad_uid, ok_gids_));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ sub_dir_, text_file_, bad_uid, ok_gids_));
+
+ // Another group does not control the paths.
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, sub_dir_, uid_, bad_gids_));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, text_file_, uid_, bad_gids_));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ sub_dir_, text_file_, uid_, bad_gids_));
+}
+
+TEST_F(VerifyPathControlledByUserTest, GroupWriteTest) {
+ // Make all files and directories writable only by their owner.
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(base_dir_, 0u, S_IWOTH|S_IWGRP));
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(sub_dir_, 0u, S_IWOTH|S_IWGRP));
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(text_file_, 0u, S_IWOTH|S_IWGRP));
+
+ // Any group is okay because the path is not group-writable.
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, sub_dir_, uid_, ok_gids_));
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, text_file_, uid_, ok_gids_));
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ sub_dir_, text_file_, uid_, ok_gids_));
+
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, sub_dir_, uid_, bad_gids_));
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, text_file_, uid_, bad_gids_));
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ sub_dir_, text_file_, uid_, bad_gids_));
+
+ // No group is okay, because we don't check the group
+ // if no group can write.
+ std::set<gid_t> no_gids; // Empty set of gids.
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, sub_dir_, uid_, no_gids));
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, text_file_, uid_, no_gids));
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ sub_dir_, text_file_, uid_, no_gids));
+
+
+ // Make all files and directories writable by their group.
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(base_dir_, S_IWGRP, 0u));
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(sub_dir_, S_IWGRP, 0u));
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(text_file_, S_IWGRP, 0u));
+
+ // Now |ok_gids_| works, but |bad_gids_| fails.
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, sub_dir_, uid_, ok_gids_));
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, text_file_, uid_, ok_gids_));
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ sub_dir_, text_file_, uid_, ok_gids_));
+
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, sub_dir_, uid_, bad_gids_));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, text_file_, uid_, bad_gids_));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ sub_dir_, text_file_, uid_, bad_gids_));
+
+ // Because any group in the group set is allowed,
+ // the union of good and bad gids passes.
+
+ std::set<gid_t> multiple_gids;
+ std::set_union(
+ ok_gids_.begin(), ok_gids_.end(),
+ bad_gids_.begin(), bad_gids_.end(),
+ std::inserter(multiple_gids, multiple_gids.begin()));
+
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, sub_dir_, uid_, multiple_gids));
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, text_file_, uid_, multiple_gids));
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ sub_dir_, text_file_, uid_, multiple_gids));
+}
+
+TEST_F(VerifyPathControlledByUserTest, WriteBitChecks) {
+ // Make all files and directories non-world-writable.
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(base_dir_, 0u, S_IWOTH));
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(sub_dir_, 0u, S_IWOTH));
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(text_file_, 0u, S_IWOTH));
+
+ // Initialy, we control all parts of the path.
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, sub_dir_, uid_, ok_gids_));
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, text_file_, uid_, ok_gids_));
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ sub_dir_, text_file_, uid_, ok_gids_));
+
+ // Make base_dir_ world-writable.
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(base_dir_, S_IWOTH, 0u));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, sub_dir_, uid_, ok_gids_));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, text_file_, uid_, ok_gids_));
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ sub_dir_, text_file_, uid_, ok_gids_));
+
+ // Make sub_dir_ world writable.
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(sub_dir_, S_IWOTH, 0u));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, sub_dir_, uid_, ok_gids_));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, text_file_, uid_, ok_gids_));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ sub_dir_, text_file_, uid_, ok_gids_));
+
+ // Make text_file_ world writable.
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(text_file_, S_IWOTH, 0u));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, sub_dir_, uid_, ok_gids_));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, text_file_, uid_, ok_gids_));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ sub_dir_, text_file_, uid_, ok_gids_));
+
+ // Make sub_dir_ non-world writable.
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(sub_dir_, 0u, S_IWOTH));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, sub_dir_, uid_, ok_gids_));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, text_file_, uid_, ok_gids_));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ sub_dir_, text_file_, uid_, ok_gids_));
+
+ // Make base_dir_ non-world-writable.
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(base_dir_, 0u, S_IWOTH));
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, sub_dir_, uid_, ok_gids_));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, text_file_, uid_, ok_gids_));
+ EXPECT_FALSE(
+ file_util::VerifyPathControlledByUser(
+ sub_dir_, text_file_, uid_, ok_gids_));
+
+ // Back to the initial state: Nothing is writable, so every path
+ // should pass.
+ ASSERT_NO_FATAL_FAILURE(
+ ChangePosixFilePermissions(text_file_, 0u, S_IWOTH));
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, sub_dir_, uid_, ok_gids_));
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ base_dir_, text_file_, uid_, ok_gids_));
+ EXPECT_TRUE(
+ file_util::VerifyPathControlledByUser(
+ sub_dir_, text_file_, uid_, ok_gids_));
+}
+#endif
+
+#endif // defined(OS_POSIX)
+
+} // namespace
diff --git a/src/base/file_util_win.cc b/src/base/file_util_win.cc
new file mode 100644
index 0000000..ef51594
--- /dev/null
+++ b/src/base/file_util_win.cc
@@ -0,0 +1,977 @@
+// Copyright (c) 2012 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/file_util.h"
+
+#include <windows.h>
+#include <psapi.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#include <time.h>
+
+#include <limits>
+#include <string>
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/process_util.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+
+namespace file_util {
+
+namespace {
+
+const DWORD kFileShareAll =
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+
+} // namespace
+
+bool AbsolutePath(FilePath* path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ wchar_t file_path_buf[MAX_PATH];
+ if (!_wfullpath(file_path_buf, path->value().c_str(), MAX_PATH))
+ return false;
+ *path = FilePath(file_path_buf);
+ return true;
+}
+
+int CountFilesCreatedAfter(const FilePath& path,
+ const base::Time& comparison_time) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ int file_count = 0;
+ FILETIME comparison_filetime(comparison_time.ToFileTime());
+
+ WIN32_FIND_DATA find_file_data;
+ // All files in given dir
+ std::wstring filename_spec = path.Append(L"*").value();
+ HANDLE find_handle = FindFirstFile(filename_spec.c_str(), &find_file_data);
+ if (find_handle != INVALID_HANDLE_VALUE) {
+ do {
+ // Don't count current or parent directories.
+ if ((wcscmp(find_file_data.cFileName, L"..") == 0) ||
+ (wcscmp(find_file_data.cFileName, L".") == 0))
+ continue;
+
+ long result = CompareFileTime(&find_file_data.ftCreationTime, // NOLINT
+ &comparison_filetime);
+ // File was created after or on comparison time
+ if ((result == 1) || (result == 0))
+ ++file_count;
+ } while (FindNextFile(find_handle, &find_file_data));
+ FindClose(find_handle);
+ }
+
+ return file_count;
+}
+
+bool Delete(const FilePath& path, bool recursive) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ if (path.value().length() >= MAX_PATH)
+ return false;
+
+ if (!recursive) {
+ // If not recursing, then first check to see if |path| is a directory.
+ // If it is, then remove it with RemoveDirectory.
+ base::PlatformFileInfo file_info;
+ if (GetFileInfo(path, &file_info) && file_info.is_directory)
+ return RemoveDirectory(path.value().c_str()) != 0;
+
+ // Otherwise, it's a file, wildcard or non-existant. Try DeleteFile first
+ // because it should be faster. If DeleteFile fails, then we fall through
+ // to SHFileOperation, which will do the right thing.
+ if (DeleteFile(path.value().c_str()) != 0)
+ return true;
+ }
+
+ // SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
+ // so we have to use wcscpy because wcscpy_s writes non-NULLs
+ // into the rest of the buffer.
+ wchar_t double_terminated_path[MAX_PATH + 1] = {0};
+#pragma warning(suppress:4996) // don't complain about wcscpy deprecation
+ if (g_bug108724_debug)
+ LOG(WARNING) << "copying ";
+ wcscpy(double_terminated_path, path.value().c_str());
+
+ SHFILEOPSTRUCT file_operation = {0};
+ file_operation.wFunc = FO_DELETE;
+ file_operation.pFrom = double_terminated_path;
+ file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION;
+ if (!recursive)
+ file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
+ if (g_bug108724_debug)
+ LOG(WARNING) << "Performing shell operation";
+ int err = SHFileOperation(&file_operation);
+ if (g_bug108724_debug)
+ LOG(WARNING) << "Done: " << err;
+
+ // Since we're passing flags to the operation telling it to be silent,
+ // it's possible for the operation to be aborted/cancelled without err
+ // being set (although MSDN doesn't give any scenarios for how this can
+ // happen). See MSDN for SHFileOperation and SHFILEOPTSTRUCT.
+ if (file_operation.fAnyOperationsAborted)
+ return false;
+
+ // Some versions of Windows return ERROR_FILE_NOT_FOUND (0x2) when deleting
+ // an empty directory and some return 0x402 when they should be returning
+ // ERROR_FILE_NOT_FOUND. MSDN says Vista and up won't return 0x402.
+ return (err == 0 || err == ERROR_FILE_NOT_FOUND || err == 0x402);
+}
+
+bool DeleteAfterReboot(const FilePath& path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ if (path.value().length() >= MAX_PATH)
+ return false;
+
+ return MoveFileEx(path.value().c_str(), NULL,
+ MOVEFILE_DELAY_UNTIL_REBOOT |
+ MOVEFILE_REPLACE_EXISTING) != FALSE;
+}
+
+bool Move(const FilePath& from_path, const FilePath& to_path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ // NOTE: I suspect we could support longer paths, but that would involve
+ // analyzing all our usage of files.
+ if (from_path.value().length() >= MAX_PATH ||
+ to_path.value().length() >= MAX_PATH) {
+ return false;
+ }
+ if (MoveFileEx(from_path.value().c_str(), to_path.value().c_str(),
+ MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0)
+ return true;
+
+ // Keep the last error value from MoveFileEx around in case the below
+ // fails.
+ bool ret = false;
+ DWORD last_error = ::GetLastError();
+
+ if (DirectoryExists(from_path)) {
+ // MoveFileEx fails if moving directory across volumes. We will simulate
+ // the move by using Copy and Delete. Ideally we could check whether
+ // from_path and to_path are indeed in different volumes.
+ ret = CopyAndDeleteDirectory(from_path, to_path);
+ }
+
+ if (!ret) {
+ // Leave a clue about what went wrong so that it can be (at least) picked
+ // up by a PLOG entry.
+ ::SetLastError(last_error);
+ }
+
+ return ret;
+}
+
+bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ // Try a simple move first. It will only succeed when |to_path| doesn't
+ // already exist.
+ if (::MoveFile(from_path.value().c_str(), to_path.value().c_str()))
+ return true;
+ // Try the full-blown replace if the move fails, as ReplaceFile will only
+ // succeed when |to_path| does exist. When writing to a network share, we may
+ // not be able to change the ACLs. Ignore ACL errors then
+ // (REPLACEFILE_IGNORE_MERGE_ERRORS).
+ if (::ReplaceFile(to_path.value().c_str(), from_path.value().c_str(), NULL,
+ REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL)) {
+ return true;
+ }
+ return false;
+}
+
+bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ // NOTE: I suspect we could support longer paths, but that would involve
+ // analyzing all our usage of files.
+ if (from_path.value().length() >= MAX_PATH ||
+ to_path.value().length() >= MAX_PATH) {
+ return false;
+ }
+ return (::CopyFile(from_path.value().c_str(), to_path.value().c_str(),
+ false) != 0);
+}
+
+bool ShellCopy(const FilePath& from_path, const FilePath& to_path,
+ bool recursive) {
+ // WinXP SHFileOperation doesn't like trailing separators.
+ FilePath stripped_from = from_path.StripTrailingSeparators();
+ FilePath stripped_to = to_path.StripTrailingSeparators();
+
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ // NOTE: I suspect we could support longer paths, but that would involve
+ // analyzing all our usage of files.
+ if (stripped_from.value().length() >= MAX_PATH ||
+ stripped_to.value().length() >= MAX_PATH) {
+ return false;
+ }
+
+ // SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
+ // so we have to use wcscpy because wcscpy_s writes non-NULLs
+ // into the rest of the buffer.
+ wchar_t double_terminated_path_from[MAX_PATH + 1] = {0};
+ wchar_t double_terminated_path_to[MAX_PATH + 1] = {0};
+#pragma warning(suppress:4996) // don't complain about wcscpy deprecation
+ wcscpy(double_terminated_path_from, stripped_from.value().c_str());
+#pragma warning(suppress:4996) // don't complain about wcscpy deprecation
+ wcscpy(double_terminated_path_to, stripped_to.value().c_str());
+
+ SHFILEOPSTRUCT file_operation = {0};
+ file_operation.wFunc = FO_COPY;
+ file_operation.pFrom = double_terminated_path_from;
+ file_operation.pTo = double_terminated_path_to;
+ file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION |
+ FOF_NOCONFIRMMKDIR;
+ if (!recursive)
+ file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
+
+ return (SHFileOperation(&file_operation) == 0);
+}
+
+bool CopyDirectory(const FilePath& from_path, const FilePath& to_path,
+ bool recursive) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ if (recursive)
+ return ShellCopy(from_path, to_path, true);
+
+ // The following code assumes that from path is a directory.
+ DCHECK(DirectoryExists(from_path));
+
+ // Instead of creating a new directory, we copy the old one to include the
+ // security information of the folder as part of the copy.
+ if (!PathExists(to_path)) {
+ // Except that Vista fails to do that, and instead do a recursive copy if
+ // the target directory doesn't exist.
+ if (base::win::GetVersion() >= base::win::VERSION_VISTA)
+ CreateDirectory(to_path);
+ else
+ ShellCopy(from_path, to_path, false);
+ }
+
+ FilePath directory = from_path.Append(L"*.*");
+ return ShellCopy(directory, to_path, false);
+}
+
+bool CopyAndDeleteDirectory(const FilePath& from_path,
+ const FilePath& to_path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (CopyDirectory(from_path, to_path, true)) {
+ if (Delete(from_path, true)) {
+ return true;
+ }
+ // Like Move, this function is not transactional, so we just
+ // leave the copied bits behind if deleting from_path fails.
+ // If to_path exists previously then we have already overwritten
+ // it by now, we don't get better off by deleting the new bits.
+ }
+ return false;
+}
+
+
+bool PathExists(const FilePath& path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES);
+}
+
+bool PathIsWritable(const FilePath& path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ HANDLE dir =
+ CreateFile(path.value().c_str(), FILE_ADD_FILE, kFileShareAll,
+ NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+
+ if (dir == INVALID_HANDLE_VALUE)
+ return false;
+
+ CloseHandle(dir);
+ return true;
+}
+
+bool DirectoryExists(const FilePath& path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DWORD fileattr = GetFileAttributes(path.value().c_str());
+ if (fileattr != INVALID_FILE_ATTRIBUTES)
+ return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ return false;
+}
+
+bool GetFileCreationLocalTimeFromHandle(HANDLE file_handle,
+ LPSYSTEMTIME creation_time) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (!file_handle)
+ return false;
+
+ FILETIME utc_filetime;
+ if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
+ return false;
+
+ FILETIME local_filetime;
+ if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
+ return false;
+
+ return !!FileTimeToSystemTime(&local_filetime, creation_time);
+}
+
+bool GetFileCreationLocalTime(const std::wstring& filename,
+ LPSYSTEMTIME creation_time) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ base::win::ScopedHandle file_handle(
+ CreateFile(filename.c_str(), GENERIC_READ, kFileShareAll, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
+ return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
+}
+
+bool GetTempDir(FilePath* path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ wchar_t temp_path[MAX_PATH + 1];
+ DWORD path_len = ::GetTempPath(MAX_PATH, temp_path);
+ if (path_len >= MAX_PATH || path_len <= 0)
+ return false;
+ // TODO(evanm): the old behavior of this function was to always strip the
+ // trailing slash. We duplicate this here, but it shouldn't be necessary
+ // when everyone is using the appropriate FilePath APIs.
+ *path = FilePath(temp_path).StripTrailingSeparators();
+ return true;
+}
+
+bool GetShmemTempDir(FilePath* path, bool executable) {
+ return GetTempDir(path);
+}
+
+bool CreateTemporaryFile(FilePath* path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ FilePath temp_file;
+
+ if (!GetTempDir(path))
+ return false;
+
+ if (CreateTemporaryFileInDir(*path, &temp_file)) {
+ *path = temp_file;
+ return true;
+ }
+
+ return false;
+}
+
+FILE* CreateAndOpenTemporaryShmemFile(FilePath* path, bool executable) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return CreateAndOpenTemporaryFile(path);
+}
+
+// On POSIX we have semantics to create and open a temporary file
+// atomically.
+// TODO(jrg): is there equivalent call to use on Windows instead of
+// going 2-step?
+FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (!CreateTemporaryFileInDir(dir, path)) {
+ return NULL;
+ }
+ // Open file in binary mode, to avoid problems with fwrite. On Windows
+ // it replaces \n's with \r\n's, which may surprise you.
+ // Reference: http://msdn.microsoft.com/en-us/library/h9t88zwz(VS.71).aspx
+ return OpenFile(*path, "wb+");
+}
+
+bool CreateTemporaryFileInDir(const FilePath& dir,
+ FilePath* temp_file) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ wchar_t temp_name[MAX_PATH + 1];
+
+ if (!GetTempFileName(dir.value().c_str(), L"", 0, temp_name)) {
+ DPLOG(WARNING) << "Failed to get temporary file name in " << dir.value();
+ return false;
+ }
+
+ wchar_t long_temp_name[MAX_PATH + 1];
+ DWORD long_name_len = GetLongPathName(temp_name, long_temp_name, MAX_PATH);
+ if (long_name_len > MAX_PATH || long_name_len == 0) {
+ // GetLongPathName() failed, but we still have a temporary file.
+ *temp_file = FilePath(temp_name);
+ return true;
+ }
+
+ FilePath::StringType long_temp_name_str;
+ long_temp_name_str.assign(long_temp_name, long_name_len);
+ *temp_file = FilePath(long_temp_name_str);
+ return true;
+}
+
+bool CreateTemporaryDirInDir(const FilePath& base_dir,
+ const FilePath::StringType& prefix,
+ FilePath* new_dir) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ FilePath path_to_create;
+ srand(static_cast<uint32>(time(NULL)));
+
+ for (int count = 0; count < 50; ++count) {
+ // Try create a new temporary directory with random generated name. If
+ // the one exists, keep trying another path name until we reach some limit.
+ string16 new_dir_name;
+ new_dir_name.assign(prefix);
+ new_dir_name.append(base::IntToString16(::base::GetCurrentProcId()));
+ new_dir_name.push_back('_');
+ new_dir_name.append(base::IntToString16(rand() % kint16max));
+
+ path_to_create = base_dir.Append(new_dir_name);
+ if (::CreateDirectory(path_to_create.value().c_str(), NULL)) {
+ *new_dir = path_to_create;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool CreateNewTempDirectory(const FilePath::StringType& prefix,
+ FilePath* new_temp_path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ FilePath system_temp_dir;
+ if (!GetTempDir(&system_temp_dir))
+ return false;
+
+ return CreateTemporaryDirInDir(system_temp_dir, prefix, new_temp_path);
+}
+
+bool CreateDirectory(const FilePath& full_path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ // If the path exists, we've succeeded if it's a directory, failed otherwise.
+ const wchar_t* full_path_str = full_path.value().c_str();
+ DWORD fileattr = ::GetFileAttributes(full_path_str);
+ if (fileattr != INVALID_FILE_ATTRIBUTES) {
+ if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+ DVLOG(1) << "CreateDirectory(" << full_path_str << "), "
+ << "directory already exists.";
+ return true;
+ }
+ DLOG(WARNING) << "CreateDirectory(" << full_path_str << "), "
+ << "conflicts with existing file.";
+ return false;
+ }
+
+ // Invariant: Path does not exist as file or directory.
+
+ // Attempt to create the parent recursively. This will immediately return
+ // true if it already exists, otherwise will create all required parent
+ // directories starting with the highest-level missing parent.
+ FilePath parent_path(full_path.DirName());
+ if (parent_path.value() == full_path.value()) {
+ return false;
+ }
+ if (!CreateDirectory(parent_path)) {
+ DLOG(WARNING) << "Failed to create one of the parent directories.";
+ return false;
+ }
+
+ if (!::CreateDirectory(full_path_str, NULL)) {
+ DWORD error_code = ::GetLastError();
+ if (error_code == ERROR_ALREADY_EXISTS && DirectoryExists(full_path)) {
+ // This error code ERROR_ALREADY_EXISTS doesn't indicate whether we
+ // were racing with someone creating the same directory, or a file
+ // with the same path. If DirectoryExists() returns true, we lost the
+ // race to create the same directory.
+ return true;
+ } else {
+ DLOG(WARNING) << "Failed to create directory " << full_path_str
+ << ", last error is " << error_code << ".";
+ return false;
+ }
+ } else {
+ return true;
+ }
+}
+
+// TODO(rkc): Work out if we want to handle NTFS junctions here or not, handle
+// them if we do decide to.
+bool IsLink(const FilePath& file_path) {
+ return false;
+}
+
+bool GetFileInfo(const FilePath& file_path, base::PlatformFileInfo* results) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ WIN32_FILE_ATTRIBUTE_DATA attr;
+ if (!GetFileAttributesEx(file_path.value().c_str(),
+ GetFileExInfoStandard, &attr)) {
+ return false;
+ }
+
+ ULARGE_INTEGER size;
+ size.HighPart = attr.nFileSizeHigh;
+ size.LowPart = attr.nFileSizeLow;
+ results->size = size.QuadPart;
+
+ results->is_directory =
+ (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ results->last_modified = base::Time::FromFileTime(attr.ftLastWriteTime);
+ results->last_accessed = base::Time::FromFileTime(attr.ftLastAccessTime);
+ results->creation_time = base::Time::FromFileTime(attr.ftCreationTime);
+
+ return true;
+}
+
+FILE* OpenFile(const FilePath& filename, const char* mode) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ std::wstring w_mode = ASCIIToWide(std::string(mode));
+ return _wfsopen(filename.value().c_str(), w_mode.c_str(), _SH_DENYNO);
+}
+
+FILE* OpenFile(const std::string& filename, const char* mode) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return _fsopen(filename.c_str(), mode, _SH_DENYNO);
+}
+
+int ReadFile(const FilePath& filename, char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ base::win::ScopedHandle file(CreateFile(filename.value().c_str(),
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_SEQUENTIAL_SCAN,
+ NULL));
+ if (!file)
+ return -1;
+
+ DWORD read;
+ if (::ReadFile(file, data, size, &read, NULL) &&
+ static_cast<int>(read) == size)
+ return read;
+ return -1;
+}
+
+int WriteFile(const FilePath& filename, const char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ base::win::ScopedHandle file(CreateFile(filename.value().c_str(),
+ GENERIC_WRITE,
+ 0,
+ NULL,
+ CREATE_ALWAYS,
+ 0,
+ NULL));
+ if (!file) {
+ DLOG(WARNING) << "CreateFile failed for path " << filename.value()
+ << " error code=" << GetLastError();
+ return -1;
+ }
+
+ DWORD written;
+ BOOL result = ::WriteFile(file, data, size, &written, NULL);
+ if (result && static_cast<int>(written) == size)
+ return written;
+
+ if (!result) {
+ // WriteFile failed.
+ DLOG(WARNING) << "writing file " << filename.value()
+ << " failed, error code=" << GetLastError();
+ } else {
+ // Didn't write all the bytes.
+ DLOG(WARNING) << "wrote" << written << " bytes to "
+ << filename.value() << " expected " << size;
+ }
+ return -1;
+}
+
+int AppendToFile(const FilePath& filename, const char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ base::win::ScopedHandle file(CreateFile(filename.value().c_str(),
+ FILE_APPEND_DATA,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL));
+ if (!file) {
+ DLOG(WARNING) << "CreateFile failed for path " << filename.value()
+ << " error code=" << GetLastError();
+ return -1;
+ }
+
+ DWORD written;
+ BOOL result = ::WriteFile(file, data, size, &written, NULL);
+ if (result && static_cast<int>(written) == size)
+ return written;
+
+ if (!result) {
+ // WriteFile failed.
+ DLOG(WARNING) << "writing file " << filename.value()
+ << " failed, error code=" << GetLastError();
+ } else {
+ // Didn't write all the bytes.
+ DLOG(WARNING) << "wrote" << written << " bytes to "
+ << filename.value() << " expected " << size;
+ }
+ return -1;
+}
+
+// Gets the current working directory for the process.
+bool GetCurrentDirectory(FilePath* dir) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ wchar_t system_buffer[MAX_PATH];
+ system_buffer[0] = 0;
+ DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer);
+ if (len == 0 || len > MAX_PATH)
+ return false;
+ // TODO(evanm): the old behavior of this function was to always strip the
+ // trailing slash. We duplicate this here, but it shouldn't be necessary
+ // when everyone is using the appropriate FilePath APIs.
+ std::wstring dir_str(system_buffer);
+ *dir = FilePath(dir_str).StripTrailingSeparators();
+ return true;
+}
+
+// Sets the current working directory for the process.
+bool SetCurrentDirectory(const FilePath& directory) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ BOOL ret = ::SetCurrentDirectory(directory.value().c_str());
+ return ret != 0;
+}
+
+///////////////////////////////////////////////
+// FileEnumerator
+
+FileEnumerator::FileEnumerator(const FilePath& root_path,
+ bool recursive,
+ int file_type)
+ : recursive_(recursive),
+ file_type_(file_type),
+ has_find_data_(false),
+ find_handle_(INVALID_HANDLE_VALUE) {
+ // INCLUDE_DOT_DOT must not be specified if recursive.
+ DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
+ memset(&find_data_, 0, sizeof(find_data_));
+ pending_paths_.push(root_path);
+}
+
+FileEnumerator::FileEnumerator(const FilePath& root_path,
+ bool recursive,
+ int file_type,
+ const FilePath::StringType& pattern)
+ : recursive_(recursive),
+ file_type_(file_type),
+ has_find_data_(false),
+ pattern_(pattern),
+ find_handle_(INVALID_HANDLE_VALUE) {
+ // INCLUDE_DOT_DOT must not be specified if recursive.
+ DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
+ memset(&find_data_, 0, sizeof(find_data_));
+ pending_paths_.push(root_path);
+}
+
+FileEnumerator::~FileEnumerator() {
+ if (find_handle_ != INVALID_HANDLE_VALUE)
+ FindClose(find_handle_);
+}
+
+void FileEnumerator::GetFindInfo(FindInfo* info) {
+ DCHECK(info);
+
+ if (!has_find_data_)
+ return;
+
+ memcpy(info, &find_data_, sizeof(*info));
+}
+
+// static
+bool FileEnumerator::IsDirectory(const FindInfo& info) {
+ return (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+}
+
+// static
+FilePath FileEnumerator::GetFilename(const FindInfo& find_info) {
+ return FilePath(find_info.cFileName);
+}
+
+// static
+int64 FileEnumerator::GetFilesize(const FindInfo& find_info) {
+ ULARGE_INTEGER size;
+ size.HighPart = find_info.nFileSizeHigh;
+ size.LowPart = find_info.nFileSizeLow;
+ DCHECK_LE(size.QuadPart, std::numeric_limits<int64>::max());
+ return static_cast<int64>(size.QuadPart);
+}
+
+// static
+base::Time FileEnumerator::GetLastModifiedTime(const FindInfo& find_info) {
+ return base::Time::FromFileTime(find_info.ftLastWriteTime);
+}
+
+FilePath FileEnumerator::Next() {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ while (has_find_data_ || !pending_paths_.empty()) {
+ if (!has_find_data_) {
+ // The last find FindFirstFile operation is done, prepare a new one.
+ root_path_ = pending_paths_.top();
+ pending_paths_.pop();
+
+ // Start a new find operation.
+ FilePath src = root_path_;
+
+ if (pattern_.empty())
+ src = src.Append(L"*"); // No pattern = match everything.
+ else
+ src = src.Append(pattern_);
+
+ find_handle_ = FindFirstFile(src.value().c_str(), &find_data_);
+ has_find_data_ = true;
+ } else {
+ // Search for the next file/directory.
+ if (!FindNextFile(find_handle_, &find_data_)) {
+ FindClose(find_handle_);
+ find_handle_ = INVALID_HANDLE_VALUE;
+ }
+ }
+
+ if (INVALID_HANDLE_VALUE == find_handle_) {
+ has_find_data_ = false;
+
+ // This is reached when we have finished a directory and are advancing to
+ // the next one in the queue. We applied the pattern (if any) to the files
+ // in the root search directory, but for those directories which were
+ // matched, we want to enumerate all files inside them. This will happen
+ // when the handle is empty.
+ pattern_ = FilePath::StringType();
+
+ continue;
+ }
+
+ FilePath cur_file(find_data_.cFileName);
+ if (ShouldSkip(cur_file))
+ continue;
+
+ // Construct the absolute filename.
+ cur_file = root_path_.Append(find_data_.cFileName);
+
+ if (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ if (recursive_) {
+ // If |cur_file| is a directory, and we are doing recursive searching,
+ // add it to pending_paths_ so we scan it after we finish scanning this
+ // directory.
+ pending_paths_.push(cur_file);
+ }
+ if (file_type_ & FileEnumerator::DIRECTORIES)
+ return cur_file;
+ } else if (file_type_ & FileEnumerator::FILES) {
+ return cur_file;
+ }
+ }
+
+ return FilePath();
+}
+
+///////////////////////////////////////////////
+// MemoryMappedFile
+
+MemoryMappedFile::MemoryMappedFile()
+ : file_(INVALID_HANDLE_VALUE),
+ file_mapping_(INVALID_HANDLE_VALUE),
+ data_(NULL),
+ length_(INVALID_FILE_SIZE) {
+}
+
+bool MemoryMappedFile::InitializeAsImageSection(const FilePath& file_name) {
+ if (IsValid())
+ return false;
+ file_ = base::CreatePlatformFile(
+ file_name, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
+ NULL, NULL);
+
+ if (file_ == base::kInvalidPlatformFileValue) {
+ DLOG(ERROR) << "Couldn't open " << file_name.value();
+ return false;
+ }
+
+ if (!MapFileToMemoryInternalEx(SEC_IMAGE)) {
+ CloseHandles();
+ return false;
+ }
+
+ return true;
+}
+
+bool MemoryMappedFile::MapFileToMemoryInternal() {
+ return MapFileToMemoryInternalEx(0);
+}
+
+bool MemoryMappedFile::MapFileToMemoryInternalEx(int flags) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ if (file_ == INVALID_HANDLE_VALUE)
+ return false;
+
+ length_ = ::GetFileSize(file_, NULL);
+ if (length_ == INVALID_FILE_SIZE)
+ return false;
+
+ file_mapping_ = ::CreateFileMapping(file_, NULL, PAGE_READONLY | flags,
+ 0, 0, NULL);
+ if (!file_mapping_) {
+ // According to msdn, system error codes are only reserved up to 15999.
+ // http://msdn.microsoft.com/en-us/library/ms681381(v=VS.85).aspx.
+ UMA_HISTOGRAM_ENUMERATION("MemoryMappedFile.CreateFileMapping",
+ logging::GetLastSystemErrorCode(), 16000);
+ return false;
+ }
+
+ data_ = static_cast<uint8*>(
+ ::MapViewOfFile(file_mapping_, FILE_MAP_READ, 0, 0, 0));
+ if (!data_) {
+ UMA_HISTOGRAM_ENUMERATION("MemoryMappedFile.MapViewOfFile",
+ logging::GetLastSystemErrorCode(), 16000);
+ }
+ return data_ != NULL;
+}
+
+void MemoryMappedFile::CloseHandles() {
+ if (data_)
+ ::UnmapViewOfFile(data_);
+ if (file_mapping_ != INVALID_HANDLE_VALUE)
+ ::CloseHandle(file_mapping_);
+ if (file_ != INVALID_HANDLE_VALUE)
+ ::CloseHandle(file_);
+
+ data_ = NULL;
+ file_mapping_ = file_ = INVALID_HANDLE_VALUE;
+ length_ = INVALID_FILE_SIZE;
+}
+
+bool HasFileBeenModifiedSince(const FileEnumerator::FindInfo& find_info,
+ const base::Time& cutoff_time) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ FILETIME file_time = cutoff_time.ToFileTime();
+ long result = CompareFileTime(&find_info.ftLastWriteTime, // NOLINT
+ &file_time);
+ return result == 1 || result == 0;
+}
+
+bool NormalizeFilePath(const FilePath& path, FilePath* real_path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ FilePath mapped_file;
+ if (!NormalizeToNativeFilePath(path, &mapped_file))
+ return false;
+ // NormalizeToNativeFilePath() will return a path that starts with
+ // "\Device\Harddisk...". Helper DevicePathToDriveLetterPath()
+ // will find a drive letter which maps to the path's device, so
+ // that we return a path starting with a drive letter.
+ return DevicePathToDriveLetterPath(mapped_file, real_path);
+}
+
+bool DevicePathToDriveLetterPath(const FilePath& nt_device_path,
+ FilePath* out_drive_letter_path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ // Get the mapping of drive letters to device paths.
+ const int kDriveMappingSize = 1024;
+ wchar_t drive_mapping[kDriveMappingSize] = {'\0'};
+ if (!::GetLogicalDriveStrings(kDriveMappingSize - 1, drive_mapping)) {
+ DLOG(ERROR) << "Failed to get drive mapping.";
+ return false;
+ }
+
+ // The drive mapping is a sequence of null terminated strings.
+ // The last string is empty.
+ wchar_t* drive_map_ptr = drive_mapping;
+ wchar_t device_path_as_string[MAX_PATH];
+ wchar_t drive[] = L" :";
+
+ // For each string in the drive mapping, get the junction that links
+ // to it. If that junction is a prefix of |device_path|, then we
+ // know that |drive| is the real path prefix.
+ while (*drive_map_ptr) {
+ drive[0] = drive_map_ptr[0]; // Copy the drive letter.
+
+ if (QueryDosDevice(drive, device_path_as_string, MAX_PATH)) {
+ FilePath device_path(device_path_as_string);
+ if (device_path == nt_device_path ||
+ device_path.IsParent(nt_device_path)) {
+ *out_drive_letter_path = FilePath(drive +
+ nt_device_path.value().substr(wcslen(device_path_as_string)));
+ return true;
+ }
+ }
+ // Move to the next drive letter string, which starts one
+ // increment after the '\0' that terminates the current string.
+ while (*drive_map_ptr++);
+ }
+
+ // No drive matched. The path does not start with a device junction
+ // that is mounted as a drive letter. This means there is no drive
+ // letter path to the volume that holds |device_path|, so fail.
+ return false;
+}
+
+bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ // In Vista, GetFinalPathNameByHandle() would give us the real path
+ // from a file handle. If we ever deprecate XP, consider changing the
+ // code below to a call to GetFinalPathNameByHandle(). The method this
+ // function uses is explained in the following msdn article:
+ // http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
+ base::win::ScopedHandle file_handle(
+ ::CreateFile(path.value().c_str(),
+ GENERIC_READ,
+ kFileShareAll,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL));
+ if (!file_handle)
+ return false;
+
+ // Create a file mapping object. Can't easily use MemoryMappedFile, because
+ // we only map the first byte, and need direct access to the handle. You can
+ // not map an empty file, this call fails in that case.
+ base::win::ScopedHandle file_map_handle(
+ ::CreateFileMapping(file_handle.Get(),
+ NULL,
+ PAGE_READONLY,
+ 0,
+ 1, // Just one byte. No need to look at the data.
+ NULL));
+ if (!file_map_handle)
+ return false;
+
+ // Use a view of the file to get the path to the file.
+ void* file_view = MapViewOfFile(file_map_handle.Get(),
+ FILE_MAP_READ, 0, 0, 1);
+ if (!file_view)
+ return false;
+
+ // The expansion of |path| into a full path may make it longer.
+ // GetMappedFileName() will fail if the result is longer than MAX_PATH.
+ // Pad a bit to be safe. If kMaxPathLength is ever changed to be less
+ // than MAX_PATH, it would be nessisary to test that GetMappedFileName()
+ // not return kMaxPathLength. This would mean that only part of the
+ // path fit in |mapped_file_path|.
+ const int kMaxPathLength = MAX_PATH + 10;
+ wchar_t mapped_file_path[kMaxPathLength];
+ bool success = false;
+ HANDLE cp = GetCurrentProcess();
+ if (::GetMappedFileNameW(cp, file_view, mapped_file_path, kMaxPathLength)) {
+ *nt_path = FilePath(mapped_file_path);
+ success = true;
+ }
+ ::UnmapViewOfFile(file_view);
+ return success;
+}
+
+} // namespace file_util
diff --git a/src/base/file_version_info.h b/src/base/file_version_info.h
new file mode 100644
index 0000000..84eec41
--- /dev/null
+++ b/src/base/file_version_info.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_FILE_VERSION_INFO_H__
+#define BASE_FILE_VERSION_INFO_H__
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+// http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
+extern "C" IMAGE_DOS_HEADER __ImageBase;
+#endif // OS_WIN
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/string16.h"
+
+class FilePath;
+
+// Provides an interface for accessing the version information for a file. This
+// is the information you access when you select a file in the Windows Explorer,
+// right-click select Properties, then click the Version tab, and on the Mac
+// when you select a file in the Finder and do a Get Info.
+//
+// This list of properties is straight out of Win32's VerQueryValue
+// <http://msdn.microsoft.com/en-us/library/ms647464.aspx> and the Mac
+// version returns values from the Info.plist as appropriate. TODO(avi): make
+// this a less-obvious Windows-ism.
+
+class FileVersionInfo {
+ public:
+ virtual ~FileVersionInfo() {}
+#if defined(OS_WIN) || defined(OS_MACOSX)
+ // Creates a FileVersionInfo for the specified path. Returns NULL if something
+ // goes wrong (typically the file does not exit or cannot be opened). The
+ // returned object should be deleted when you are done with it.
+ BASE_EXPORT static FileVersionInfo* CreateFileVersionInfo(
+ const FilePath& file_path);
+#endif // OS_WIN || OS_MACOSX
+
+#if defined(OS_WIN)
+ // Creates a FileVersionInfo for the specified module. Returns NULL in case
+ // of error. The returned object should be deleted when you are done with it.
+ BASE_EXPORT static FileVersionInfo* CreateFileVersionInfoForModule(
+ HMODULE module);
+
+ // Creates a FileVersionInfo for the current module. Returns NULL in case
+ // of error. The returned object should be deleted when you are done with it.
+ // This function should be inlined so that the "current module" is evaluated
+ // correctly, instead of being the module that contains base.
+ __forceinline static FileVersionInfo*
+ CreateFileVersionInfoForCurrentModule() {
+ HMODULE module = reinterpret_cast<HMODULE>(&__ImageBase);
+ return CreateFileVersionInfoForModule(module);
+ }
+#else
+ // Creates a FileVersionInfo for the current module. Returns NULL in case
+ // of error. The returned object should be deleted when you are done with it.
+ BASE_EXPORT static FileVersionInfo* CreateFileVersionInfoForCurrentModule();
+#endif // OS_WIN
+
+ // Accessors to the different version properties.
+ // Returns an empty string if the property is not found.
+ virtual string16 company_name() = 0;
+ virtual string16 company_short_name() = 0;
+ virtual string16 product_name() = 0;
+ virtual string16 product_short_name() = 0;
+ virtual string16 internal_name() = 0;
+ virtual string16 product_version() = 0;
+ virtual string16 private_build() = 0;
+ virtual string16 special_build() = 0;
+ virtual string16 comments() = 0;
+ virtual string16 original_filename() = 0;
+ virtual string16 file_description() = 0;
+ virtual string16 file_version() = 0;
+ virtual string16 legal_copyright() = 0;
+ virtual string16 legal_trademarks() = 0;
+ virtual string16 last_change() = 0;
+ virtual bool is_official_build() = 0;
+};
+
+#endif // BASE_FILE_VERSION_INFO_H__
diff --git a/src/base/file_version_info_mac.h b/src/base/file_version_info_mac.h
new file mode 100644
index 0000000..0ab3296
--- /dev/null
+++ b/src/base/file_version_info_mac.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_FILE_VERSION_INFO_MAC_H_
+#define BASE_FILE_VERSION_INFO_MAC_H_
+
+#include <string>
+
+#include "base/file_version_info.h"
+#include "base/memory/scoped_nsobject.h"
+
+#ifdef __OBJC__
+@class NSBundle;
+#else
+class NSBundle;
+#endif
+
+class FileVersionInfoMac : public FileVersionInfo {
+ public:
+ explicit FileVersionInfoMac(NSBundle *bundle);
+ virtual ~FileVersionInfoMac();
+
+ // Accessors to the different version properties.
+ // Returns an empty string if the property is not found.
+ virtual string16 company_name() OVERRIDE;
+ virtual string16 company_short_name() OVERRIDE;
+ virtual string16 product_name() OVERRIDE;
+ virtual string16 product_short_name() OVERRIDE;
+ virtual string16 internal_name() OVERRIDE;
+ virtual string16 product_version() OVERRIDE;
+ virtual string16 private_build() OVERRIDE;
+ virtual string16 special_build() OVERRIDE;
+ virtual string16 comments() OVERRIDE;
+ virtual string16 original_filename() OVERRIDE;
+ virtual string16 file_description() OVERRIDE;
+ virtual string16 file_version() OVERRIDE;
+ virtual string16 legal_copyright() OVERRIDE;
+ virtual string16 legal_trademarks() OVERRIDE;
+ virtual string16 last_change() OVERRIDE;
+ virtual bool is_official_build() OVERRIDE;
+
+ private:
+ // Returns a string16 value for a property name.
+ // Returns the empty string if the property does not exist.
+ string16 GetString16Value(CFStringRef name);
+
+ scoped_nsobject<NSBundle> bundle_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileVersionInfoMac);
+};
+
+#endif // BASE_FILE_VERSION_INFO_MAC_H_
diff --git a/src/base/file_version_info_mac.mm b/src/base/file_version_info_mac.mm
new file mode 100644
index 0000000..3a6df81
--- /dev/null
+++ b/src/base/file_version_info_mac.mm
@@ -0,0 +1,120 @@
+// Copyright (c) 2012 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/file_version_info_mac.h"
+
+#import <Foundation/Foundation.h>
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/mac/bundle_locations.h"
+#include "base/mac/foundation_util.h"
+#include "base/sys_string_conversions.h"
+
+FileVersionInfoMac::FileVersionInfoMac(NSBundle *bundle)
+ : bundle_([bundle retain]) {
+}
+
+FileVersionInfoMac::~FileVersionInfoMac() {}
+
+// static
+FileVersionInfo* FileVersionInfo::CreateFileVersionInfoForCurrentModule() {
+ return CreateFileVersionInfo(base::mac::FrameworkBundlePath());
+}
+
+// static
+FileVersionInfo* FileVersionInfo::CreateFileVersionInfo(
+ const FilePath& file_path) {
+ NSString* path = base::SysUTF8ToNSString(file_path.value());
+ NSBundle* bundle = [NSBundle bundleWithPath:path];
+ return new FileVersionInfoMac(bundle);
+}
+
+string16 FileVersionInfoMac::company_name() {
+ return string16();
+}
+
+string16 FileVersionInfoMac::company_short_name() {
+ return string16();
+}
+
+string16 FileVersionInfoMac::internal_name() {
+ return string16();
+}
+
+string16 FileVersionInfoMac::product_name() {
+ return GetString16Value(kCFBundleNameKey);
+}
+
+string16 FileVersionInfoMac::product_short_name() {
+ return GetString16Value(kCFBundleNameKey);
+}
+
+string16 FileVersionInfoMac::comments() {
+ return string16();
+}
+
+string16 FileVersionInfoMac::legal_copyright() {
+ return GetString16Value(CFSTR("CFBundleGetInfoString"));
+}
+
+string16 FileVersionInfoMac::product_version() {
+ // On OS X, CFBundleVersion is used by LaunchServices, and must follow
+ // specific formatting rules, so the four-part Chrome version is in
+ // CFBundleShortVersionString. On iOS, however, CFBundleVersion can be the
+ // full version, but CFBundleShortVersionString has a policy-enfoced limit
+ // of three version components.
+#if defined(OS_IOS)
+ return GetString16Value(CFSTR("CFBundleVersion"));
+#else
+ return GetString16Value(CFSTR("CFBundleShortVersionString"));
+#endif // defined(OS_IOS)
+}
+
+string16 FileVersionInfoMac::file_description() {
+ return string16();
+}
+
+string16 FileVersionInfoMac::legal_trademarks() {
+ return string16();
+}
+
+string16 FileVersionInfoMac::private_build() {
+ return string16();
+}
+
+string16 FileVersionInfoMac::file_version() {
+ return product_version();
+}
+
+string16 FileVersionInfoMac::original_filename() {
+ return GetString16Value(kCFBundleNameKey);
+}
+
+string16 FileVersionInfoMac::special_build() {
+ return string16();
+}
+
+string16 FileVersionInfoMac::last_change() {
+ return GetString16Value(CFSTR("SCMRevision"));
+}
+
+bool FileVersionInfoMac::is_official_build() {
+#if defined (GOOGLE_CHROME_BUILD)
+ return true;
+#else
+ return false;
+#endif
+}
+
+string16 FileVersionInfoMac::GetString16Value(CFStringRef name) {
+ if (bundle_) {
+ NSString *ns_name = base::mac::CFToNSCast(name);
+ NSString* value = [bundle_ objectForInfoDictionaryKey:ns_name];
+ if (value) {
+ return base::SysNSStringToUTF16(value);
+ }
+ }
+ return string16();
+}
diff --git a/src/base/file_version_info_unittest.cc b/src/base/file_version_info_unittest.cc
new file mode 100644
index 0000000..916e7bc
--- /dev/null
+++ b/src/base/file_version_info_unittest.cc
@@ -0,0 +1,138 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/file_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/path_service.h"
+#include "base/file_version_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include "base/file_version_info_win.h"
+#endif
+
+namespace {
+
+#if defined(OS_WIN)
+FilePath GetTestDataPath() {
+ FilePath path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ path = path.AppendASCII("base");
+ path = path.AppendASCII("data");
+ path = path.AppendASCII("file_version_info_unittest");
+ return path;
+}
+#endif
+
+}
+
+#if defined(OS_WIN)
+TEST(FileVersionInfoTest, HardCodedProperties) {
+ const wchar_t* kDLLNames[] = {
+ L"FileVersionInfoTest1.dll"
+ };
+
+ const wchar_t* kExpectedValues[1][15] = {
+ // FileVersionInfoTest.dll
+ L"Goooooogle", // company_name
+ L"Google", // company_short_name
+ L"This is the product name", // product_name
+ L"This is the product short name", // product_short_name
+ L"The Internal Name", // internal_name
+ L"4.3.2.1", // product_version
+ L"Private build property", // private_build
+ L"Special build property", // special_build
+ L"This is a particularly interesting comment", // comments
+ L"This is the original filename", // original_filename
+ L"This is my file description", // file_description
+ L"1.2.3.4", // file_version
+ L"This is the legal copyright", // legal_copyright
+ L"This is the legal trademarks", // legal_trademarks
+ L"This is the last change", // last_change
+ };
+
+ for (int i = 0; i < arraysize(kDLLNames); ++i) {
+ FilePath dll_path = GetTestDataPath();
+ dll_path = dll_path.Append(kDLLNames[i]);
+
+ scoped_ptr<FileVersionInfo> version_info(
+ FileVersionInfo::CreateFileVersionInfo(dll_path));
+
+ int j = 0;
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->company_name());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->company_short_name());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->product_name());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->product_short_name());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->internal_name());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->product_version());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->private_build());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->special_build());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->comments());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->original_filename());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->file_description());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->file_version());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->legal_copyright());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->legal_trademarks());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->last_change());
+ }
+}
+#endif
+
+#if defined(OS_WIN)
+TEST(FileVersionInfoTest, IsOfficialBuild) {
+ const wchar_t* kDLLNames[] = {
+ L"FileVersionInfoTest1.dll",
+ L"FileVersionInfoTest2.dll"
+ };
+
+ const bool kExpected[] = {
+ true,
+ false,
+ };
+
+ // Test consistency check.
+ ASSERT_EQ(arraysize(kDLLNames), arraysize(kExpected));
+
+ for (int i = 0; i < arraysize(kDLLNames); ++i) {
+ FilePath dll_path = GetTestDataPath();
+ dll_path = dll_path.Append(kDLLNames[i]);
+
+ scoped_ptr<FileVersionInfo> version_info(
+ FileVersionInfo::CreateFileVersionInfo(dll_path));
+
+ EXPECT_EQ(kExpected[i], version_info->is_official_build());
+ }
+}
+#endif
+
+#if defined(OS_WIN)
+TEST(FileVersionInfoTest, CustomProperties) {
+ FilePath dll_path = GetTestDataPath();
+ dll_path = dll_path.AppendASCII("FileVersionInfoTest1.dll");
+
+ scoped_ptr<FileVersionInfo> version_info(
+ FileVersionInfo::CreateFileVersionInfo(dll_path));
+
+ // Test few existing properties.
+ std::wstring str;
+ FileVersionInfoWin* version_info_win =
+ static_cast<FileVersionInfoWin*>(version_info.get());
+ EXPECT_TRUE(version_info_win->GetValue(L"Custom prop 1", &str));
+ EXPECT_EQ(L"Un", str);
+ EXPECT_EQ(L"Un", version_info_win->GetStringValue(L"Custom prop 1"));
+
+ EXPECT_TRUE(version_info_win->GetValue(L"Custom prop 2", &str));
+ EXPECT_EQ(L"Deux", str);
+ EXPECT_EQ(L"Deux", version_info_win->GetStringValue(L"Custom prop 2"));
+
+ EXPECT_TRUE(version_info_win->GetValue(L"Custom prop 3", &str));
+ EXPECT_EQ(L"1600 Amphitheatre Parkway Mountain View, CA 94043", str);
+ EXPECT_EQ(L"1600 Amphitheatre Parkway Mountain View, CA 94043",
+ version_info_win->GetStringValue(L"Custom prop 3"));
+
+ // Test an non-existing property.
+ EXPECT_FALSE(version_info_win->GetValue(L"Unknown property", &str));
+ EXPECT_EQ(L"", version_info_win->GetStringValue(L"Unknown property"));
+}
+#endif
diff --git a/src/base/file_version_info_win.cc b/src/base/file_version_info_win.cc
new file mode 100644
index 0000000..6528ca3
--- /dev/null
+++ b/src/base/file_version_info_win.cc
@@ -0,0 +1,187 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/file_version_info_win.h"
+
+#include <windows.h>
+
+#include "base/file_path.h"
+#include "base/file_version_info.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/threading/thread_restrictions.h"
+
+FileVersionInfoWin::FileVersionInfoWin(void* data, int language, int code_page)
+ : language_(language), code_page_(code_page) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ data_.reset((char*) data);
+ fixed_file_info_ = NULL;
+ UINT size;
+ ::VerQueryValue(data_.get(), L"\\", (LPVOID*)&fixed_file_info_, &size);
+}
+
+FileVersionInfoWin::~FileVersionInfoWin() {
+ DCHECK(data_.get());
+}
+
+typedef struct {
+ WORD language;
+ WORD code_page;
+} LanguageAndCodePage;
+
+// static
+FileVersionInfo* FileVersionInfo::CreateFileVersionInfoForModule(
+ HMODULE module) {
+ // Note that the use of MAX_PATH is basically in line with what we do for
+ // all registered paths (PathProviderWin).
+ wchar_t system_buffer[MAX_PATH];
+ system_buffer[0] = 0;
+ if (!GetModuleFileName(module, system_buffer, MAX_PATH))
+ return NULL;
+
+ FilePath app_path(system_buffer);
+ return CreateFileVersionInfo(app_path);
+}
+
+// static
+FileVersionInfo* FileVersionInfo::CreateFileVersionInfo(
+ const FilePath& file_path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ DWORD dummy;
+ const wchar_t* path = file_path.value().c_str();
+ DWORD length = ::GetFileVersionInfoSize(path, &dummy);
+ if (length == 0)
+ return NULL;
+
+ void* data = calloc(length, 1);
+ if (!data)
+ return NULL;
+
+ if (!::GetFileVersionInfo(path, dummy, length, data)) {
+ free(data);
+ return NULL;
+ }
+
+ LanguageAndCodePage* translate = NULL;
+ uint32 page_count;
+ BOOL query_result = VerQueryValue(data, L"\\VarFileInfo\\Translation",
+ (void**) &translate, &page_count);
+
+ if (query_result && translate) {
+ return new FileVersionInfoWin(data, translate->language,
+ translate->code_page);
+
+ } else {
+ free(data);
+ return NULL;
+ }
+}
+
+string16 FileVersionInfoWin::company_name() {
+ return GetStringValue(L"CompanyName");
+}
+
+string16 FileVersionInfoWin::company_short_name() {
+ return GetStringValue(L"CompanyShortName");
+}
+
+string16 FileVersionInfoWin::internal_name() {
+ return GetStringValue(L"InternalName");
+}
+
+string16 FileVersionInfoWin::product_name() {
+ return GetStringValue(L"ProductName");
+}
+
+string16 FileVersionInfoWin::product_short_name() {
+ return GetStringValue(L"ProductShortName");
+}
+
+string16 FileVersionInfoWin::comments() {
+ return GetStringValue(L"Comments");
+}
+
+string16 FileVersionInfoWin::legal_copyright() {
+ return GetStringValue(L"LegalCopyright");
+}
+
+string16 FileVersionInfoWin::product_version() {
+ return GetStringValue(L"ProductVersion");
+}
+
+string16 FileVersionInfoWin::file_description() {
+ return GetStringValue(L"FileDescription");
+}
+
+string16 FileVersionInfoWin::legal_trademarks() {
+ return GetStringValue(L"LegalTrademarks");
+}
+
+string16 FileVersionInfoWin::private_build() {
+ return GetStringValue(L"PrivateBuild");
+}
+
+string16 FileVersionInfoWin::file_version() {
+ return GetStringValue(L"FileVersion");
+}
+
+string16 FileVersionInfoWin::original_filename() {
+ return GetStringValue(L"OriginalFilename");
+}
+
+string16 FileVersionInfoWin::special_build() {
+ return GetStringValue(L"SpecialBuild");
+}
+
+string16 FileVersionInfoWin::last_change() {
+ return GetStringValue(L"LastChange");
+}
+
+bool FileVersionInfoWin::is_official_build() {
+ return (GetStringValue(L"Official Build").compare(L"1") == 0);
+}
+
+bool FileVersionInfoWin::GetValue(const wchar_t* name,
+ std::wstring* value_str) {
+ WORD lang_codepage[8];
+ int i = 0;
+ // Use the language and codepage from the DLL.
+ lang_codepage[i++] = language_;
+ lang_codepage[i++] = code_page_;
+ // Use the default language and codepage from the DLL.
+ lang_codepage[i++] = ::GetUserDefaultLangID();
+ lang_codepage[i++] = code_page_;
+ // Use the language from the DLL and Latin codepage (most common).
+ lang_codepage[i++] = language_;
+ lang_codepage[i++] = 1252;
+ // Use the default language and Latin codepage (most common).
+ lang_codepage[i++] = ::GetUserDefaultLangID();
+ lang_codepage[i++] = 1252;
+
+ i = 0;
+ while (i < arraysize(lang_codepage)) {
+ wchar_t sub_block[MAX_PATH];
+ WORD language = lang_codepage[i++];
+ WORD code_page = lang_codepage[i++];
+ _snwprintf_s(sub_block, MAX_PATH, MAX_PATH,
+ L"\\StringFileInfo\\%04x%04x\\%ls", language, code_page, name);
+ LPVOID value = NULL;
+ uint32 size;
+ BOOL r = ::VerQueryValue(data_.get(), sub_block, &value, &size);
+ if (r && value) {
+ value_str->assign(static_cast<wchar_t*>(value));
+ return true;
+ }
+ }
+ return false;
+}
+
+std::wstring FileVersionInfoWin::GetStringValue(const wchar_t* name) {
+ std::wstring str;
+ if (GetValue(name, &str))
+ return str;
+ else
+ return L"";
+}
diff --git a/src/base/file_version_info_win.h b/src/base/file_version_info_win.h
new file mode 100644
index 0000000..a378577
--- /dev/null
+++ b/src/base/file_version_info_win.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_FILE_VERSION_INFO_WIN_H_
+#define BASE_FILE_VERSION_INFO_WIN_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/file_version_info.h"
+#include "base/memory/scoped_ptr.h"
+
+struct tagVS_FIXEDFILEINFO;
+typedef tagVS_FIXEDFILEINFO VS_FIXEDFILEINFO;
+
+class FileVersionInfoWin : public FileVersionInfo {
+ public:
+ BASE_EXPORT FileVersionInfoWin(void* data, int language, int code_page);
+ BASE_EXPORT ~FileVersionInfoWin();
+
+ // Accessors to the different version properties.
+ // Returns an empty string if the property is not found.
+ virtual string16 company_name() OVERRIDE;
+ virtual string16 company_short_name() OVERRIDE;
+ virtual string16 product_name() OVERRIDE;
+ virtual string16 product_short_name() OVERRIDE;
+ virtual string16 internal_name() OVERRIDE;
+ virtual string16 product_version() OVERRIDE;
+ virtual string16 private_build() OVERRIDE;
+ virtual string16 special_build() OVERRIDE;
+ virtual string16 comments() OVERRIDE;
+ virtual string16 original_filename() OVERRIDE;
+ virtual string16 file_description() OVERRIDE;
+ virtual string16 file_version() OVERRIDE;
+ virtual string16 legal_copyright() OVERRIDE;
+ virtual string16 legal_trademarks() OVERRIDE;
+ virtual string16 last_change() OVERRIDE;
+ virtual bool is_official_build() OVERRIDE;
+
+ // Lets you access other properties not covered above.
+ BASE_EXPORT bool GetValue(const wchar_t* name, std::wstring* value);
+
+ // Similar to GetValue but returns a wstring (empty string if the property
+ // does not exist).
+ BASE_EXPORT std::wstring GetStringValue(const wchar_t* name);
+
+ // Get the fixed file info if it exists. Otherwise NULL
+ VS_FIXEDFILEINFO* fixed_file_info() { return fixed_file_info_; }
+
+ private:
+ scoped_ptr_malloc<char> data_;
+ int language_;
+ int code_page_;
+ // This is a pointer into the data_ if it exists. Otherwise NULL.
+ VS_FIXEDFILEINFO* fixed_file_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileVersionInfoWin);
+};
+
+#endif // BASE_FILE_VERSION_INFO_WIN_H_
diff --git a/src/base/files/dir_reader_dirent.h b/src/base/files/dir_reader_dirent.h
new file mode 100644
index 0000000..c2d88d5
--- /dev/null
+++ b/src/base/files/dir_reader_dirent.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2012 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Follows the interface defined in dir_reader_posix.h/dir_reader_fallback.h
+// but isn't directly included as the memory usage of this object may be higher
+// than that offered by the dir_reader_linux implementation. Therefore the
+// programmer must ask for this DirReaderDirent implementation by name. Also
+// we add a few methods to this object to make file enumeration more convenient.
+
+#ifndef BASE_DIR_READER_DIRENT_H_
+#define BASE_DIR_READER_DIRENT_H_
+#pragma once
+
+#include <dirent.h>
+
+#include "base/logging.h"
+
+namespace base {
+
+class DirReaderDirent {
+ public:
+ explicit DirReaderDirent(const char* directory_path)
+ : current_(NULL) {
+ dir_ = opendir(directory_path);
+ }
+
+ ~DirReaderDirent() {
+ if (dir_) {
+ closedir(dir_);
+ }
+ }
+
+ bool IsValid() const {
+ return dir_ != NULL;
+ }
+
+ bool Next() {
+ if (!IsValid()) return false;
+ current_ = readdir(dir_);
+ return current_ != NULL;
+ }
+
+ const char* name() const {
+ if (!IsValid()) return NULL;
+ if (!current_) return NULL;
+ return current_->d_name;
+ }
+
+#if !defined(__LB_SHELL__)
+ // reading the linux implementation this is clear it is a call for the
+ // opened directory fd
+ int fd() const {
+ if (!IsValid()) return -1;
+ return dir_->fd;
+ }
+#endif
+
+ static bool IsFallback() {
+ return false;
+ }
+
+ //**************** extra convenience methods
+
+ // returns true if the current enumerated object is a file
+ bool IsFile() const {
+ if (!IsValid()) return false;
+ if (!current_) return false;
+ return (current_->d_type == DT_REG);
+ }
+
+ private:
+ DIR* dir_;
+ dirent* current_;
+};
+
+}
+#endif // BASE_DIR_READER_DIRENT_H_
diff --git a/src/base/files/dir_reader_fallback.h b/src/base/files/dir_reader_fallback.h
new file mode 100644
index 0000000..a435f25
--- /dev/null
+++ b/src/base/files/dir_reader_fallback.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2012 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_FILES_DIR_READER_FALLBACK_H_
+#define BASE_FILES_DIR_READER_FALLBACK_H_
+
+namespace base {
+
+class DirReaderFallback {
+ public:
+ // Open a directory. If |IsValid| is true, then |Next| can be called to start
+ // the iteration at the beginning of the directory.
+ explicit DirReaderFallback(const char* directory_path) {}
+
+ // After construction, IsValid returns true iff the directory was
+ // successfully opened.
+ bool IsValid() const { return false; }
+
+ // Move to the next entry returning false if the iteration is complete.
+ bool Next() { return false; }
+
+ // Return the name of the current directory entry.
+ const char* name() { return 0;}
+
+ // Return the file descriptor which is being used.
+ int fd() const { return -1; }
+
+ // Returns true if this is a no-op fallback class (for testing).
+ static bool IsFallback() { return true; }
+};
+
+} // namespace base
+
+#endif // BASE_FILES_DIR_READER_FALLBACK_H_
diff --git a/src/base/files/dir_reader_linux.h b/src/base/files/dir_reader_linux.h
new file mode 100644
index 0000000..fda955f
--- /dev/null
+++ b/src/base/files/dir_reader_linux.h
@@ -0,0 +1,109 @@
+// Copyright (c) 2012 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_FILES_DIR_READER_LINUX_H_
+#define BASE_FILES_DIR_READER_LINUX_H_
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+// See the comments in dir_reader_posix.h about this.
+
+namespace base {
+
+struct linux_dirent {
+ uint64_t d_ino;
+ int64_t d_off;
+ unsigned short d_reclen;
+ unsigned char d_type;
+ char d_name[0];
+};
+
+class DirReaderLinux {
+ public:
+ explicit DirReaderLinux(const char* directory_path)
+ : fd_(open(directory_path, O_RDONLY | O_DIRECTORY)),
+ offset_(0),
+ size_(0) {
+ memset(buf_, 0, sizeof(buf_));
+ }
+
+ ~DirReaderLinux() {
+ if (fd_ >= 0) {
+ if (HANDLE_EINTR(close(fd_)))
+ RAW_LOG(ERROR, "Failed to close directory handle");
+ }
+ }
+
+ bool IsValid() const {
+ return fd_ >= 0;
+ }
+
+ // Move to the next entry returning false if the iteration is complete.
+ bool Next() {
+ if (size_) {
+ linux_dirent* dirent = reinterpret_cast<linux_dirent*>(&buf_[offset_]);
+ offset_ += dirent->d_reclen;
+ }
+
+ if (offset_ != size_)
+ return true;
+
+ const int r = syscall(__NR_getdents64, fd_, buf_, sizeof(buf_));
+ if (r == 0)
+ return false;
+ if (r == -1) {
+ DPLOG(FATAL) << "getdents64 returned an error: " << errno;
+ return false;
+ }
+ size_ = r;
+ offset_ = 0;
+ return true;
+ }
+
+ const char* name() const {
+ if (!size_)
+ return NULL;
+
+ const linux_dirent* dirent =
+ reinterpret_cast<const linux_dirent*>(&buf_[offset_]);
+ return dirent->d_name;
+ }
+
+ int fd() const {
+ return fd_;
+ }
+
+ static bool IsFallback() {
+ return false;
+ }
+
+ //**************** extra convenience methods
+
+ // returns true if the current enumerated object is a file
+ bool IsFile() const {
+ if (!IsValid()) return false;
+ const linux_dirent* dirent =
+ reinterpret_cast<const linux_dirent*>(&buf_[offset_]);
+ return (dirent->d_type == DT_REG);
+ }
+
+ private:
+ const int fd_;
+ unsigned char buf_[512];
+ size_t offset_, size_;
+
+ DISALLOW_COPY_AND_ASSIGN(DirReaderLinux);
+};
+
+} // namespace base
+
+#endif // BASE_FILES_DIR_READER_LINUX_H_
diff --git a/src/base/files/dir_reader_posix.h b/src/base/files/dir_reader_posix.h
new file mode 100644
index 0000000..6a20ced
--- /dev/null
+++ b/src/base/files/dir_reader_posix.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 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_FILES_DIR_READER_POSIX_H_
+#define BASE_FILES_DIR_READER_POSIX_H_
+
+#include "build/build_config.h"
+
+// This header provides a class, DirReaderPosix, which allows one to open and
+// read from directories without allocating memory. For the interface, see
+// the generic fallback in dir_reader_fallback.h.
+
+// Mac note: OS X has getdirentries, but it only works if we restrict Chrome to
+// 32-bit inodes. There is a getdirentries64 syscall in 10.6, but it's not
+// wrapped and the direct syscall interface is unstable. Using an unstable API
+// seems worse than falling back to enumerating all file descriptors so we will
+// probably never implement this on the Mac.
+
+#if defined(OS_LINUX)
+#include "base/files/dir_reader_linux.h"
+#else
+#include "base/files/dir_reader_fallback.h"
+#endif
+
+namespace base {
+
+#if defined(OS_LINUX)
+typedef DirReaderLinux DirReaderPosix;
+#else
+typedef DirReaderFallback DirReaderPosix;
+#endif
+
+} // namespace base
+
+#endif // BASE_FILES_DIR_READER_POSIX_H_
diff --git a/src/base/files/dir_reader_posix_unittest.cc b/src/base/files/dir_reader_posix_unittest.cc
new file mode 100644
index 0000000..0685031
--- /dev/null
+++ b/src/base/files/dir_reader_posix_unittest.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2012 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/files/dir_reader_posix.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_ANDROID)
+#include "base/os_compat_android.h"
+#endif
+
+namespace base {
+
+TEST(DirReaderPosixUnittest, Read) {
+ static const unsigned kNumFiles = 100;
+
+ if (DirReaderPosix::IsFallback())
+ return;
+
+ char kDirTemplate[] = "/tmp/org.chromium.dir-reader-posix-XXXXXX";
+ const char* dir = mkdtemp(kDirTemplate);
+ ASSERT_TRUE(dir);
+
+ const int prev_wd = open(".", O_RDONLY | O_DIRECTORY);
+ DCHECK_GE(prev_wd, 0);
+
+ PCHECK(chdir(dir) == 0);
+
+ for (unsigned i = 0; i < kNumFiles; i++) {
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%d", i);
+ const int fd = open(buf, O_CREAT | O_RDONLY | O_EXCL, 0600);
+ PCHECK(fd >= 0);
+ PCHECK(close(fd) == 0);
+ }
+
+ std::set<unsigned> seen;
+
+ DirReaderPosix reader(dir);
+ EXPECT_TRUE(reader.IsValid());
+
+ if (!reader.IsValid())
+ return;
+
+ bool seen_dot = false, seen_dotdot = false;
+
+ for (; reader.Next(); ) {
+ if (strcmp(reader.name(), ".") == 0) {
+ seen_dot = true;
+ continue;
+ }
+ if (strcmp(reader.name(), "..") == 0) {
+ seen_dotdot = true;
+ continue;
+ }
+
+ SCOPED_TRACE(testing::Message() << "reader.name(): " << reader.name());
+
+ char *endptr;
+ const unsigned long value = strtoul(reader.name(), &endptr, 10);
+
+ EXPECT_FALSE(*endptr);
+ EXPECT_LT(value, kNumFiles);
+ EXPECT_EQ(0u, seen.count(value));
+ seen.insert(value);
+ }
+
+ for (unsigned i = 0; i < kNumFiles; i++) {
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%d", i);
+ PCHECK(unlink(buf) == 0);
+ }
+
+ PCHECK(rmdir(dir) == 0);
+
+ PCHECK(fchdir(prev_wd) == 0);
+ PCHECK(close(prev_wd) == 0);
+
+ EXPECT_TRUE(seen_dot);
+ EXPECT_TRUE(seen_dotdot);
+ EXPECT_EQ(kNumFiles, seen.size());
+}
+
+} // namespace base
diff --git a/src/base/files/file_path_watcher.cc b/src/base/files/file_path_watcher.cc
new file mode 100644
index 0000000..bc8db37
--- /dev/null
+++ b/src/base/files/file_path_watcher.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Cross platform methods for FilePathWatcher. See the various platform
+// specific implementation files, too.
+
+#include "base/files/file_path_watcher.h"
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+
+namespace base {
+namespace files {
+
+namespace {
+
+// A delegate implementation for the callback interface.
+class FilePathWatcherDelegate : public base::files::FilePathWatcher::Delegate {
+ public:
+ explicit FilePathWatcherDelegate(const FilePathWatcher::Callback& callback)
+ : callback_(callback) {}
+
+ // FilePathWatcher::Delegate implementation.
+ virtual void OnFilePathChanged(const FilePath& path) OVERRIDE {
+ callback_.Run(path, false);
+ }
+
+ virtual void OnFilePathError(const FilePath& path) OVERRIDE {
+ callback_.Run(path, true);
+ }
+
+ private:
+ virtual ~FilePathWatcherDelegate() {}
+
+ FilePathWatcher::Callback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(FilePathWatcherDelegate);
+};
+
+} // namespace
+
+FilePathWatcher::~FilePathWatcher() {
+ impl_->Cancel();
+}
+
+// static
+void FilePathWatcher::CancelWatch(
+ const scoped_refptr<PlatformDelegate>& delegate) {
+ delegate->CancelOnMessageLoopThread();
+}
+
+bool FilePathWatcher::Watch(const FilePath& path, Delegate* delegate) {
+ DCHECK(path.IsAbsolute());
+ return impl_->Watch(path, false, delegate);
+}
+
+FilePathWatcher::PlatformDelegate::PlatformDelegate(): cancelled_(false) {
+}
+
+FilePathWatcher::PlatformDelegate::~PlatformDelegate() {
+ DCHECK(is_cancelled());
+}
+
+bool FilePathWatcher::Watch(const FilePath& path,
+ bool recursive,
+ const Callback& callback) {
+ DCHECK(path.IsAbsolute());
+ return impl_->Watch(path, recursive, new FilePathWatcherDelegate(callback));
+}
+
+} // namespace files
+} // namespace base
diff --git a/src/base/files/file_path_watcher.h b/src/base/files/file_path_watcher.h
new file mode 100644
index 0000000..94a3f9a
--- /dev/null
+++ b/src/base/files/file_path_watcher.h
@@ -0,0 +1,140 @@
+// Copyright (c) 2012 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 file or directory for changes.
+
+#ifndef BASE_FILES_FILE_PATH_WATCHER_H_
+#define BASE_FILES_FILE_PATH_WATCHER_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop_proxy.h"
+
+namespace base {
+namespace files {
+
+// This class lets you register interest in changes on a FilePath.
+// The delegate will get called whenever the file or directory referenced by the
+// FilePath is changed, including created or deleted. Due to limitations in the
+// underlying OS APIs, FilePathWatcher has slightly different semantics on OS X
+// than on Windows or Linux. FilePathWatcher on Linux and Windows will detect
+// modifications to files in a watched directory. FilePathWatcher on Mac will
+// detect the creation and deletion of files in a watched directory, but will
+// not detect modifications to those files. See file_path_watcher_kqueue.cc for
+// details.
+class BASE_EXPORT FilePathWatcher {
+ public:
+ // Callback type for Watch(). |path| points to the file that was updated,
+ // and |error| is true if the platform specific code detected an error. In
+ // that case, the callback won't be invoked again.
+ typedef base::Callback<void(const FilePath& path, bool error)> Callback;
+
+ // Declares the callback client code implements to receive notifications. Note
+ // that implementations of this interface should not keep a reference to the
+ // corresponding FileWatcher object to prevent a reference cycle.
+ //
+ // Deprecated: see comment on Watch() below.
+ class Delegate : public base::RefCountedThreadSafe<Delegate> {
+ public:
+ virtual void OnFilePathChanged(const FilePath& path) = 0;
+ // Called when platform specific code detected an error. The watcher will
+ // not call OnFilePathChanged for future changes.
+ virtual void OnFilePathError(const FilePath& path) {}
+
+ protected:
+ friend class base::RefCountedThreadSafe<Delegate>;
+ virtual ~Delegate() {}
+ };
+
+ // Used internally to encapsulate different members on different platforms.
+ // TODO(jhawkins): Move this into its own file. Also fix the confusing naming
+ // wrt Delegate vs PlatformDelegate.
+ class PlatformDelegate : public base::RefCountedThreadSafe<PlatformDelegate> {
+ public:
+ PlatformDelegate();
+
+ // Start watching for the given |path| and notify |delegate| about changes.
+ virtual bool Watch(const FilePath& path,
+ bool recursive,
+ Delegate* delegate) WARN_UNUSED_RESULT = 0;
+
+ // Stop watching. This is called from FilePathWatcher's dtor in order to
+ // allow to shut down properly while the object is still alive.
+ // It can be called from any thread.
+ virtual void Cancel() = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<PlatformDelegate>;
+ friend class FilePathWatcher;
+
+ virtual ~PlatformDelegate();
+
+ // Stop watching. This is only called on the thread of the appropriate
+ // message loop. Since it can also be called more than once, it should
+ // check |is_cancelled()| to avoid duplicate work.
+ virtual void CancelOnMessageLoopThread() = 0;
+
+ scoped_refptr<base::MessageLoopProxy> message_loop() const {
+ return message_loop_;
+ }
+
+ void set_message_loop(base::MessageLoopProxy* loop) {
+ message_loop_ = loop;
+ }
+
+ // Must be called before the PlatformDelegate is deleted.
+ void set_cancelled() {
+ cancelled_ = true;
+ }
+
+ bool is_cancelled() const {
+ return cancelled_;
+ }
+
+ private:
+ scoped_refptr<base::MessageLoopProxy> message_loop_;
+ bool cancelled_;
+ };
+
+ FilePathWatcher();
+ virtual ~FilePathWatcher();
+
+ // A callback that always cleans up the PlatformDelegate, either when executed
+ // or when deleted without having been executed at all, as can happen during
+ // shutdown.
+ static void CancelWatch(const scoped_refptr<PlatformDelegate>& delegate);
+
+ // Register interest in any changes on |path|. OnPathChanged will be called
+ // back for each change. Returns true on success.
+ // OnFilePathChanged() will be called on the same thread as Watch() is called,
+ // which should have a MessageLoop of TYPE_IO.
+ //
+ // Deprecated: new code should use the callback interface, declared below.
+ // The FilePathWatcher::Delegate interface will be removed once all client
+ // code has been updated. http://crbug.com/130980
+ virtual bool Watch(const FilePath& path, Delegate* delegate)
+ WARN_UNUSED_RESULT;
+
+ // Invokes |callback| whenever updates to |path| are detected. This should be
+ // called at most once, and from a MessageLoop of TYPE_IO. Set |recursive| to
+ // true, to watch |path| and its children. The callback will be invoked on
+ // the same loop. Returns true on success.
+ //
+ // NOTE: Recursive watch is not supported on all platforms and file systems.
+ // Watch() will return false in the case of failure.
+ bool Watch(const FilePath& path, bool recursive, const Callback& callback);
+
+ private:
+ scoped_refptr<PlatformDelegate> impl_;
+
+ DISALLOW_COPY_AND_ASSIGN(FilePathWatcher);
+};
+
+} // namespace files
+} // namespace base
+
+#endif // BASE_FILES_FILE_PATH_WATCHER_H_
diff --git a/src/base/files/file_path_watcher_browsertest.cc b/src/base/files/file_path_watcher_browsertest.cc
new file mode 100644
index 0000000..9a7c6c4
--- /dev/null
+++ b/src/base/files/file_path_watcher_browsertest.cc
@@ -0,0 +1,916 @@
+// Copyright (c) 2012 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/files/file_path_watcher.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include <aclapi.h>
+#elif defined(OS_POSIX)
+#include <sys/stat.h>
+#endif
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/stl_util.h"
+#include "base/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/test_file_util.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace files {
+
+namespace {
+
+class TestDelegate;
+
+// Aggregates notifications from the test delegates and breaks the message loop
+// the test thread is waiting on once they all came in.
+class NotificationCollector
+ : public base::RefCountedThreadSafe<NotificationCollector> {
+ public:
+ NotificationCollector()
+ : loop_(base::MessageLoopProxy::current()) {}
+
+ // Called from the file thread by the delegates.
+ void OnChange(TestDelegate* delegate) {
+ loop_->PostTask(FROM_HERE,
+ base::Bind(&NotificationCollector::RecordChange, this,
+ base::Unretained(delegate)));
+ }
+
+ void Register(TestDelegate* delegate) {
+ delegates_.insert(delegate);
+ }
+
+ void Reset() {
+ signaled_.clear();
+ }
+
+ bool Success() {
+ return signaled_ == delegates_;
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<NotificationCollector>;
+ ~NotificationCollector() {}
+
+ void RecordChange(TestDelegate* delegate) {
+ // Warning: |delegate| is Unretained. Do not dereference.
+ ASSERT_TRUE(loop_->BelongsToCurrentThread());
+ ASSERT_TRUE(delegates_.count(delegate));
+ signaled_.insert(delegate);
+
+ // Check whether all delegates have been signaled.
+ if (signaled_ == delegates_)
+ loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure());
+ }
+
+ // Set of registered delegates.
+ std::set<TestDelegate*> delegates_;
+
+ // Set of signaled delegates.
+ std::set<TestDelegate*> signaled_;
+
+ // The loop we should break after all delegates signaled.
+ scoped_refptr<base::MessageLoopProxy> loop_;
+};
+
+class TestDelegateBase : public SupportsWeakPtr<TestDelegateBase> {
+ public:
+ TestDelegateBase() {}
+ virtual ~TestDelegateBase() {}
+
+ virtual void OnFileChanged(const FilePath& path, bool error) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestDelegateBase);
+};
+
+// A mock class for testing. Gmock is not appropriate because it is not
+// thread-safe for setting expectations. Thus the test code cannot safely
+// reset expectations while the file watcher is running.
+// Instead, TestDelegate gets the notifications from FilePathWatcher and uses
+// NotificationCollector to aggregate the results.
+class TestDelegate : public TestDelegateBase {
+ public:
+ explicit TestDelegate(NotificationCollector* collector)
+ : collector_(collector) {
+ collector_->Register(this);
+ }
+ ~TestDelegate() {}
+
+ virtual void OnFileChanged(const FilePath& path, bool error) OVERRIDE {
+ if (error)
+ ADD_FAILURE() << "Error " << path.value();
+ else
+ collector_->OnChange(this);
+ }
+
+ private:
+ scoped_refptr<NotificationCollector> collector_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestDelegate);
+};
+
+void SetupWatchCallback(const FilePath& target,
+ FilePathWatcher* watcher,
+ TestDelegateBase* delegate,
+ bool recursive_watch,
+ bool* result,
+ base::WaitableEvent* completion) {
+ *result = watcher->Watch(target, recursive_watch,
+ base::Bind(&TestDelegateBase::OnFileChanged,
+ delegate->AsWeakPtr()));
+ completion->Signal();
+}
+
+void QuitLoopWatchCallback(MessageLoop* loop,
+ const FilePath& expected_path,
+ bool expected_error,
+ bool* flag,
+ const FilePath& path,
+ bool error) {
+ ASSERT_TRUE(flag);
+ *flag = true;
+ EXPECT_EQ(expected_path, path);
+ EXPECT_EQ(expected_error, error);
+ loop->PostTask(FROM_HERE, loop->QuitClosure());
+}
+
+class FilePathWatcherTest : public testing::Test {
+ public:
+ FilePathWatcherTest()
+ : file_thread_("FilePathWatcherTest") {}
+
+ virtual ~FilePathWatcherTest() {}
+
+ protected:
+ virtual void SetUp() OVERRIDE {
+ // Create a separate file thread in order to test proper thread usage.
+ base::Thread::Options options(MessageLoop::TYPE_IO, 0);
+ ASSERT_TRUE(file_thread_.StartWithOptions(options));
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ collector_ = new NotificationCollector();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ loop_.RunUntilIdle();
+ }
+
+ void DeleteDelegateOnFileThread(TestDelegate* delegate) {
+ file_thread_.message_loop_proxy()->DeleteSoon(FROM_HERE, delegate);
+ }
+
+ FilePath test_file() {
+ return temp_dir_.path().AppendASCII("FilePathWatcherTest");
+ }
+
+ FilePath test_link() {
+ return temp_dir_.path().AppendASCII("FilePathWatcherTest.lnk");
+ }
+
+ // Write |content| to |file|. Returns true on success.
+ bool WriteFile(const FilePath& file, const std::string& content) {
+ int write_size = file_util::WriteFile(file, content.c_str(),
+ content.length());
+ return write_size == static_cast<int>(content.length());
+ }
+
+ bool SetupWatch(const FilePath& target,
+ FilePathWatcher* watcher,
+ TestDelegateBase* delegate,
+ bool recursive_watch) WARN_UNUSED_RESULT;
+
+ bool WaitForEvents() WARN_UNUSED_RESULT {
+ collector_->Reset();
+ loop_.Run();
+ return collector_->Success();
+ }
+
+ NotificationCollector* collector() { return collector_.get(); }
+
+ MessageLoop loop_;
+ base::Thread file_thread_;
+ ScopedTempDir temp_dir_;
+ scoped_refptr<NotificationCollector> collector_;
+
+ DISALLOW_COPY_AND_ASSIGN(FilePathWatcherTest);
+};
+
+bool FilePathWatcherTest::SetupWatch(const FilePath& target,
+ FilePathWatcher* watcher,
+ TestDelegateBase* delegate,
+ bool recursive_watch) {
+ base::WaitableEvent completion(false, false);
+ bool result;
+ file_thread_.message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(SetupWatchCallback,
+ target, watcher, delegate, recursive_watch, &result,
+ &completion));
+ completion.Wait();
+ return result;
+}
+
+// Basic test: Create the file and verify that we notice.
+TEST_F(FilePathWatcherTest, NewFile) {
+ FilePathWatcher watcher;
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
+
+ ASSERT_TRUE(WriteFile(test_file(), "content"));
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+// Verify that modifying the file is caught.
+TEST_F(FilePathWatcherTest, ModifiedFile) {
+ ASSERT_TRUE(WriteFile(test_file(), "content"));
+
+ FilePathWatcher watcher;
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
+
+ // Now make sure we get notified if the file is modified.
+ ASSERT_TRUE(WriteFile(test_file(), "new content"));
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+// Verify that moving the file into place is caught.
+TEST_F(FilePathWatcherTest, MovedFile) {
+ FilePath source_file(temp_dir_.path().AppendASCII("source"));
+ ASSERT_TRUE(WriteFile(source_file, "content"));
+
+ FilePathWatcher watcher;
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
+
+ // Now make sure we get notified if the file is modified.
+ ASSERT_TRUE(file_util::Move(source_file, test_file()));
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+TEST_F(FilePathWatcherTest, DeletedFile) {
+ ASSERT_TRUE(WriteFile(test_file(), "content"));
+
+ FilePathWatcher watcher;
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
+
+ // Now make sure we get notified if the file is deleted.
+ file_util::Delete(test_file(), false);
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+// Used by the DeleteDuringNotify test below.
+// Deletes the FilePathWatcher when it's notified.
+class Deleter : public TestDelegateBase {
+ public:
+ Deleter(FilePathWatcher* watcher, MessageLoop* loop)
+ : watcher_(watcher),
+ loop_(loop) {
+ }
+ ~Deleter() {}
+
+ virtual void OnFileChanged(const FilePath&, bool) OVERRIDE {
+ watcher_.reset();
+ loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure());
+ }
+
+ FilePathWatcher* watcher() const { return watcher_.get(); }
+
+ private:
+ scoped_ptr<FilePathWatcher> watcher_;
+ MessageLoop* loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(Deleter);
+};
+
+// Verify that deleting a watcher during the callback doesn't crash.
+TEST_F(FilePathWatcherTest, DeleteDuringNotify) {
+ FilePathWatcher* watcher = new FilePathWatcher;
+ // Takes ownership of watcher.
+ scoped_ptr<Deleter> deleter(new Deleter(watcher, &loop_));
+ ASSERT_TRUE(SetupWatch(test_file(), watcher, deleter.get(), false));
+
+ ASSERT_TRUE(WriteFile(test_file(), "content"));
+ ASSERT_TRUE(WaitForEvents());
+
+ // We win if we haven't crashed yet.
+ // Might as well double-check it got deleted, too.
+ ASSERT_TRUE(deleter->watcher() == NULL);
+}
+
+// Verify that deleting the watcher works even if there is a pending
+// notification.
+// Flaky on MacOS. http://crbug.com/85930
+#if defined(OS_MACOSX)
+#define MAYBE_DestroyWithPendingNotification \
+ DISABLED_DestroyWithPendingNotification
+#else
+#define MAYBE_DestroyWithPendingNotification DestroyWithPendingNotification
+#endif
+TEST_F(FilePathWatcherTest, MAYBE_DestroyWithPendingNotification) {
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ FilePathWatcher* watcher = new FilePathWatcher;
+ ASSERT_TRUE(SetupWatch(test_file(), watcher, delegate.get(), false));
+ ASSERT_TRUE(WriteFile(test_file(), "content"));
+ file_thread_.message_loop_proxy()->DeleteSoon(FROM_HERE, watcher);
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+TEST_F(FilePathWatcherTest, MultipleWatchersSingleFile) {
+ FilePathWatcher watcher1, watcher2;
+ scoped_ptr<TestDelegate> delegate1(new TestDelegate(collector()));
+ scoped_ptr<TestDelegate> delegate2(new TestDelegate(collector()));
+ ASSERT_TRUE(SetupWatch(test_file(), &watcher1, delegate1.get(), false));
+ ASSERT_TRUE(SetupWatch(test_file(), &watcher2, delegate2.get(), false));
+
+ ASSERT_TRUE(WriteFile(test_file(), "content"));
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate1.release());
+ DeleteDelegateOnFileThread(delegate2.release());
+}
+
+// Verify that watching a file whose parent directory doesn't exist yet works if
+// the directory and file are created eventually.
+TEST_F(FilePathWatcherTest, NonExistentDirectory) {
+ FilePathWatcher watcher;
+ FilePath dir(temp_dir_.path().AppendASCII("dir"));
+ FilePath file(dir.AppendASCII("file"));
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false));
+
+ ASSERT_TRUE(file_util::CreateDirectory(dir));
+
+ ASSERT_TRUE(WriteFile(file, "content"));
+
+ VLOG(1) << "Waiting for file creation";
+ ASSERT_TRUE(WaitForEvents());
+
+ ASSERT_TRUE(WriteFile(file, "content v2"));
+ VLOG(1) << "Waiting for file change";
+ ASSERT_TRUE(WaitForEvents());
+
+ ASSERT_TRUE(file_util::Delete(file, false));
+ VLOG(1) << "Waiting for file deletion";
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+// Exercises watch reconfiguration for the case that directories on the path
+// are rapidly created.
+TEST_F(FilePathWatcherTest, DirectoryChain) {
+ FilePath path(temp_dir_.path());
+ std::vector<std::string> dir_names;
+ for (int i = 0; i < 20; i++) {
+ std::string dir(base::StringPrintf("d%d", i));
+ dir_names.push_back(dir);
+ path = path.AppendASCII(dir);
+ }
+
+ FilePathWatcher watcher;
+ FilePath file(path.AppendASCII("file"));
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false));
+
+ FilePath sub_path(temp_dir_.path());
+ for (std::vector<std::string>::const_iterator d(dir_names.begin());
+ d != dir_names.end(); ++d) {
+ sub_path = sub_path.AppendASCII(*d);
+ ASSERT_TRUE(file_util::CreateDirectory(sub_path));
+ }
+ VLOG(1) << "Create File";
+ ASSERT_TRUE(WriteFile(file, "content"));
+ VLOG(1) << "Waiting for file creation";
+ ASSERT_TRUE(WaitForEvents());
+
+ ASSERT_TRUE(WriteFile(file, "content v2"));
+ VLOG(1) << "Waiting for file modification";
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+#if defined(OS_MACOSX)
+// http://crbug.com/85930
+#define DisappearingDirectory DISABLED_DisappearingDirectory
+#endif
+TEST_F(FilePathWatcherTest, DisappearingDirectory) {
+ FilePathWatcher watcher;
+ FilePath dir(temp_dir_.path().AppendASCII("dir"));
+ FilePath file(dir.AppendASCII("file"));
+ ASSERT_TRUE(file_util::CreateDirectory(dir));
+ ASSERT_TRUE(WriteFile(file, "content"));
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false));
+
+ ASSERT_TRUE(file_util::Delete(dir, true));
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+// Tests that a file that is deleted and reappears is tracked correctly.
+TEST_F(FilePathWatcherTest, DeleteAndRecreate) {
+ ASSERT_TRUE(WriteFile(test_file(), "content"));
+ FilePathWatcher watcher;
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
+
+ ASSERT_TRUE(file_util::Delete(test_file(), false));
+ VLOG(1) << "Waiting for file deletion";
+ ASSERT_TRUE(WaitForEvents());
+
+ ASSERT_TRUE(WriteFile(test_file(), "content"));
+ VLOG(1) << "Waiting for file creation";
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+TEST_F(FilePathWatcherTest, WatchDirectory) {
+ FilePathWatcher watcher;
+ FilePath dir(temp_dir_.path().AppendASCII("dir"));
+ FilePath file1(dir.AppendASCII("file1"));
+ FilePath file2(dir.AppendASCII("file2"));
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ ASSERT_TRUE(SetupWatch(dir, &watcher, delegate.get(), false));
+
+ ASSERT_TRUE(file_util::CreateDirectory(dir));
+ VLOG(1) << "Waiting for directory creation";
+ ASSERT_TRUE(WaitForEvents());
+
+ ASSERT_TRUE(WriteFile(file1, "content"));
+ VLOG(1) << "Waiting for file1 creation";
+ ASSERT_TRUE(WaitForEvents());
+
+#if !defined(OS_MACOSX)
+ // Mac implementation does not detect files modified in a directory.
+ ASSERT_TRUE(WriteFile(file1, "content v2"));
+ VLOG(1) << "Waiting for file1 modification";
+ ASSERT_TRUE(WaitForEvents());
+#endif // !OS_MACOSX
+
+ ASSERT_TRUE(file_util::Delete(file1, false));
+ VLOG(1) << "Waiting for file1 deletion";
+ ASSERT_TRUE(WaitForEvents());
+
+ ASSERT_TRUE(WriteFile(file2, "content"));
+ VLOG(1) << "Waiting for file2 creation";
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+TEST_F(FilePathWatcherTest, MoveParent) {
+ FilePathWatcher file_watcher;
+ FilePathWatcher subdir_watcher;
+ FilePath dir(temp_dir_.path().AppendASCII("dir"));
+ FilePath dest(temp_dir_.path().AppendASCII("dest"));
+ FilePath subdir(dir.AppendASCII("subdir"));
+ FilePath file(subdir.AppendASCII("file"));
+ scoped_ptr<TestDelegate> file_delegate(new TestDelegate(collector()));
+ ASSERT_TRUE(SetupWatch(file, &file_watcher, file_delegate.get(), false));
+ scoped_ptr<TestDelegate> subdir_delegate(new TestDelegate(collector()));
+ ASSERT_TRUE(SetupWatch(subdir, &subdir_watcher, subdir_delegate.get(),
+ false));
+
+ // Setup a directory hierarchy.
+ ASSERT_TRUE(file_util::CreateDirectory(subdir));
+ ASSERT_TRUE(WriteFile(file, "content"));
+ VLOG(1) << "Waiting for file creation";
+ ASSERT_TRUE(WaitForEvents());
+
+ // Move the parent directory.
+ file_util::Move(dir, dest);
+ VLOG(1) << "Waiting for directory move";
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(file_delegate.release());
+ DeleteDelegateOnFileThread(subdir_delegate.release());
+}
+
+#if defined(OS_WIN)
+TEST_F(FilePathWatcherTest, RecursiveWatch) {
+ FilePathWatcher watcher;
+ FilePath dir(temp_dir_.path().AppendASCII("dir"));
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ ASSERT_TRUE(SetupWatch(dir, &watcher, delegate.get(), true));
+
+ // Main directory("dir") creation.
+ ASSERT_TRUE(file_util::CreateDirectory(dir));
+ ASSERT_TRUE(WaitForEvents());
+
+ // Create "$dir/file1".
+ FilePath file1(dir.AppendASCII("file1"));
+ ASSERT_TRUE(WriteFile(file1, "content"));
+ ASSERT_TRUE(WaitForEvents());
+
+ // Create "$dir/subdir".
+ FilePath subdir(dir.AppendASCII("subdir"));
+ ASSERT_TRUE(file_util::CreateDirectory(subdir));
+ ASSERT_TRUE(WaitForEvents());
+
+ // Create "$dir/subdir/subdir_file1".
+ FilePath subdir_file1(subdir.AppendASCII("subdir_file1"));
+ ASSERT_TRUE(WriteFile(subdir_file1, "content"));
+ ASSERT_TRUE(WaitForEvents());
+
+ // Create "$dir/subdir/subdir_child_dir".
+ FilePath subdir_child_dir(subdir.AppendASCII("subdir_child_dir"));
+ ASSERT_TRUE(file_util::CreateDirectory(subdir_child_dir));
+ ASSERT_TRUE(WaitForEvents());
+
+ // Create "$dir/subdir/subdir_child_dir/child_dir_file1".
+ FilePath child_dir_file1(subdir_child_dir.AppendASCII("child_dir_file1"));
+ ASSERT_TRUE(WriteFile(child_dir_file1, "content v2"));
+ ASSERT_TRUE(WaitForEvents());
+
+ // Write into "$dir/subdir/subdir_child_dir/child_dir_file1".
+ ASSERT_TRUE(WriteFile(child_dir_file1, "content"));
+ ASSERT_TRUE(WaitForEvents());
+
+ // Modify "$dir/subdir/subdir_child_dir/child_dir_file1" attributes.
+ ASSERT_TRUE(file_util::MakeFileUnreadable(child_dir_file1));
+ ASSERT_TRUE(WaitForEvents());
+
+ // Delete "$dir/subdir/subdir_file1".
+ ASSERT_TRUE(file_util::Delete(subdir_file1, false));
+ ASSERT_TRUE(WaitForEvents());
+
+ // Delete "$dir/subdir/subdir_child_dir/child_dir_file1".
+ ASSERT_TRUE(file_util::Delete(child_dir_file1, false));
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
+}
+#else
+TEST_F(FilePathWatcherTest, RecursiveWatch) {
+ FilePathWatcher watcher;
+ FilePath dir(temp_dir_.path().AppendASCII("dir"));
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ // Non-Windows implementaion does not support recursive watching.
+ ASSERT_FALSE(SetupWatch(dir, &watcher, delegate.get(), true));
+ DeleteDelegateOnFileThread(delegate.release());
+}
+#endif
+
+TEST_F(FilePathWatcherTest, MoveChild) {
+ FilePathWatcher file_watcher;
+ FilePathWatcher subdir_watcher;
+ FilePath source_dir(temp_dir_.path().AppendASCII("source"));
+ FilePath source_subdir(source_dir.AppendASCII("subdir"));
+ FilePath source_file(source_subdir.AppendASCII("file"));
+ FilePath dest_dir(temp_dir_.path().AppendASCII("dest"));
+ FilePath dest_subdir(dest_dir.AppendASCII("subdir"));
+ FilePath dest_file(dest_subdir.AppendASCII("file"));
+
+ // Setup a directory hierarchy.
+ ASSERT_TRUE(file_util::CreateDirectory(source_subdir));
+ ASSERT_TRUE(WriteFile(source_file, "content"));
+
+ scoped_ptr<TestDelegate> file_delegate(new TestDelegate(collector()));
+ ASSERT_TRUE(SetupWatch(dest_file, &file_watcher, file_delegate.get(), false));
+ scoped_ptr<TestDelegate> subdir_delegate(new TestDelegate(collector()));
+ ASSERT_TRUE(SetupWatch(dest_subdir, &subdir_watcher, subdir_delegate.get(),
+ false));
+
+ // Move the directory into place, s.t. the watched file appears.
+ ASSERT_TRUE(file_util::Move(source_dir, dest_dir));
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(file_delegate.release());
+ DeleteDelegateOnFileThread(subdir_delegate.release());
+}
+
+#if !defined(OS_LINUX)
+// Linux implementation of FilePathWatcher doesn't catch attribute changes.
+// http://crbug.com/78043
+
+// Verify that changing attributes on a file is caught
+TEST_F(FilePathWatcherTest, FileAttributesChanged) {
+ ASSERT_TRUE(WriteFile(test_file(), "content"));
+ FilePathWatcher watcher;
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false));
+
+ // Now make sure we get notified if the file is modified.
+ ASSERT_TRUE(file_util::MakeFileUnreadable(test_file()));
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+#endif // !OS_LINUX
+
+#if defined(OS_LINUX)
+
+// Verify that creating a symlink is caught.
+TEST_F(FilePathWatcherTest, CreateLink) {
+ FilePathWatcher watcher;
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ // Note that we are watching the symlink
+ ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
+
+ // Now make sure we get notified if the link is created.
+ // Note that test_file() doesn't have to exist.
+ ASSERT_TRUE(file_util::CreateSymbolicLink(test_file(), test_link()));
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+// Verify that deleting a symlink is caught.
+TEST_F(FilePathWatcherTest, DeleteLink) {
+ // Unfortunately this test case only works if the link target exists.
+ // TODO(craig) fix this as part of crbug.com/91561.
+ ASSERT_TRUE(WriteFile(test_file(), "content"));
+ ASSERT_TRUE(file_util::CreateSymbolicLink(test_file(), test_link()));
+ FilePathWatcher watcher;
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
+
+ // Now make sure we get notified if the link is deleted.
+ ASSERT_TRUE(file_util::Delete(test_link(), false));
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+// Verify that modifying a target file that a link is pointing to
+// when we are watching the link is caught.
+TEST_F(FilePathWatcherTest, ModifiedLinkedFile) {
+ ASSERT_TRUE(WriteFile(test_file(), "content"));
+ ASSERT_TRUE(file_util::CreateSymbolicLink(test_file(), test_link()));
+ FilePathWatcher watcher;
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ // Note that we are watching the symlink.
+ ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
+
+ // Now make sure we get notified if the file is modified.
+ ASSERT_TRUE(WriteFile(test_file(), "new content"));
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+// Verify that creating a target file that a link is pointing to
+// when we are watching the link is caught.
+TEST_F(FilePathWatcherTest, CreateTargetLinkedFile) {
+ ASSERT_TRUE(file_util::CreateSymbolicLink(test_file(), test_link()));
+ FilePathWatcher watcher;
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ // Note that we are watching the symlink.
+ ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
+
+ // Now make sure we get notified if the target file is created.
+ ASSERT_TRUE(WriteFile(test_file(), "content"));
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+// Verify that deleting a target file that a link is pointing to
+// when we are watching the link is caught.
+TEST_F(FilePathWatcherTest, DeleteTargetLinkedFile) {
+ ASSERT_TRUE(WriteFile(test_file(), "content"));
+ ASSERT_TRUE(file_util::CreateSymbolicLink(test_file(), test_link()));
+ FilePathWatcher watcher;
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ // Note that we are watching the symlink.
+ ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
+
+ // Now make sure we get notified if the target file is deleted.
+ ASSERT_TRUE(file_util::Delete(test_file(), false));
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+// Verify that watching a file whose parent directory is a link that
+// doesn't exist yet works if the symlink is created eventually.
+TEST_F(FilePathWatcherTest, LinkedDirectoryPart1) {
+ FilePathWatcher watcher;
+ FilePath dir(temp_dir_.path().AppendASCII("dir"));
+ FilePath link_dir(temp_dir_.path().AppendASCII("dir.lnk"));
+ FilePath file(dir.AppendASCII("file"));
+ FilePath linkfile(link_dir.AppendASCII("file"));
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ // dir/file should exist.
+ ASSERT_TRUE(file_util::CreateDirectory(dir));
+ ASSERT_TRUE(WriteFile(file, "content"));
+ // Note that we are watching dir.lnk/file which doesn't exist yet.
+ ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(), false));
+
+ ASSERT_TRUE(file_util::CreateSymbolicLink(dir, link_dir));
+ VLOG(1) << "Waiting for link creation";
+ ASSERT_TRUE(WaitForEvents());
+
+ ASSERT_TRUE(WriteFile(file, "content v2"));
+ VLOG(1) << "Waiting for file change";
+ ASSERT_TRUE(WaitForEvents());
+
+ ASSERT_TRUE(file_util::Delete(file, false));
+ VLOG(1) << "Waiting for file deletion";
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+// Verify that watching a file whose parent directory is a
+// dangling symlink works if the directory is created eventually.
+TEST_F(FilePathWatcherTest, LinkedDirectoryPart2) {
+ FilePathWatcher watcher;
+ FilePath dir(temp_dir_.path().AppendASCII("dir"));
+ FilePath link_dir(temp_dir_.path().AppendASCII("dir.lnk"));
+ FilePath file(dir.AppendASCII("file"));
+ FilePath linkfile(link_dir.AppendASCII("file"));
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ // Now create the link from dir.lnk pointing to dir but
+ // neither dir nor dir/file exist yet.
+ ASSERT_TRUE(file_util::CreateSymbolicLink(dir, link_dir));
+ // Note that we are watching dir.lnk/file.
+ ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(), false));
+
+ ASSERT_TRUE(file_util::CreateDirectory(dir));
+ ASSERT_TRUE(WriteFile(file, "content"));
+ VLOG(1) << "Waiting for dir/file creation";
+ ASSERT_TRUE(WaitForEvents());
+
+ ASSERT_TRUE(WriteFile(file, "content v2"));
+ VLOG(1) << "Waiting for file change";
+ ASSERT_TRUE(WaitForEvents());
+
+ ASSERT_TRUE(file_util::Delete(file, false));
+ VLOG(1) << "Waiting for file deletion";
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+// Verify that watching a file with a symlink on the path
+// to the file works.
+TEST_F(FilePathWatcherTest, LinkedDirectoryPart3) {
+ FilePathWatcher watcher;
+ FilePath dir(temp_dir_.path().AppendASCII("dir"));
+ FilePath link_dir(temp_dir_.path().AppendASCII("dir.lnk"));
+ FilePath file(dir.AppendASCII("file"));
+ FilePath linkfile(link_dir.AppendASCII("file"));
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ ASSERT_TRUE(file_util::CreateDirectory(dir));
+ ASSERT_TRUE(file_util::CreateSymbolicLink(dir, link_dir));
+ // Note that we are watching dir.lnk/file but the file doesn't exist yet.
+ ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(), false));
+
+ ASSERT_TRUE(WriteFile(file, "content"));
+ VLOG(1) << "Waiting for file creation";
+ ASSERT_TRUE(WaitForEvents());
+
+ ASSERT_TRUE(WriteFile(file, "content v2"));
+ VLOG(1) << "Waiting for file change";
+ ASSERT_TRUE(WaitForEvents());
+
+ ASSERT_TRUE(file_util::Delete(file, false));
+ VLOG(1) << "Waiting for file deletion";
+ ASSERT_TRUE(WaitForEvents());
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+#endif // OS_LINUX
+
+enum Permission {
+ Read,
+ Write,
+ Execute
+};
+
+bool ChangeFilePermissions(const FilePath& path, Permission perm, bool allow) {
+#if defined(OS_POSIX)
+ struct stat stat_buf;
+
+ if (stat(path.value().c_str(), &stat_buf) != 0)
+ return false;
+
+ mode_t mode = 0;
+ switch (perm) {
+ case Read:
+ mode = S_IRUSR | S_IRGRP | S_IROTH;
+ break;
+ case Write:
+ mode = S_IWUSR | S_IWGRP | S_IWOTH;
+ break;
+ case Execute:
+ mode = S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ default:
+ ADD_FAILURE() << "unknown perm " << perm;
+ return false;
+ }
+ if (allow) {
+ stat_buf.st_mode |= mode;
+ } else {
+ stat_buf.st_mode &= ~mode;
+ }
+ return chmod(path.value().c_str(), stat_buf.st_mode) == 0;
+
+#elif defined(OS_WIN)
+ PACL old_dacl;
+ PSECURITY_DESCRIPTOR security_descriptor;
+ if (GetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
+ SE_FILE_OBJECT,
+ DACL_SECURITY_INFORMATION, NULL, NULL, &old_dacl,
+ NULL, &security_descriptor) != ERROR_SUCCESS)
+ return false;
+
+ DWORD mode = 0;
+ switch (perm) {
+ case Read:
+ mode = GENERIC_READ;
+ break;
+ case Write:
+ mode = GENERIC_WRITE;
+ break;
+ case Execute:
+ mode = GENERIC_EXECUTE;
+ break;
+ default:
+ ADD_FAILURE() << "unknown perm " << perm;
+ return false;
+ }
+
+ // Deny Read access for the current user.
+ EXPLICIT_ACCESS change;
+ change.grfAccessPermissions = mode;
+ change.grfAccessMode = allow ? GRANT_ACCESS : DENY_ACCESS;
+ change.grfInheritance = 0;
+ change.Trustee.pMultipleTrustee = NULL;
+ change.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+ change.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
+ change.Trustee.TrusteeType = TRUSTEE_IS_USER;
+ change.Trustee.ptstrName = L"CURRENT_USER";
+
+ PACL new_dacl;
+ if (SetEntriesInAcl(1, &change, old_dacl, &new_dacl) != ERROR_SUCCESS) {
+ LocalFree(security_descriptor);
+ return false;
+ }
+
+ DWORD rc = SetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
+ SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
+ NULL, NULL, new_dacl, NULL);
+ LocalFree(security_descriptor);
+ LocalFree(new_dacl);
+
+ return rc == ERROR_SUCCESS;
+#else
+ NOTIMPLEMENTED();
+ return false;
+#endif
+}
+
+#if defined(OS_MACOSX)
+// Linux implementation of FilePathWatcher doesn't catch attribute changes.
+// http://crbug.com/78043
+// Windows implementation of FilePathWatcher catches attribute changes that
+// don't affect the path being watched.
+// http://crbug.com/78045
+
+// Verify that changing attributes on a directory works.
+TEST_F(FilePathWatcherTest, DirAttributesChanged) {
+ FilePath test_dir1(temp_dir_.path().AppendASCII("DirAttributesChangedDir1"));
+ FilePath test_dir2(test_dir1.AppendASCII("DirAttributesChangedDir2"));
+ FilePath test_file(test_dir2.AppendASCII("DirAttributesChangedFile"));
+ // Setup a directory hierarchy.
+ ASSERT_TRUE(file_util::CreateDirectory(test_dir1));
+ ASSERT_TRUE(file_util::CreateDirectory(test_dir2));
+ ASSERT_TRUE(WriteFile(test_file, "content"));
+
+ FilePathWatcher watcher;
+ scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
+ ASSERT_TRUE(SetupWatch(test_file, &watcher, delegate.get(), false));
+
+ // We should not get notified in this case as it hasn't affected our ability
+ // to access the file.
+ ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, false));
+ loop_.PostDelayedTask(FROM_HERE,
+ MessageLoop::QuitClosure(),
+ TestTimeouts::tiny_timeout());
+ ASSERT_FALSE(WaitForEvents());
+ ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, true));
+
+ // We should get notified in this case because filepathwatcher can no
+ // longer access the file
+ ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, false));
+ ASSERT_TRUE(WaitForEvents());
+ ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, true));
+ DeleteDelegateOnFileThread(delegate.release());
+}
+
+#endif // OS_MACOSX
+} // namespace
+
+} // namespace files
+} // namespace base
diff --git a/src/base/files/file_path_watcher_kqueue.cc b/src/base/files/file_path_watcher_kqueue.cc
new file mode 100644
index 0000000..aebe15a
--- /dev/null
+++ b/src/base/files/file_path_watcher_kqueue.cc
@@ -0,0 +1,513 @@
+// Copyright (c) 2012 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/files/file_path_watcher.h"
+
+#include <fcntl.h>
+#include <sys/event.h>
+#include <sys/param.h>
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/stringprintf.h"
+
+// On some platforms these are not defined.
+#if !defined(EV_RECEIPT)
+#define EV_RECEIPT 0
+#endif
+#if !defined(O_EVTONLY)
+#define O_EVTONLY O_RDONLY
+#endif
+
+namespace base {
+namespace files {
+
+namespace {
+
+// Mac-specific file watcher implementation based on kqueue.
+// Originally it was based on FSEvents so that the semantics were equivalent
+// on Linux, OSX and Windows where it was able to detect:
+// - file creation/deletion/modification in a watched directory
+// - file creation/deletion/modification for a watched file
+// - modifications to the paths to a watched object that would affect the
+// object such as renaming/attibute changes etc.
+// The FSEvents version did all of the above except handling attribute changes
+// to path components. Unfortunately FSEvents appears to have an issue where the
+// current implementation (Mac OS X 10.6.7) sometimes drops events and doesn't
+// send notifications. See
+// http://code.google.com/p/chromium/issues/detail?id=54822#c31 for source that
+// will reproduce the problem. FSEvents also required having a CFRunLoop
+// backing the thread that it was running on, that caused added complexity
+// in the interfaces.
+// The kqueue implementation will handle all of the items in the list above
+// except for detecting modifications to files in a watched directory. It will
+// detect the creation and deletion of files, just not the modification of
+// files. It does however detect the attribute changes that the FSEvents impl
+// would miss.
+class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
+ public MessageLoopForIO::Watcher,
+ public MessageLoop::DestructionObserver {
+ public:
+ FilePathWatcherImpl() : kqueue_(-1) {}
+
+ // MessageLoopForIO::Watcher overrides.
+ virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
+ virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
+
+ // MessageLoop::DestructionObserver overrides.
+ virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
+
+ // FilePathWatcher::PlatformDelegate overrides.
+ virtual bool Watch(const FilePath& path,
+ bool recursive,
+ FilePathWatcher::Delegate* delegate) OVERRIDE;
+ virtual void Cancel() OVERRIDE;
+
+ protected:
+ virtual ~FilePathWatcherImpl() {}
+
+ private:
+ class EventData {
+ public:
+ EventData(const FilePath& path, const FilePath::StringType& subdir)
+ : path_(path), subdir_(subdir) { }
+ FilePath path_; // Full path to this item.
+ FilePath::StringType subdir_; // Path to any sub item.
+ };
+ typedef std::vector<struct kevent> EventVector;
+
+ // Can only be called on |io_message_loop_|'s thread.
+ virtual void CancelOnMessageLoopThread() OVERRIDE;
+
+ // Returns true if the kevent values are error free.
+ bool AreKeventValuesValid(struct kevent* kevents, int count);
+
+ // Respond to a change of attributes of the path component represented by
+ // |event|. Sets |target_file_affected| to true if |target_| is affected.
+ // Sets |update_watches| to true if |events_| need to be updated.
+ void HandleAttributesChange(const EventVector::iterator& event,
+ bool* target_file_affected,
+ bool* update_watches);
+
+ // Respond to a move of deletion of the path component represented by
+ // |event|. Sets |target_file_affected| to true if |target_| is affected.
+ // Sets |update_watches| to true if |events_| need to be updated.
+ void HandleDeleteOrMoveChange(const EventVector::iterator& event,
+ bool* target_file_affected,
+ bool* update_watches);
+
+ // Respond to a creation of an item in the path component represented by
+ // |event|. Sets |target_file_affected| to true if |target_| is affected.
+ // Sets |update_watches| to true if |events_| need to be updated.
+ void HandleCreateItemChange(const EventVector::iterator& event,
+ bool* target_file_affected,
+ bool* update_watches);
+
+ // Update |events_| with the current status of the system.
+ // Sets |target_file_affected| to true if |target_| is affected.
+ // Returns false if an error occurs.
+ bool UpdateWatches(bool* target_file_affected);
+
+ // Fills |events| with one kevent per component in |path|.
+ // Returns the number of valid events created where a valid event is
+ // defined as one that has a ident (file descriptor) field != -1.
+ static int EventsForPath(FilePath path, EventVector *events);
+
+ // Release a kevent generated by EventsForPath.
+ static void ReleaseEvent(struct kevent& event);
+
+ // Returns a file descriptor that will not block the system from deleting
+ // the file it references.
+ static int FileDescriptorForPath(const FilePath& path);
+
+ // Closes |*fd| and sets |*fd| to -1.
+ static void CloseFileDescriptor(int* fd);
+
+ // Returns true if kevent has open file descriptor.
+ static bool IsKeventFileDescriptorOpen(const struct kevent& event) {
+ return event.ident != static_cast<uintptr_t>(-1);
+ }
+
+ static EventData* EventDataForKevent(const struct kevent& event) {
+ return reinterpret_cast<EventData*>(event.udata);
+ }
+
+ EventVector events_;
+ scoped_refptr<base::MessageLoopProxy> io_message_loop_;
+ MessageLoopForIO::FileDescriptorWatcher kqueue_watcher_;
+ scoped_refptr<FilePathWatcher::Delegate> delegate_;
+ FilePath target_;
+ int kqueue_;
+
+ DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
+};
+
+void FilePathWatcherImpl::ReleaseEvent(struct kevent& event) {
+ CloseFileDescriptor(reinterpret_cast<int*>(&event.ident));
+ EventData* entry = EventDataForKevent(event);
+ delete entry;
+ event.udata = NULL;
+}
+
+int FilePathWatcherImpl::EventsForPath(FilePath path, EventVector* events) {
+ DCHECK(MessageLoopForIO::current());
+ // Make sure that we are working with a clean slate.
+ DCHECK(events->empty());
+
+ std::vector<FilePath::StringType> components;
+ path.GetComponents(&components);
+
+ if (components.size() < 1) {
+ return -1;
+ }
+
+ int last_existing_entry = 0;
+ FilePath built_path;
+ bool path_still_exists = true;
+ for(std::vector<FilePath::StringType>::iterator i = components.begin();
+ i != components.end(); ++i) {
+ if (i == components.begin()) {
+ built_path = FilePath(*i);
+ } else {
+ built_path = built_path.Append(*i);
+ }
+ int fd = -1;
+ if (path_still_exists) {
+ fd = FileDescriptorForPath(built_path);
+ if (fd == -1) {
+ path_still_exists = false;
+ } else {
+ ++last_existing_entry;
+ }
+ }
+ FilePath::StringType subdir = (i != (components.end() - 1)) ? *(i + 1) : "";
+ EventData* data = new EventData(built_path, subdir);
+ struct kevent event;
+ EV_SET(&event, fd, EVFILT_VNODE, (EV_ADD | EV_CLEAR | EV_RECEIPT),
+ (NOTE_DELETE | NOTE_WRITE | NOTE_ATTRIB |
+ NOTE_RENAME | NOTE_REVOKE | NOTE_EXTEND), 0, data);
+ events->push_back(event);
+ }
+ return last_existing_entry;
+}
+
+int FilePathWatcherImpl::FileDescriptorForPath(const FilePath& path) {
+ return HANDLE_EINTR(open(path.value().c_str(), O_EVTONLY));
+}
+
+void FilePathWatcherImpl::CloseFileDescriptor(int *fd) {
+ if (*fd == -1) {
+ return;
+ }
+
+ if (HANDLE_EINTR(close(*fd)) != 0) {
+ DPLOG(ERROR) << "close";
+ }
+ *fd = -1;
+}
+
+bool FilePathWatcherImpl::AreKeventValuesValid(struct kevent* kevents,
+ int count) {
+ if (count < 0) {
+ DPLOG(ERROR) << "kevent";
+ return false;
+ }
+ bool valid = true;
+ for (int i = 0; i < count; ++i) {
+ if (kevents[i].flags & EV_ERROR && kevents[i].data) {
+ // Find the kevent in |events_| that matches the kevent with the error.
+ EventVector::iterator event = events_.begin();
+ for (; event != events_.end(); ++event) {
+ if (event->ident == kevents[i].ident) {
+ break;
+ }
+ }
+ std::string path_name;
+ if (event != events_.end()) {
+ EventData* event_data = EventDataForKevent(*event);
+ if (event_data != NULL) {
+ path_name = event_data->path_.value();
+ }
+ }
+ if (path_name.empty()) {
+ path_name = base::StringPrintf(
+ "fd %d", *reinterpret_cast<int*>(&kevents[i].ident));
+ }
+ DLOG(ERROR) << "Error: " << kevents[i].data << " for " << path_name;
+ valid = false;
+ }
+ }
+ return valid;
+}
+
+void FilePathWatcherImpl::HandleAttributesChange(
+ const EventVector::iterator& event,
+ bool* target_file_affected,
+ bool* update_watches) {
+ EventVector::iterator next_event = event + 1;
+ EventData* next_event_data = EventDataForKevent(*next_event);
+ // Check to see if the next item in path is still accessible.
+ int have_access = FileDescriptorForPath(next_event_data->path_);
+ if (have_access == -1) {
+ *target_file_affected = true;
+ *update_watches = true;
+ EventVector::iterator local_event(event);
+ for (; local_event != events_.end(); ++local_event) {
+ // Close all nodes from the event down. This has the side effect of
+ // potentially rendering other events in |updates| invalid.
+ // There is no need to remove the events from |kqueue_| because this
+ // happens as a side effect of closing the file descriptor.
+ CloseFileDescriptor(reinterpret_cast<int*>(&local_event->ident));
+ }
+ } else {
+ CloseFileDescriptor(&have_access);
+ }
+}
+
+void FilePathWatcherImpl::HandleDeleteOrMoveChange(
+ const EventVector::iterator& event,
+ bool* target_file_affected,
+ bool* update_watches) {
+ *target_file_affected = true;
+ *update_watches = true;
+ EventVector::iterator local_event(event);
+ for (; local_event != events_.end(); ++local_event) {
+ // Close all nodes from the event down. This has the side effect of
+ // potentially rendering other events in |updates| invalid.
+ // There is no need to remove the events from |kqueue_| because this
+ // happens as a side effect of closing the file descriptor.
+ CloseFileDescriptor(reinterpret_cast<int*>(&local_event->ident));
+ }
+}
+
+void FilePathWatcherImpl::HandleCreateItemChange(
+ const EventVector::iterator& event,
+ bool* target_file_affected,
+ bool* update_watches) {
+ // Get the next item in the path.
+ EventVector::iterator next_event = event + 1;
+ EventData* next_event_data = EventDataForKevent(*next_event);
+
+ // Check to see if it already has a valid file descriptor.
+ if (!IsKeventFileDescriptorOpen(*next_event)) {
+ // If not, attempt to open a file descriptor for it.
+ next_event->ident = FileDescriptorForPath(next_event_data->path_);
+ if (IsKeventFileDescriptorOpen(*next_event)) {
+ *update_watches = true;
+ if (next_event_data->subdir_.empty()) {
+ *target_file_affected = true;
+ }
+ }
+ }
+}
+
+bool FilePathWatcherImpl::UpdateWatches(bool* target_file_affected) {
+ // Iterate over events adding kevents for items that exist to the kqueue.
+ // Then check to see if new components in the path have been created.
+ // Repeat until no new components in the path are detected.
+ // This is to get around races in directory creation in a watched path.
+ bool update_watches = true;
+ while (update_watches) {
+ size_t valid;
+ for (valid = 0; valid < events_.size(); ++valid) {
+ if (!IsKeventFileDescriptorOpen(events_[valid])) {
+ break;
+ }
+ }
+ if (valid == 0) {
+ // The root of the file path is inaccessible?
+ return false;
+ }
+
+ EventVector updates(valid);
+ int count = HANDLE_EINTR(kevent(kqueue_, &events_[0], valid, &updates[0],
+ valid, NULL));
+ if (!AreKeventValuesValid(&updates[0], count)) {
+ return false;
+ }
+ update_watches = false;
+ for (; valid < events_.size(); ++valid) {
+ EventData* event_data = EventDataForKevent(events_[valid]);
+ events_[valid].ident = FileDescriptorForPath(event_data->path_);
+ if (IsKeventFileDescriptorOpen(events_[valid])) {
+ update_watches = true;
+ if (event_data->subdir_.empty()) {
+ *target_file_affected = true;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+void FilePathWatcherImpl::OnFileCanReadWithoutBlocking(int fd) {
+ DCHECK(MessageLoopForIO::current());
+ DCHECK_EQ(fd, kqueue_);
+ DCHECK(events_.size());
+
+ // Request the file system update notifications that have occurred and return
+ // them in |updates|. |count| will contain the number of updates that have
+ // occurred.
+ EventVector updates(events_.size());
+ struct timespec timeout = {0, 0};
+ int count = HANDLE_EINTR(kevent(kqueue_, NULL, 0, &updates[0], updates.size(),
+ &timeout));
+
+ // Error values are stored within updates, so check to make sure that no
+ // errors occurred.
+ if (!AreKeventValuesValid(&updates[0], count)) {
+ delegate_->OnFilePathError(target_);
+ Cancel();
+ return;
+ }
+
+ bool update_watches = false;
+ bool send_notification = false;
+
+ // Iterate through each of the updates and react to them.
+ for (int i = 0; i < count; ++i) {
+ // Find our kevent record that matches the update notification.
+ EventVector::iterator event = events_.begin();
+ for (; event != events_.end(); ++event) {
+ if (!IsKeventFileDescriptorOpen(*event) ||
+ event->ident == updates[i].ident) {
+ break;
+ }
+ }
+ if (!IsKeventFileDescriptorOpen(*event) || event == events_.end()) {
+ // The event may no longer exist in |events_| because another event
+ // modified |events_| in such a way to make it invalid. For example if
+ // the path is /foo/bar/bam and foo is deleted, NOTE_DELETE events for
+ // foo, bar and bam will be sent. If foo is processed first, then
+ // the file descriptors for bar and bam will already be closed and set
+ // to -1 before they get a chance to be processed.
+ continue;
+ }
+
+ EventData* event_data = EventDataForKevent(*event);
+
+ // If the subdir is empty, this is the last item on the path and is the
+ // target file.
+ bool target_file_affected = event_data->subdir_.empty();
+ if ((updates[i].fflags & NOTE_ATTRIB) && !target_file_affected) {
+ HandleAttributesChange(event, &target_file_affected, &update_watches);
+ }
+ if (updates[i].fflags & (NOTE_DELETE | NOTE_REVOKE | NOTE_RENAME)) {
+ HandleDeleteOrMoveChange(event, &target_file_affected, &update_watches);
+ }
+ if ((updates[i].fflags & NOTE_WRITE) && !target_file_affected) {
+ HandleCreateItemChange(event, &target_file_affected, &update_watches);
+ }
+ send_notification |= target_file_affected;
+ }
+
+ if (update_watches) {
+ if (!UpdateWatches(&send_notification)) {
+ delegate_->OnFilePathError(target_);
+ Cancel();
+ }
+ }
+
+ if (send_notification) {
+ delegate_->OnFilePathChanged(target_);
+ }
+}
+
+void FilePathWatcherImpl::OnFileCanWriteWithoutBlocking(int fd) {
+ NOTREACHED();
+}
+
+void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() {
+ CancelOnMessageLoopThread();
+}
+
+bool FilePathWatcherImpl::Watch(const FilePath& path,
+ bool recursive,
+ FilePathWatcher::Delegate* delegate) {
+ DCHECK(MessageLoopForIO::current());
+ DCHECK(target_.value().empty()); // Can only watch one path.
+ DCHECK(delegate);
+ DCHECK_EQ(kqueue_, -1);
+
+ if (recursive) {
+ // Recursive watch is not supported on this platform.
+ NOTIMPLEMENTED();
+ return false;
+ }
+
+ delegate_ = delegate;
+ target_ = path;
+
+ MessageLoop::current()->AddDestructionObserver(this);
+ io_message_loop_ = base::MessageLoopProxy::current();
+
+ kqueue_ = kqueue();
+ if (kqueue_ == -1) {
+ DPLOG(ERROR) << "kqueue";
+ return false;
+ }
+
+ int last_entry = EventsForPath(target_, &events_);
+ DCHECK_NE(last_entry, 0);
+
+ EventVector responses(last_entry);
+
+ int count = HANDLE_EINTR(kevent(kqueue_, &events_[0], last_entry,
+ &responses[0], last_entry, NULL));
+ if (!AreKeventValuesValid(&responses[0], count)) {
+ // Calling Cancel() here to close any file descriptors that were opened.
+ // This would happen in the destructor anyways, but FilePathWatchers tend to
+ // be long lived, and if an error has occurred, there is no reason to waste
+ // the file descriptors.
+ Cancel();
+ return false;
+ }
+
+ return MessageLoopForIO::current()->WatchFileDescriptor(
+ kqueue_, true, MessageLoopForIO::WATCH_READ, &kqueue_watcher_, this);
+}
+
+void FilePathWatcherImpl::Cancel() {
+ base::MessageLoopProxy* proxy = io_message_loop_.get();
+ if (!proxy) {
+ set_cancelled();
+ return;
+ }
+ if (!proxy->BelongsToCurrentThread()) {
+ proxy->PostTask(FROM_HERE,
+ base::Bind(&FilePathWatcherImpl::Cancel, this));
+ return;
+ }
+ CancelOnMessageLoopThread();
+}
+
+void FilePathWatcherImpl::CancelOnMessageLoopThread() {
+ DCHECK(MessageLoopForIO::current());
+ if (!is_cancelled()) {
+ set_cancelled();
+ kqueue_watcher_.StopWatchingFileDescriptor();
+ CloseFileDescriptor(&kqueue_);
+ std::for_each(events_.begin(), events_.end(), ReleaseEvent);
+ events_.clear();
+ io_message_loop_ = NULL;
+ MessageLoop::current()->RemoveDestructionObserver(this);
+ delegate_ = NULL;
+ }
+}
+
+} // namespace
+
+FilePathWatcher::FilePathWatcher() {
+ impl_ = new FilePathWatcherImpl();
+}
+
+} // namespace files
+} // namespace base
diff --git a/src/base/files/file_path_watcher_linux.cc b/src/base/files/file_path_watcher_linux.cc
new file mode 100644
index 0000000..9e55022
--- /dev/null
+++ b/src/base/files/file_path_watcher_linux.cc
@@ -0,0 +1,490 @@
+// Copyright (c) 2012 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/files/file_path_watcher.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/hash_tables.h"
+#include "base/lazy_instance.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread.h"
+
+namespace base {
+namespace files {
+
+namespace {
+
+class FilePathWatcherImpl;
+
+// Singleton to manage all inotify watches.
+// TODO(tony): It would be nice if this wasn't a singleton.
+// http://crbug.com/38174
+class InotifyReader {
+ public:
+ typedef int Watch; // Watch descriptor used by AddWatch and RemoveWatch.
+ static const Watch kInvalidWatch = -1;
+
+ // Watch directory |path| for changes. |watcher| will be notified on each
+ // change. Returns kInvalidWatch on failure.
+ Watch AddWatch(const FilePath& path, FilePathWatcherImpl* watcher);
+
+ // Remove |watch|. Returns true on success.
+ bool RemoveWatch(Watch watch, FilePathWatcherImpl* watcher);
+
+ // Callback for InotifyReaderTask.
+ void OnInotifyEvent(const inotify_event* event);
+
+ private:
+ friend struct ::base::DefaultLazyInstanceTraits<InotifyReader>;
+
+ typedef std::set<FilePathWatcherImpl*> WatcherSet;
+
+ InotifyReader();
+ ~InotifyReader();
+
+ // We keep track of which delegates want to be notified on which watches.
+ base::hash_map<Watch, WatcherSet> watchers_;
+
+ // Lock to protect watchers_.
+ base::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 FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
+ public MessageLoop::DestructionObserver {
+ public:
+ FilePathWatcherImpl();
+
+ // Called for each event coming from the watch. |fired_watch| identifies the
+ // watch that fired, |child| indicates what has changed, and is relative to
+ // the currently watched path for |fired_watch|. The flag |created| is true if
+ // the object appears.
+ void OnFilePathChanged(InotifyReader::Watch fired_watch,
+ const FilePath::StringType& child,
+ bool created);
+
+ // Start watching |path| for changes and notify |delegate| on each change.
+ // Returns true if watch for |path| has been added successfully.
+ virtual bool Watch(const FilePath& path,
+ bool recursive,
+ FilePathWatcher::Delegate* delegate) OVERRIDE;
+
+ // Cancel the watch. This unregisters the instance with InotifyReader.
+ virtual void Cancel() OVERRIDE;
+
+ // Deletion of the FilePathWatcher will call Cancel() to dispose of this
+ // object in the right thread. This also observes destruction of the required
+ // cleanup thread, in case it quits before Cancel() is called.
+ virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
+
+ protected:
+ virtual ~FilePathWatcherImpl() {}
+
+ private:
+ // Cleans up and stops observing the |message_loop_| thread.
+ virtual void CancelOnMessageLoopThread() OVERRIDE;
+
+ // Inotify watches are installed for all directory components of |target_|. A
+ // WatchEntry instance holds the watch descriptor for a component and the
+ // subdirectory for that identifies the next component. If a symbolic link
+ // is being watched, the target of the link is also kept.
+ struct WatchEntry {
+ WatchEntry(InotifyReader::Watch watch, const FilePath::StringType& subdir)
+ : watch_(watch),
+ subdir_(subdir) {}
+
+ InotifyReader::Watch watch_;
+ FilePath::StringType subdir_;
+ FilePath::StringType linkname_;
+ };
+ typedef std::vector<WatchEntry> WatchVector;
+
+ // Reconfigure to watch for the most specific parent directory of |target_|
+ // that exists. Updates |watched_path_|. Returns true on success.
+ bool UpdateWatches() WARN_UNUSED_RESULT;
+
+ // Delegate to notify upon changes.
+ scoped_refptr<FilePathWatcher::Delegate> delegate_;
+
+ // The file or directory we're supposed to watch.
+ FilePath target_;
+
+ // The vector of watches and next component names for all path components,
+ // starting at the root directory. The last entry corresponds to the watch for
+ // |target_| and always stores an empty next component name in |subdir_|.
+ WatchVector watches_;
+
+ DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
+};
+
+void InotifyReaderCallback(InotifyReader* reader, int inotify_fd,
+ int shutdown_fd) {
+ // Make sure the file descriptors are good for use with select().
+ CHECK_LE(0, inotify_fd);
+ CHECK_GT(FD_SETSIZE, inotify_fd);
+ CHECK_LE(0, shutdown_fd);
+ CHECK_GT(FD_SETSIZE, shutdown_fd);
+
+ 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) {
+ DPLOG(WARNING) << "select failed";
+ 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) {
+ DPLOG(WARNING) << "ioctl failed";
+ return;
+ }
+
+ std::vector<char> buffer(buffer_size);
+
+ ssize_t bytes_read = HANDLE_EINTR(read(inotify_fd, &buffer[0],
+ buffer_size));
+
+ if (bytes_read < 0) {
+ DPLOG(WARNING) << "read from inotify fd failed";
+ 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;
+ }
+ }
+}
+
+static base::LazyInstance<InotifyReader>::Leaky g_inotify_reader =
+ LAZY_INSTANCE_INITIALIZER;
+
+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, base::Bind(&InotifyReaderCallback, 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.
+ ssize_t ret = HANDLE_EINTR(write(shutdown_pipe_[1], "", 1));
+ DPCHECK(ret > 0);
+ DCHECK_EQ(ret, 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, FilePathWatcherImpl* watcher) {
+ if (!valid_)
+ return kInvalidWatch;
+
+ base::AutoLock auto_lock(lock_);
+
+ Watch watch = inotify_add_watch(inotify_fd_, path.value().c_str(),
+ IN_CREATE | IN_DELETE |
+ IN_CLOSE_WRITE | IN_MOVE |
+ IN_ONLYDIR);
+
+ if (watch == kInvalidWatch)
+ return kInvalidWatch;
+
+ watchers_[watch].insert(watcher);
+
+ return watch;
+}
+
+bool InotifyReader::RemoveWatch(Watch watch,
+ FilePathWatcherImpl* watcher) {
+ if (!valid_)
+ return false;
+
+ base::AutoLock auto_lock(lock_);
+
+ watchers_[watch].erase(watcher);
+
+ if (watchers_[watch].empty()) {
+ watchers_.erase(watch);
+ return (inotify_rm_watch(inotify_fd_, watch) == 0);
+ }
+
+ return true;
+}
+
+void InotifyReader::OnInotifyEvent(const inotify_event* event) {
+ if (event->mask & IN_IGNORED)
+ return;
+
+ FilePath::StringType child(event->len ? event->name : FILE_PATH_LITERAL(""));
+ base::AutoLock auto_lock(lock_);
+
+ for (WatcherSet::iterator watcher = watchers_[event->wd].begin();
+ watcher != watchers_[event->wd].end();
+ ++watcher) {
+ (*watcher)->OnFilePathChanged(event->wd,
+ child,
+ event->mask & (IN_CREATE | IN_MOVED_TO));
+ }
+}
+
+FilePathWatcherImpl::FilePathWatcherImpl()
+ : delegate_(NULL) {
+}
+
+void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch,
+ const FilePath::StringType& child,
+ bool created) {
+ if (!message_loop()->BelongsToCurrentThread()) {
+ // Switch to message_loop_ to access watches_ safely.
+ message_loop()->PostTask(FROM_HERE,
+ base::Bind(&FilePathWatcherImpl::OnFilePathChanged,
+ this,
+ fired_watch,
+ child,
+ created));
+ return;
+ }
+
+ DCHECK(MessageLoopForIO::current());
+
+ // Find the entry in |watches_| that corresponds to |fired_watch|.
+ WatchVector::const_iterator watch_entry(watches_.begin());
+ for ( ; watch_entry != watches_.end(); ++watch_entry) {
+ if (fired_watch == watch_entry->watch_) {
+ // Check whether a path component of |target_| changed.
+ bool change_on_target_path = child.empty() ||
+ ((child == watch_entry->subdir_) && watch_entry->linkname_.empty()) ||
+ (child == watch_entry->linkname_);
+
+ // Check whether the change references |target_| or a direct child.
+ DCHECK(watch_entry->subdir_.empty() ||
+ (watch_entry + 1) != watches_.end());
+ bool target_changed =
+ (watch_entry->subdir_.empty() && (child == watch_entry->linkname_)) ||
+ (watch_entry->subdir_.empty() && watch_entry->linkname_.empty()) ||
+ (watch_entry->subdir_ == child && (watch_entry + 1)->subdir_.empty());
+
+ // Update watches if a directory component of the |target_| path
+ // (dis)appears. Note that we don't add the additional restriction
+ // of checking the event mask to see if it is for a directory here
+ // as changes to symlinks on the target path will not have
+ // IN_ISDIR set in the event masks. As a result we may sometimes
+ // call UpdateWatches() unnecessarily.
+ if (change_on_target_path && !UpdateWatches()) {
+ delegate_->OnFilePathError(target_);
+ return;
+ }
+
+ // Report the following events:
+ // - The target or a direct child of the target got changed (in case the
+ // watched path refers to a directory).
+ // - One of the parent directories got moved or deleted, since the target
+ // disappears in this case.
+ // - One of the parent directories appears. The event corresponding to
+ // the target appearing might have been missed in this case, so
+ // recheck.
+ if (target_changed ||
+ (change_on_target_path && !created) ||
+ (change_on_target_path && file_util::PathExists(target_))) {
+ delegate_->OnFilePathChanged(target_);
+ return;
+ }
+ }
+ }
+}
+
+bool FilePathWatcherImpl::Watch(const FilePath& path,
+ bool recursive,
+ FilePathWatcher::Delegate* delegate) {
+ DCHECK(target_.empty());
+ DCHECK(MessageLoopForIO::current());
+ if (recursive) {
+ // Recursive watch is not supported on this platform.
+ NOTIMPLEMENTED();
+ return false;
+ }
+
+ set_message_loop(base::MessageLoopProxy::current());
+ delegate_ = delegate;
+ target_ = path;
+ MessageLoop::current()->AddDestructionObserver(this);
+
+ std::vector<FilePath::StringType> comps;
+ target_.GetComponents(&comps);
+ DCHECK(!comps.empty());
+ std::vector<FilePath::StringType>::const_iterator comp = comps.begin();
+ for (++comp; comp != comps.end(); ++comp)
+ watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, *comp));
+
+ watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch,
+ FilePath::StringType()));
+ return UpdateWatches();
+}
+
+void FilePathWatcherImpl::Cancel() {
+ if (!delegate_) {
+ // Watch was never called, or the |message_loop_| thread is already gone.
+ set_cancelled();
+ return;
+ }
+
+ // Switch to the message_loop_ if necessary so we can access |watches_|.
+ if (!message_loop()->BelongsToCurrentThread()) {
+ message_loop()->PostTask(FROM_HERE,
+ base::Bind(&FilePathWatcher::CancelWatch,
+ make_scoped_refptr(this)));
+ } else {
+ CancelOnMessageLoopThread();
+ }
+}
+
+void FilePathWatcherImpl::CancelOnMessageLoopThread() {
+ if (!is_cancelled())
+ set_cancelled();
+
+ if (delegate_) {
+ MessageLoop::current()->RemoveDestructionObserver(this);
+ delegate_ = NULL;
+ }
+
+ for (WatchVector::iterator watch_entry(watches_.begin());
+ watch_entry != watches_.end(); ++watch_entry) {
+ if (watch_entry->watch_ != InotifyReader::kInvalidWatch)
+ g_inotify_reader.Get().RemoveWatch(watch_entry->watch_, this);
+ }
+ watches_.clear();
+ target_.clear();
+}
+
+void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() {
+ CancelOnMessageLoopThread();
+}
+
+bool FilePathWatcherImpl::UpdateWatches() {
+ // Ensure this runs on the |message_loop_| exclusively in order to avoid
+ // concurrency issues.
+ DCHECK(message_loop()->BelongsToCurrentThread());
+
+ // Walk the list of watches and update them as we go.
+ FilePath path(FILE_PATH_LITERAL("/"));
+ bool path_valid = true;
+ for (WatchVector::iterator watch_entry(watches_.begin());
+ watch_entry != watches_.end(); ++watch_entry) {
+ InotifyReader::Watch old_watch = watch_entry->watch_;
+ if (path_valid) {
+ watch_entry->watch_ = g_inotify_reader.Get().AddWatch(path, this);
+ if ((watch_entry->watch_ == InotifyReader::kInvalidWatch) &&
+ file_util::IsLink(path)) {
+ FilePath link;
+ if (file_util::ReadSymbolicLink(path, &link)) {
+ if (!link.IsAbsolute())
+ link = path.DirName().Append(link);
+ // Try watching symlink target directory. If the link target is "/",
+ // then we shouldn't get here in normal situations and if we do, we'd
+ // watch "/" for changes to a component "/" which is harmless so no
+ // special treatment of this case is required.
+ watch_entry->watch_ =
+ g_inotify_reader.Get().AddWatch(link.DirName(), this);
+ if (watch_entry->watch_ != InotifyReader::kInvalidWatch) {
+ watch_entry->linkname_ = link.BaseName().value();
+ } else {
+ DPLOG(WARNING) << "Watch failed for " << link.DirName().value();
+ // TODO(craig) Symlinks only work if the parent directory
+ // for the target exist. Ideally we should make sure we've
+ // watched all the components of the symlink path for
+ // changes. See crbug.com/91561 for details.
+ }
+ }
+ }
+ if (watch_entry->watch_ == InotifyReader::kInvalidWatch) {
+ path_valid = false;
+ }
+ } else {
+ watch_entry->watch_ = InotifyReader::kInvalidWatch;
+ }
+ if (old_watch != InotifyReader::kInvalidWatch &&
+ old_watch != watch_entry->watch_) {
+ g_inotify_reader.Get().RemoveWatch(old_watch, this);
+ }
+ path = path.Append(watch_entry->subdir_);
+ }
+
+ return true;
+}
+
+} // namespace
+
+FilePathWatcher::FilePathWatcher() {
+ impl_ = new FilePathWatcherImpl();
+}
+
+} // namespace files
+} // namespace base
diff --git a/src/base/files/file_path_watcher_stub.cc b/src/base/files/file_path_watcher_stub.cc
new file mode 100644
index 0000000..0c25d7f
--- /dev/null
+++ b/src/base/files/file_path_watcher_stub.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 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 Unix systems which don't have the inotify headers, and
+// thus cannot build file_watcher_inotify.cc
+
+#include "base/files/file_path_watcher.h"
+
+namespace base {
+namespace files {
+
+namespace {
+
+class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate {
+ public:
+ virtual bool Watch(const FilePath& path,
+ bool recursive,
+ FilePathWatcher::Delegate* delegate) OVERRIDE {
+ return false;
+ }
+
+ virtual void Cancel() OVERRIDE {}
+
+ virtual void CancelOnMessageLoopThread() OVERRIDE {}
+
+ protected:
+ virtual ~FilePathWatcherImpl() {}
+};
+
+} // namespace
+
+FilePathWatcher::FilePathWatcher() {
+ impl_ = new FilePathWatcherImpl();
+}
+
+} // namespace files
+} // namespace base
diff --git a/src/base/files/file_path_watcher_win.cc b/src/base/files/file_path_watcher_win.cc
new file mode 100644
index 0000000..c25260c
--- /dev/null
+++ b/src/base/files/file_path_watcher_win.cc
@@ -0,0 +1,295 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_path_watcher.h"
+
+#include "base/bind.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop_proxy.h"
+#include "base/time.h"
+#include "base/win/object_watcher.h"
+
+namespace base {
+namespace files {
+
+namespace {
+
+class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
+ public base::win::ObjectWatcher::Delegate,
+ public MessageLoop::DestructionObserver {
+ public:
+ FilePathWatcherImpl()
+ : delegate_(NULL),
+ handle_(INVALID_HANDLE_VALUE),
+ recursive_watch_(false) {}
+
+ // FilePathWatcher::PlatformDelegate overrides.
+ virtual bool Watch(const FilePath& path,
+ bool recursive,
+ FilePathWatcher::Delegate* delegate) OVERRIDE;
+ virtual void Cancel() OVERRIDE;
+
+ // Deletion of the FilePathWatcher will call Cancel() to dispose of this
+ // object in the right thread. This also observes destruction of the required
+ // cleanup thread, in case it quits before Cancel() is called.
+ virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
+
+ // Callback from MessageLoopForIO.
+ virtual void OnObjectSignaled(HANDLE object);
+
+ private:
+ virtual ~FilePathWatcherImpl() {}
+
+ // Setup a watch handle for directory |dir|. Set |recursive| to true to watch
+ // the directory sub trees. Returns true if no fatal error occurs. |handle|
+ // will receive the handle value if |dir| is watchable, otherwise
+ // INVALID_HANDLE_VALUE.
+ static bool SetupWatchHandle(const FilePath& dir,
+ bool recursive,
+ HANDLE* handle) WARN_UNUSED_RESULT;
+
+ // (Re-)Initialize the watch handle.
+ bool UpdateWatch() WARN_UNUSED_RESULT;
+
+ // Destroy the watch handle.
+ void DestroyWatch();
+
+ // Cleans up and stops observing the |message_loop_| thread.
+ void CancelOnMessageLoopThread() OVERRIDE;
+
+ // Delegate to notify upon changes.
+ scoped_refptr<FilePathWatcher::Delegate> delegate_;
+
+ // Path we're supposed to watch (passed to delegate).
+ FilePath target_;
+
+ // Handle for FindFirstChangeNotification.
+ HANDLE handle_;
+
+ // ObjectWatcher to watch handle_ for events.
+ base::win::ObjectWatcher watcher_;
+
+ // Set to true to watch the sub trees of the specified directory file path.
+ bool recursive_watch_;
+
+ // Keep track of the last modified time of the file. We use nulltime
+ // to represent the file not existing.
+ base::Time last_modified_;
+
+ // The time at which we processed the first notification with the
+ // |last_modified_| time stamp.
+ base::Time first_notification_;
+
+ DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
+};
+
+bool FilePathWatcherImpl::Watch(const FilePath& path,
+ bool recursive,
+ FilePathWatcher::Delegate* delegate) {
+ DCHECK(target_.value().empty()); // Can only watch one path.
+
+ set_message_loop(base::MessageLoopProxy::current());
+ delegate_ = delegate;
+ target_ = path;
+ recursive_watch_ = recursive;
+ MessageLoop::current()->AddDestructionObserver(this);
+
+ if (!UpdateWatch())
+ return false;
+
+ watcher_.StartWatching(handle_, this);
+
+ return true;
+}
+
+void FilePathWatcherImpl::Cancel() {
+ if (!delegate_) {
+ // Watch was never called, or the |message_loop_| has already quit.
+ set_cancelled();
+ return;
+ }
+
+ // Switch to the file thread if necessary so we can stop |watcher_|.
+ if (!message_loop()->BelongsToCurrentThread()) {
+ message_loop()->PostTask(FROM_HERE,
+ base::Bind(&FilePathWatcher::CancelWatch,
+ make_scoped_refptr(this)));
+ } else {
+ CancelOnMessageLoopThread();
+ }
+}
+
+void FilePathWatcherImpl::CancelOnMessageLoopThread() {
+ set_cancelled();
+
+ if (handle_ != INVALID_HANDLE_VALUE)
+ DestroyWatch();
+
+ if (delegate_) {
+ MessageLoop::current()->RemoveDestructionObserver(this);
+ delegate_ = NULL;
+ }
+}
+
+void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() {
+ CancelOnMessageLoopThread();
+}
+
+void FilePathWatcherImpl::OnObjectSignaled(HANDLE object) {
+ DCHECK(object == handle_);
+ // Make sure we stay alive through the body of this function.
+ scoped_refptr<FilePathWatcherImpl> keep_alive(this);
+
+ if (!UpdateWatch()) {
+ delegate_->OnFilePathError(target_);
+ return;
+ }
+
+ // Check whether the event applies to |target_| and notify the delegate.
+ base::PlatformFileInfo file_info;
+ bool file_exists = file_util::GetFileInfo(target_, &file_info);
+ if (file_exists && (last_modified_.is_null() ||
+ last_modified_ != file_info.last_modified)) {
+ last_modified_ = file_info.last_modified;
+ first_notification_ = base::Time::Now();
+ delegate_->OnFilePathChanged(target_);
+ } else if (file_exists && !first_notification_.is_null()) {
+ // The target's last modification time is equal to what's on record. This
+ // means that either an unrelated event occurred, or the target changed
+ // again (file modification times only have a resolution of 1s). Comparing
+ // file modification times against the wall clock is not reliable to find
+ // out whether the change is recent, since this code might just run too
+ // late. Moreover, there's no guarantee that file modification time and wall
+ // clock times come from the same source.
+ //
+ // Instead, the time at which the first notification carrying the current
+ // |last_notified_| time stamp is recorded. Later notifications that find
+ // the same file modification time only need to be forwarded until wall
+ // clock has advanced one second from the initial notification. After that
+ // interval, client code is guaranteed to having seen the current revision
+ // of the file.
+ if (base::Time::Now() - first_notification_ >
+ base::TimeDelta::FromSeconds(1)) {
+ // Stop further notifications for this |last_modification_| time stamp.
+ first_notification_ = base::Time();
+ }
+ delegate_->OnFilePathChanged(target_);
+ } else if (!file_exists && !last_modified_.is_null()) {
+ last_modified_ = base::Time();
+ delegate_->OnFilePathChanged(target_);
+ }
+
+ // The watch may have been cancelled by the callback.
+ if (handle_ != INVALID_HANDLE_VALUE)
+ watcher_.StartWatching(handle_, this);
+}
+
+// static
+bool FilePathWatcherImpl::SetupWatchHandle(const FilePath& dir,
+ bool recursive,
+ HANDLE* handle) {
+ *handle = FindFirstChangeNotification(
+ dir.value().c_str(),
+ recursive,
+ FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE |
+ FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME |
+ FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SECURITY);
+ if (*handle != INVALID_HANDLE_VALUE) {
+ // Make sure the handle we got points to an existing directory. It seems
+ // that windows sometimes hands out watches to directories that are
+ // about to go away, but doesn't sent notifications if that happens.
+ if (!file_util::DirectoryExists(dir)) {
+ FindCloseChangeNotification(*handle);
+ *handle = INVALID_HANDLE_VALUE;
+ }
+ return true;
+ }
+
+ // If FindFirstChangeNotification failed because the target directory
+ // doesn't exist, access is denied (happens if the file is already gone but
+ // there are still handles open), or the target is not a directory, try the
+ // immediate parent directory instead.
+ DWORD error_code = GetLastError();
+ if (error_code != ERROR_FILE_NOT_FOUND &&
+ error_code != ERROR_PATH_NOT_FOUND &&
+ error_code != ERROR_ACCESS_DENIED &&
+ error_code != ERROR_SHARING_VIOLATION &&
+ error_code != ERROR_DIRECTORY) {
+ using ::operator<<; // Pick the right operator<< below.
+ DPLOG(ERROR) << "FindFirstChangeNotification failed for "
+ << dir.value();
+ return false;
+ }
+
+ return true;
+}
+
+bool FilePathWatcherImpl::UpdateWatch() {
+ if (handle_ != INVALID_HANDLE_VALUE)
+ DestroyWatch();
+
+ base::PlatformFileInfo file_info;
+ if (file_util::GetFileInfo(target_, &file_info)) {
+ last_modified_ = file_info.last_modified;
+ first_notification_ = base::Time::Now();
+ }
+
+ // Start at the target and walk up the directory chain until we succesfully
+ // create a watch handle in |handle_|. |child_dirs| keeps a stack of child
+ // directories stripped from target, in reverse order.
+ std::vector<FilePath> child_dirs;
+ FilePath watched_path(target_);
+ while (true) {
+ if (!SetupWatchHandle(watched_path, recursive_watch_, &handle_))
+ return false;
+
+ // Break if a valid handle is returned. Try the parent directory otherwise.
+ if (handle_ != INVALID_HANDLE_VALUE)
+ break;
+
+ // Abort if we hit the root directory.
+ child_dirs.push_back(watched_path.BaseName());
+ FilePath parent(watched_path.DirName());
+ if (parent == watched_path) {
+ DLOG(ERROR) << "Reached the root directory";
+ return false;
+ }
+ watched_path = parent;
+ }
+
+ // At this point, handle_ is valid. However, the bottom-up search that the
+ // above code performs races against directory creation. So try to walk back
+ // down and see whether any children appeared in the mean time.
+ while (!child_dirs.empty()) {
+ watched_path = watched_path.Append(child_dirs.back());
+ child_dirs.pop_back();
+ HANDLE temp_handle = INVALID_HANDLE_VALUE;
+ if (!SetupWatchHandle(watched_path, recursive_watch_, &temp_handle))
+ return false;
+ if (temp_handle == INVALID_HANDLE_VALUE)
+ break;
+ FindCloseChangeNotification(handle_);
+ handle_ = temp_handle;
+ }
+
+ return true;
+}
+
+void FilePathWatcherImpl::DestroyWatch() {
+ watcher_.StopWatching();
+ FindCloseChangeNotification(handle_);
+ handle_ = INVALID_HANDLE_VALUE;
+}
+
+} // namespace
+
+FilePathWatcher::FilePathWatcher() {
+ impl_ = new FilePathWatcherImpl();
+}
+
+} // namespace files
+} // namespace base
diff --git a/src/base/files/important_file_writer.cc b/src/base/files/important_file_writer.cc
new file mode 100644
index 0000000..351adc2
--- /dev/null
+++ b/src/base/files/important_file_writer.cc
@@ -0,0 +1,167 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/important_file_writer.h"
+
+#include <stdio.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/critical_closure.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/task_runner.h"
+#include "base/metrics/histogram.h"
+#include "base/string_number_conversions.h"
+#include "base/threading/thread.h"
+#include "base/time.h"
+
+namespace base {
+
+namespace {
+
+const int kDefaultCommitIntervalMs = 10000;
+
+enum TempFileFailure {
+ FAILED_CREATING,
+ FAILED_OPENING,
+ FAILED_CLOSING,
+ FAILED_WRITING,
+ FAILED_RENAMING,
+ TEMP_FILE_FAILURE_MAX
+};
+
+void LogFailure(const FilePath& path, TempFileFailure failure_code,
+ const std::string& message) {
+ UMA_HISTOGRAM_ENUMERATION("ImportantFile.TempFileFailures", failure_code,
+ TEMP_FILE_FAILURE_MAX);
+ DPLOG(WARNING) << "temp file failure: " << path.value().c_str()
+ << " : " << message;
+}
+
+} // namespace
+
+// static
+bool ImportantFileWriter::WriteFileAtomically(const FilePath& path,
+ const std::string& data) {
+ // Write the data to a temp file then rename to avoid data loss if we crash
+ // while writing the file. Ensure that the temp file is on the same volume
+ // as target file, so it can be moved in one step, and that the temp file
+ // is securely created.
+ FilePath tmp_file_path;
+ if (!file_util::CreateTemporaryFileInDir(path.DirName(), &tmp_file_path)) {
+ LogFailure(path, FAILED_CREATING, "could not create temporary file");
+ return false;
+ }
+
+ int flags = PLATFORM_FILE_OPEN | PLATFORM_FILE_WRITE;
+ PlatformFile tmp_file =
+ CreatePlatformFile(tmp_file_path, flags, NULL, NULL);
+ if (tmp_file == kInvalidPlatformFileValue) {
+ LogFailure(path, FAILED_OPENING, "could not open temporary file");
+ return false;
+ }
+
+ // If this happens in the wild something really bad is going on.
+ CHECK_LE(data.length(), static_cast<size_t>(kint32max));
+ int bytes_written = WritePlatformFile(
+ tmp_file, 0, data.data(), static_cast<int>(data.length()));
+ FlushPlatformFile(tmp_file); // Ignore return value.
+
+ if (!ClosePlatformFile(tmp_file)) {
+ LogFailure(path, FAILED_CLOSING, "failed to close temporary file");
+ file_util::Delete(tmp_file_path, false);
+ return false;
+ }
+
+ if (bytes_written < static_cast<int>(data.length())) {
+ LogFailure(path, FAILED_WRITING, "error writing, bytes_written=" +
+ IntToString(bytes_written));
+ file_util::Delete(tmp_file_path, false);
+ return false;
+ }
+
+ if (!file_util::ReplaceFile(tmp_file_path, path)) {
+ LogFailure(path, FAILED_RENAMING, "could not rename temporary file");
+ file_util::Delete(tmp_file_path, false);
+ return false;
+ }
+
+ return true;
+}
+
+ImportantFileWriter::ImportantFileWriter(
+ const FilePath& path, base::SequencedTaskRunner* task_runner)
+ : path_(path),
+ task_runner_(task_runner),
+ serializer_(NULL),
+ commit_interval_(TimeDelta::FromMilliseconds(
+ kDefaultCommitIntervalMs)) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(task_runner_.get());
+}
+
+ImportantFileWriter::~ImportantFileWriter() {
+ // We're usually a member variable of some other object, which also tends
+ // to be our serializer. It may not be safe to call back to the parent object
+ // being destructed.
+ DCHECK(!HasPendingWrite());
+}
+
+bool ImportantFileWriter::HasPendingWrite() const {
+ DCHECK(CalledOnValidThread());
+ return timer_.IsRunning();
+}
+
+void ImportantFileWriter::WriteNow(const std::string& data) {
+ DCHECK(CalledOnValidThread());
+ if (data.length() > static_cast<size_t>(kint32max)) {
+ NOTREACHED();
+ return;
+ }
+
+ if (HasPendingWrite())
+ timer_.Stop();
+
+ if (!task_runner_->PostTask(
+ FROM_HERE,
+ MakeCriticalClosure(
+ Bind(IgnoreResult(&ImportantFileWriter::WriteFileAtomically),
+ path_, data)))) {
+ // Posting the task to background message loop is not expected
+ // to fail, but if it does, avoid losing data and just hit the disk
+ // on the current thread.
+ NOTREACHED();
+
+ WriteFileAtomically(path_, data);
+ }
+}
+
+void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) {
+ DCHECK(CalledOnValidThread());
+
+ DCHECK(serializer);
+ serializer_ = serializer;
+
+ if (!timer_.IsRunning()) {
+ timer_.Start(FROM_HERE, commit_interval_, this,
+ &ImportantFileWriter::DoScheduledWrite);
+ }
+}
+
+void ImportantFileWriter::DoScheduledWrite() {
+ DCHECK(serializer_);
+ std::string data;
+ if (serializer_->SerializeData(&data)) {
+ WriteNow(data);
+ } else {
+ DLOG(WARNING) << "failed to serialize data to be saved in "
+ << path_.value().c_str();
+ }
+ serializer_ = NULL;
+}
+
+} // namespace base
diff --git a/src/base/files/important_file_writer.h b/src/base/files/important_file_writer.h
new file mode 100644
index 0000000..9bc8f07
--- /dev/null
+++ b/src/base/files/important_file_writer.h
@@ -0,0 +1,121 @@
+// Copyright (c) 2012 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_FILES_IMPORTANT_FILE_WRITER_H_
+#define BASE_FILES_IMPORTANT_FILE_WRITER_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/time.h"
+#include "base/timer.h"
+
+namespace base {
+
+class SequencedTaskRunner;
+class Thread;
+
+// Helper to ensure that a file won't be corrupted by the write (for example on
+// application crash). Consider a naive way to save an important file F:
+//
+// 1. Open F for writing, truncating it.
+// 2. Write new data to F.
+//
+// It's good when it works, but it gets very bad if step 2. doesn't complete.
+// It can be caused by a crash, a computer hang, or a weird I/O error. And you
+// end up with a broken file.
+//
+// To be safe, we don't start with writing directly to F. Instead, we write to
+// to a temporary file. Only after that write is successful, we rename the
+// temporary file to target filename.
+//
+// If you want to know more about this approach and ext3/ext4 fsync issues, see
+// http://valhenson.livejournal.com/37921.html
+class BASE_EXPORT ImportantFileWriter : public NonThreadSafe {
+ public:
+ // Used by ScheduleSave to lazily provide the data to be saved. Allows us
+ // to also batch data serializations.
+ class BASE_EXPORT DataSerializer {
+ public:
+ // Should put serialized string in |data| and return true on successful
+ // serialization. Will be called on the same thread on which
+ // ImportantFileWriter has been created.
+ virtual bool SerializeData(std::string* data) = 0;
+
+ protected:
+ virtual ~DataSerializer() {}
+ };
+
+ // Save |data| to |path| in an atomic manner (see the class comment above).
+ // Blocks and writes data on the current thread.
+ static bool WriteFileAtomically(const FilePath& path,
+ const std::string& data);
+
+ // Initialize the writer.
+ // |path| is the name of file to write.
+ // |task_runner| is the SequencedTaskRunner instance where on which we will
+ // execute file I/O operations.
+ // All non-const methods, ctor and dtor must be called on the same thread.
+ ImportantFileWriter(const FilePath& path,
+ base::SequencedTaskRunner* task_runner);
+
+ // You have to ensure that there are no pending writes at the moment
+ // of destruction.
+ ~ImportantFileWriter();
+
+ const FilePath& path() const { return path_; }
+
+ // Returns true if there is a scheduled write pending which has not yet
+ // been started.
+ bool HasPendingWrite() const;
+
+ // Save |data| to target filename. Does not block. If there is a pending write
+ // scheduled by ScheduleWrite, it is cancelled.
+ void WriteNow(const std::string& data);
+
+ // Schedule a save to target filename. Data will be serialized and saved
+ // to disk after the commit interval. If another ScheduleWrite is issued
+ // before that, only one serialization and write to disk will happen, and
+ // the most recent |serializer| will be used. This operation does not block.
+ // |serializer| should remain valid through the lifetime of
+ // ImportantFileWriter.
+ void ScheduleWrite(DataSerializer* serializer);
+
+ // Serialize data pending to be saved and execute write on backend thread.
+ void DoScheduledWrite();
+
+ TimeDelta commit_interval() const {
+ return commit_interval_;
+ }
+
+ void set_commit_interval(const TimeDelta& interval) {
+ commit_interval_ = interval;
+ }
+
+ private:
+ // Path being written to.
+ const FilePath path_;
+
+ // TaskRunner for the thread on which file I/O can be done.
+ const scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+ // Timer used to schedule commit after ScheduleWrite.
+ OneShotTimer<ImportantFileWriter> timer_;
+
+ // Serializer which will provide the data to be saved.
+ DataSerializer* serializer_;
+
+ // Time delta after which scheduled data will be written to disk.
+ TimeDelta commit_interval_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImportantFileWriter);
+};
+
+} // namespace base
+
+#endif // BASE_FILES_IMPORTANT_FILE_WRITER_H_
diff --git a/src/base/files/important_file_writer_unittest.cc b/src/base/files/important_file_writer_unittest.cc
new file mode 100644
index 0000000..3bd3016
--- /dev/null
+++ b/src/base/files/important_file_writer_unittest.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2012 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/files/important_file_writer.h"
+
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/threading/thread.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+std::string GetFileContent(const FilePath& path) {
+ std::string content;
+ if (!file_util::ReadFileToString(path, &content)) {
+ NOTREACHED();
+ }
+ return content;
+}
+
+class DataSerializer : public ImportantFileWriter::DataSerializer {
+ public:
+ explicit DataSerializer(const std::string& data) : data_(data) {
+ }
+
+ virtual bool SerializeData(std::string* output) {
+ output->assign(data_);
+ return true;
+ }
+
+ private:
+ const std::string data_;
+};
+
+} // namespace
+
+class ImportantFileWriterTest : public testing::Test {
+ public:
+ ImportantFileWriterTest() { }
+ virtual void SetUp() {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ file_ = temp_dir_.path().AppendASCII("test-file");
+ }
+
+ protected:
+ FilePath file_;
+ MessageLoop loop_;
+
+ private:
+ ScopedTempDir temp_dir_;
+};
+
+TEST_F(ImportantFileWriterTest, Basic) {
+ ImportantFileWriter writer(file_,
+ MessageLoopProxy::current());
+ EXPECT_FALSE(file_util::PathExists(writer.path()));
+ writer.WriteNow("foo");
+ loop_.RunUntilIdle();
+
+ ASSERT_TRUE(file_util::PathExists(writer.path()));
+ EXPECT_EQ("foo", GetFileContent(writer.path()));
+}
+
+TEST_F(ImportantFileWriterTest, ScheduleWrite) {
+ ImportantFileWriter writer(file_,
+ MessageLoopProxy::current());
+ writer.set_commit_interval(TimeDelta::FromMilliseconds(25));
+ EXPECT_FALSE(writer.HasPendingWrite());
+ DataSerializer serializer("foo");
+ writer.ScheduleWrite(&serializer);
+ EXPECT_TRUE(writer.HasPendingWrite());
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ MessageLoop::QuitClosure(),
+ TimeDelta::FromMilliseconds(100));
+ MessageLoop::current()->Run();
+ EXPECT_FALSE(writer.HasPendingWrite());
+ ASSERT_TRUE(file_util::PathExists(writer.path()));
+ EXPECT_EQ("foo", GetFileContent(writer.path()));
+}
+
+TEST_F(ImportantFileWriterTest, DoScheduledWrite) {
+ ImportantFileWriter writer(file_,
+ MessageLoopProxy::current());
+ EXPECT_FALSE(writer.HasPendingWrite());
+ DataSerializer serializer("foo");
+ writer.ScheduleWrite(&serializer);
+ EXPECT_TRUE(writer.HasPendingWrite());
+ writer.DoScheduledWrite();
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ MessageLoop::QuitClosure(),
+ TimeDelta::FromMilliseconds(100));
+ MessageLoop::current()->Run();
+ EXPECT_FALSE(writer.HasPendingWrite());
+ ASSERT_TRUE(file_util::PathExists(writer.path()));
+ EXPECT_EQ("foo", GetFileContent(writer.path()));
+}
+
+TEST_F(ImportantFileWriterTest, BatchingWrites) {
+ ImportantFileWriter writer(file_,
+ MessageLoopProxy::current());
+ writer.set_commit_interval(TimeDelta::FromMilliseconds(25));
+ DataSerializer foo("foo"), bar("bar"), baz("baz");
+ writer.ScheduleWrite(&foo);
+ writer.ScheduleWrite(&bar);
+ writer.ScheduleWrite(&baz);
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ MessageLoop::QuitClosure(),
+ TimeDelta::FromMilliseconds(100));
+ MessageLoop::current()->Run();
+ ASSERT_TRUE(file_util::PathExists(writer.path()));
+ EXPECT_EQ("baz", GetFileContent(writer.path()));
+}
+
+} // namespace base
diff --git a/src/base/files/scoped_temp_dir.cc b/src/base/files/scoped_temp_dir.cc
new file mode 100644
index 0000000..509f808
--- /dev/null
+++ b/src/base/files/scoped_temp_dir.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/scoped_temp_dir.h"
+
+#include "base/file_util.h"
+#include "base/logging.h"
+
+namespace base {
+
+ScopedTempDir::ScopedTempDir() {
+}
+
+ScopedTempDir::~ScopedTempDir() {
+ if (!path_.empty() && !Delete())
+ DLOG(WARNING) << "Could not delete temp dir in dtor.";
+}
+
+bool ScopedTempDir::CreateUniqueTempDir() {
+ if (!path_.empty())
+ return false;
+
+ // This "scoped_dir" prefix is only used on Windows and serves as a template
+ // for the unique name.
+ if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("scoped_dir"),
+ &path_))
+ return false;
+
+ return true;
+}
+
+bool ScopedTempDir::CreateUniqueTempDirUnderPath(const FilePath& base_path) {
+ if (!path_.empty())
+ return false;
+
+ // If |base_path| does not exist, create it.
+ if (!file_util::CreateDirectory(base_path))
+ return false;
+
+ // Create a new, uniquely named directory under |base_path|.
+ if (!file_util::CreateTemporaryDirInDir(
+ base_path,
+ FILE_PATH_LITERAL("scoped_dir_"),
+ &path_))
+ return false;
+
+ return true;
+}
+
+bool ScopedTempDir::Set(const FilePath& path) {
+ if (!path_.empty())
+ return false;
+
+ if (!file_util::DirectoryExists(path) &&
+ !file_util::CreateDirectory(path))
+ return false;
+
+ path_ = path;
+ return true;
+}
+
+bool ScopedTempDir::Delete() {
+ if (path_.empty())
+ return false;
+
+ bool ret = file_util::Delete(path_, true);
+ if (ret) {
+ // We only clear the path if deleted the directory.
+ path_.clear();
+ }
+
+ return ret;
+}
+
+FilePath ScopedTempDir::Take() {
+ FilePath ret = path_;
+ path_ = FilePath();
+ return ret;
+}
+
+bool ScopedTempDir::IsValid() const {
+ return !path_.empty() && file_util::DirectoryExists(path_);
+}
+
+} // namespace base
diff --git a/src/base/files/scoped_temp_dir.h b/src/base/files/scoped_temp_dir.h
new file mode 100644
index 0000000..641e7ef
--- /dev/null
+++ b/src/base/files/scoped_temp_dir.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_FILES_SCOPED_TEMP_DIR_H_
+#define BASE_FILES_SCOPED_TEMP_DIR_H_
+
+// An object representing a temporary / scratch directory that should be cleaned
+// up (recursively) when this object goes out of scope. Note that since
+// deletion occurs during the destructor, no further error handling is possible
+// if the directory fails to be deleted. As a result, deletion is not
+// guaranteed by this class.
+//
+// Multiple calls to the methods which establish a temporary directory
+// (CreateUniqueTempDir, CreateUniqueTempDirUnderPath, and Set) must have
+// intervening calls to Delete or Take, or the calls will fail.
+
+#include "base/base_export.h"
+#include "base/file_path.h"
+
+namespace base {
+
+class BASE_EXPORT ScopedTempDir {
+ public:
+ // No directory is owned/created initially.
+ ScopedTempDir();
+
+ // Recursively delete path.
+ ~ScopedTempDir();
+
+ // Creates a unique directory in TempPath, and takes ownership of it.
+ // See file_util::CreateNewTemporaryDirectory.
+ bool CreateUniqueTempDir() WARN_UNUSED_RESULT;
+
+ // Creates a unique directory under a given path, and takes ownership of it.
+ bool CreateUniqueTempDirUnderPath(const FilePath& path) WARN_UNUSED_RESULT;
+
+ // Takes ownership of directory at |path|, creating it if necessary.
+ // Don't call multiple times unless Take() has been called first.
+ bool Set(const FilePath& path) WARN_UNUSED_RESULT;
+
+ // Deletes the temporary directory wrapped by this object.
+ bool Delete() WARN_UNUSED_RESULT;
+
+ // Caller takes ownership of the temporary directory so it won't be destroyed
+ // when this object goes out of scope.
+ FilePath Take();
+
+ const FilePath& path() const { return path_; }
+
+ // Returns true if path_ is non-empty and exists.
+ bool IsValid() const;
+
+ private:
+ FilePath path_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedTempDir);
+};
+
+} // namespace base
+
+#endif // BASE_FILES_SCOPED_TEMP_DIR_H_
diff --git a/src/base/files/scoped_temp_dir_unittest.cc b/src/base/files/scoped_temp_dir_unittest.cc
new file mode 100644
index 0000000..8497ac6
--- /dev/null
+++ b/src/base/files/scoped_temp_dir_unittest.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/platform_file.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(ScopedTempDir, FullPath) {
+ FilePath test_path;
+ file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("scoped_temp_dir"),
+ &test_path);
+
+ // Against an existing dir, it should get destroyed when leaving scope.
+ EXPECT_TRUE(file_util::DirectoryExists(test_path));
+ {
+ ScopedTempDir dir;
+ EXPECT_TRUE(dir.Set(test_path));
+ EXPECT_TRUE(dir.IsValid());
+ }
+ EXPECT_FALSE(file_util::DirectoryExists(test_path));
+
+ {
+ ScopedTempDir dir;
+ EXPECT_TRUE(dir.Set(test_path));
+ // Now the dir doesn't exist, so ensure that it gets created.
+ EXPECT_TRUE(file_util::DirectoryExists(test_path));
+ // When we call Release(), it shouldn't get destroyed when leaving scope.
+ FilePath path = dir.Take();
+ EXPECT_EQ(path.value(), test_path.value());
+ EXPECT_FALSE(dir.IsValid());
+ }
+ EXPECT_TRUE(file_util::DirectoryExists(test_path));
+
+ // Clean up.
+ {
+ ScopedTempDir dir;
+ EXPECT_TRUE(dir.Set(test_path));
+ }
+ EXPECT_FALSE(file_util::DirectoryExists(test_path));
+}
+
+TEST(ScopedTempDir, TempDir) {
+ // In this case, just verify that a directory was created and that it's a
+ // child of TempDir.
+ FilePath test_path;
+ {
+ ScopedTempDir dir;
+ EXPECT_TRUE(dir.CreateUniqueTempDir());
+ test_path = dir.path();
+ EXPECT_TRUE(file_util::DirectoryExists(test_path));
+ FilePath tmp_dir;
+ EXPECT_TRUE(file_util::GetTempDir(&tmp_dir));
+ EXPECT_TRUE(test_path.value().find(tmp_dir.value()) != std::string::npos);
+ }
+ EXPECT_FALSE(file_util::DirectoryExists(test_path));
+}
+
+TEST(ScopedTempDir, UniqueTempDirUnderPath) {
+ // Create a path which will contain a unique temp path.
+ FilePath base_path;
+ ASSERT_TRUE(file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("base_dir"),
+ &base_path));
+
+ FilePath test_path;
+ {
+ ScopedTempDir dir;
+ EXPECT_TRUE(dir.CreateUniqueTempDirUnderPath(base_path));
+ test_path = dir.path();
+ EXPECT_TRUE(file_util::DirectoryExists(test_path));
+ EXPECT_TRUE(base_path.IsParent(test_path));
+ EXPECT_TRUE(test_path.value().find(base_path.value()) != std::string::npos);
+ }
+ EXPECT_FALSE(file_util::DirectoryExists(test_path));
+ file_util::Delete(base_path, true);
+}
+
+TEST(ScopedTempDir, MultipleInvocations) {
+ ScopedTempDir dir;
+ EXPECT_TRUE(dir.CreateUniqueTempDir());
+ EXPECT_FALSE(dir.CreateUniqueTempDir());
+ EXPECT_TRUE(dir.Delete());
+ EXPECT_TRUE(dir.CreateUniqueTempDir());
+ EXPECT_FALSE(dir.CreateUniqueTempDir());
+ ScopedTempDir other_dir;
+ EXPECT_TRUE(other_dir.Set(dir.Take()));
+ EXPECT_TRUE(dir.CreateUniqueTempDir());
+ EXPECT_FALSE(dir.CreateUniqueTempDir());
+ EXPECT_FALSE(other_dir.CreateUniqueTempDir());
+}
+
+#if defined(OS_WIN)
+TEST(ScopedTempDir, LockedTempDir) {
+ ScopedTempDir dir;
+ EXPECT_TRUE(dir.CreateUniqueTempDir());
+ int file_flags = base::PLATFORM_FILE_CREATE_ALWAYS |
+ base::PLATFORM_FILE_WRITE;
+ base::PlatformFileError error_code = base::PLATFORM_FILE_OK;
+ FilePath file_path(dir.path().Append(FILE_PATH_LITERAL("temp")));
+ base::PlatformFile file = base::CreatePlatformFile(file_path, file_flags,
+ NULL, &error_code);
+ EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
+ EXPECT_FALSE(dir.Delete()); // We should not be able to delete.
+ EXPECT_FALSE(dir.path().empty()); // We should still have a valid path.
+ EXPECT_TRUE(base::ClosePlatformFile(file));
+ // Now, we should be able to delete.
+ EXPECT_TRUE(dir.Delete());
+}
+#endif // defined(OS_WIN)
+
+} // namespace base
diff --git a/src/base/float_util.h b/src/base/float_util.h
new file mode 100644
index 0000000..e2fa484
--- /dev/null
+++ b/src/base/float_util.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 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_FLOAT_UTIL_H_
+#define BASE_FLOAT_UTIL_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_STARBOARD)
+#include "starboard/double.h"
+#else
+#include <float.h>
+#include <math.h>
+#include <cmath>
+#endif
+
+namespace base {
+
+inline bool IsFinite(const double& number) {
+#if defined(OS_STARBOARD)
+ return SbDoubleIsFinite(number);
+#elif defined(__LB_SHELL__) && defined(__LB_LINUX__)
+ // On Linux, math.h defines fpclassify() as a macro which is undefined in
+ // cmath. However, as math.h has guard to avoid being included again, the
+ // fpclassify() macro is no longer available if cmath is included after
+ // math.h, even if math.h is included again. So we have to use
+ // std::fpclassify() on Linux.
+ return std::fpclassify(number) != FP_INFINITE;
+#elif defined(__LB_SHELL__)
+ return fpclassify(number) != FP_INFINITE;
+#elif defined(OS_ANDROID)
+ // isfinite isn't available on Android: http://b.android.com/34793
+ return finite(number) != 0;
+#elif defined(OS_POSIX)
+ return isfinite(number) != 0;
+#elif defined(OS_WIN)
+ return _finite(number) != 0;
+#endif
+}
+
+} // namespace base
+
+#endif // BASE_FLOAT_UTIL_H_
diff --git a/src/base/format_macros.h b/src/base/format_macros.h
new file mode 100644
index 0000000..a073b83
--- /dev/null
+++ b/src/base/format_macros.h
@@ -0,0 +1,77 @@
+// 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_FORMAT_MACROS_H_
+#define BASE_FORMAT_MACROS_H_
+
+// This file defines the format macros for some integer types.
+
+// To print a 64-bit value in a portable way:
+// int64_t value;
+// printf("xyz:%" PRId64, value);
+// The "d" in the macro corresponds to %d; you can also use PRIu64 etc.
+//
+// For wide strings, prepend "Wide" to the macro:
+// int64_t value;
+// StringPrintf(L"xyz: %" WidePRId64, value);
+//
+// To print a size_t value in a portable way:
+// size_t size;
+// printf("xyz: %" PRIuS, size);
+// The "u" in the macro corresponds to %u, and S is for "size".
+
+#include "build/build_config.h"
+
+#if defined(OS_STARBOARD)
+#include "starboard/types.h"
+#endif
+
+#if (defined(OS_POSIX) || defined(OS_STARBOARD)) && !defined(COMPILER_MSVC)
+
+#if (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && !defined(PRId64)
+#error "inttypes.h has already been included before this header file, but "
+#error "without __STDC_FORMAT_MACROS defined."
+#endif
+
+#if !defined(__STDC_FORMAT_MACROS)
+#define __STDC_FORMAT_MACROS
+#endif
+
+#include <inttypes.h>
+
+// GCC will concatenate wide and narrow strings correctly, so nothing needs to
+// be done here.
+#define WidePRId64 PRId64
+#define WidePRIu64 PRIu64
+#define WidePRIx64 PRIx64
+
+#if !defined(PRIuS)
+#define PRIuS "zu"
+#endif
+
+#else // OS_WIN || __LB_XB1__ || __LB_XB360__
+
+#if !defined(PRId64)
+#define PRId64 "I64d"
+#endif
+
+#if !defined(PRIu64)
+#define PRIu64 "I64u"
+#endif
+
+#if !defined(PRIx64)
+#define PRIx64 "I64x"
+#endif
+
+#define WidePRId64 L"I64d"
+#define WidePRIu64 L"I64u"
+#define WidePRIx64 L"I64x"
+
+#if !defined(PRIuS)
+#define PRIuS "Iu"
+#endif
+
+#endif
+
+#endif // BASE_FORMAT_MACROS_H_
diff --git a/src/base/gmock_unittest.cc b/src/base/gmock_unittest.cc
new file mode 100644
index 0000000..855380a
--- /dev/null
+++ b/src/base/gmock_unittest.cc
@@ -0,0 +1,137 @@
+// 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 test is a simple sanity check to make sure gmock is able to build/link
+// correctly. It just instantiates a mock object and runs through a couple of
+// the basic mock features.
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Gmock matchers and actions that we use below.
+using testing::AnyOf;
+using testing::Eq;
+using testing::Return;
+using testing::SetArgumentPointee;
+using testing::WithArg;
+using testing::_;
+
+namespace {
+
+// Simple class that we can mock out the behavior for. Everything is virtual
+// for easy mocking.
+class SampleClass {
+ public:
+ SampleClass() {}
+ virtual ~SampleClass() {}
+
+ virtual int ReturnSomething() {
+ return -1;
+ }
+
+ virtual void ReturnNothingConstly() const {
+ }
+
+ virtual void OutputParam(int* a) {
+ }
+
+ virtual int ReturnSecond(int a, int b) {
+ return b;
+ }
+};
+
+// Declare a mock for the class.
+class MockSampleClass : public SampleClass {
+ public:
+ MOCK_METHOD0(ReturnSomething, int());
+ MOCK_CONST_METHOD0(ReturnNothingConstly, void());
+ MOCK_METHOD1(OutputParam, void(int* a));
+ MOCK_METHOD2(ReturnSecond, int(int a, int b));
+};
+
+// Create a couple of custom actions. Custom actions can be used for adding
+// more complex behavior into your mock...though if you start needing these, ask
+// if you're asking your mock to do too much.
+ACTION(ReturnVal) {
+ // Return the first argument received.
+ return arg0;
+}
+ACTION(ReturnSecond) {
+ // Returns the second argument. This basically implemetns ReturnSecond.
+ return arg1;
+}
+
+TEST(GmockTest, SimpleMatchAndActions) {
+ // Basic test of some simple gmock matchers, actions, and cardinality
+ // expectations.
+ MockSampleClass mock;
+
+ EXPECT_CALL(mock, ReturnSomething())
+ .WillOnce(Return(1))
+ .WillOnce(Return(2))
+ .WillOnce(Return(3));
+ EXPECT_EQ(1, mock.ReturnSomething());
+ EXPECT_EQ(2, mock.ReturnSomething());
+ EXPECT_EQ(3, mock.ReturnSomething());
+
+ EXPECT_CALL(mock, ReturnNothingConstly()).Times(2);
+ mock.ReturnNothingConstly();
+ mock.ReturnNothingConstly();
+}
+
+TEST(GmockTest, AssignArgument) {
+ // Capture an argument for examination.
+ MockSampleClass mock;
+
+ EXPECT_CALL(mock, OutputParam(_))
+ .WillRepeatedly(SetArgumentPointee<0>(5));
+
+ int arg = 0;
+ mock.OutputParam(&arg);
+ EXPECT_EQ(5, arg);
+}
+
+TEST(GmockTest, SideEffects) {
+ // Capture an argument for examination.
+ MockSampleClass mock;
+
+ EXPECT_CALL(mock, OutputParam(_))
+ .WillRepeatedly(SetArgumentPointee<0>(5));
+
+ int arg = 0;
+ mock.OutputParam(&arg);
+ EXPECT_EQ(5, arg);
+}
+
+TEST(GmockTest, CustomAction_ReturnSecond) {
+ // Test a mock of the ReturnSecond behavior using an action that provides an
+ // alternate implementation of the function. Danger here though, this is
+ // starting to add too much behavior of the mock, which means the mock
+ // implementation might start to have bugs itself.
+ MockSampleClass mock;
+
+ EXPECT_CALL(mock, ReturnSecond(_, AnyOf(Eq(4), Eq(5))))
+ .WillRepeatedly(ReturnSecond());
+ EXPECT_EQ(4, mock.ReturnSecond(-1, 4));
+ EXPECT_EQ(5, mock.ReturnSecond(0, 5));
+ EXPECT_EQ(4, mock.ReturnSecond(0xdeadbeef, 4));
+ EXPECT_EQ(4, mock.ReturnSecond(112358, 4));
+ EXPECT_EQ(5, mock.ReturnSecond(1337, 5));
+}
+
+TEST(GmockTest, CustomAction_ReturnVal) {
+ // Alternate implemention of ReturnSecond using a more general custom action,
+ // and a WithArg adapter to bridge the interfaces.
+ MockSampleClass mock;
+
+ EXPECT_CALL(mock, ReturnSecond(_, AnyOf(Eq(4), Eq(5))))
+ .WillRepeatedly(WithArg<1>(ReturnVal()));
+ EXPECT_EQ(4, mock.ReturnSecond(-1, 4));
+ EXPECT_EQ(5, mock.ReturnSecond(0, 5));
+ EXPECT_EQ(4, mock.ReturnSecond(0xdeadbeef, 4));
+ EXPECT_EQ(4, mock.ReturnSecond(112358, 4));
+ EXPECT_EQ(5, mock.ReturnSecond(1337, 5));
+}
+
+} // namespace
diff --git a/src/base/gtest_prod_util.h b/src/base/gtest_prod_util.h
new file mode 100644
index 0000000..3289e63
--- /dev/null
+++ b/src/base/gtest_prod_util.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2012 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_GTEST_PROD_UTIL_H_
+#define BASE_GTEST_PROD_UTIL_H_
+
+#include "testing/gtest/include/gtest/gtest_prod.h"
+
+// This is a wrapper for gtest's FRIEND_TEST macro that friends
+// test with all possible prefixes. This is very helpful when changing the test
+// prefix, because the friend declarations don't need to be updated.
+//
+// Example usage:
+//
+// class MyClass {
+// private:
+// void MyMethod();
+// FRIEND_TEST_ALL_PREFIXES(MyClassTest, MyMethod);
+// };
+#define FRIEND_TEST_ALL_PREFIXES(test_case_name, test_name) \
+ FRIEND_TEST(test_case_name, test_name); \
+ FRIEND_TEST(test_case_name, DISABLED_##test_name); \
+ FRIEND_TEST(test_case_name, FLAKY_##test_name)
+
+// C++ compilers will refuse to compile the following code:
+//
+// namespace foo {
+// class MyClass {
+// private:
+// FRIEND_TEST_ALL_PREFIXES(MyClassTest, TestMethod);
+// bool private_var;
+// };
+// } // namespace foo
+//
+// class MyClassTest::TestMethod() {
+// foo::MyClass foo_class;
+// foo_class.private_var = true;
+// }
+//
+// Unless you forward declare MyClassTest::TestMethod outside of namespace foo.
+// Use FORWARD_DECLARE_TEST to do so for all possible prefixes.
+//
+// Example usage:
+//
+// FORWARD_DECLARE_TEST(MyClassTest, TestMethod);
+//
+// namespace foo {
+// class MyClass {
+// private:
+// FRIEND_TEST_ALL_PREFIXES(::MyClassTest, TestMethod); // NOTE use of ::
+// bool private_var;
+// };
+// } // namespace foo
+//
+// class MyClassTest::TestMethod() {
+// foo::MyClass foo_class;
+// foo_class.private_var = true;
+// }
+
+#define FORWARD_DECLARE_TEST(test_case_name, test_name) \
+ class test_case_name##_##test_name##_Test; \
+ class test_case_name##_##DISABLED_##test_name##_Test; \
+ class test_case_name##_##FLAKY_##test_name##_Test
+
+#endif // BASE_GTEST_PROD_UTIL_H_
diff --git a/src/base/guid.cc b/src/base/guid.cc
new file mode 100644
index 0000000..920dae5
--- /dev/null
+++ b/src/base/guid.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 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/guid.h"
+
+#include "base/rand_util.h"
+#include "base/stringprintf.h"
+
+namespace base {
+
+bool IsValidGUID(const std::string& guid) {
+ const size_t kGUIDLength = 36U;
+ if (guid.length() != kGUIDLength)
+ return false;
+
+ std::string hexchars = "0123456789ABCDEF";
+ for (uint32 i = 0; i < guid.length(); ++i) {
+ char current = guid[i];
+ if (i == 8 || i == 13 || i == 18 || i == 23) {
+ if (current != '-')
+ return false;
+ } else {
+ if (hexchars.find(current) == std::string::npos)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace guid
diff --git a/src/base/guid.h b/src/base/guid.h
new file mode 100644
index 0000000..e730f4c
--- /dev/null
+++ b/src/base/guid.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 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_GUID_H_
+#define BASE_GUID_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "build/build_config.h"
+
+namespace base {
+
+// Generate a 128-bit random GUID of the form: "%08X-%04X-%04X-%04X-%012llX".
+// If GUID generation fails an empty string is returned.
+// The POSIX implementation uses psuedo random number generation to create
+// the GUID. The Windows implementation uses system services.
+BASE_EXPORT std::string GenerateGUID();
+
+// Returns true if the input string conforms to the GUID format.
+BASE_EXPORT bool IsValidGUID(const std::string& guid);
+
+#if defined(OS_POSIX) || defined(OS_STARBOARD)
+// For unit testing purposes only. Do not use outside of tests.
+BASE_EXPORT std::string RandomDataToGUIDString(const uint64 bytes[2]);
+#endif
+
+} // namespace guid
+
+#endif // BASE_GUID_H_
diff --git a/src/base/guid_posix.cc b/src/base/guid_posix.cc
new file mode 100644
index 0000000..89811d0
--- /dev/null
+++ b/src/base/guid_posix.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 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/guid.h"
+
+#include "base/rand_util.h"
+#include "base/stringprintf.h"
+
+namespace base {
+
+std::string GenerateGUID() {
+ uint64 sixteen_bytes[2] = { base::RandUint64(), base::RandUint64() };
+ return RandomDataToGUIDString(sixteen_bytes);
+}
+
+// TODO(cmasone): Once we're comfortable this works, migrate Windows code to
+// use this as well.
+std::string RandomDataToGUIDString(const uint64 bytes[2]) {
+ return StringPrintf("%08X-%04X-%04X-%04X-%012llX",
+ static_cast<unsigned int>(bytes[0] >> 32),
+ static_cast<unsigned int>((bytes[0] >> 16) & 0x0000ffff),
+ static_cast<unsigned int>(bytes[0] & 0x0000ffff),
+ static_cast<unsigned int>(bytes[1] >> 48),
+ bytes[1] & 0x0000ffffffffffffULL);
+}
+
+} // namespace guid
diff --git a/src/base/guid_shell.cc b/src/base/guid_shell.cc
new file mode 100644
index 0000000..8336580
--- /dev/null
+++ b/src/base/guid_shell.cc
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2012 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "guid_posix.cc"
diff --git a/src/base/guid_starboard.cc b/src/base/guid_starboard.cc
new file mode 100644
index 0000000..f050ba0
--- /dev/null
+++ b/src/base/guid_starboard.cc
@@ -0,0 +1,15 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "guid_posix.cc"
diff --git a/src/base/guid_unittest.cc b/src/base/guid_unittest.cc
new file mode 100644
index 0000000..18c04a9
--- /dev/null
+++ b/src/base/guid_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2012 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/guid.h"
+
+#include <limits>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_POSIX)
+TEST(GUIDTest, GUIDGeneratesAllZeroes) {
+ uint64 bytes[] = { 0, 0 };
+ std::string clientid = base::RandomDataToGUIDString(bytes);
+ EXPECT_EQ("00000000-0000-0000-0000-000000000000", clientid);
+}
+
+TEST(GUIDTest, GUIDGeneratesCorrectly) {
+ uint64 bytes[] = { 0x0123456789ABCDEFULL, 0xFEDCBA9876543210ULL };
+ std::string clientid = base::RandomDataToGUIDString(bytes);
+ EXPECT_EQ("01234567-89AB-CDEF-FEDC-BA9876543210", clientid);
+}
+#endif
+
+TEST(GUIDTest, GUIDCorrectlyFormatted) {
+ const int kIterations = 10;
+ for (int it = 0; it < kIterations; ++it) {
+ std::string guid = base::GenerateGUID();
+ EXPECT_TRUE(base::IsValidGUID(guid));
+ }
+}
+
+TEST(GUIDTest, GUIDBasicUniqueness) {
+ const int kIterations = 10;
+ for (int it = 0; it < kIterations; ++it) {
+ std::string guid1 = base::GenerateGUID();
+ std::string guid2 = base::GenerateGUID();
+ EXPECT_EQ(36U, guid1.length());
+ EXPECT_EQ(36U, guid2.length());
+ EXPECT_NE(guid1, guid2);
+ }
+}
diff --git a/src/base/guid_win.cc b/src/base/guid_win.cc
new file mode 100644
index 0000000..1cddff8
--- /dev/null
+++ b/src/base/guid_win.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 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/guid.h"
+
+#include <stdlib.h>
+
+#include <objbase.h>
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+
+namespace base {
+
+std::string GenerateGUID() {
+ const int kGUIDSize = 39;
+
+ GUID guid;
+ HRESULT guid_result = CoCreateGuid(&guid);
+ DCHECK(SUCCEEDED(guid_result));
+ if (!SUCCEEDED(guid_result))
+ return std::string();
+
+ std::wstring guid_string;
+ int result = StringFromGUID2(guid,
+ WriteInto(&guid_string, kGUIDSize), kGUIDSize);
+ DCHECK(result == kGUIDSize);
+ if (result != kGUIDSize)
+ return std::string();
+
+ return WideToUTF8(guid_string.substr(1, guid_string.length() - 2));
+}
+
+} // namespace guid
diff --git a/src/base/hash.cc b/src/base/hash.cc
new file mode 100644
index 0000000..411e186
--- /dev/null
+++ b/src/base/hash.cc
@@ -0,0 +1,100 @@
+// Copyright (c) 2010, Paul Hsieh
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// * 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.
+// * Neither my name, Paul Hsieh, nor the names of any other contributors to the
+// code use may not be used to endorse or promote products derived from this
+// software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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.
+
+// From http://www.azillionmonkeys.com/qed/hash.html
+
+#include "base/hash.h"
+
+typedef uint32 uint32_t;
+typedef uint16 uint16_t;
+
+namespace base {
+
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
+ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
+ +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+
+uint32 SuperFastHash(const char * data, int len) {
+ uint32_t hash = len, tmp;
+ int rem;
+
+ if (len <= 0 || data == NULL)
+ return 0;
+
+ rem = len & 3;
+ len >>= 2;
+
+ /* Main loop */
+ for (; len > 0; len--) {
+ hash += get16bits(data);
+ tmp = (get16bits(data + 2) << 11) ^ hash;
+ hash = (hash << 16) ^ tmp;
+ data += 2 * sizeof(uint16_t);
+ hash += hash >> 11;
+ }
+
+ /* Handle end cases */
+ switch (rem) {
+ case 3:
+ hash += get16bits(data);
+ hash ^= hash << 16;
+
+ // Treat the final character as signed. This ensures all platforms behave
+ // consistently with the original x86 code.
+ hash ^= static_cast<signed char>(data[sizeof(uint16_t)]) << 18;
+ hash += hash >> 11;
+ break;
+ case 2:
+ hash += get16bits(data);
+ hash ^= hash << 11;
+ hash += hash >> 17;
+ break;
+ case 1:
+ hash += static_cast<signed char>(*data);
+ hash ^= hash << 10;
+ hash += hash >> 1;
+ }
+
+ /* Force "avalanching" of final 127 bits */
+ hash ^= hash << 3;
+ hash += hash >> 5;
+ hash ^= hash << 4;
+ hash += hash >> 17;
+ hash ^= hash << 25;
+ hash += hash >> 6;
+
+ return hash;
+}
+
+} // namespace base
diff --git a/src/base/hash.h b/src/base/hash.h
new file mode 100644
index 0000000..cf8ea3a
--- /dev/null
+++ b/src/base/hash.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_HASH_H_
+#define BASE_HASH_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+
+// From http://www.azillionmonkeys.com/qed/hash.html
+// This is the hash used on WebCore/platform/stringhash
+BASE_EXPORT uint32 SuperFastHash(const char * data, int len);
+
+inline uint32 Hash(const char* key, size_t length) {
+ return SuperFastHash(key, static_cast<int>(length));
+}
+
+inline uint32 Hash(const std::string& key) {
+ if (key.empty())
+ return 0;
+ return SuperFastHash(key.data(), static_cast<int>(key.size()));
+}
+
+} // namespace base
+
+#endif // BASE_HASH_H_
diff --git a/src/base/hash_tables.h b/src/base/hash_tables.h
new file mode 100644
index 0000000..b6f40be
--- /dev/null
+++ b/src/base/hash_tables.h
@@ -0,0 +1,162 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+//
+// Deal with the differences between Microsoft and GNU implemenations
+// of hash_map. Allows all platforms to use |base::hash_map| and
+// |base::hash_set|.
+// eg:
+// base::hash_map<int> my_map;
+// base::hash_set<int> my_set;
+//
+// NOTE: It is an explicit non-goal of this class to provide a generic hash
+// function for pointers. If you want to hash a pointers to a particular class,
+// please define the template specialization elsewhere (for example, in its
+// header file) and keep it specific to just pointers to that class. This is
+// because identity hashes are not desirable for all types that might show up
+// in containers as pointers.
+
+#ifndef BASE_HASH_TABLES_H_
+#define BASE_HASH_TABLES_H_
+
+#include <string>
+
+#include "build/build_config.h"
+
+#include "base/string16.h"
+
+#if defined(OS_STARBOARD)
+#include "starboard/configuration.h"
+#define BASE_HASH_DEFINE_LONG_LONG_HASHES !SB_HAS(LONG_LONG_HASH)
+#define BASE_HASH_DEFINE_STRING_HASHES !SB_HAS(STRING_HASH)
+#define BASE_HASH_USE_HASH !SB_HAS(HASH_USING)
+#define BASE_HASH_MAP_INCLUDE SB_HASH_MAP_INCLUDE
+#define BASE_HASH_NAMESPACE SB_HASH_NAMESPACE
+#define BASE_HASH_SET_INCLUDE SB_HASH_SET_INCLUDE
+#if !SB_HAS(HASH_VALUE)
+#define BASE_HASH_USE_HASH_STRUCT
+#endif
+#elif defined(COMPILER_MSVC)
+#define BASE_HASH_DEFINE_LONG_LONG_HASHES 0
+#define BASE_HASH_DEFINE_STRING_HASHES 0
+#define BASE_HASH_USE_HASH 0
+#define BASE_HASH_MAP_INCLUDE <hash_map>
+#define BASE_HASH_NAMESPACE stdext
+#define BASE_HASH_SET_INCLUDE <hash_set>
+#elif defined(COMPILER_GCC)
+#if defined(OS_ANDROID) || (defined(__LB_SHELL__) && !defined(__LB_LINUX__))
+#define BASE_HASH_DEFINE_LONG_LONG_HASHES 0
+#define BASE_HASH_DEFINE_STRING_HASHES !defined(__LB_SHELL__)
+#define BASE_HASH_MAP_INCLUDE <hash_map>
+#define BASE_HASH_NAMESPACE std
+#define BASE_HASH_SET_INCLUDE <hash_set>
+#else
+#define BASE_HASH_DEFINE_LONG_LONG_HASHES 1
+#define BASE_HASH_DEFINE_STRING_HASHES 1
+#define BASE_HASH_MAP_INCLUDE <ext/hash_map>
+#define BASE_HASH_NAMESPACE __gnu_cxx
+#define BASE_HASH_SET_INCLUDE <ext/hash_set>
+#define BASE_HASH_USE_HASH_STRUCT
+#endif
+#if defined(__LB_LINUX__)
+#define BASE_HASH_USE_HASH 1
+#else
+#define BASE_HASH_USE_HASH 0
+#endif
+#else // COMPILER
+#error define BASE_HASH_NAMESPACE for your compiler
+#endif // COMPILER
+
+// This is a hack to disable the gcc 4.4 warning about hash_map and hash_set
+// being deprecated. We can get rid of this when we upgrade to VS2008 and we
+// can use <tr1/unordered_map> and <tr1/unordered_set>.
+#ifdef __DEPRECATED
+#define CHROME_OLD__DEPRECATED __DEPRECATED
+#undef __DEPRECATED
+#endif
+
+#include BASE_HASH_MAP_INCLUDE
+#include BASE_HASH_SET_INCLUDE
+
+#ifdef CHROME_OLD__DEPRECATED
+#define __DEPRECATED CHROME_OLD__DEPRECATED
+#undef CHROME_OLD__DEPRECATED
+#endif
+
+#if BASE_HASH_DEFINE_LONG_LONG_HASHES
+// The GNU C++ library provides identity hash functions for many integral types,
+// but not for |long long|. This hash function will truncate if |size_t| is
+// narrower than |long long|. This is probably good enough for what we will
+// use it for.
+
+#define DEFINE_TRIVIAL_HASH(integral_type) \
+ template<> \
+ struct hash<integral_type> { \
+ std::size_t operator()(integral_type value) const { \
+ return static_cast<std::size_t>(value); \
+ } \
+ }
+
+namespace BASE_HASH_NAMESPACE {
+DEFINE_TRIVIAL_HASH(long long);
+DEFINE_TRIVIAL_HASH(unsigned long long);
+
+template <typename T>
+struct hash<T*> {
+ std::size_t operator()(T* value) const {
+ return BASE_HASH_NAMESPACE::hash<uintptr_t>()(
+ reinterpret_cast<uintptr_t>(value));
+ }
+};
+} // namespace BASE_HASH_NAMESPACE
+
+#undef DEFINE_TRIVIAL_HASH
+#endif // BASE_HASH_DEFINE_LONG_LONG_HASHES
+
+
+#if BASE_HASH_DEFINE_STRING_HASHES
+// Implement string hash functions so that strings of various flavors can
+// be used as keys in STL maps and sets. The hash algorithm comes from the
+// GNU C++ library, in <tr1/functional>. It is duplicated here because GCC
+// versions prior to 4.3.2 are unable to compile <tr1/functional> when RTTI
+// is disabled, as it is in our build.
+
+#define DEFINE_STRING_HASH(string_type) \
+ template<> \
+ struct hash<string_type> { \
+ std::size_t operator()(const string_type& s) const { \
+ std::size_t result = 0; \
+ for (string_type::const_iterator i = s.begin(); i != s.end(); ++i) \
+ result = (result * 131) + *i; \
+ return result; \
+ } \
+ }
+
+namespace BASE_HASH_NAMESPACE {
+DEFINE_STRING_HASH(std::string);
+DEFINE_STRING_HASH(string16);
+} // namespace BASE_HASH_NAMESPACE
+
+#undef DEFINE_STRING_HASH
+#endif // BASE_HASH_DEFINE_STRING_HASHES
+
+
+namespace base {
+#if BASE_HASH_USE_HASH
+using BASE_HASH_NAMESPACE::hash;
+#endif
+using BASE_HASH_NAMESPACE::hash_map;
+using BASE_HASH_NAMESPACE::hash_multimap;
+using BASE_HASH_NAMESPACE::hash_multiset;
+using BASE_HASH_NAMESPACE::hash_set;
+}
+
+#undef BASE_HASH_DEFINE_LONG_LONG_HASHES
+#undef BASE_HASH_DEFINE_STRING_HASHES
+#undef BASE_HASH_MAP_INCLUDE
+#undef BASE_HASH_SET_INCLUDE
+#undef BASE_HASH_USE_HASH
+
+#endif // BASE_HASH_TABLES_H_
diff --git a/src/base/hi_res_timer_manager.h b/src/base/hi_res_timer_manager.h
new file mode 100644
index 0000000..1bd5538
--- /dev/null
+++ b/src/base/hi_res_timer_manager.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_HI_RES_TIMER_MANAGER_H_
+#define BASE_HI_RES_TIMER_MANAGER_H_
+
+#include "base/base_export.h"
+#include "base/system_monitor/system_monitor.h"
+
+// Ensures that the Windows high resolution timer is only used
+// when not running on battery power.
+class BASE_EXPORT HighResolutionTimerManager
+ : public base::SystemMonitor::PowerObserver {
+ public:
+ HighResolutionTimerManager();
+ virtual ~HighResolutionTimerManager();
+
+ // base::SystemMonitor::PowerObserver:
+ virtual void OnPowerStateChange(bool on_battery_power) OVERRIDE;
+
+ // Returns true if the hi resolution clock could be used right now.
+ bool hi_res_clock_available() const { return hi_res_clock_available_; }
+
+ private:
+ // Enable or disable the faster multimedia timer.
+ void UseHiResClock(bool use);
+
+ bool hi_res_clock_available_;
+
+ DISALLOW_COPY_AND_ASSIGN(HighResolutionTimerManager);
+};
+
+#endif // BASE_HI_RES_TIMER_MANAGER_H_
diff --git a/src/base/hi_res_timer_manager_posix.cc b/src/base/hi_res_timer_manager_posix.cc
new file mode 100644
index 0000000..7c16eb7
--- /dev/null
+++ b/src/base/hi_res_timer_manager_posix.cc
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/hi_res_timer_manager.h"
+
+// On POSIX we don't need to do anything special with the system timer.
+
+HighResolutionTimerManager::HighResolutionTimerManager()
+ : hi_res_clock_available_(false) {
+}
+
+HighResolutionTimerManager::~HighResolutionTimerManager() {
+}
+
+void HighResolutionTimerManager::OnPowerStateChange(bool on_battery_power) {
+}
+
+void HighResolutionTimerManager::UseHiResClock(bool use) {
+}
diff --git a/src/base/hi_res_timer_manager_unittest.cc b/src/base/hi_res_timer_manager_unittest.cc
new file mode 100644
index 0000000..07651ce
--- /dev/null
+++ b/src/base/hi_res_timer_manager_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2012 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/hi_res_timer_manager.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/system_monitor/system_monitor.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+// http://crbug.com/114048
+TEST(HiResTimerManagerTest, DISABLED_ToggleOnOff) {
+ MessageLoop loop;
+ scoped_ptr<base::SystemMonitor> system_monitor(new base::SystemMonitor());
+ HighResolutionTimerManager manager;
+
+ // At this point, we don't know if the high resolution timers are on or off,
+ // it depends on what system the tests are running on (for example, if this
+ // test is running on a laptop/battery, then the SystemMonitor would have
+ // already set the PowerState to battery power; but if we're running on a
+ // desktop, then the PowerState will be non-battery power). Simulate a power
+ // level change to get to a deterministic state.
+ manager.OnPowerStateChange(/* on_battery */ false);
+
+ // Loop a few times to test power toggling.
+ for (int loop = 2; loop >= 0; --loop) {
+ // The manager has the high resolution clock enabled now.
+ EXPECT_TRUE(manager.hi_res_clock_available());
+ // But the Time class has it off, because it hasn't been activated.
+ EXPECT_FALSE(base::Time::IsHighResolutionTimerInUse());
+
+ // Activate the high resolution timer.
+ base::Time::ActivateHighResolutionTimer(true);
+ EXPECT_TRUE(base::Time::IsHighResolutionTimerInUse());
+
+ // Simulate a on-battery power event.
+ manager.OnPowerStateChange(/* on_battery */ true);
+ EXPECT_FALSE(manager.hi_res_clock_available());
+ EXPECT_FALSE(base::Time::IsHighResolutionTimerInUse());
+
+ // Simulate a off-battery power event.
+ manager.OnPowerStateChange(/* on_battery */ false);
+ EXPECT_TRUE(manager.hi_res_clock_available());
+ EXPECT_TRUE(base::Time::IsHighResolutionTimerInUse());
+
+ // De-activate the high resolution timer.
+ base::Time::ActivateHighResolutionTimer(false);
+ }
+}
+#endif // defined(OS_WIN)
diff --git a/src/base/hi_res_timer_manager_win.cc b/src/base/hi_res_timer_manager_win.cc
new file mode 100644
index 0000000..1a92394
--- /dev/null
+++ b/src/base/hi_res_timer_manager_win.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/hi_res_timer_manager.h"
+
+#include "base/time.h"
+
+HighResolutionTimerManager::HighResolutionTimerManager()
+ : hi_res_clock_available_(false) {
+ base::SystemMonitor* system_monitor = base::SystemMonitor::Get();
+ system_monitor->AddPowerObserver(this);
+ UseHiResClock(!system_monitor->BatteryPower());
+}
+
+HighResolutionTimerManager::~HighResolutionTimerManager() {
+ base::SystemMonitor::Get()->RemovePowerObserver(this);
+ UseHiResClock(false);
+}
+
+void HighResolutionTimerManager::OnPowerStateChange(bool on_battery_power) {
+ UseHiResClock(!on_battery_power);
+}
+
+void HighResolutionTimerManager::UseHiResClock(bool use) {
+ if (use == hi_res_clock_available_)
+ return;
+ hi_res_clock_available_ = use;
+ base::Time::EnableHighResolutionTimer(use);
+}
diff --git a/src/base/i18n/base_i18n_export.h b/src/base/i18n/base_i18n_export.h
new file mode 100644
index 0000000..7ef4ee8
--- /dev/null
+++ b/src/base/i18n/base_i18n_export.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_BASE_I18N_EXPORT_H_
+#define BASE_I18N_BASE_I18N_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(_MSC_VER)
+
+#if defined(BASE_I18N_IMPLEMENTATION)
+#define BASE_I18N_EXPORT __declspec(dllexport)
+#else
+#define BASE_I18N_EXPORT __declspec(dllimport)
+#endif // defined(BASE_I18N_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(BASE_I18N_IMPLEMENTATION)
+#define BASE_I18N_EXPORT __attribute__((visibility("default")))
+#else
+#define BASE_I18N_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define BASE_I18N_EXPORT
+#endif
+
+#endif // BASE_I18N_BASE_I18N_EXPORT_H_
diff --git a/src/base/i18n/bidi_line_iterator.cc b/src/base/i18n/bidi_line_iterator.cc
new file mode 100644
index 0000000..6251185
--- /dev/null
+++ b/src/base/i18n/bidi_line_iterator.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/bidi_line_iterator.h"
+
+#include "base/logging.h"
+
+namespace base {
+namespace i18n {
+
+namespace {
+ UBiDiLevel GetParagraphLevelForDirection(TextDirection direction) {
+ switch (direction) {
+ case UNKNOWN_DIRECTION:
+ return UBIDI_DEFAULT_LTR;
+ break;
+ case RIGHT_TO_LEFT:
+ return 1; // Highest RTL level.
+ break;
+ case LEFT_TO_RIGHT:
+ return 0; // Highest LTR level.
+ break;
+ default:
+ NOTREACHED();
+ return 0;
+ }
+ }
+} // namespace
+
+BiDiLineIterator::BiDiLineIterator() : bidi_(NULL) {
+}
+
+BiDiLineIterator::~BiDiLineIterator() {
+ if (bidi_) {
+ ubidi_close(bidi_);
+ bidi_ = NULL;
+ }
+}
+
+bool BiDiLineIterator::Open(const string16& text, TextDirection direction) {
+ DCHECK(!bidi_);
+ UErrorCode error = U_ZERO_ERROR;
+ bidi_ = ubidi_openSized(static_cast<int>(text.length()), 0, &error);
+ if (U_FAILURE(error))
+ return false;
+ ubidi_setPara(bidi_, text.data(), static_cast<int>(text.length()),
+ GetParagraphLevelForDirection(direction), NULL, &error);
+ return (U_SUCCESS(error) == TRUE);
+}
+
+int BiDiLineIterator::CountRuns() {
+ DCHECK(bidi_ != NULL);
+ UErrorCode error = U_ZERO_ERROR;
+ const int runs = ubidi_countRuns(bidi_, &error);
+ return U_SUCCESS(error) ? runs : 0;
+}
+
+UBiDiDirection BiDiLineIterator::GetVisualRun(int index,
+ int* start,
+ int* length) {
+ DCHECK(bidi_ != NULL);
+ return ubidi_getVisualRun(bidi_, index, start, length);
+}
+
+void BiDiLineIterator::GetLogicalRun(int start,
+ int* end,
+ UBiDiLevel* level) {
+ DCHECK(bidi_ != NULL);
+ ubidi_getLogicalRun(bidi_, start, end, level);
+}
+
+} // namespace i18n
+} // namespace base
diff --git a/src/base/i18n/bidi_line_iterator.h b/src/base/i18n/bidi_line_iterator.h
new file mode 100644
index 0000000..8e1217b
--- /dev/null
+++ b/src/base/i18n/bidi_line_iterator.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_BIDI_LINE_ITERATOR_H_
+#define BASE_I18N_BIDI_LINE_ITERATOR_H_
+
+#include "unicode/ubidi.h"
+
+#include "base/basictypes.h"
+#include "base/i18n/base_i18n_export.h"
+#include "base/i18n/rtl.h"
+#include "base/string16.h"
+
+namespace base {
+namespace i18n {
+
+// A simple wrapper class for the bidirectional iterator of ICU.
+// This class uses the bidirectional iterator of ICU to split a line of
+// bidirectional texts into visual runs in its display order.
+class BASE_I18N_EXPORT BiDiLineIterator {
+ public:
+ BiDiLineIterator();
+ ~BiDiLineIterator();
+
+ // Initializes the bidirectional iterator with the specified text. Returns
+ // whether initialization succeeded.
+ bool Open(const string16& text, TextDirection direction);
+
+ // Returns the number of visual runs in the text, or zero on error.
+ int CountRuns();
+
+ // Gets the logical offset, length, and direction of the specified visual run.
+ UBiDiDirection GetVisualRun(int index, int* start, int* length);
+
+ // Given a start position, figure out where the run ends (and the BiDiLevel).
+ void GetLogicalRun(int start, int* end, UBiDiLevel* level);
+
+ private:
+ UBiDi* bidi_;
+
+ DISALLOW_COPY_AND_ASSIGN(BiDiLineIterator);
+};
+
+} // namespace i18n
+} // namespace base
+
+#endif // BASE_I18N_BIDI_LINE_ITERATOR_H_
diff --git a/src/base/i18n/break_iterator.cc b/src/base/i18n/break_iterator.cc
new file mode 100644
index 0000000..339de3d
--- /dev/null
+++ b/src/base/i18n/break_iterator.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/break_iterator.h"
+
+#include "base/logging.h"
+#include "unicode/ubrk.h"
+#include "unicode/uchar.h"
+#include "unicode/ustring.h"
+
+namespace base {
+namespace i18n {
+
+const size_t npos = -1;
+
+BreakIterator::BreakIterator(const string16& str, BreakType break_type)
+ : iter_(NULL),
+ string_(str),
+ break_type_(break_type),
+ prev_(npos),
+ pos_(0) {
+}
+
+BreakIterator::~BreakIterator() {
+ if (iter_)
+ ubrk_close(static_cast<UBreakIterator*>(iter_));
+}
+
+bool BreakIterator::Init() {
+ UErrorCode status = U_ZERO_ERROR;
+ UBreakIteratorType break_type;
+ switch (break_type_) {
+ case BREAK_CHARACTER:
+ break_type = UBRK_CHARACTER;
+ break;
+ case BREAK_WORD:
+ break_type = UBRK_WORD;
+ break;
+ case BREAK_LINE:
+ case BREAK_NEWLINE:
+ break_type = UBRK_LINE;
+ break;
+ default:
+ NOTREACHED() << "invalid break_type_";
+ return false;
+ }
+ iter_ = ubrk_open(break_type, NULL,
+ string_.data(), static_cast<int32_t>(string_.size()),
+ &status);
+ if (U_FAILURE(status)) {
+ NOTREACHED() << "ubrk_open failed: " << u_errorName(status);
+ return false;
+ }
+ // Move the iterator to the beginning of the string.
+ ubrk_first(static_cast<UBreakIterator*>(iter_));
+ return true;
+}
+
+bool BreakIterator::Advance() {
+ int32_t pos;
+ int32_t status;
+ prev_ = pos_;
+ switch (break_type_) {
+ case BREAK_CHARACTER:
+ case BREAK_WORD:
+ case BREAK_LINE:
+ pos = ubrk_next(static_cast<UBreakIterator*>(iter_));
+ if (pos == UBRK_DONE) {
+ pos_ = npos;
+ return false;
+ }
+ pos_ = static_cast<size_t>(pos);
+ return true;
+ case BREAK_NEWLINE:
+ do {
+ pos = ubrk_next(static_cast<UBreakIterator*>(iter_));
+ if (pos == UBRK_DONE)
+ break;
+ pos_ = static_cast<size_t>(pos);
+ status = ubrk_getRuleStatus(static_cast<UBreakIterator*>(iter_));
+ } while (status >= UBRK_LINE_SOFT && status < UBRK_LINE_SOFT_LIMIT);
+ if (pos == UBRK_DONE && prev_ == pos_) {
+ pos_ = npos;
+ return false;
+ }
+ return true;
+ default:
+ NOTREACHED() << "invalid break_type_";
+ return false;
+ }
+}
+
+bool BreakIterator::IsWord() const {
+ int32_t status = ubrk_getRuleStatus(static_cast<UBreakIterator*>(iter_));
+ return (break_type_ == BREAK_WORD && status != UBRK_WORD_NONE);
+}
+
+bool BreakIterator::IsEndOfWord(size_t position) const {
+ if (break_type_ != BREAK_WORD)
+ return false;
+
+ UBreakIterator* iter = static_cast<UBreakIterator*>(iter_);
+ UBool boundary = ubrk_isBoundary(iter, static_cast<int32_t>(position));
+ int32_t status = ubrk_getRuleStatus(iter);
+ return (!!boundary && status != UBRK_WORD_NONE);
+}
+
+bool BreakIterator::IsStartOfWord(size_t position) const {
+ if (break_type_ != BREAK_WORD)
+ return false;
+
+ UBreakIterator* iter = static_cast<UBreakIterator*>(iter_);
+ UBool boundary = ubrk_isBoundary(iter, static_cast<int32_t>(position));
+ ubrk_next(iter);
+ int32_t next_status = ubrk_getRuleStatus(iter);
+ return (!!boundary && next_status != UBRK_WORD_NONE);
+}
+
+string16 BreakIterator::GetString() const {
+ DCHECK(prev_ != npos && pos_ != npos);
+ return string_.substr(prev_, pos_ - prev_);
+}
+
+} // namespace i18n
+} // namespace base
diff --git a/src/base/i18n/break_iterator.h b/src/base/i18n/break_iterator.h
new file mode 100644
index 0000000..d558e23
--- /dev/null
+++ b/src/base/i18n/break_iterator.h
@@ -0,0 +1,131 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_BREAK_ITERATOR_H_
+#define BASE_I18N_BREAK_ITERATOR_H_
+
+#include "base/basictypes.h"
+#include "base/string16.h"
+#include "base/i18n/base_i18n_export.h"
+
+// The BreakIterator class iterates through the words, word breaks, and
+// line breaks in a UTF-16 string.
+//
+// It provides several modes, BREAK_WORD, BREAK_LINE, and BREAK_NEWLINE,
+// which modify how characters are aggregated into the returned string.
+//
+// Under BREAK_WORD mode, once a word is encountered any non-word
+// characters are not included in the returned string (e.g. in the
+// UTF-16 equivalent of the string " foo bar! ", the word breaks are at
+// the periods in ". .foo. .bar.!. .").
+// Note that Chinese/Japanese/Thai do not use spaces between words so that
+// boundaries can fall in the middle of a continuous run of non-space /
+// non-punctuation characters.
+//
+// Under BREAK_LINE mode, once a line breaking opportunity is encountered,
+// any non-word characters are included in the returned string, breaking
+// only when a space-equivalent character or a line breaking opportunity
+// is encountered (e.g. in the UTF16-equivalent of the string " foo bar! ",
+// the breaks are at the periods in ". .foo .bar! .").
+//
+// Note that lines can be broken at any character/syllable/grapheme cluster
+// boundary in Chinese/Japanese/Korean and at word boundaries in Thai
+// (Thai does not use spaces between words). Therefore, this is NOT the same
+// as breaking only at space-equivalent characters where its former
+// name (BREAK_SPACE) implied.
+//
+// Under BREAK_NEWLINE mode, all characters are included in the returned
+// string, breking only when a newline-equivalent character is encountered
+// (eg. in the UTF-16 equivalent of the string "foo\nbar!\n\n", the line
+// breaks are at the periods in ".foo\n.bar\n.\n.").
+//
+// To extract the words from a string, move a BREAK_WORD BreakIterator
+// through the string and test whether IsWord() is true. E.g.,
+// BreakIterator iter(str, BreakIterator::BREAK_WORD);
+// if (!iter.Init())
+// return false;
+// while (iter.Advance()) {
+// if (iter.IsWord()) {
+// // Region [iter.prev(), iter.pos()) contains a word.
+// VLOG(1) << "word: " << iter.GetString();
+// }
+// }
+
+namespace base {
+namespace i18n {
+
+class BASE_I18N_EXPORT BreakIterator {
+ public:
+ enum BreakType {
+ BREAK_WORD,
+ BREAK_LINE,
+ // TODO(jshin): Remove this after reviewing call sites.
+ // If call sites really need break only on space-like characters
+ // implement it separately.
+ BREAK_SPACE = BREAK_LINE,
+ BREAK_NEWLINE,
+ BREAK_CHARACTER,
+ };
+
+ // Requires |str| to live as long as the BreakIterator does.
+ BreakIterator(const string16& str, BreakType break_type);
+ ~BreakIterator();
+
+ // Init() must be called before any of the iterators are valid.
+ // Returns false if ICU failed to initialize.
+ bool Init();
+
+ // Advance to the next break. Returns false if we've run past the end of
+ // the string. (Note that the very last "break" is after the final
+ // character in the string, and when we advance to that position it's the
+ // last time Advance() returns true.)
+ bool Advance();
+
+ // Under BREAK_WORD mode, returns true if the break we just hit is the
+ // end of a word. (Otherwise, the break iterator just skipped over e.g.
+ // whitespace or punctuation.) Under BREAK_LINE and BREAK_NEWLINE modes,
+ // this distinction doesn't apply and it always retuns false.
+ bool IsWord() const;
+
+ // Under BREAK_WORD mode, returns true if |position| is at the end of word or
+ // at the start of word. It always retuns false under BREAK_LINE and
+ // BREAK_NEWLINE modes.
+ bool IsEndOfWord(size_t position) const;
+ bool IsStartOfWord(size_t position) const;
+
+ // Returns the string between prev() and pos().
+ // Advance() must have been called successfully at least once for pos() to
+ // have advanced to somewhere useful.
+ string16 GetString() const;
+
+ // Returns the value of pos() returned before Advance() was last called.
+ size_t prev() const { return prev_; }
+
+ // Returns the current break position within the string,
+ // or BreakIterator::npos when done.
+ size_t pos() const { return pos_; }
+
+ private:
+ // ICU iterator, avoiding ICU ubrk.h dependence.
+ // This is actually an ICU UBreakiterator* type, which turns out to be
+ // a typedef for a void* in the ICU headers. Using void* directly prevents
+ // callers from needing access to the ICU public headers directory.
+ void* iter_;
+
+ // The string we're iterating over.
+ const string16& string_;
+
+ // The breaking style (word/space/newline).
+ BreakType break_type_;
+
+ // Previous and current iterator positions.
+ size_t prev_, pos_;
+
+ DISALLOW_COPY_AND_ASSIGN(BreakIterator);
+};
+
+} // namespace i18n
+} // namespace base
+
+#endif // BASE_I18N_BREAK_ITERATOR_H_
diff --git a/src/base/i18n/break_iterator_unittest.cc b/src/base/i18n/break_iterator_unittest.cc
new file mode 100644
index 0000000..afb780c
--- /dev/null
+++ b/src/base/i18n/break_iterator_unittest.cc
@@ -0,0 +1,338 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/break_iterator.h"
+
+#include "base/string_piece.h"
+#include "base/stringprintf.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace i18n {
+
+TEST(BreakIteratorTest, BreakWordEmpty) {
+ string16 empty;
+ BreakIterator iter(empty, BreakIterator::BREAK_WORD);
+ ASSERT_TRUE(iter.Init());
+ EXPECT_FALSE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end.
+ EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakWord) {
+ string16 space(UTF8ToUTF16(" "));
+ string16 str(UTF8ToUTF16(" foo bar! \npouet boom"));
+ BreakIterator iter(str, BreakIterator::BREAK_WORD);
+ ASSERT_TRUE(iter.Init());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(space, iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_TRUE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("foo"), iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(space, iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_TRUE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("bar"), iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("!"), iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(space, iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("\n"), iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_TRUE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("pouet"), iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(space, iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_TRUE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("boom"), iter.GetString());
+ EXPECT_FALSE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end.
+ EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakWide16) {
+ // Two greek words separated by space.
+ const string16 str(WideToUTF16(
+ L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
+ L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2"));
+ const string16 word1(str.substr(0, 10));
+ const string16 word2(str.substr(11, 5));
+ BreakIterator iter(str, BreakIterator::BREAK_WORD);
+ ASSERT_TRUE(iter.Init());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_TRUE(iter.IsWord());
+ EXPECT_EQ(word1, iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16(" "), iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_TRUE(iter.IsWord());
+ EXPECT_EQ(word2, iter.GetString());
+ EXPECT_FALSE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end.
+ EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakWide32) {
+ // U+1D49C MATHEMATICAL SCRIPT CAPITAL A
+ const char* very_wide_char = "\xF0\x9D\x92\x9C";
+ const string16 str(
+ UTF8ToUTF16(base::StringPrintf("%s a", very_wide_char)));
+ const string16 very_wide_word(str.substr(0, 2));
+
+ BreakIterator iter(str, BreakIterator::BREAK_WORD);
+ ASSERT_TRUE(iter.Init());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_TRUE(iter.IsWord());
+ EXPECT_EQ(very_wide_word, iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16(" "), iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_TRUE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("a"), iter.GetString());
+ EXPECT_FALSE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end.
+ EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakSpaceEmpty) {
+ string16 empty;
+ BreakIterator iter(empty, BreakIterator::BREAK_SPACE);
+ ASSERT_TRUE(iter.Init());
+ EXPECT_FALSE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end.
+ EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakSpace) {
+ string16 str(UTF8ToUTF16(" foo bar! \npouet boom"));
+ BreakIterator iter(str, BreakIterator::BREAK_SPACE);
+ ASSERT_TRUE(iter.Init());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16(" "), iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("foo "), iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("bar! \n"), iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("pouet "), iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("boom"), iter.GetString());
+ EXPECT_FALSE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end.
+ EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakSpaceSP) {
+ string16 str(UTF8ToUTF16(" foo bar! \npouet boom "));
+ BreakIterator iter(str, BreakIterator::BREAK_SPACE);
+ ASSERT_TRUE(iter.Init());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16(" "), iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("foo "), iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("bar! \n"), iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("pouet "), iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("boom "), iter.GetString());
+ EXPECT_FALSE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end.
+ EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakSpacekWide16) {
+ // Two Greek words.
+ const string16 str(WideToUTF16(
+ L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
+ L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2"));
+ const string16 word1(str.substr(0, 11));
+ const string16 word2(str.substr(11, 5));
+ BreakIterator iter(str, BreakIterator::BREAK_SPACE);
+ ASSERT_TRUE(iter.Init());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(word1, iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(word2, iter.GetString());
+ EXPECT_FALSE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end.
+ EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakSpaceWide32) {
+ // U+1D49C MATHEMATICAL SCRIPT CAPITAL A
+ const char* very_wide_char = "\xF0\x9D\x92\x9C";
+ const string16 str(
+ UTF8ToUTF16(base::StringPrintf("%s a", very_wide_char)));
+ const string16 very_wide_word(str.substr(0, 3));
+
+ BreakIterator iter(str, BreakIterator::BREAK_SPACE);
+ ASSERT_TRUE(iter.Init());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(very_wide_word, iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("a"), iter.GetString());
+ EXPECT_FALSE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end.
+ EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakLineEmpty) {
+ string16 empty;
+ BreakIterator iter(empty, BreakIterator::BREAK_NEWLINE);
+ ASSERT_TRUE(iter.Init());
+ EXPECT_FALSE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end.
+ EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakLine) {
+ string16 nl(UTF8ToUTF16("\n"));
+ string16 str(UTF8ToUTF16("\nfoo bar!\n\npouet boom"));
+ BreakIterator iter(str, BreakIterator::BREAK_NEWLINE);
+ ASSERT_TRUE(iter.Init());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(nl, iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("foo bar!\n"), iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(nl, iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("pouet boom"), iter.GetString());
+ EXPECT_FALSE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end.
+ EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakLineNL) {
+ string16 nl(UTF8ToUTF16("\n"));
+ string16 str(UTF8ToUTF16("\nfoo bar!\n\npouet boom\n"));
+ BreakIterator iter(str, BreakIterator::BREAK_NEWLINE);
+ ASSERT_TRUE(iter.Init());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(nl, iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("foo bar!\n"), iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(nl, iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("pouet boom\n"), iter.GetString());
+ EXPECT_FALSE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end.
+ EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakLineWide16) {
+ // Two Greek words separated by newline.
+ const string16 str(WideToUTF16(
+ L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
+ L"\x03bf\x03c2\x000a\x0399\x03c3\x03c4\x03cc\x03c2"));
+ const string16 line1(str.substr(0, 11));
+ const string16 line2(str.substr(11, 5));
+ BreakIterator iter(str, BreakIterator::BREAK_NEWLINE);
+ ASSERT_TRUE(iter.Init());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(line1, iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(line2, iter.GetString());
+ EXPECT_FALSE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end.
+ EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakLineWide32) {
+ // U+1D49C MATHEMATICAL SCRIPT CAPITAL A
+ const char* very_wide_char = "\xF0\x9D\x92\x9C";
+ const string16 str(
+ UTF8ToUTF16(base::StringPrintf("%s\na", very_wide_char)));
+ const string16 very_wide_line(str.substr(0, 3));
+ BreakIterator iter(str, BreakIterator::BREAK_NEWLINE);
+ ASSERT_TRUE(iter.Init());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(very_wide_line, iter.GetString());
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_EQ(UTF8ToUTF16("a"), iter.GetString());
+ EXPECT_FALSE(iter.Advance());
+ EXPECT_FALSE(iter.IsWord());
+ EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end.
+ EXPECT_FALSE(iter.IsWord());
+}
+
+TEST(BreakIteratorTest, BreakCharacter) {
+ static const wchar_t* kCharacters[] = {
+ // An English word consisting of four ASCII characters.
+ L"w", L"o", L"r", L"d", L" ",
+ // A Hindi word (which means "Hindi") consisting of three Devanagari
+ // characters.
+ L"\x0939\x093F", L"\x0928\x094D", L"\x0926\x0940", L" ",
+ // A Thai word (which means "feel") consisting of three Thai characters.
+ L"\x0E23\x0E39\x0E49", L"\x0E2A\x0E36", L"\x0E01", L" ",
+ };
+ std::vector<string16> characters;
+ string16 text;
+ for (size_t i = 0; i < arraysize(kCharacters); ++i) {
+ characters.push_back(WideToUTF16(kCharacters[i]));
+ text.append(characters.back());
+ }
+ BreakIterator iter(text, BreakIterator::BREAK_CHARACTER);
+ ASSERT_TRUE(iter.Init());
+ for (size_t i = 0; i < arraysize(kCharacters); ++i) {
+ EXPECT_TRUE(iter.Advance());
+ EXPECT_EQ(characters[i], iter.GetString());
+ }
+}
+
+} // namespace i18n
+} // namespace base
diff --git a/src/base/i18n/case_conversion.cc b/src/base/i18n/case_conversion.cc
new file mode 100644
index 0000000..d3b06c9
--- /dev/null
+++ b/src/base/i18n/case_conversion.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/case_conversion.h"
+
+#include "base/string16.h"
+#include "unicode/unistr.h"
+
+namespace base {
+namespace i18n {
+
+string16 ToLower(const StringPiece16& string) {
+ icu::UnicodeString unicode_string(string.data(), string.size());
+ unicode_string.toLower();
+ return string16(unicode_string.getBuffer(), unicode_string.length());
+}
+
+string16 ToUpper(const StringPiece16& string) {
+ icu::UnicodeString unicode_string(string.data(), string.size());
+ unicode_string.toUpper();
+ return string16(unicode_string.getBuffer(), unicode_string.length());
+}
+
+} // namespace i18n
+} // namespace base
diff --git a/src/base/i18n/case_conversion.h b/src/base/i18n/case_conversion.h
new file mode 100644
index 0000000..14fef00
--- /dev/null
+++ b/src/base/i18n/case_conversion.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_CASE_CONVERSION_H_
+#define BASE_I18N_CASE_CONVERSION_H_
+
+#include "base/i18n/base_i18n_export.h"
+#include "base/string16.h"
+#include "base/string_piece.h"
+
+namespace base {
+namespace i18n {
+
+// Returns the lower case equivalent of string. Uses ICU's default locale.
+BASE_I18N_EXPORT string16 ToLower(const StringPiece16& string);
+
+// Returns the upper case equivalent of string. Uses ICU's default locale.
+BASE_I18N_EXPORT string16 ToUpper(const StringPiece16& string);
+
+} // namespace i18n
+} // namespace base
+
+#endif // BASE_I18N_CASE_CONVERSION_H_
diff --git a/src/base/i18n/case_conversion_unittest.cc b/src/base/i18n/case_conversion_unittest.cc
new file mode 100644
index 0000000..63900ec
--- /dev/null
+++ b/src/base/i18n/case_conversion_unittest.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/case_conversion.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Test upper and lower case string conversion.
+TEST(CaseConversionTest, UpperLower) {
+ string16 mixed(ASCIIToUTF16("Text with UPPer & lowER casE."));
+ const string16 expected_lower(ASCIIToUTF16("text with upper & lower case."));
+ const string16 expected_upper(ASCIIToUTF16("TEXT WITH UPPER & LOWER CASE."));
+
+ string16 result = base::i18n::ToLower(mixed);
+ EXPECT_EQ(expected_lower, result);
+
+ result = base::i18n::ToUpper(mixed);
+ EXPECT_EQ(expected_upper, result);
+}
+
+// TODO(jshin): More tests are needed, especially with non-ASCII characters.
+
+} // namespace
diff --git a/src/base/i18n/char_iterator.cc b/src/base/i18n/char_iterator.cc
new file mode 100644
index 0000000..ce4d513
--- /dev/null
+++ b/src/base/i18n/char_iterator.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/char_iterator.h"
+
+#include "unicode/utf8.h"
+#include "unicode/utf16.h"
+
+namespace base {
+namespace i18n {
+
+UTF8CharIterator::UTF8CharIterator(const std::string* str)
+ : str_(reinterpret_cast<const uint8_t*>(str->data())),
+ len_(str->size()),
+ array_pos_(0),
+ next_pos_(0),
+ char_pos_(0),
+ char_(0) {
+ if (len_)
+ U8_NEXT(str_, next_pos_, len_, char_);
+}
+
+UTF8CharIterator::~UTF8CharIterator() {
+}
+
+bool UTF8CharIterator::Advance() {
+ if (array_pos_ >= len_)
+ return false;
+
+ array_pos_ = next_pos_;
+ char_pos_++;
+ if (next_pos_ < len_)
+ U8_NEXT(str_, next_pos_, len_, char_);
+
+ return true;
+}
+
+UTF16CharIterator::UTF16CharIterator(const string16* str)
+ : str_(reinterpret_cast<const char16*>(str->data())),
+ len_(str->size()),
+ array_pos_(0),
+ next_pos_(0),
+ char_pos_(0),
+ char_(0) {
+ if (len_)
+ ReadChar();
+}
+
+UTF16CharIterator::UTF16CharIterator(const char16* str, size_t str_len)
+ : str_(str),
+ len_(str_len),
+ array_pos_(0),
+ next_pos_(0),
+ char_pos_(0),
+ char_(0) {
+ if (len_)
+ ReadChar();
+}
+
+UTF16CharIterator::~UTF16CharIterator() {
+}
+
+bool UTF16CharIterator::Advance() {
+ if (array_pos_ >= len_)
+ return false;
+
+ array_pos_ = next_pos_;
+ char_pos_++;
+ if (next_pos_ < len_)
+ ReadChar();
+
+ return true;
+}
+
+void UTF16CharIterator::ReadChar() {
+ // This is actually a huge macro, so is worth having in a separate function.
+ U16_NEXT(str_, next_pos_, len_, char_);
+}
+
+} // namespace i18n
+} // namespace base
diff --git a/src/base/i18n/char_iterator.h b/src/base/i18n/char_iterator.h
new file mode 100644
index 0000000..431e350
--- /dev/null
+++ b/src/base/i18n/char_iterator.h
@@ -0,0 +1,130 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_CHAR_ITERATOR_H_
+#define BASE_I18N_CHAR_ITERATOR_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/i18n/base_i18n_export.h"
+#include "base/string16.h"
+
+// The CharIterator classes iterate through the characters in UTF8 and
+// UTF16 strings. Example usage:
+//
+// UTF8CharIterator iter(&str);
+// while (!iter.End()) {
+// VLOG(1) << iter.get();
+// iter.Advance();
+// }
+
+#if defined(OS_WIN)
+typedef unsigned char uint8_t;
+#endif
+
+namespace base {
+namespace i18n {
+
+class BASE_I18N_EXPORT UTF8CharIterator {
+ public:
+ // Requires |str| to live as long as the UTF8CharIterator does.
+ UTF8CharIterator(const std::string* str);
+ ~UTF8CharIterator();
+
+ // Return the starting array index of the current character within the
+ // string.
+ int32 array_pos() const { return array_pos_; }
+
+ // Return the logical index of the current character, independent of the
+ // number of bytes each character takes.
+ int32 char_pos() const { return char_pos_; }
+
+ // Return the current char.
+ int32 get() const { return char_; }
+
+ // Returns true if we're at the end of the string.
+ bool end() const { return array_pos_ == len_; }
+
+ // Advance to the next actual character. Returns false if we're at the
+ // end of the string.
+ bool Advance();
+
+ private:
+ // The string we're iterating over.
+ const uint8_t* str_;
+
+ // The length of the encoded string.
+ int32 len_;
+
+ // Array index.
+ int32 array_pos_;
+
+ // The next array index.
+ int32 next_pos_;
+
+ // Character index.
+ int32 char_pos_;
+
+ // The current character.
+ int32 char_;
+
+ DISALLOW_COPY_AND_ASSIGN(UTF8CharIterator);
+};
+
+class BASE_I18N_EXPORT UTF16CharIterator {
+ public:
+ // Requires |str| to live as long as the UTF16CharIterator does.
+ UTF16CharIterator(const string16* str);
+ UTF16CharIterator(const char16* str, size_t str_len);
+ ~UTF16CharIterator();
+
+ // Return the starting array index of the current character within the
+ // string.
+ int32 array_pos() const { return array_pos_; }
+
+ // Return the logical index of the current character, independent of the
+ // number of codewords each character takes.
+ int32 char_pos() const { return char_pos_; }
+
+ // Return the current char.
+ int32 get() const { return char_; }
+
+ // Returns true if we're at the end of the string.
+ bool end() const { return array_pos_ == len_; }
+
+ // Advance to the next actual character. Returns false if we're at the
+ // end of the string.
+ bool Advance();
+
+ private:
+ // Fills in the current character we found and advances to the next
+ // character, updating all flags as necessary.
+ void ReadChar();
+
+ // The string we're iterating over.
+ const char16* str_;
+
+ // The length of the encoded string.
+ int32 len_;
+
+ // Array index.
+ int32 array_pos_;
+
+ // The next array index.
+ int32 next_pos_;
+
+ // Character index.
+ int32 char_pos_;
+
+ // The current character.
+ int32 char_;
+
+ DISALLOW_COPY_AND_ASSIGN(UTF16CharIterator);
+};
+
+} // namespace i18n
+} // namespace base
+
+#endif // BASE_I18N_CHAR_ITERATOR_H_
diff --git a/src/base/i18n/char_iterator_unittest.cc b/src/base/i18n/char_iterator_unittest.cc
new file mode 100644
index 0000000..6d1294e
--- /dev/null
+++ b/src/base/i18n/char_iterator_unittest.cc
@@ -0,0 +1,101 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/char_iterator.h"
+
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace i18n {
+
+TEST(CharIteratorsTest, TestUTF8) {
+ std::string empty("");
+ UTF8CharIterator empty_iter(&empty);
+ ASSERT_TRUE(empty_iter.end());
+ ASSERT_EQ(0, empty_iter.array_pos());
+ ASSERT_EQ(0, empty_iter.char_pos());
+ ASSERT_FALSE(empty_iter.Advance());
+
+ std::string str("s\303\273r"); // [u with circumflex]
+ UTF8CharIterator iter(&str);
+ ASSERT_FALSE(iter.end());
+ ASSERT_EQ(0, iter.array_pos());
+ ASSERT_EQ(0, iter.char_pos());
+ ASSERT_EQ('s', iter.get());
+ ASSERT_TRUE(iter.Advance());
+
+ ASSERT_FALSE(iter.end());
+ ASSERT_EQ(1, iter.array_pos());
+ ASSERT_EQ(1, iter.char_pos());
+ ASSERT_EQ(251, iter.get());
+ ASSERT_TRUE(iter.Advance());
+
+ ASSERT_FALSE(iter.end());
+ ASSERT_EQ(3, iter.array_pos());
+ ASSERT_EQ(2, iter.char_pos());
+ ASSERT_EQ('r', iter.get());
+ ASSERT_TRUE(iter.Advance());
+
+ ASSERT_TRUE(iter.end());
+ ASSERT_EQ(4, iter.array_pos());
+ ASSERT_EQ(3, iter.char_pos());
+
+ // Don't care what it returns, but this shouldn't crash
+ iter.get();
+
+ ASSERT_FALSE(iter.Advance());
+}
+
+TEST(CharIteratorsTest, TestUTF16) {
+ string16 empty = UTF8ToUTF16("");
+ UTF16CharIterator empty_iter(&empty);
+ ASSERT_TRUE(empty_iter.end());
+ ASSERT_EQ(0, empty_iter.array_pos());
+ ASSERT_EQ(0, empty_iter.char_pos());
+ ASSERT_FALSE(empty_iter.Advance());
+
+ // This test string contains 4 characters:
+ // x
+ // u with circumflex - 2 bytes in UTF8, 1 codeword in UTF16
+ // math double-struck A - 4 bytes in UTF8, 2 codewords in UTF16
+ // z
+ string16 str = UTF8ToUTF16("x\303\273\360\235\224\270z");
+ UTF16CharIterator iter(&str);
+ ASSERT_FALSE(iter.end());
+ ASSERT_EQ(0, iter.array_pos());
+ ASSERT_EQ(0, iter.char_pos());
+ ASSERT_EQ('x', iter.get());
+ ASSERT_TRUE(iter.Advance());
+
+ ASSERT_FALSE(iter.end());
+ ASSERT_EQ(1, iter.array_pos());
+ ASSERT_EQ(1, iter.char_pos());
+ ASSERT_EQ(251, iter.get());
+ ASSERT_TRUE(iter.Advance());
+
+ ASSERT_FALSE(iter.end());
+ ASSERT_EQ(2, iter.array_pos());
+ ASSERT_EQ(2, iter.char_pos());
+ ASSERT_EQ(120120, iter.get());
+ ASSERT_TRUE(iter.Advance());
+
+ ASSERT_FALSE(iter.end());
+ ASSERT_EQ(4, iter.array_pos());
+ ASSERT_EQ(3, iter.char_pos());
+ ASSERT_EQ('z', iter.get());
+ ASSERT_TRUE(iter.Advance());
+
+ ASSERT_TRUE(iter.end());
+ ASSERT_EQ(5, iter.array_pos());
+ ASSERT_EQ(4, iter.char_pos());
+
+ // Don't care what it returns, but this shouldn't crash
+ iter.get();
+
+ ASSERT_FALSE(iter.Advance());
+}
+
+} // namespace i18n
+} // namespace base
diff --git a/src/base/i18n/file_util_icu.cc b/src/base/i18n/file_util_icu.cc
new file mode 100644
index 0000000..fc07d13
--- /dev/null
+++ b/src/base/i18n/file_util_icu.cc
@@ -0,0 +1,214 @@
+// Copyright (c) 2012 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.
+
+// File utilities that use the ICU library go in this file.
+
+#include "base/i18n/file_util_icu.h"
+
+#include "base/file_path.h"
+#include "base/i18n/icu_string_conversions.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "base/sys_string_conversions.h"
+#include "build/build_config.h"
+#include "unicode/coll.h"
+#include "unicode/uniset.h"
+
+namespace {
+
+class IllegalCharacters {
+ public:
+ static IllegalCharacters* GetInstance() {
+ return Singleton<IllegalCharacters>::get();
+ }
+
+ bool contains(UChar32 ucs4) {
+ return !!set->contains(ucs4);
+ }
+
+ bool containsNone(const string16 &s) {
+ return !!set->containsNone(icu::UnicodeString(s.c_str(), s.size()));
+ }
+
+ private:
+ friend class Singleton<IllegalCharacters>;
+ friend struct DefaultSingletonTraits<IllegalCharacters>;
+
+ IllegalCharacters();
+ ~IllegalCharacters() { }
+
+ scoped_ptr<icu::UnicodeSet> set;
+
+ DISALLOW_COPY_AND_ASSIGN(IllegalCharacters);
+};
+
+IllegalCharacters::IllegalCharacters() {
+ UErrorCode status = U_ZERO_ERROR;
+ // Control characters, formatting characters, non-characters, and
+ // some printable ASCII characters regarded as dangerous ('"*/:<>?\\').
+ // See http://blogs.msdn.com/michkap/archive/2006/11/03/941420.aspx
+ // and http://msdn2.microsoft.com/en-us/library/Aa365247.aspx
+ // TODO(jungshik): Revisit the set. ZWJ and ZWNJ are excluded because they
+ // are legitimate in Arabic and some S/SE Asian scripts. However, when used
+ // elsewhere, they can be confusing/problematic.
+ // Also, consider wrapping the set with our Singleton class to create and
+ // freeze it only once. Note that there's a trade-off between memory and
+ // speed.
+#if defined(WCHAR_T_IS_UTF16)
+ set.reset(new icu::UnicodeSet(icu::UnicodeString(
+ L"[[\"*/:<>?\\\\|][:Cc:][:Cf:] - [\u200c\u200d]]"), status));
+#else
+ set.reset(new icu::UnicodeSet(UNICODE_STRING_SIMPLE(
+ "[[\"*/:<>?\\\\|][:Cc:][:Cf:] - [\\u200c\\u200d]]").unescape(),
+ status));
+#endif
+ DCHECK(U_SUCCESS(status));
+ // Add non-characters. If this becomes a performance bottleneck by
+ // any chance, do not add these to |set| and change IsFilenameLegal()
+ // to check |ucs4 & 0xFFFEu == 0xFFFEu|, in addiition to calling
+ // containsNone().
+ set->add(0xFDD0, 0xFDEF);
+ for (int i = 0; i <= 0x10; ++i) {
+ int plane_base = 0x10000 * i;
+ set->add(plane_base + 0xFFFE, plane_base + 0xFFFF);
+ }
+ set->freeze();
+}
+
+class LocaleAwareComparator {
+ public:
+ static LocaleAwareComparator* GetInstance() {
+ return Singleton<LocaleAwareComparator,
+ LeakySingletonTraits<LocaleAwareComparator> >::get();
+ }
+
+ // Note: A similar function is available in l10n_util.
+ // We cannot use it because base should not depend on l10n_util.
+ // TODO(yuzo): Move some of l10n_util to base.
+ int Compare(const string16& a, const string16& b) {
+ // We are not sure if Collator::compare is thread-safe.
+ // Use an AutoLock just in case.
+ base::AutoLock auto_lock(lock_);
+
+ UErrorCode error_code = U_ZERO_ERROR;
+ UCollationResult result = collator_->compare(
+ static_cast<const UChar*>(a.c_str()),
+ static_cast<int>(a.length()),
+ static_cast<const UChar*>(b.c_str()),
+ static_cast<int>(b.length()),
+ error_code);
+ DCHECK(U_SUCCESS(error_code));
+ return result;
+ }
+
+ private:
+ friend struct DefaultSingletonTraits<LocaleAwareComparator>;
+
+ LocaleAwareComparator() {
+ UErrorCode error_code = U_ZERO_ERROR;
+ // Use the default collator. The default locale should have been properly
+ // set by the time this constructor is called.
+ collator_.reset(icu::Collator::createInstance(error_code));
+ DCHECK(U_SUCCESS(error_code));
+ // Make it case-sensitive.
+ collator_->setStrength(icu::Collator::TERTIARY);
+ // Note: We do not set UCOL_NORMALIZATION_MODE attribute. In other words, we
+ // do not pay performance penalty to guarantee sort order correctness for
+ // non-FCD (http://unicode.org/notes/tn5/#FCD) file names. This should be a
+ // reasonable tradeoff because such file names should be rare and the sort
+ // order doesn't change much anyway.
+ }
+
+ scoped_ptr<icu::Collator> collator_;
+ base::Lock lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(LocaleAwareComparator);
+};
+
+} // namespace
+
+namespace file_util {
+
+bool IsFilenameLegal(const string16& file_name) {
+ return IllegalCharacters::GetInstance()->containsNone(file_name);
+}
+
+void ReplaceIllegalCharactersInPath(FilePath::StringType* file_name,
+ char replace_char) {
+ DCHECK(file_name);
+
+ DCHECK(!(IllegalCharacters::GetInstance()->contains(replace_char)));
+
+ // Remove leading and trailing whitespace.
+ TrimWhitespace(*file_name, TRIM_ALL, file_name);
+
+ IllegalCharacters* illegal = IllegalCharacters::GetInstance();
+ int cursor = 0; // The ICU macros expect an int.
+ while (cursor < static_cast<int>(file_name->size())) {
+ int char_begin = cursor;
+ uint32 code_point;
+#if defined(OS_MACOSX)
+ // Mac uses UTF-8 encoding for filenames.
+ U8_NEXT(file_name->data(), cursor, static_cast<int>(file_name->length()),
+ code_point);
+#elif defined(OS_WIN)
+ // Windows uses UTF-16 encoding for filenames.
+ U16_NEXT(file_name->data(), cursor, static_cast<int>(file_name->length()),
+ code_point);
+#elif defined(OS_POSIX) || defined(OS_STARBAORD)
+ // Linux doesn't actually define an encoding. It basically allows anything
+ // except for a few special ASCII characters.
+ unsigned char cur_char = static_cast<unsigned char>((*file_name)[cursor++]);
+ if (cur_char >= 0x80)
+ continue;
+ code_point = cur_char;
+#else
+ NOTREACHED();
+#endif
+
+ if (illegal->contains(code_point)) {
+ file_name->replace(char_begin, cursor - char_begin, 1, replace_char);
+ // We just made the potentially multi-byte/word char into one that only
+ // takes one byte/word, so need to adjust the cursor to point to the next
+ // character again.
+ cursor = char_begin + 1;
+ }
+ }
+}
+
+bool LocaleAwareCompareFilenames(const FilePath& a, const FilePath& b) {
+#if defined(OS_WIN)
+ return LocaleAwareComparator::GetInstance()->Compare(a.value().c_str(),
+ b.value().c_str()) < 0;
+
+#elif defined(OS_POSIX) || defined(OS_STARBOARD)
+ // On linux, the file system encoding is not defined. We assume
+ // SysNativeMBToWide takes care of it.
+ //
+ // ICU's collator can take strings in OS native encoding. But we convert the
+ // strings to UTF-16 ourselves to ensure conversion consistency.
+ // TODO(yuzo): Perhaps we should define SysNativeMBToUTF16?
+ return LocaleAwareComparator::GetInstance()->Compare(
+ WideToUTF16(base::SysNativeMBToWide(a.value().c_str())),
+ WideToUTF16(base::SysNativeMBToWide(b.value().c_str()))) < 0;
+#else
+ #error Not implemented on your system
+#endif
+}
+
+void NormalizeFileNameEncoding(FilePath* file_name) {
+#if defined(OS_CHROMEOS)
+ std::string normalized_str;
+ if (base::ConvertToUtf8AndNormalize(file_name->BaseName().value(),
+ base::kCodepageUTF8,
+ &normalized_str)) {
+ *file_name = file_name->DirName().Append(FilePath(normalized_str));
+ }
+#endif
+}
+
+} // namespace
diff --git a/src/base/i18n/file_util_icu.h b/src/base/i18n/file_util_icu.h
new file mode 100644
index 0000000..4672135
--- /dev/null
+++ b/src/base/i18n/file_util_icu.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2012 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_I18N_FILE_UTIL_ICU_H_
+#define BASE_I18N_FILE_UTIL_ICU_H_
+
+// File utilities that use the ICU library go in this file.
+
+#include "base/file_path.h"
+#include "base/i18n/base_i18n_export.h"
+#include "base/string16.h"
+
+namespace file_util {
+
+// Returns true if file_name does not have any illegal character. The input
+// param has the same restriction as that for ReplaceIllegalCharacters.
+BASE_I18N_EXPORT bool IsFilenameLegal(const string16& file_name);
+
+// Replaces characters in 'file_name' that are illegal for file names with
+// 'replace_char'. 'file_name' must not be a full or relative path, but just the
+// file name component (since slashes are considered illegal). Any leading or
+// trailing whitespace in 'file_name' is removed.
+// Example:
+// file_name == "bad:file*name?.txt", changed to: "bad-file-name-.txt" when
+// 'replace_char' is '-'.
+BASE_I18N_EXPORT void ReplaceIllegalCharactersInPath(
+ FilePath::StringType* file_name,
+ char replace_char);
+
+// Compares two filenames using the current locale information. This can be
+// used to sort directory listings. It behaves like "operator<" for use in
+// std::sort.
+BASE_I18N_EXPORT bool LocaleAwareCompareFilenames(const FilePath& a,
+ const FilePath& b);
+
+// Calculates the canonical file-system representation of |file_name| base name.
+// Modifies |file_name| in place. No-op if not on ChromeOS.
+BASE_I18N_EXPORT void NormalizeFileNameEncoding(FilePath* file_name);
+
+} // namespace file_util
+
+#endif // BASE_I18N_FILE_UTIL_ICU_H_
diff --git a/src/base/i18n/file_util_icu_unittest.cc b/src/base/i18n/file_util_icu_unittest.cc
new file mode 100644
index 0000000..dfc4943
--- /dev/null
+++ b/src/base/i18n/file_util_icu_unittest.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 2012 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/i18n/file_util_icu.h"
+
+#include "base/file_util.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+// file_util winds up using autoreleased objects on the Mac, so this needs
+// to be a PlatformTest
+class FileUtilICUTest : public PlatformTest {
+};
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+
+// Linux disallows some evil ASCII characters, but passes all non-ASCII.
+static const struct goodbad_pair {
+ const char* bad_name;
+ const char* good_name;
+} kIllegalCharacterCases[] = {
+ {"bad*file:name?.jpg", "bad-file-name-.jpg"},
+ {"**********::::.txt", "--------------.txt"},
+ {"\xe9\xf0zzzz.\xff", "\xe9\xf0zzzz.\xff"},
+};
+
+TEST_F(FileUtilICUTest, ReplaceIllegalCharacersInPathLinuxTest) {
+ for (size_t i = 0; i < arraysize(kIllegalCharacterCases); ++i) {
+ std::string bad_name(kIllegalCharacterCases[i].bad_name);
+ file_util::ReplaceIllegalCharactersInPath(&bad_name, '-');
+ EXPECT_EQ(kIllegalCharacterCases[i].good_name, bad_name);
+ }
+}
+
+#else
+
+// For Mac & Windows, which both do Unicode validation on filenames. These
+// characters are given as wide strings since its more convenient to specify
+// unicode characters. For Mac they should be converted to UTF-8.
+static const struct goodbad_pair {
+ const wchar_t* bad_name;
+ const wchar_t* good_name;
+} kIllegalCharacterCases[] = {
+ {L"bad*file:name?.jpg", L"bad-file-name-.jpg"},
+ {L"**********::::.txt", L"--------------.txt"},
+ // We can't use UCNs (universal character names) for C0/C1 characters and
+ // U+007F, but \x escape is interpreted by MSVC and gcc as we intend.
+ {L"bad\x0003\x0091 file\u200E\u200Fname.png", L"bad-- file--name.png"},
+#if defined(OS_WIN)
+ {L"bad*file\\name.jpg", L"bad-file-name.jpg"},
+ {L"\t bad*file\\name/.jpg ", L"bad-file-name-.jpg"},
+#elif defined(OS_MACOSX)
+ {L"bad*file?name.jpg", L"bad-file-name.jpg"},
+ {L"\t bad*file?name/.jpg ", L"bad-file-name-.jpg"},
+#endif
+ {L"this_file_name is okay!.mp3", L"this_file_name is okay!.mp3"},
+ {L"\u4E00\uAC00.mp3", L"\u4E00\uAC00.mp3"},
+ {L"\u0635\u200C\u0644.mp3", L"\u0635\u200C\u0644.mp3"},
+ {L"\U00010330\U00010331.mp3", L"\U00010330\U00010331.mp3"},
+ // Unassigned codepoints are ok.
+ {L"\u0378\U00040001.mp3", L"\u0378\U00040001.mp3"},
+ // Non-characters are not allowed.
+ {L"bad\uFFFFfile\U0010FFFEname.jpg ", L"bad-file-name.jpg"},
+ {L"bad\uFDD0file\uFDEFname.jpg ", L"bad-file-name.jpg"},
+};
+
+TEST_F(FileUtilICUTest, ReplaceIllegalCharactersInPathTest) {
+ for (size_t i = 0; i < arraysize(kIllegalCharacterCases); ++i) {
+#if defined(OS_WIN)
+ std::wstring bad_name(kIllegalCharacterCases[i].bad_name);
+ file_util::ReplaceIllegalCharactersInPath(&bad_name, '-');
+ EXPECT_EQ(kIllegalCharacterCases[i].good_name, bad_name);
+#elif defined(OS_MACOSX)
+ std::string bad_name(WideToUTF8(kIllegalCharacterCases[i].bad_name));
+ file_util::ReplaceIllegalCharactersInPath(&bad_name, '-');
+ EXPECT_EQ(WideToUTF8(kIllegalCharacterCases[i].good_name), bad_name);
+#endif
+ }
+}
+
+#endif
+
+#if defined(OS_CHROMEOS)
+static const struct normalize_name_encoding_test_cases {
+ const char* original_path;
+ const char* normalized_path;
+} kNormalizeFileNameEncodingTestCases[] = {
+ { "foo_na\xcc\x88me.foo", "foo_n\xc3\xa4me.foo"},
+ { "foo_dir_na\xcc\x88me/foo_na\xcc\x88me.foo",
+ "foo_dir_na\xcc\x88me/foo_n\xc3\xa4me.foo"},
+ { "", ""},
+ { "foo_dir_na\xcc\x88me/", "foo_dir_n\xc3\xa4me"}
+};
+
+TEST_F(FileUtilICUTest, NormalizeFileNameEncoding) {
+ for (size_t i = 0; i < arraysize(kNormalizeFileNameEncodingTestCases); i++) {
+ FilePath path(kNormalizeFileNameEncodingTestCases[i].original_path);
+ file_util::NormalizeFileNameEncoding(&path);
+ EXPECT_EQ(FilePath(kNormalizeFileNameEncodingTestCases[i].normalized_path),
+ path);
+ }
+}
+
+#endif
diff --git a/src/base/i18n/icu_encoding_detection.cc b/src/base/i18n/icu_encoding_detection.cc
new file mode 100644
index 0000000..2081c7a
--- /dev/null
+++ b/src/base/i18n/icu_encoding_detection.cc
@@ -0,0 +1,104 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/icu_encoding_detection.h"
+
+#include <set>
+
+#include "base/string_util.h"
+#include "unicode/ucsdet.h"
+
+namespace base {
+
+bool DetectEncoding(const std::string& text, std::string* encoding) {
+ if (IsStringASCII(text)) {
+ *encoding = std::string();
+ return true;
+ }
+
+ UErrorCode status = U_ZERO_ERROR;
+ UCharsetDetector* detector = ucsdet_open(&status);
+ ucsdet_setText(detector, text.data(), static_cast<int32_t>(text.length()),
+ &status);
+ const UCharsetMatch* match = ucsdet_detect(detector, &status);
+ if (match == NULL)
+ return false;
+ const char* detected_encoding = ucsdet_getName(match, &status);
+ ucsdet_close(detector);
+
+ if (U_FAILURE(status))
+ return false;
+
+ *encoding = detected_encoding;
+ return true;
+}
+
+bool DetectAllEncodings(const std::string& text,
+ std::vector<std::string>* encodings) {
+ UErrorCode status = U_ZERO_ERROR;
+ UCharsetDetector* detector = ucsdet_open(&status);
+ ucsdet_setText(detector, text.data(), static_cast<int32_t>(text.length()),
+ &status);
+ int matches_count = 0;
+ const UCharsetMatch** matches = ucsdet_detectAll(detector,
+ &matches_count,
+ &status);
+ if (U_FAILURE(status)) {
+ ucsdet_close(detector);
+ return false;
+ }
+
+ // ICU has some heuristics for encoding detection, such that the more likely
+ // encodings should be returned first. However, it doesn't always return
+ // all encodings that properly decode |text|, so we'll append more encodings
+ // later. To make that efficient, keep track of encodings sniffed in this
+ // first phase.
+ std::set<std::string> sniffed_encodings;
+
+ encodings->clear();
+ for (int i = 0; i < matches_count; i++) {
+ UErrorCode get_name_status = U_ZERO_ERROR;
+ const char* encoding_name = ucsdet_getName(matches[i], &get_name_status);
+
+ // If we failed to get the encoding's name, ignore the error.
+ if (U_FAILURE(get_name_status))
+ continue;
+
+ int32_t confidence = ucsdet_getConfidence(matches[i], &get_name_status);
+
+ // We also treat this error as non-fatal.
+ if (U_FAILURE(get_name_status))
+ continue;
+
+ // A confidence level >= 10 means that the encoding is expected to properly
+ // decode the text. Drop all encodings with lower confidence level.
+ if (confidence < 10)
+ continue;
+
+ encodings->push_back(encoding_name);
+ sniffed_encodings.insert(encoding_name);
+ }
+
+ // Append all encodings not included earlier, in arbitrary order.
+ // TODO(jshin): This shouldn't be necessary, possible ICU bug.
+ // See also http://crbug.com/65917.
+ UEnumeration* detectable_encodings = ucsdet_getAllDetectableCharsets(detector,
+ &status);
+ int detectable_count = uenum_count(detectable_encodings, &status);
+ for (int i = 0; i < detectable_count; i++) {
+ int name_length;
+ const char* name_raw = uenum_next(detectable_encodings,
+ &name_length,
+ &status);
+ std::string name(name_raw, name_length);
+ if (sniffed_encodings.find(name) == sniffed_encodings.end())
+ encodings->push_back(name);
+ }
+ uenum_close(detectable_encodings);
+
+ ucsdet_close(detector);
+ return !encodings->empty();
+}
+
+} // namespace base
diff --git a/src/base/i18n/icu_encoding_detection.h b/src/base/i18n/icu_encoding_detection.h
new file mode 100644
index 0000000..6d1e71c
--- /dev/null
+++ b/src/base/i18n/icu_encoding_detection.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_ICU_ENCODING_DETECTION_H_
+#define BASE_I18N_ICU_ENCODING_DETECTION_H_
+
+#include <string>
+#include <vector>
+
+#include "base/i18n/base_i18n_export.h"
+
+namespace base {
+
+// Detect encoding of |text| and put the name of encoding (as returned by ICU)
+// in |encoding|. For ASCII texts |encoding| will be set to an empty string.
+// Returns true on success.
+BASE_I18N_EXPORT bool DetectEncoding(const std::string& text,
+ std::string* encoding);
+
+// Detect all possible encodings of |text| and put their names
+// (as returned by ICU) in |encodings|. Returns true on success.
+// Note: this function may return encodings that may fail to decode |text|,
+// the caller is responsible for handling that.
+BASE_I18N_EXPORT bool DetectAllEncodings(const std::string& text,
+ std::vector<std::string>* encodings);
+
+} // namespace base
+
+#endif // BASE_I18N_ICU_ENCODING_DETECTION_H_
diff --git a/src/base/i18n/icu_string_conversions.cc b/src/base/i18n/icu_string_conversions.cc
new file mode 100644
index 0000000..5ebadf3
--- /dev/null
+++ b/src/base/i18n/icu_string_conversions.cc
@@ -0,0 +1,297 @@
+// Copyright (c) 2012 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/i18n/icu_string_conversions.h"
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "unicode/ucnv.h"
+#include "unicode/ucnv_cb.h"
+#include "unicode/ucnv_err.h"
+#include "unicode/unorm.h"
+#include "unicode/ustring.h"
+
+namespace base {
+
+namespace {
+// ToUnicodeCallbackSubstitute() is based on UCNV_TO_U_CALLBACK_SUBSTITUTE
+// in source/common/ucnv_err.c.
+
+// Copyright (c) 1995-2006 International Business Machines Corporation
+// and others
+//
+// All rights reserved.
+//
+
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, and/or
+// sell copies of the Software, and to permit persons to whom the Software
+// is furnished to do so, provided that the above copyright notice(s) and
+// this permission notice appear in all copies of the Software and that
+// both the above copyright notice(s) and this permission notice appear in
+// supporting documentation.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+// OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+// INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+// OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+// OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
+// OR PERFORMANCE OF THIS SOFTWARE.
+//
+// Except as contained in this notice, the name of a copyright holder
+// shall not be used in advertising or otherwise to promote the sale, use
+// or other dealings in this Software without prior written authorization
+// of the copyright holder.
+
+// ___________________________________________________________________________
+//
+// All trademarks and registered trademarks mentioned herein are the property
+// of their respective owners.
+
+void ToUnicodeCallbackSubstitute(const void* context,
+ UConverterToUnicodeArgs *to_args,
+ const char* code_units,
+ int32_t length,
+ UConverterCallbackReason reason,
+ UErrorCode * err) {
+ static const UChar kReplacementChar = 0xFFFD;
+ if (reason <= UCNV_IRREGULAR) {
+ if (context == NULL ||
+ (*(reinterpret_cast<const char*>(context)) == 'i' &&
+ reason == UCNV_UNASSIGNED)) {
+ *err = U_ZERO_ERROR;
+ ucnv_cbToUWriteUChars(to_args, &kReplacementChar, 1, 0, err);
+ }
+ // else the caller must have set the error code accordingly.
+ }
+ // else ignore the reset, close and clone calls.
+}
+
+bool ConvertFromUTF16(UConverter* converter, const UChar* uchar_src,
+ int uchar_len, OnStringConversionError::Type on_error,
+ std::string* encoded) {
+ int encoded_max_length = UCNV_GET_MAX_BYTES_FOR_STRING(uchar_len,
+ ucnv_getMaxCharSize(converter));
+ encoded->resize(encoded_max_length);
+
+ UErrorCode status = U_ZERO_ERROR;
+
+ // Setup our error handler.
+ switch (on_error) {
+ case OnStringConversionError::FAIL:
+ ucnv_setFromUCallBack(converter, UCNV_FROM_U_CALLBACK_STOP, 0,
+ NULL, NULL, &status);
+ break;
+ case OnStringConversionError::SKIP:
+ case OnStringConversionError::SUBSTITUTE:
+ ucnv_setFromUCallBack(converter, UCNV_FROM_U_CALLBACK_SKIP, 0,
+ NULL, NULL, &status);
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ // ucnv_fromUChars returns size not including terminating null
+ int actual_size = ucnv_fromUChars(converter, &(*encoded)[0],
+ encoded_max_length, uchar_src, uchar_len, &status);
+ encoded->resize(actual_size);
+ ucnv_close(converter);
+ if (U_SUCCESS(status))
+ return true;
+ encoded->clear(); // Make sure the output is empty on error.
+ return false;
+}
+
+// Set up our error handler for ToUTF-16 converters
+void SetUpErrorHandlerForToUChars(OnStringConversionError::Type on_error,
+ UConverter* converter, UErrorCode* status) {
+ switch (on_error) {
+ case OnStringConversionError::FAIL:
+ ucnv_setToUCallBack(converter, UCNV_TO_U_CALLBACK_STOP, 0,
+ NULL, NULL, status);
+ break;
+ case OnStringConversionError::SKIP:
+ ucnv_setToUCallBack(converter, UCNV_TO_U_CALLBACK_SKIP, 0,
+ NULL, NULL, status);
+ break;
+ case OnStringConversionError::SUBSTITUTE:
+ ucnv_setToUCallBack(converter, ToUnicodeCallbackSubstitute, 0,
+ NULL, NULL, status);
+ break;
+ default:
+ NOTREACHED();
+ }
+}
+
+inline UConverterType utf32_platform_endian() {
+#if U_IS_BIG_ENDIAN
+ return UCNV_UTF32_BigEndian;
+#else
+ return UCNV_UTF32_LittleEndian;
+#endif
+}
+
+} // namespace
+
+const char kCodepageLatin1[] = "ISO-8859-1";
+const char kCodepageUTF8[] = "UTF-8";
+const char kCodepageUTF16BE[] = "UTF-16BE";
+const char kCodepageUTF16LE[] = "UTF-16LE";
+
+// Codepage <-> Wide/UTF-16 ---------------------------------------------------
+
+bool UTF16ToCodepage(const string16& utf16,
+ const char* codepage_name,
+ OnStringConversionError::Type on_error,
+ std::string* encoded) {
+ encoded->clear();
+
+ UErrorCode status = U_ZERO_ERROR;
+ UConverter* converter = ucnv_open(codepage_name, &status);
+ if (!U_SUCCESS(status))
+ return false;
+
+ return ConvertFromUTF16(converter, utf16.c_str(),
+ static_cast<int>(utf16.length()), on_error, encoded);
+}
+
+bool CodepageToUTF16(const std::string& encoded,
+ const char* codepage_name,
+ OnStringConversionError::Type on_error,
+ string16* utf16) {
+ utf16->clear();
+
+ UErrorCode status = U_ZERO_ERROR;
+ UConverter* converter = ucnv_open(codepage_name, &status);
+ if (!U_SUCCESS(status))
+ return false;
+
+ // Even in the worst case, the maximum length in 2-byte units of UTF-16
+ // output would be at most the same as the number of bytes in input. There
+ // is no single-byte encoding in which a character is mapped to a
+ // non-BMP character requiring two 2-byte units.
+ //
+ // Moreover, non-BMP characters in legacy multibyte encodings
+ // (e.g. EUC-JP, GB18030) take at least 2 bytes. The only exceptions are
+ // BOCU and SCSU, but we don't care about them.
+ size_t uchar_max_length = encoded.length() + 1;
+
+ SetUpErrorHandlerForToUChars(on_error, converter, &status);
+ scoped_array<char16> buffer(new char16[uchar_max_length]);
+ int actual_size = ucnv_toUChars(converter, buffer.get(),
+ static_cast<int>(uchar_max_length), encoded.data(),
+ static_cast<int>(encoded.length()), &status);
+ ucnv_close(converter);
+ if (!U_SUCCESS(status)) {
+ utf16->clear(); // Make sure the output is empty on error.
+ return false;
+ }
+
+ utf16->assign(buffer.get(), actual_size);
+ return true;
+}
+
+bool WideToCodepage(const std::wstring& wide,
+ const char* codepage_name,
+ OnStringConversionError::Type on_error,
+ std::string* encoded) {
+#if defined(WCHAR_T_IS_UTF16)
+ return UTF16ToCodepage(wide, codepage_name, on_error, encoded);
+#elif defined(WCHAR_T_IS_UTF32)
+ encoded->clear();
+
+ UErrorCode status = U_ZERO_ERROR;
+ UConverter* converter = ucnv_open(codepage_name, &status);
+ if (!U_SUCCESS(status))
+ return false;
+
+ int utf16_len;
+ // When wchar_t is wider than UChar (16 bits), transform |wide| into a
+ // UChar* string. Size the UChar* buffer to be large enough to hold twice
+ // as many UTF-16 code units (UChar's) as there are Unicode code points,
+ // in case each code points translates to a UTF-16 surrogate pair,
+ // and leave room for a NUL terminator.
+ std::vector<UChar> utf16(wide.length() * 2 + 1);
+ u_strFromUTF32(&utf16[0], utf16.size(), &utf16_len,
+ reinterpret_cast<const UChar32*>(wide.c_str()),
+ wide.length(), &status);
+ DCHECK(U_SUCCESS(status)) << "failed to convert wstring to UChar*";
+
+ return ConvertFromUTF16(converter, &utf16[0], utf16_len, on_error, encoded);
+#endif // defined(WCHAR_T_IS_UTF32)
+}
+
+bool CodepageToWide(const std::string& encoded,
+ const char* codepage_name,
+ OnStringConversionError::Type on_error,
+ std::wstring* wide) {
+#if defined(WCHAR_T_IS_UTF16)
+ return CodepageToUTF16(encoded, codepage_name, on_error, wide);
+#elif defined(WCHAR_T_IS_UTF32)
+ wide->clear();
+
+ UErrorCode status = U_ZERO_ERROR;
+ UConverter* converter = ucnv_open(codepage_name, &status);
+ if (!U_SUCCESS(status))
+ return false;
+
+ // The maximum length in 4 byte unit of UTF-32 output would be
+ // at most the same as the number of bytes in input. In the worst
+ // case of GB18030 (excluding escaped-based encodings like ISO-2022-JP),
+ // this can be 4 times larger than actually needed.
+ size_t wchar_max_length = encoded.length() + 1;
+
+ SetUpErrorHandlerForToUChars(on_error, converter, &status);
+ scoped_array<wchar_t> buffer(new wchar_t[wchar_max_length]);
+ int actual_size = ucnv_toAlgorithmic(utf32_platform_endian(), converter,
+ reinterpret_cast<char*>(buffer.get()),
+ static_cast<int>(wchar_max_length) * sizeof(wchar_t), encoded.data(),
+ static_cast<int>(encoded.length()), &status);
+ ucnv_close(converter);
+ if (!U_SUCCESS(status)) {
+ wide->clear(); // Make sure the output is empty on error.
+ return false;
+ }
+
+ // actual_size is # of bytes.
+ wide->assign(buffer.get(), actual_size / sizeof(wchar_t));
+ return true;
+#endif // defined(WCHAR_T_IS_UTF32)
+}
+
+bool ConvertToUtf8AndNormalize(const std::string& text,
+ const std::string& charset,
+ std::string* result) {
+ result->clear();
+ string16 utf16;
+ if (!CodepageToUTF16(
+ text, charset.c_str(), OnStringConversionError::FAIL, &utf16))
+ return false;
+
+ UErrorCode status = U_ZERO_ERROR;
+ size_t max_length = utf16.length() + 1;
+ string16 normalized_utf16;
+ scoped_array<char16> buffer(new char16[max_length]);
+ int actual_length = unorm_normalize(
+ utf16.c_str(), utf16.length(), UNORM_NFC, 0,
+ buffer.get(), static_cast<int>(max_length), &status);
+ if (!U_SUCCESS(status))
+ return false;
+ normalized_utf16.assign(buffer.get(), actual_length);
+
+ return UTF16ToUTF8(normalized_utf16.data(),
+ normalized_utf16.length(), result);
+}
+
+} // namespace base
diff --git a/src/base/i18n/icu_string_conversions.h b/src/base/i18n/icu_string_conversions.h
new file mode 100644
index 0000000..31a57df
--- /dev/null
+++ b/src/base/i18n/icu_string_conversions.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_ICU_STRING_CONVERSIONS_H_
+#define BASE_I18N_ICU_STRING_CONVERSIONS_H_
+
+#include <string>
+
+#include "base/i18n/base_i18n_export.h"
+#include "base/string16.h"
+
+namespace base {
+
+// Defines the error handling modes of UTF16ToCodepage, CodepageToUTF16,
+// WideToCodepage and CodepageToWide.
+class OnStringConversionError {
+ public:
+ enum Type {
+ // The function will return failure. The output buffer will be empty.
+ FAIL,
+
+ // The offending characters are skipped and the conversion will proceed as
+ // if they did not exist.
+ SKIP,
+
+ // When converting to Unicode, the offending byte sequences are substituted
+ // by Unicode replacement character (U+FFFD). When converting from Unicode,
+ // this is the same as SKIP.
+ SUBSTITUTE,
+ };
+
+ private:
+ OnStringConversionError();
+};
+
+// Names of codepages (charsets) understood by icu.
+BASE_I18N_EXPORT extern const char kCodepageLatin1[]; // a.k.a. ISO 8859-1
+BASE_I18N_EXPORT extern const char kCodepageUTF8[];
+BASE_I18N_EXPORT extern const char kCodepageUTF16BE[];
+BASE_I18N_EXPORT extern const char kCodepageUTF16LE[];
+
+// Converts between UTF-16 strings and the encoding specified. If the
+// encoding doesn't exist or the encoding fails (when on_error is FAIL),
+// returns false.
+BASE_I18N_EXPORT bool UTF16ToCodepage(const string16& utf16,
+ const char* codepage_name,
+ OnStringConversionError::Type on_error,
+ std::string* encoded);
+BASE_I18N_EXPORT bool CodepageToUTF16(const std::string& encoded,
+ const char* codepage_name,
+ OnStringConversionError::Type on_error,
+ string16* utf16);
+
+// Converts between wide strings and the encoding specified. If the
+// encoding doesn't exist or the encoding fails (when on_error is FAIL),
+// returns false.
+BASE_I18N_EXPORT bool WideToCodepage(const std::wstring& wide,
+ const char* codepage_name,
+ OnStringConversionError::Type on_error,
+ std::string* encoded);
+BASE_I18N_EXPORT bool CodepageToWide(const std::string& encoded,
+ const char* codepage_name,
+ OnStringConversionError::Type on_error,
+ std::wstring* wide);
+
+// Converts from any codepage to UTF-8 and ensures the resulting UTF-8 is
+// normalized.
+BASE_I18N_EXPORT bool ConvertToUtf8AndNormalize(const std::string& text,
+ const std::string& charset,
+ std::string* result);
+
+} // namespace base
+
+#endif // BASE_I18N_ICU_STRING_CONVERSIONS_H_
diff --git a/src/base/i18n/icu_string_conversions_unittest.cc b/src/base/i18n/icu_string_conversions_unittest.cc
new file mode 100644
index 0000000..5401759
--- /dev/null
+++ b/src/base/i18n/icu_string_conversions_unittest.cc
@@ -0,0 +1,386 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <math.h>
+#include <stdarg.h>
+
+#include <limits>
+#include <sstream>
+
+#include "base/basictypes.h"
+#include "base/format_macros.h"
+#include "base/i18n/icu_string_conversions.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "base/string_piece.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+// Given a null-terminated string of wchar_t with each wchar_t representing
+// a UTF-16 code unit, returns a string16 made up of wchar_t's in the input.
+// Each wchar_t should be <= 0xFFFF and a non-BMP character (> U+FFFF)
+// should be represented as a surrogate pair (two UTF-16 units)
+// *even* where wchar_t is 32-bit (Linux and Mac).
+//
+// This is to help write tests for functions with string16 params until
+// the C++ 0x UTF-16 literal is well-supported by compilers.
+string16 BuildString16(const wchar_t* s) {
+#if defined(WCHAR_T_IS_UTF16)
+ return string16(s);
+#elif defined(WCHAR_T_IS_UTF32)
+ string16 u16;
+ while (*s != 0) {
+ DCHECK_LE(static_cast<unsigned int>(*s), 0xFFFFu);
+ u16.push_back(*s++);
+ }
+ return u16;
+#endif
+}
+
+const wchar_t* const kConvertRoundtripCases[] = {
+ L"Google Video",
+ // "网页 图片 资讯更多 »"
+ L"\x7f51\x9875\x0020\x56fe\x7247\x0020\x8d44\x8baf\x66f4\x591a\x0020\x00bb",
+ // "Παγκόσμιος Ιστός"
+ L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
+ L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2",
+ // "Поиск страниц на русском"
+ L"\x041f\x043e\x0438\x0441\x043a\x0020\x0441\x0442"
+ L"\x0440\x0430\x043d\x0438\x0446\x0020\x043d\x0430"
+ L"\x0020\x0440\x0443\x0441\x0441\x043a\x043e\x043c",
+ // "전체서비스"
+ L"\xc804\xccb4\xc11c\xbe44\xc2a4",
+
+ // Test characters that take more than 16 bits. This will depend on whether
+ // wchar_t is 16 or 32 bits.
+#if defined(WCHAR_T_IS_UTF16)
+ L"\xd800\xdf00",
+ // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+ L"\xd807\xdd40\xd807\xdd41\xd807\xdd42\xd807\xdd43\xd807\xdd44",
+#elif defined(WCHAR_T_IS_UTF32)
+ L"\x10300",
+ // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+ L"\x11d40\x11d41\x11d42\x11d43\x11d44",
+#endif
+};
+
+} // namespace
+
+TEST(ICUStringConversionsTest, ConvertCodepageUTF8) {
+ // Make sure WideToCodepage works like WideToUTF8.
+ for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) {
+ SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: %ls",
+ i, kConvertRoundtripCases[i]));
+
+ std::string expected(WideToUTF8(kConvertRoundtripCases[i]));
+ std::string utf8;
+ EXPECT_TRUE(WideToCodepage(kConvertRoundtripCases[i], kCodepageUTF8,
+ OnStringConversionError::SKIP, &utf8));
+ EXPECT_EQ(expected, utf8);
+ }
+}
+
+// kConverterCodepageCases is not comprehensive. There are a number of cases
+// to add if we really want to have a comprehensive coverage of various
+// codepages and their 'idiosyncrasies'. Currently, the only implementation
+// for CodepageTo* and *ToCodepage uses ICU, which has a very extensive
+// set of tests for the charset conversion. So, we can get away with a
+// relatively small number of cases listed below.
+//
+// Note about |u16_wide| in the following struct.
+// On Windows, the field is always identical to |wide|. On Mac and Linux,
+// it's identical as long as there's no character outside the
+// BMP (<= U+FFFF). When there is, it is different from |wide| and
+// is not a real wide string (UTF-32 string) in that each wchar_t in
+// the string is a UTF-16 code unit zero-extended to be 32-bit
+// even when the code unit belongs to a surrogate pair.
+// For instance, a Unicode string (U+0041 U+010000) is represented as
+// L"\x0041\xD800\xDC00" instead of L"\x0041\x10000".
+// To avoid the clutter, |u16_wide| will be set to NULL
+// if it's identical to |wide| on *all* platforms.
+
+static const struct {
+ const char* codepage_name;
+ const char* encoded;
+ OnStringConversionError::Type on_error;
+ bool success;
+ const wchar_t* wide;
+ const wchar_t* u16_wide;
+} kConvertCodepageCases[] = {
+ // Test a case where the input cannot be decoded, using SKIP, FAIL
+ // and SUBSTITUTE error handling rules. "A7 41" is valid, but "A6" isn't.
+ {"big5",
+ "\xA7\x41\xA6",
+ OnStringConversionError::FAIL,
+ false,
+ L"",
+ NULL},
+ {"big5",
+ "\xA7\x41\xA6",
+ OnStringConversionError::SKIP,
+ true,
+ L"\x4F60",
+ NULL},
+ {"big5",
+ "\xA7\x41\xA6",
+ OnStringConversionError::SUBSTITUTE,
+ true,
+ L"\x4F60\xFFFD",
+ NULL},
+ // Arabic (ISO-8859)
+ {"iso-8859-6",
+ "\xC7\xEE\xE4\xD3\xF1\xEE\xE4\xC7\xE5\xEF" " "
+ "\xD9\xEE\xE4\xEE\xEA\xF2\xE3\xEF\xE5\xF2",
+ OnStringConversionError::FAIL,
+ true,
+ L"\x0627\x064E\x0644\x0633\x0651\x064E\x0644\x0627\x0645\x064F" L" "
+ L"\x0639\x064E\x0644\x064E\x064A\x0652\x0643\x064F\x0645\x0652",
+ NULL},
+ // Chinese Simplified (GB2312)
+ {"gb2312",
+ "\xC4\xE3\xBA\xC3",
+ OnStringConversionError::FAIL,
+ true,
+ L"\x4F60\x597D",
+ NULL},
+ // Chinese (GB18030) : 4 byte sequences mapped to BMP characters
+ {"gb18030",
+ "\x81\x30\x84\x36\xA1\xA7",
+ OnStringConversionError::FAIL,
+ true,
+ L"\x00A5\x00A8",
+ NULL},
+ // Chinese (GB18030) : A 4 byte sequence mapped to plane 2 (U+20000)
+ {"gb18030",
+ "\x95\x32\x82\x36\xD2\xBB",
+ OnStringConversionError::FAIL,
+ true,
+#if defined(WCHAR_T_IS_UTF16)
+ L"\xD840\xDC00\x4E00",
+#elif defined(WCHAR_T_IS_UTF32)
+ L"\x20000\x4E00",
+#endif
+ L"\xD840\xDC00\x4E00"},
+ {"big5",
+ "\xA7\x41\xA6\x6E",
+ OnStringConversionError::FAIL,
+ true,
+ L"\x4F60\x597D",
+ NULL},
+ // Greek (ISO-8859)
+ {"iso-8859-7",
+ "\xE3\xE5\xE9\xDC" " " "\xF3\xEF\xF5",
+ OnStringConversionError::FAIL,
+ true,
+ L"\x03B3\x03B5\x03B9\x03AC" L" " L"\x03C3\x03BF\x03C5",
+ NULL},
+ // Hebrew (Windows)
+ {"windows-1255",
+ "\xF9\xD1\xC8\xEC\xE5\xC9\xED",
+ OnStringConversionError::FAIL,
+ true,
+ L"\x05E9\x05C1\x05B8\x05DC\x05D5\x05B9\x05DD",
+ NULL},
+ // Hindi Devanagari (ISCII)
+ {"iscii-dev",
+ "\xEF\x42" "\xC6\xCC\xD7\xE8\xB3\xDA\xCF",
+ OnStringConversionError::FAIL,
+ true,
+ L"\x0928\x092E\x0938\x094D\x0915\x093E\x0930",
+ NULL},
+ // Korean (EUC)
+ {"euc-kr",
+ "\xBE\xC8\xB3\xE7\xC7\xCF\xBC\xBC\xBF\xE4",
+ OnStringConversionError::FAIL,
+ true,
+ L"\xC548\xB155\xD558\xC138\xC694",
+ NULL},
+ // Japanese (EUC)
+ {"euc-jp",
+ "\xA4\xB3\xA4\xF3\xA4\xCB\xA4\xC1\xA4\xCF\xB0\xEC\x8E\xA6",
+ OnStringConversionError::FAIL,
+ true,
+ L"\x3053\x3093\x306B\x3061\x306F\x4E00\xFF66",
+ NULL},
+ // Japanese (ISO-2022)
+ {"iso-2022-jp",
+ "\x1B$B" "\x24\x33\x24\x73\x24\x4B\x24\x41\x24\x4F\x30\x6C" "\x1B(B"
+ "ab" "\x1B(J" "\x5C\x7E#$" "\x1B(B",
+ OnStringConversionError::FAIL,
+ true,
+ L"\x3053\x3093\x306B\x3061\x306F\x4E00" L"ab\x00A5\x203E#$",
+ NULL},
+ // Japanese (Shift-JIS)
+ {"sjis",
+ "\x82\xB1\x82\xF1\x82\xC9\x82\xBF\x82\xCD\x88\xEA\xA6",
+ OnStringConversionError::FAIL,
+ true,
+ L"\x3053\x3093\x306B\x3061\x306F\x4E00\xFF66",
+ NULL},
+ // Russian (KOI8)
+ {"koi8-r",
+ "\xDA\xC4\xD2\xC1\xD7\xD3\xD4\xD7\xD5\xCA\xD4\xC5",
+ OnStringConversionError::FAIL,
+ true,
+ L"\x0437\x0434\x0440\x0430\x0432\x0441\x0442\x0432"
+ L"\x0443\x0439\x0442\x0435",
+ NULL},
+ // Thai (windows-874)
+ {"windows-874",
+ "\xCA\xC7\xD1\xCA\xB4\xD5" "\xA4\xC3\xD1\xBA",
+ OnStringConversionError::FAIL,
+ true,
+ L"\x0E2A\x0E27\x0E31\x0E2A\x0E14\x0E35"
+ L"\x0E04\x0E23\x0e31\x0E1A",
+ NULL},
+ // Empty text
+ {"iscii-dev",
+ "",
+ OnStringConversionError::FAIL,
+ true,
+ L"",
+ NULL},
+};
+
+TEST(ICUStringConversionsTest, ConvertBetweenCodepageAndWide) {
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kConvertCodepageCases); ++i) {
+ SCOPED_TRACE(base::StringPrintf(
+ "Test[%" PRIuS "]: <encoded: %s> <codepage: %s>", i,
+ kConvertCodepageCases[i].encoded,
+ kConvertCodepageCases[i].codepage_name));
+
+ std::wstring wide;
+ bool success = CodepageToWide(kConvertCodepageCases[i].encoded,
+ kConvertCodepageCases[i].codepage_name,
+ kConvertCodepageCases[i].on_error,
+ &wide);
+ EXPECT_EQ(kConvertCodepageCases[i].success, success);
+ EXPECT_EQ(kConvertCodepageCases[i].wide, wide);
+
+ // When decoding was successful and nothing was skipped, we also check the
+ // reverse conversion. Not all conversions are round-trippable, but
+ // kConverterCodepageCases does not have any one-way conversion at the
+ // moment.
+ if (success &&
+ kConvertCodepageCases[i].on_error ==
+ OnStringConversionError::FAIL) {
+ std::string encoded;
+ success = WideToCodepage(wide, kConvertCodepageCases[i].codepage_name,
+ kConvertCodepageCases[i].on_error, &encoded);
+ EXPECT_EQ(kConvertCodepageCases[i].success, success);
+ EXPECT_EQ(kConvertCodepageCases[i].encoded, encoded);
+ }
+ }
+
+ // The above cases handled codepage->wide errors, but not wide->codepage.
+ // Test that here.
+ std::string encoded("Temp data"); // Make sure the string gets cleared.
+
+ // First test going to an encoding that can not represent that character.
+ EXPECT_FALSE(WideToCodepage(L"Chinese\xff27", "iso-8859-1",
+ OnStringConversionError::FAIL, &encoded));
+ EXPECT_TRUE(encoded.empty());
+ EXPECT_TRUE(WideToCodepage(L"Chinese\xff27", "iso-8859-1",
+ OnStringConversionError::SKIP, &encoded));
+ EXPECT_STREQ("Chinese", encoded.c_str());
+ // From Unicode, SUBSTITUTE is the same as SKIP for now.
+ EXPECT_TRUE(WideToCodepage(L"Chinese\xff27", "iso-8859-1",
+ OnStringConversionError::SUBSTITUTE,
+ &encoded));
+ EXPECT_STREQ("Chinese", encoded.c_str());
+
+#if defined(WCHAR_T_IS_UTF16)
+ // When we're in UTF-16 mode, test an invalid UTF-16 character in the input.
+ EXPECT_FALSE(WideToCodepage(L"a\xd800z", "iso-8859-1",
+ OnStringConversionError::FAIL, &encoded));
+ EXPECT_TRUE(encoded.empty());
+ EXPECT_TRUE(WideToCodepage(L"a\xd800z", "iso-8859-1",
+ OnStringConversionError::SKIP, &encoded));
+ EXPECT_STREQ("az", encoded.c_str());
+#endif // WCHAR_T_IS_UTF16
+
+ // Invalid characters should fail.
+ EXPECT_TRUE(WideToCodepage(L"a\xffffz", "iso-8859-1",
+ OnStringConversionError::SKIP, &encoded));
+ EXPECT_STREQ("az", encoded.c_str());
+
+ // Invalid codepages should fail.
+ EXPECT_FALSE(WideToCodepage(L"Hello, world", "awesome-8571-2",
+ OnStringConversionError::SKIP, &encoded));
+}
+
+TEST(ICUStringConversionsTest, ConvertBetweenCodepageAndUTF16) {
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kConvertCodepageCases); ++i) {
+ SCOPED_TRACE(base::StringPrintf(
+ "Test[%" PRIuS "]: <encoded: %s> <codepage: %s>", i,
+ kConvertCodepageCases[i].encoded,
+ kConvertCodepageCases[i].codepage_name));
+
+ string16 utf16;
+ bool success = CodepageToUTF16(kConvertCodepageCases[i].encoded,
+ kConvertCodepageCases[i].codepage_name,
+ kConvertCodepageCases[i].on_error,
+ &utf16);
+ string16 utf16_expected;
+ if (kConvertCodepageCases[i].u16_wide == NULL)
+ utf16_expected = BuildString16(kConvertCodepageCases[i].wide);
+ else
+ utf16_expected = BuildString16(kConvertCodepageCases[i].u16_wide);
+ EXPECT_EQ(kConvertCodepageCases[i].success, success);
+ EXPECT_EQ(utf16_expected, utf16);
+
+ // When decoding was successful and nothing was skipped, we also check the
+ // reverse conversion. See also the corresponding comment in
+ // ConvertBetweenCodepageAndWide.
+ if (success &&
+ kConvertCodepageCases[i].on_error == OnStringConversionError::FAIL) {
+ std::string encoded;
+ success = UTF16ToCodepage(utf16, kConvertCodepageCases[i].codepage_name,
+ kConvertCodepageCases[i].on_error, &encoded);
+ EXPECT_EQ(kConvertCodepageCases[i].success, success);
+ EXPECT_EQ(kConvertCodepageCases[i].encoded, encoded);
+ }
+ }
+}
+
+static const struct {
+ const char* encoded;
+ const char* codepage_name;
+ bool expected_success;
+ const char* expected_value;
+} kConvertAndNormalizeCases[] = {
+ {"foo-\xe4.html", "iso-8859-1", true, "foo-\xc3\xa4.html"},
+ {"foo-\xe4.html", "iso-8859-7", true, "foo-\xce\xb4.html"},
+ {"foo-\xe4.html", "foo-bar", false, ""},
+ {"foo-\xff.html", "ascii", false, ""},
+ {"foo.html", "ascii", true, "foo.html"},
+ {"foo-a\xcc\x88.html", "utf-8", true, "foo-\xc3\xa4.html"},
+ {"\x95\x32\x82\x36\xD2\xBB", "gb18030", true, "\xF0\xA0\x80\x80\xE4\xB8\x80"},
+ {"\xA7\x41\xA6\x6E", "big5", true, "\xE4\xBD\xA0\xE5\xA5\xBD"},
+ // Windows-1258 does have a combining character at xD2 (which is U+0309).
+ // The sequence of (U+00E2, U+0309) is also encoded as U+1EA9.
+ {"foo\xE2\xD2", "windows-1258", true, "foo\xE1\xBA\xA9"},
+ {"", "iso-8859-1", true, ""},
+};
+TEST(ICUStringConversionsTest, ConvertToUtf8AndNormalize) {
+ std::string result;
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kConvertAndNormalizeCases); ++i) {
+ SCOPED_TRACE(base::StringPrintf(
+ "Test[%" PRIuS "]: <encoded: %s> <codepage: %s>", i,
+ kConvertAndNormalizeCases[i].encoded,
+ kConvertAndNormalizeCases[i].codepage_name));
+
+ bool success = ConvertToUtf8AndNormalize(
+ kConvertAndNormalizeCases[i].encoded,
+ kConvertAndNormalizeCases[i].codepage_name, &result);
+ EXPECT_EQ(kConvertAndNormalizeCases[i].expected_success, success);
+ EXPECT_EQ(kConvertAndNormalizeCases[i].expected_value, result);
+ }
+}
+
+} // namespace base
diff --git a/src/base/i18n/icu_util.cc b/src/base/i18n/icu_util.cc
new file mode 100644
index 0000000..8c48b36
--- /dev/null
+++ b/src/base/i18n/icu_util.cc
@@ -0,0 +1,156 @@
+// Copyright (c) 2012 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/i18n/icu_util.h"
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#include <string>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "base/sys_string_conversions.h"
+#include "unicode/putil.h"
+#include "unicode/udata.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/foundation_util.h"
+#endif
+
+#if defined(OS_STARBOARD)
+#include "starboard/client_porting/icu_init/icu_init.h"
+#endif
+
+#define ICU_UTIL_DATA_FILE 0
+#define ICU_UTIL_DATA_SHARED 1
+#define ICU_UTIL_DATA_STATIC 2
+
+#ifndef ICU_UTIL_DATA_IMPL
+
+#if defined(OS_WIN)
+#define ICU_UTIL_DATA_IMPL ICU_UTIL_DATA_SHARED
+#elif defined(OS_IOS) || defined(__LB_SHELL__) || defined(OS_STARBOARD)
+#define ICU_UTIL_DATA_IMPL ICU_UTIL_DATA_FILE
+#else
+#define ICU_UTIL_DATA_IMPL ICU_UTIL_DATA_STATIC
+#endif
+
+#endif // ICU_UTIL_DATA_IMPL
+
+#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+#define ICU_UTIL_DATA_FILE_NAME "icudt" U_ICU_VERSION_SHORT "l.dat"
+#elif ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED
+#define ICU_UTIL_DATA_SYMBOL "icudt" U_ICU_VERSION_SHORT "_dat"
+#if defined(OS_WIN)
+#define ICU_UTIL_DATA_SHARED_MODULE_NAME "icudt.dll"
+#endif
+#endif
+
+namespace icu_util {
+
+bool Initialize() {
+#ifndef NDEBUG
+ // Assert that we are not called more than once. Even though calling this
+ // function isn't harmful (ICU can handle it), being called twice probably
+ // indicates a programming error.
+ static bool called_once = false;
+ DCHECK(!called_once);
+ called_once = true;
+#endif
+
+#if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED)
+ // We expect to find the ICU data module alongside the current module.
+ FilePath data_path;
+ PathService::Get(base::DIR_MODULE, &data_path);
+ data_path = data_path.AppendASCII(ICU_UTIL_DATA_SHARED_MODULE_NAME);
+
+ HMODULE module = LoadLibrary(data_path.value().c_str());
+ if (!module) {
+ DLOG(ERROR) << "Failed to load " << ICU_UTIL_DATA_SHARED_MODULE_NAME;
+ return false;
+ }
+
+ FARPROC addr = GetProcAddress(module, ICU_UTIL_DATA_SYMBOL);
+ if (!addr) {
+ DLOG(ERROR) << ICU_UTIL_DATA_SYMBOL << ": not found in "
+ << ICU_UTIL_DATA_SHARED_MODULE_NAME;
+ return false;
+ }
+
+ UErrorCode err = U_ZERO_ERROR;
+ udata_setCommonData(reinterpret_cast<void*>(addr), &err);
+ return err == U_ZERO_ERROR;
+#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC)
+ // Mac/Linux bundle the ICU data in.
+ return true;
+#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
+#if defined(__LB_SHELL__)
+ // Locate the data directory.
+ FilePath data_path;
+ bool path_ok = PathService::Get(base::DIR_EXE, &data_path);
+ DCHECK(path_ok);
+ data_path = data_path.Append("icu");
+#if U_IS_BIG_ENDIAN
+ data_path = data_path.Append("icudt46b");
+#else
+ data_path = data_path.Append("icudt46l");
+#endif
+ // set this as the data directory.
+ u_setDataDirectory(data_path.value().c_str());
+ UErrorCode err = U_ZERO_ERROR;
+ udata_setFileAccess(UDATA_FILES_FIRST, &err);
+ return err == U_ZERO_ERROR;
+#elif defined(OS_STARBOARD)
+ SbIcuInit();
+ return true;
+#elif !defined(OS_MACOSX)
+ // For now, expect the data file to be alongside the executable.
+ // This is sufficient while we work on unit tests, but will eventually
+ // likely live in a data directory.
+ FilePath data_path;
+ bool path_ok = PathService::Get(base::DIR_EXE, &data_path);
+ DCHECK(path_ok);
+ u_setDataDirectory(data_path.value().c_str());
+ // Only look for the packaged data file;
+ // the default behavior is to look for individual files.
+ UErrorCode err = U_ZERO_ERROR;
+ udata_setFileAccess(UDATA_ONLY_PACKAGES, &err);
+ return err == U_ZERO_ERROR;
+#else
+ // If the ICU data directory is set, ICU won't actually load the data until
+ // it is needed. This can fail if the process is sandboxed at that time.
+ // Instead, Mac maps the file in and hands off the data so the sandbox won't
+ // cause any problems.
+
+ // Chrome doesn't normally shut down ICU, so the mapped data shouldn't ever
+ // be released.
+ static file_util::MemoryMappedFile mapped_file;
+ if (!mapped_file.IsValid()) {
+ // Assume it is in the framework bundle's Resources directory.
+ FilePath data_path =
+ base::mac::PathForFrameworkBundleResource(CFSTR(ICU_UTIL_DATA_FILE_NAME));
+ if (data_path.empty()) {
+ DLOG(ERROR) << ICU_UTIL_DATA_FILE_NAME << " not found in bundle";
+ return false;
+ }
+ if (!mapped_file.Initialize(data_path)) {
+ DLOG(ERROR) << "Couldn't mmap " << data_path.value();
+ return false;
+ }
+ }
+ UErrorCode err = U_ZERO_ERROR;
+ udata_setCommonData(const_cast<uint8*>(mapped_file.data()), &err);
+ return err == U_ZERO_ERROR;
+#endif // OS check
+#endif
+}
+
+} // namespace icu_util
diff --git a/src/base/i18n/icu_util.h b/src/base/i18n/icu_util.h
new file mode 100644
index 0000000..f6356f1
--- /dev/null
+++ b/src/base/i18n/icu_util.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_ICU_UTIL_H_
+#define BASE_I18N_ICU_UTIL_H_
+
+#include "base/i18n/base_i18n_export.h"
+
+namespace icu_util {
+
+// Call this function to load ICU's data tables for the current process. This
+// function should be called before ICU is used.
+BASE_I18N_EXPORT bool Initialize();
+
+} // namespace icu_util
+
+#endif // BASE_I18N_ICU_UTIL_H_
diff --git a/src/base/i18n/icu_util_nacl_win64.cc b/src/base/i18n/icu_util_nacl_win64.cc
new file mode 100644
index 0000000..6e0bb6b
--- /dev/null
+++ b/src/base/i18n/icu_util_nacl_win64.cc
@@ -0,0 +1,13 @@
+// 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/i18n/icu_util.h"
+
+namespace icu_util {
+
+bool Initialize() {
+ return true;
+}
+
+} // namespace icu_util
diff --git a/src/base/i18n/number_formatting.cc b/src/base/i18n/number_formatting.cc
new file mode 100644
index 0000000..35ff08d
--- /dev/null
+++ b/src/base/i18n/number_formatting.cc
@@ -0,0 +1,87 @@
+// Copyright (c) 2012 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/i18n/number_formatting.h"
+
+#include "base/format_macros.h"
+#include "base/logging.h"
+#include "base/lazy_instance.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stringprintf.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "unicode/numfmt.h"
+#include "unicode/ustring.h"
+
+namespace base {
+
+namespace {
+
+// A simple wrapper around icu::NumberFormat that allows for resetting it
+// (as LazyInstance does not).
+struct NumberFormatWrapper {
+ NumberFormatWrapper() {
+ Reset();
+ }
+
+ void Reset() {
+ // There's no ICU call to destroy a NumberFormat object other than
+ // operator delete, so use the default Delete, which calls operator delete.
+ // This can cause problems if a different allocator is used by this file
+ // than by ICU.
+ UErrorCode status = U_ZERO_ERROR;
+ number_format.reset(icu::NumberFormat::createInstance(status));
+ DCHECK(U_SUCCESS(status));
+ }
+
+ scoped_ptr<icu::NumberFormat> number_format;
+};
+
+LazyInstance<NumberFormatWrapper> g_number_format_int =
+ LAZY_INSTANCE_INITIALIZER;
+LazyInstance<NumberFormatWrapper> g_number_format_float =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+string16 FormatNumber(int64 number) {
+ icu::NumberFormat* number_format =
+ g_number_format_int.Get().number_format.get();
+
+ if (!number_format) {
+ // As a fallback, just return the raw number in a string.
+ return UTF8ToUTF16(StringPrintf("%" PRId64, number));
+ }
+ icu::UnicodeString ustr;
+ number_format->format(number, ustr);
+
+ return string16(ustr.getBuffer(), static_cast<size_t>(ustr.length()));
+}
+
+string16 FormatDouble(double number, int fractional_digits) {
+ icu::NumberFormat* number_format =
+ g_number_format_float.Get().number_format.get();
+
+ if (!number_format) {
+ // As a fallback, just return the raw number in a string.
+ return UTF8ToUTF16(StringPrintf("%f", number));
+ }
+ number_format->setMaximumFractionDigits(fractional_digits);
+ number_format->setMinimumFractionDigits(fractional_digits);
+ icu::UnicodeString ustr;
+ number_format->format(number, ustr);
+
+ return string16(ustr.getBuffer(), static_cast<size_t>(ustr.length()));
+}
+
+namespace testing {
+
+void ResetFormatters() {
+ g_number_format_int.Get().Reset();
+ g_number_format_float.Get().Reset();
+}
+
+} // namespace testing
+
+} // namespace base
diff --git a/src/base/i18n/number_formatting.h b/src/base/i18n/number_formatting.h
new file mode 100644
index 0000000..cfc4e12
--- /dev/null
+++ b/src/base/i18n/number_formatting.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_NUMBER_FORMATTING_H_
+#define BASE_I18N_NUMBER_FORMATTING_H_
+
+#include "base/basictypes.h"
+#include "base/i18n/base_i18n_export.h"
+#include "base/string16.h"
+
+namespace base {
+
+// Return a number formatted with separators in the user's locale.
+// Ex: FormatNumber(1234567)
+// => "1,234,567" in English, "1.234.567" in German
+BASE_I18N_EXPORT string16 FormatNumber(int64 number);
+
+// Return a number formatted with separators in the user's locale.
+// Ex: FormatDouble(1234567.8, 1)
+// => "1,234,567.8" in English, "1.234.567,8" in German
+BASE_I18N_EXPORT string16 FormatDouble(double number, int fractional_digits);
+
+namespace testing {
+
+// Causes cached formatters to be discarded and recreated. Only useful for
+// testing.
+BASE_I18N_EXPORT void ResetFormatters();
+
+} // namespace testing
+
+} // namespace base
+
+#endif // BASE_I18N_NUMBER_FORMATTING_H_
diff --git a/src/base/i18n/number_formatting_unittest.cc b/src/base/i18n/number_formatting_unittest.cc
new file mode 100644
index 0000000..e35eea0
--- /dev/null
+++ b/src/base/i18n/number_formatting_unittest.cc
@@ -0,0 +1,88 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <limits>
+
+#include "base/i18n/number_formatting.h"
+#include "base/i18n/rtl.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+TEST(NumberFormattingTest, FormatNumber) {
+ static const struct {
+ int64 number;
+ const char* expected_english;
+ const char* expected_german;
+ } cases[] = {
+ {0, "0", "0"},
+ {1024, "1,024", "1.024"},
+ {std::numeric_limits<int64>::max(),
+ "9,223,372,036,854,775,807", "9.223.372.036.854.775.807"},
+ {std::numeric_limits<int64>::min(),
+ "-9,223,372,036,854,775,808", "-9.223.372.036.854.775.808"},
+ {-42, "-42", "-42"},
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+ i18n::SetICUDefaultLocale("en");
+ testing::ResetFormatters();
+ EXPECT_EQ(cases[i].expected_english,
+ UTF16ToUTF8(FormatNumber(cases[i].number)));
+ i18n::SetICUDefaultLocale("de");
+ testing::ResetFormatters();
+ EXPECT_EQ(cases[i].expected_german,
+ UTF16ToUTF8(FormatNumber(cases[i].number)));
+ }
+}
+
+TEST(NumberFormattingTest, FormatDouble) {
+ static const struct {
+ double number;
+ int frac_digits;
+ const char* expected_english;
+ const char* expected_german;
+ } cases[] = {
+ {0.0, 0, "0", "0"},
+#if !defined(OS_ANDROID) && !defined(__LB_ANDROID__)
+ // Bionic can't printf negative zero correctly.
+ {-0.0, 4, "-0.0000", "-0,0000"},
+#endif
+ {1024.2, 0, "1,024", "1.024"},
+ {-1024.223, 2, "-1,024.22", "-1.024,22"},
+ {std::numeric_limits<double>::max(), 6,
+ "179,769,313,486,232,000,000,000,000,000,000,000,000,000,000,000,000,"
+ "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
+ "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
+ "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
+ "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
+ "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,"
+ "000.000000",
+ "179.769.313.486.232.000.000.000.000.000.000.000.000.000.000.000.000."
+ "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
+ "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
+ "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
+ "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
+ "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000."
+ "000,000000"},
+ {std::numeric_limits<double>::min(), 2, "0.00", "0,00"},
+ {-42.7, 3, "-42.700", "-42,700"},
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+ i18n::SetICUDefaultLocale("en");
+ testing::ResetFormatters();
+ EXPECT_EQ(cases[i].expected_english,
+ UTF16ToUTF8(FormatDouble(cases[i].number, cases[i].frac_digits)));
+ i18n::SetICUDefaultLocale("de");
+ testing::ResetFormatters();
+ EXPECT_EQ(cases[i].expected_german,
+ UTF16ToUTF8(FormatDouble(cases[i].number, cases[i].frac_digits)));
+ }
+}
+
+} // namespace
+} // namespace base
diff --git a/src/base/i18n/rtl.cc b/src/base/i18n/rtl.cc
new file mode 100644
index 0000000..65f6a53
--- /dev/null
+++ b/src/base/i18n/rtl.cc
@@ -0,0 +1,362 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/rtl.h"
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "base/sys_string_conversions.h"
+#include "unicode/coll.h"
+#include "unicode/locid.h"
+#include "unicode/uchar.h"
+#include "unicode/uscript.h"
+
+#if defined(TOOLKIT_GTK)
+#include <gtk/gtk.h>
+#endif
+
+namespace {
+
+// Extract language, country and variant, but ignore keywords. For example,
+// en-US, ca@valencia, ca-ES@valencia.
+std::string GetLocaleString(const icu::Locale& locale) {
+ const char* language = locale.getLanguage();
+ const char* country = locale.getCountry();
+ const char* variant = locale.getVariant();
+
+ std::string result =
+ (language != NULL && *language != '\0') ? language : "und";
+
+ if (country != NULL && *country != '\0') {
+ result += '-';
+ result += country;
+ }
+
+ if (variant != NULL && *variant != '\0') {
+ std::string variant_str(variant);
+ StringToLowerASCII(&variant_str);
+ result += '@' + variant_str;
+ }
+
+ return result;
+}
+
+} // namespace
+
+namespace base {
+namespace i18n {
+
+// Represents the locale-specific ICU text direction.
+static TextDirection g_icu_text_direction = UNKNOWN_DIRECTION;
+
+// Convert the ICU default locale to a string.
+std::string GetConfiguredLocale() {
+ return GetLocaleString(icu::Locale::getDefault());
+}
+
+// Convert the ICU canonicalized locale to a string.
+std::string GetCanonicalLocale(const char* locale) {
+ return GetLocaleString(icu::Locale::createCanonical(locale));
+}
+
+// Convert Chrome locale name to ICU locale name
+std::string ICULocaleName(const std::string& locale_string) {
+ // If not Spanish, just return it.
+ if (locale_string.substr(0, 2) != "es")
+ return locale_string;
+ // Expand es to es-ES.
+ if (LowerCaseEqualsASCII(locale_string, "es"))
+ return "es-ES";
+ // Map es-419 (Latin American Spanish) to es-FOO depending on the system
+ // locale. If it's es-RR other than es-ES, map to es-RR. Otherwise, map
+ // to es-MX (the most populous in Spanish-speaking Latin America).
+ if (LowerCaseEqualsASCII(locale_string, "es-419")) {
+ const icu::Locale& locale = icu::Locale::getDefault();
+ std::string language = locale.getLanguage();
+ const char* country = locale.getCountry();
+ if (LowerCaseEqualsASCII(language, "es") &&
+ !LowerCaseEqualsASCII(country, "es")) {
+ language += '-';
+ language += country;
+ return language;
+ }
+ return "es-MX";
+ }
+ // Currently, Chrome has only "es" and "es-419", but later we may have
+ // more specific "es-RR".
+ return locale_string;
+}
+
+void SetICUDefaultLocale(const std::string& locale_string) {
+ icu::Locale locale(ICULocaleName(locale_string).c_str());
+ UErrorCode error_code = U_ZERO_ERROR;
+ icu::Locale::setDefault(locale, error_code);
+ // This return value is actually bogus because Locale object is
+ // an ID and setDefault seems to always succeed (regardless of the
+ // presence of actual locale data). However,
+ // it does not hurt to have it as a sanity check.
+ DCHECK(U_SUCCESS(error_code));
+ g_icu_text_direction = UNKNOWN_DIRECTION;
+}
+
+bool IsRTL() {
+#if defined(TOOLKIT_GTK)
+ GtkTextDirection gtk_dir = gtk_widget_get_default_direction();
+ return gtk_dir == GTK_TEXT_DIR_RTL;
+#else
+ return ICUIsRTL();
+#endif
+}
+
+bool ICUIsRTL() {
+ if (g_icu_text_direction == UNKNOWN_DIRECTION) {
+ const icu::Locale& locale = icu::Locale::getDefault();
+ g_icu_text_direction = GetTextDirectionForLocale(locale.getName());
+ }
+ return g_icu_text_direction == RIGHT_TO_LEFT;
+}
+
+TextDirection GetTextDirectionForLocale(const char* locale_name) {
+#if defined(__LB_SHELL__) || defined(OS_STARBOARD)
+ // lbshell does not have the icu tables needed to determine RTL-ness.
+ // Rather than beef up our icu tables, hard-code the list of RTL languages
+ // that Chrome supports. RTL layout is implemented by other components,
+ // so this does not affect our ability to do RTL layout nor RTL text.
+ return (!strncmp(locale_name, "he", 2) ||
+ !strncmp(locale_name, "ar", 2) ||
+ !strncmp(locale_name, "iw", 2) ||
+ !strncmp(locale_name, "fa", 2) ||
+ !strncmp(locale_name, "ur", 2))
+ ? RIGHT_TO_LEFT : LEFT_TO_RIGHT;
+#else
+ UErrorCode status = U_ZERO_ERROR;
+ ULayoutType layout_dir = uloc_getCharacterOrientation(locale_name, &status);
+ DCHECK(U_SUCCESS(status));
+ // Treat anything other than RTL as LTR.
+ return (layout_dir != ULOC_LAYOUT_RTL) ? LEFT_TO_RIGHT : RIGHT_TO_LEFT;
+#endif
+}
+
+TextDirection GetFirstStrongCharacterDirection(const string16& text) {
+ const UChar* string = text.c_str();
+ size_t length = text.length();
+ size_t position = 0;
+ while (position < length) {
+ UChar32 character;
+ size_t next_position = position;
+ U16_NEXT(string, next_position, length, character);
+
+ // Now that we have the character, we use ICU in order to query for the
+ // appropriate Unicode BiDi character type.
+ int32_t property = u_getIntPropertyValue(character, UCHAR_BIDI_CLASS);
+ if ((property == U_RIGHT_TO_LEFT) ||
+ (property == U_RIGHT_TO_LEFT_ARABIC) ||
+ (property == U_RIGHT_TO_LEFT_EMBEDDING) ||
+ (property == U_RIGHT_TO_LEFT_OVERRIDE)) {
+ return RIGHT_TO_LEFT;
+ } else if ((property == U_LEFT_TO_RIGHT) ||
+ (property == U_LEFT_TO_RIGHT_EMBEDDING) ||
+ (property == U_LEFT_TO_RIGHT_OVERRIDE)) {
+ return LEFT_TO_RIGHT;
+ }
+
+ position = next_position;
+ }
+
+ return LEFT_TO_RIGHT;
+}
+
+#if defined(OS_WIN)
+bool AdjustStringForLocaleDirection(string16* text) {
+ if (!IsRTL() || text->empty())
+ return false;
+
+ // Marking the string as LTR if the locale is RTL and the string does not
+ // contain strong RTL characters. Otherwise, mark the string as RTL.
+ bool has_rtl_chars = StringContainsStrongRTLChars(*text);
+ if (!has_rtl_chars)
+ WrapStringWithLTRFormatting(text);
+ else
+ WrapStringWithRTLFormatting(text);
+
+ return true;
+}
+
+bool UnadjustStringForLocaleDirection(string16* text) {
+ if (!IsRTL() || text->empty())
+ return false;
+
+ *text = StripWrappingBidiControlCharacters(*text);
+ return true;
+}
+#else
+bool AdjustStringForLocaleDirection(string16* text) {
+ // On OS X & GTK the directionality of a label is determined by the first
+ // strongly directional character.
+ // However, we want to make sure that in an LTR-language-UI all strings are
+ // left aligned and vice versa.
+ // A problem can arise if we display a string which starts with user input.
+ // User input may be of the opposite directionality to the UI. So the whole
+ // string will be displayed in the opposite directionality, e.g. if we want to
+ // display in an LTR UI [such as US English]:
+ //
+ // EMAN_NOISNETXE is now installed.
+ //
+ // Since EXTENSION_NAME begins with a strong RTL char, the label's
+ // directionality will be set to RTL and the string will be displayed visually
+ // as:
+ //
+ // .is now installed EMAN_NOISNETXE
+ //
+ // In order to solve this issue, we prepend an LRM to the string. An LRM is a
+ // strongly directional LTR char.
+ // We also append an LRM at the end, which ensures that we're in an LTR
+ // context.
+
+ // Unlike Windows, Linux and OS X can correctly display RTL glyphs out of the
+ // box so there is no issue with displaying zero-width bidi control characters
+ // on any system. Thus no need for the !IsRTL() check here.
+ if (text->empty())
+ return false;
+
+ bool ui_direction_is_rtl = IsRTL();
+
+ bool has_rtl_chars = StringContainsStrongRTLChars(*text);
+ if (!ui_direction_is_rtl && has_rtl_chars) {
+ WrapStringWithRTLFormatting(text);
+ text->insert(0U, 1U, kLeftToRightMark);
+ text->push_back(kLeftToRightMark);
+ } else if (ui_direction_is_rtl && has_rtl_chars) {
+ WrapStringWithRTLFormatting(text);
+ text->insert(0U, 1U, kRightToLeftMark);
+ text->push_back(kRightToLeftMark);
+ } else if (ui_direction_is_rtl) {
+ WrapStringWithLTRFormatting(text);
+ text->insert(0U, 1U, kRightToLeftMark);
+ text->push_back(kRightToLeftMark);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool UnadjustStringForLocaleDirection(string16* text) {
+ if (text->empty())
+ return false;
+
+ size_t begin_index = 0;
+ char16 begin = text->at(begin_index);
+ if (begin == kLeftToRightMark ||
+ begin == kRightToLeftMark) {
+ ++begin_index;
+ }
+
+ size_t end_index = text->length() - 1;
+ char16 end = text->at(end_index);
+ if (end == kLeftToRightMark ||
+ end == kRightToLeftMark) {
+ --end_index;
+ }
+
+ string16 unmarked_text =
+ text->substr(begin_index, end_index - begin_index + 1);
+ *text = StripWrappingBidiControlCharacters(unmarked_text);
+ return true;
+}
+
+#endif // !OS_WIN
+
+bool StringContainsStrongRTLChars(const string16& text) {
+ const UChar* string = text.c_str();
+ size_t length = text.length();
+ size_t position = 0;
+ while (position < length) {
+ UChar32 character;
+ size_t next_position = position;
+ U16_NEXT(string, next_position, length, character);
+
+ // Now that we have the character, we use ICU in order to query for the
+ // appropriate Unicode BiDi character type.
+ int32_t property = u_getIntPropertyValue(character, UCHAR_BIDI_CLASS);
+ if ((property == U_RIGHT_TO_LEFT) || (property == U_RIGHT_TO_LEFT_ARABIC))
+ return true;
+
+ position = next_position;
+ }
+
+ return false;
+}
+
+void WrapStringWithLTRFormatting(string16* text) {
+ if (text->empty())
+ return;
+
+ // Inserting an LRE (Left-To-Right Embedding) mark as the first character.
+ text->insert(0U, 1U, kLeftToRightEmbeddingMark);
+
+ // Inserting a PDF (Pop Directional Formatting) mark as the last character.
+ text->push_back(kPopDirectionalFormatting);
+}
+
+void WrapStringWithRTLFormatting(string16* text) {
+ if (text->empty())
+ return;
+
+ // Inserting an RLE (Right-To-Left Embedding) mark as the first character.
+ text->insert(0U, 1U, kRightToLeftEmbeddingMark);
+
+ // Inserting a PDF (Pop Directional Formatting) mark as the last character.
+ text->push_back(kPopDirectionalFormatting);
+}
+
+void WrapPathWithLTRFormatting(const FilePath& path,
+ string16* rtl_safe_path) {
+ // Wrap the overall path with LRE-PDF pair which essentialy marks the
+ // string as a Left-To-Right string.
+ // Inserting an LRE (Left-To-Right Embedding) mark as the first character.
+ rtl_safe_path->push_back(kLeftToRightEmbeddingMark);
+#if defined(OS_MACOSX)
+ rtl_safe_path->append(UTF8ToUTF16(path.value()));
+#elif defined(OS_WIN)
+ rtl_safe_path->append(path.value());
+#else // defined(OS_POSIX) && !defined(OS_MACOSX)
+ std::wstring wide_path = base::SysNativeMBToWide(path.value());
+ rtl_safe_path->append(WideToUTF16(wide_path));
+#endif
+ // Inserting a PDF (Pop Directional Formatting) mark as the last character.
+ rtl_safe_path->push_back(kPopDirectionalFormatting);
+}
+
+string16 GetDisplayStringInLTRDirectionality(const string16& text) {
+ // Always wrap the string in RTL UI (it may be appended to RTL string).
+ // Also wrap strings with an RTL first strong character direction in LTR UI.
+ if (IsRTL() || GetFirstStrongCharacterDirection(text) == RIGHT_TO_LEFT) {
+ string16 text_mutable(text);
+ WrapStringWithLTRFormatting(&text_mutable);
+ return text_mutable;
+ }
+ return text;
+}
+
+string16 StripWrappingBidiControlCharacters(const string16& text) {
+ if (text.empty())
+ return text;
+ size_t begin_index = 0;
+ char16 begin = text[begin_index];
+ if (begin == kLeftToRightEmbeddingMark ||
+ begin == kRightToLeftEmbeddingMark ||
+ begin == kLeftToRightOverride ||
+ begin == kRightToLeftOverride)
+ ++begin_index;
+ size_t end_index = text.length() - 1;
+ if (text[end_index] == kPopDirectionalFormatting)
+ --end_index;
+ return text.substr(begin_index, end_index - begin_index + 1);
+}
+
+} // namespace i18n
+} // namespace base
diff --git a/src/base/i18n/rtl.h b/src/base/i18n/rtl.h
new file mode 100644
index 0000000..202a126
--- /dev/null
+++ b/src/base/i18n/rtl.h
@@ -0,0 +1,136 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_RTL_H_
+#define BASE_I18N_RTL_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/i18n/base_i18n_export.h"
+#include "base/string16.h"
+#include "build/build_config.h"
+
+class FilePath;
+
+namespace base {
+namespace i18n {
+
+const char16 kRightToLeftMark = 0x200F;
+const char16 kLeftToRightMark = 0x200E;
+const char16 kLeftToRightEmbeddingMark = 0x202A;
+const char16 kRightToLeftEmbeddingMark = 0x202B;
+const char16 kPopDirectionalFormatting = 0x202C;
+const char16 kLeftToRightOverride = 0x202D;
+const char16 kRightToLeftOverride = 0x202E;
+
+enum TextDirection {
+ UNKNOWN_DIRECTION,
+ RIGHT_TO_LEFT,
+ LEFT_TO_RIGHT,
+};
+
+// Get the locale that the currently running process has been configured to use.
+// The return value is of the form language[-country] (e.g., en-US) where the
+// language is the 2 or 3 letter code from ISO-639.
+BASE_I18N_EXPORT std::string GetConfiguredLocale();
+
+// Canonicalize a string (eg. a POSIX locale string) to a Chrome locale name.
+BASE_I18N_EXPORT std::string GetCanonicalLocale(const char* locale);
+
+// Sets the default locale of ICU.
+// Once the application locale of Chrome in GetApplicationLocale is determined,
+// the default locale of ICU need to be changed to match the application locale
+// so that ICU functions work correctly in a locale-dependent manner.
+// This is handy in that we don't have to call GetApplicationLocale()
+// everytime we call locale-dependent ICU APIs as long as we make sure
+// that this is called before any locale-dependent API is called.
+BASE_I18N_EXPORT void SetICUDefaultLocale(const std::string& locale_string);
+
+// Returns true if the application text direction is right-to-left.
+BASE_I18N_EXPORT bool IsRTL();
+
+// Returns whether the text direction for the default ICU locale is RTL. This
+// assumes that SetICUDefaultLocale has been called to set the default locale to
+// the UI locale of Chrome.
+// NOTE: Generally, you should call IsRTL() instead of this.
+BASE_I18N_EXPORT bool ICUIsRTL();
+
+// Returns the text direction for |locale_name|.
+BASE_I18N_EXPORT TextDirection GetTextDirectionForLocale(
+ const char* locale_name);
+
+// Given the string in |text|, returns the directionality of the first
+// character with strong directionality in the string. If no character in the
+// text has strong directionality, LEFT_TO_RIGHT is returned. The Bidi
+// character types L, LRE, LRO, R, AL, RLE, and RLO are considered as strong
+// directionality characters. Please refer to http://unicode.org/reports/tr9/
+// for more information.
+BASE_I18N_EXPORT TextDirection GetFirstStrongCharacterDirection(
+ const string16& text);
+
+// Given the string in |text|, this function modifies the string in place with
+// the appropriate Unicode formatting marks that mark the string direction
+// (either left-to-right or right-to-left). The function checks both the current
+// locale and the contents of the string in order to determine the direction of
+// the returned string. The function returns true if the string in |text| was
+// properly adjusted.
+//
+// Certain LTR strings are not rendered correctly when the context is RTL. For
+// example, the string "Foo!" will appear as "!Foo" if it is rendered as is in
+// an RTL context. Calling this function will make sure the returned localized
+// string is always treated as a right-to-left string. This is done by
+// inserting certain Unicode formatting marks into the returned string.
+//
+// ** Notes about the Windows version of this function:
+// TODO(idana) bug 6806: this function adjusts the string in question only
+// if the current locale is right-to-left. The function does not take care of
+// the opposite case (an RTL string displayed in an LTR context) since
+// adjusting the string involves inserting Unicode formatting characters that
+// Windows does not handle well unless right-to-left language support is
+// installed. Since the English version of Windows doesn't have right-to-left
+// language support installed by default, inserting the direction Unicode mark
+// results in Windows displaying squares.
+BASE_I18N_EXPORT bool AdjustStringForLocaleDirection(string16* text);
+
+// Undoes the actions of the above function (AdjustStringForLocaleDirection).
+BASE_I18N_EXPORT bool UnadjustStringForLocaleDirection(string16* text);
+
+// Returns true if the string contains at least one character with strong right
+// to left directionality; that is, a character with either R or AL Unicode
+// BiDi character type.
+BASE_I18N_EXPORT bool StringContainsStrongRTLChars(const string16& text);
+
+// Wraps a string with an LRE-PDF pair which essentialy marks the string as a
+// Left-To-Right string. Doing this is useful in order to make sure LTR
+// strings are rendered properly in an RTL context.
+BASE_I18N_EXPORT void WrapStringWithLTRFormatting(string16* text);
+
+// Wraps a string with an RLE-PDF pair which essentialy marks the string as a
+// Right-To-Left string. Doing this is useful in order to make sure RTL
+// strings are rendered properly in an LTR context.
+BASE_I18N_EXPORT void WrapStringWithRTLFormatting(string16* text);
+
+// Wraps file path to get it to display correctly in RTL UI. All filepaths
+// should be passed through this function before display in UI for RTL locales.
+BASE_I18N_EXPORT void WrapPathWithLTRFormatting(const FilePath& path,
+ string16* rtl_safe_path);
+
+// Return the string in |text| wrapped with LRE (Left-To-Right Embedding) and
+// PDF (Pop Directional Formatting) marks, if needed for UI display purposes.
+BASE_I18N_EXPORT string16 GetDisplayStringInLTRDirectionality(
+ const string16& text) WARN_UNUSED_RESULT;
+
+// Strip the beginning (U+202A..U+202B, U+202D..U+202E) and/or ending (U+202C)
+// explicit bidi control characters from |text|, if there are any. Otherwise,
+// return the text itself. Explicit bidi control characters display and have
+// semantic effect. They can be deleted so they might not always appear in a
+// pair.
+BASE_I18N_EXPORT string16 StripWrappingBidiControlCharacters(
+ const string16& text) WARN_UNUSED_RESULT;
+
+} // namespace i18n
+} // namespace base
+
+#endif // BASE_I18N_RTL_H_
diff --git a/src/base/i18n/rtl_unittest.cc b/src/base/i18n/rtl_unittest.cc
new file mode 100644
index 0000000..176715a
--- /dev/null
+++ b/src/base/i18n/rtl_unittest.cc
@@ -0,0 +1,307 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/rtl.h"
+
+#include <algorithm>
+
+#include "base/file_path.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "base/sys_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+#include "unicode/usearch.h"
+
+#if defined(TOOLKIT_GTK)
+#include <gtk/gtk.h>
+#endif
+
+namespace base {
+namespace i18n {
+
+namespace {
+
+// A test utility function to set the application default text direction.
+void SetRTL(bool rtl) {
+ // Override the current locale/direction.
+ SetICUDefaultLocale(rtl ? "he" : "en");
+#if defined(TOOLKIT_GTK)
+ // Do the same for GTK, which does not rely on the ICU default locale.
+ gtk_widget_set_default_direction(rtl ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR);
+#endif
+ EXPECT_EQ(rtl, IsRTL());
+}
+
+} // namespace
+
+class RTLTest : public PlatformTest {
+};
+
+TEST_F(RTLTest, GetFirstStrongCharacterDirection) {
+ struct {
+ const wchar_t* text;
+ TextDirection direction;
+ } cases[] = {
+ // Test pure LTR string.
+ { L"foo bar", LEFT_TO_RIGHT },
+ // Test bidi string in which the first character with strong directionality
+ // is a character with type L.
+ { L"foo \x05d0 bar", LEFT_TO_RIGHT },
+ // Test bidi string in which the first character with strong directionality
+ // is a character with type R.
+ { L"\x05d0 foo bar", RIGHT_TO_LEFT },
+ // Test bidi string which starts with a character with weak directionality
+ // and in which the first character with strong directionality is a
+ // character with type L.
+ { L"!foo \x05d0 bar", LEFT_TO_RIGHT },
+ // Test bidi string which starts with a character with weak directionality
+ // and in which the first character with strong directionality is a
+ // character with type R.
+ { L",\x05d0 foo bar", RIGHT_TO_LEFT },
+ // Test bidi string in which the first character with strong directionality
+ // is a character with type LRE.
+ { L"\x202a \x05d0 foo bar", LEFT_TO_RIGHT },
+ // Test bidi string in which the first character with strong directionality
+ // is a character with type LRO.
+ { L"\x202d \x05d0 foo bar", LEFT_TO_RIGHT },
+ // Test bidi string in which the first character with strong directionality
+ // is a character with type RLE.
+ { L"\x202b foo \x05d0 bar", RIGHT_TO_LEFT },
+ // Test bidi string in which the first character with strong directionality
+ // is a character with type RLO.
+ { L"\x202e foo \x05d0 bar", RIGHT_TO_LEFT },
+ // Test bidi string in which the first character with strong directionality
+ // is a character with type AL.
+ { L"\x0622 foo \x05d0 bar", RIGHT_TO_LEFT },
+ // Test a string without strong directionality characters.
+ { L",!.{}", LEFT_TO_RIGHT },
+ // Test empty string.
+ { L"", LEFT_TO_RIGHT },
+ // Test characters in non-BMP (e.g. Phoenician letters. Please refer to
+ // http://demo.icu-project.org/icu-bin/ubrowse?scr=151&b=10910 for more
+ // information).
+ {
+#if defined(WCHAR_T_IS_UTF32)
+ L" ! \x10910" L"abc 123",
+#elif defined(WCHAR_T_IS_UTF16)
+ L" ! \xd802\xdd10" L"abc 123",
+#else
+#error wchar_t should be either UTF-16 or UTF-32
+#endif
+ RIGHT_TO_LEFT },
+ {
+#if defined(WCHAR_T_IS_UTF32)
+ L" ! \x10401" L"abc 123",
+#elif defined(WCHAR_T_IS_UTF16)
+ L" ! \xd801\xdc01" L"abc 123",
+#else
+#error wchar_t should be either UTF-16 or UTF-32
+#endif
+ LEFT_TO_RIGHT },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i)
+ EXPECT_EQ(cases[i].direction,
+ GetFirstStrongCharacterDirection(WideToUTF16(cases[i].text)));
+}
+
+TEST_F(RTLTest, WrapPathWithLTRFormatting) {
+ const wchar_t* cases[] = {
+ // Test common path, such as "c:\foo\bar".
+ L"c:/foo/bar",
+ // Test path with file name, such as "c:\foo\bar\test.jpg".
+ L"c:/foo/bar/test.jpg",
+ // Test path ending with punctuation, such as "c:\(foo)\bar.".
+ L"c:/(foo)/bar.",
+ // Test path ending with separator, such as "c:\foo\bar\".
+ L"c:/foo/bar/",
+ // Test path with RTL character.
+ L"c:/\x05d0",
+ // Test path with 2 level RTL directory names.
+ L"c:/\x05d0/\x0622",
+ // Test path with mixed RTL/LTR directory names and ending with punctuation.
+ L"c:/\x05d0/\x0622/(foo)/b.a.r.",
+ // Test path without driver name, such as "/foo/bar/test/jpg".
+ L"/foo/bar/test.jpg",
+ // Test path start with current directory, such as "./foo".
+ L"./foo",
+ // Test path start with parent directory, such as "../foo/bar.jpg".
+ L"../foo/bar.jpg",
+ // Test absolute path, such as "//foo/bar.jpg".
+ L"//foo/bar.jpg",
+ // Test path with mixed RTL/LTR directory names.
+ L"c:/foo/\x05d0/\x0622/\x05d1.jpg",
+ // Test empty path.
+ L""
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ FilePath path;
+#if defined(OS_WIN)
+ std::wstring win_path(cases[i]);
+ std::replace(win_path.begin(), win_path.end(), '/', '\\');
+ path = FilePath(win_path);
+ std::wstring wrapped_expected =
+ std::wstring(L"\x202a") + win_path + L"\x202c";
+#else
+ path = FilePath(base::SysWideToNativeMB(cases[i]));
+ std::wstring wrapped_expected =
+ std::wstring(L"\x202a") + cases[i] + L"\x202c";
+#endif
+ string16 localized_file_path_string;
+ WrapPathWithLTRFormatting(path, &localized_file_path_string);
+
+ std::wstring wrapped_actual = UTF16ToWide(localized_file_path_string);
+ EXPECT_EQ(wrapped_expected, wrapped_actual);
+ }
+}
+
+TEST_F(RTLTest, WrapString) {
+ const wchar_t* cases[] = {
+ L" . ",
+ L"abc",
+ L"a" L"\x5d0\x5d1",
+ L"a" L"\x5d1" L"b",
+ L"\x5d0\x5d1\x5d2",
+ L"\x5d0\x5d1" L"a",
+ L"\x5d0" L"a" L"\x5d1",
+ };
+
+ const bool was_rtl = IsRTL();
+
+ for (size_t i = 0; i < 2; ++i) {
+ // Toggle the application default text direction (to try each direction).
+ SetRTL(!IsRTL());
+
+ string16 empty;
+ WrapStringWithLTRFormatting(&empty);
+ EXPECT_TRUE(empty.empty());
+ WrapStringWithRTLFormatting(&empty);
+ EXPECT_TRUE(empty.empty());
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ string16 input = WideToUTF16(cases[i]);
+ string16 ltr_wrap = input;
+ WrapStringWithLTRFormatting(<r_wrap);
+ EXPECT_EQ(ltr_wrap[0], kLeftToRightEmbeddingMark);
+ EXPECT_EQ(ltr_wrap.substr(1, ltr_wrap.length() - 2), input);
+ EXPECT_EQ(ltr_wrap[ltr_wrap.length() -1], kPopDirectionalFormatting);
+
+ string16 rtl_wrap = input;
+ WrapStringWithRTLFormatting(&rtl_wrap);
+ EXPECT_EQ(rtl_wrap[0], kRightToLeftEmbeddingMark);
+ EXPECT_EQ(rtl_wrap.substr(1, rtl_wrap.length() - 2), input);
+ EXPECT_EQ(rtl_wrap[rtl_wrap.length() -1], kPopDirectionalFormatting);
+ }
+ }
+
+ EXPECT_EQ(was_rtl, IsRTL());
+}
+
+TEST_F(RTLTest, GetDisplayStringInLTRDirectionality) {
+ struct {
+ const wchar_t* path;
+ bool wrap_ltr;
+ bool wrap_rtl;
+ } cases[] = {
+ { L"test", false, true },
+ { L"test.html", false, true },
+ { L"\x05d0\x05d1\x05d2", true, true },
+ { L"\x05d0\x05d1\x05d2.txt", true, true },
+ { L"\x05d0" L"abc", true, true },
+ { L"\x05d0" L"abc.txt", true, true },
+ { L"abc\x05d0\x05d1", false, true },
+ { L"abc\x05d0\x05d1.jpg", false, true },
+ };
+
+ const bool was_rtl = IsRTL();
+
+ for (size_t i = 0; i < 2; ++i) {
+ // Toggle the application default text direction (to try each direction).
+ SetRTL(!IsRTL());
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+ string16 input = WideToUTF16(cases[i].path);
+ string16 output = GetDisplayStringInLTRDirectionality(input);
+ // Test the expected wrapping behavior for the current UI directionality.
+ if (IsRTL() ? cases[i].wrap_rtl : cases[i].wrap_ltr)
+ EXPECT_NE(output, input);
+ else
+ EXPECT_EQ(output, input);
+ }
+ }
+
+ EXPECT_EQ(was_rtl, IsRTL());
+}
+
+TEST_F(RTLTest, GetTextDirection) {
+ EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("ar"));
+ EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("ar_EG"));
+ EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("he"));
+ EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("he_IL"));
+ // iw is an obsolete code for Hebrew.
+ EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("iw"));
+ // Although we're not yet localized to Farsi and Urdu, we
+ // do have the text layout direction information for them.
+ EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("fa"));
+ EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("ur"));
+#if 0
+ // Enable these when we include the minimal locale data for Azerbaijani
+ // written in Arabic and Dhivehi. At the moment, our copy of
+ // ICU data does not have entries for them.
+ EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("az_Arab"));
+ // Dhivehi that uses Thaana script.
+ EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("dv"));
+#endif
+ EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocale("en"));
+ // Chinese in China with '-'.
+ EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocale("zh-CN"));
+ // Filipino : 3-letter code
+ EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocale("fil"));
+ // Russian
+ EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocale("ru"));
+ // Japanese that uses multiple scripts
+ EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocale("ja"));
+}
+
+TEST_F(RTLTest, UnadjustStringForLocaleDirection) {
+ // These test strings are borrowed from WrapPathWithLTRFormatting
+ const wchar_t* cases[] = {
+ L"foo bar",
+ L"foo \x05d0 bar",
+ L"\x05d0 foo bar",
+ L"!foo \x05d0 bar",
+ L",\x05d0 foo bar",
+ L"\x202a \x05d0 foo bar",
+ L"\x202d \x05d0 foo bar",
+ L"\x202b foo \x05d0 bar",
+ L"\x202e foo \x05d0 bar",
+ L"\x0622 foo \x05d0 bar",
+ };
+
+ const bool was_rtl = IsRTL();
+
+ for (size_t i = 0; i < 2; ++i) {
+ // Toggle the application default text direction (to try each direction).
+ SetRTL(!IsRTL());
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ string16 test_case = WideToUTF16(cases[i]);
+ string16 adjusted_string = test_case;
+
+ if (!AdjustStringForLocaleDirection(&adjusted_string))
+ continue;
+
+ EXPECT_NE(test_case, adjusted_string);
+ EXPECT_TRUE(UnadjustStringForLocaleDirection(&adjusted_string));
+ EXPECT_EQ(test_case, adjusted_string) << " for test case [" << test_case
+ << "] with IsRTL() == " << IsRTL();
+ }
+ }
+
+ EXPECT_EQ(was_rtl, IsRTL());
+}
+
+} // namespace i18n
+} // namespace base
diff --git a/src/base/i18n/string_search.cc b/src/base/i18n/string_search.cc
new file mode 100644
index 0000000..9dc84ca
--- /dev/null
+++ b/src/base/i18n/string_search.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/string_search.h"
+#include "base/logging.h"
+
+#include "unicode/usearch.h"
+
+namespace {
+
+bool CollationSensitiveStringSearch(const string16& find_this,
+ const string16& in_this,
+ UCollationStrength strength,
+ size_t* match_index,
+ size_t* match_length) {
+ UErrorCode status = U_ZERO_ERROR;
+
+ UStringSearch* search = usearch_open(find_this.data(), -1,
+ in_this.data(), -1,
+ uloc_getDefault(),
+ NULL, // breakiter
+ &status);
+
+ // Default to basic substring search if usearch fails. According to
+ // http://icu-project.org/apiref/icu4c/usearch_8h.html, usearch_open will fail
+ // if either |find_this| or |in_this| are empty. In either case basic
+ // substring search will give the correct return value.
+ if (!U_SUCCESS(status)) {
+ size_t index = in_this.find(find_this);
+ if (index == string16::npos) {
+ return false;
+ } else {
+ if (match_index)
+ *match_index = index;
+ if (match_length)
+ *match_length = find_this.size();
+ return true;
+ }
+ }
+
+ UCollator* collator = usearch_getCollator(search);
+ ucol_setStrength(collator, strength);
+ usearch_reset(search);
+
+ int32_t index = usearch_first(search, &status);
+ if (!U_SUCCESS(status) || index == USEARCH_DONE) {
+ usearch_close(search);
+ return false;
+ }
+
+ if (match_index)
+ *match_index = static_cast<size_t>(index);
+ if (match_length)
+ *match_length = static_cast<size_t>(usearch_getMatchedLength(search));
+
+ usearch_close(search);
+ return true;
+}
+
+} // namespace
+
+namespace base {
+namespace i18n {
+
+bool StringSearchIgnoringCaseAndAccents(const string16& find_this,
+ const string16& in_this,
+ size_t* match_index,
+ size_t* match_length) {
+ return CollationSensitiveStringSearch(find_this,
+ in_this,
+ UCOL_PRIMARY,
+ match_index,
+ match_length);
+}
+
+} // namespace i18n
+} // namespace base
diff --git a/src/base/i18n/string_search.h b/src/base/i18n/string_search.h
new file mode 100644
index 0000000..2069b0f
--- /dev/null
+++ b/src/base/i18n/string_search.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_I18N_STRING_SEARCH_H_
+#define BASE_I18N_STRING_SEARCH_H_
+
+#include "base/i18n/base_i18n_export.h"
+#include "base/string16.h"
+
+namespace base {
+namespace i18n {
+
+// Returns true if |in_this| contains |find_this|. If |match_index| or
+// |match_length| are non-NULL, they are assigned the start position and total
+// length of the match.
+//
+// Only differences between base letters are taken into consideration. Case and
+// accent differences are ignored. Please refer to 'primary level' in
+// http://userguide.icu-project.org/collation/concepts for additional details.
+BASE_I18N_EXPORT
+ bool StringSearchIgnoringCaseAndAccents(const string16& find_this,
+ const string16& in_this,
+ size_t* match_index,
+ size_t* match_length);
+
+} // namespace i18n
+} // namespace base
+
+#endif // BASE_I18N_STRING_SEARCH_H_
diff --git a/src/base/i18n/string_search_unittest.cc b/src/base/i18n/string_search_unittest.cc
new file mode 100644
index 0000000..e6ca1c5
--- /dev/null
+++ b/src/base/i18n/string_search_unittest.cc
@@ -0,0 +1,203 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "base/i18n/rtl.h"
+#include "base/i18n/string_search.h"
+#include "base/string16.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "unicode/usearch.h"
+
+namespace base {
+namespace i18n {
+
+// Note on setting default locale for testing: The current default locale on
+// the Mac trybot is en_US_POSIX, with which primary-level collation strength
+// string search is case-sensitive, when normally it should be
+// case-insensitive. In other locales (including en_US which English speakers
+// in the U.S. use), this search would be case-insensitive as expected.
+
+TEST(StringSearchTest, ASCII) {
+ std::string default_locale(uloc_getDefault());
+ bool locale_is_posix = (default_locale == "en_US_POSIX");
+ if (locale_is_posix)
+ SetICUDefaultLocale("en_US");
+
+ size_t index = 0;
+ size_t length = 0;
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ ASCIIToUTF16("hello"), ASCIIToUTF16("hello world"), &index, &length));
+ EXPECT_EQ(0U, index);
+ EXPECT_EQ(5U, length);
+
+ EXPECT_FALSE(StringSearchIgnoringCaseAndAccents(
+ ASCIIToUTF16("h e l l o"), ASCIIToUTF16("h e l l o"),
+ &index, &length));
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ ASCIIToUTF16("aabaaa"), ASCIIToUTF16("aaabaabaaa"), &index, &length));
+ EXPECT_EQ(4U, index);
+ EXPECT_EQ(6U, length);
+
+ EXPECT_FALSE(StringSearchIgnoringCaseAndAccents(
+ ASCIIToUTF16("searching within empty string"), string16(),
+ &index, &length));
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ string16(), ASCIIToUTF16("searching for empty string"), &index, &length));
+ EXPECT_EQ(0U, index);
+ EXPECT_EQ(0U, length);
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ ASCIIToUTF16("case insensitivity"), ASCIIToUTF16("CaSe InSeNsItIvItY"),
+ &index, &length));
+ EXPECT_EQ(0U, index);
+ EXPECT_EQ(18U, length);
+
+ if (locale_is_posix)
+ SetICUDefaultLocale(default_locale.data());
+}
+
+TEST(StringSearchTest, UnicodeLocaleIndependent) {
+ // Base characters
+ const string16 e_base = WideToUTF16(L"e");
+ const string16 E_base = WideToUTF16(L"E");
+ const string16 a_base = WideToUTF16(L"a");
+
+ // Composed characters
+ const string16 e_with_acute_accent = WideToUTF16(L"\u00e9");
+ const string16 E_with_acute_accent = WideToUTF16(L"\u00c9");
+ const string16 e_with_grave_accent = WideToUTF16(L"\u00e8");
+ const string16 E_with_grave_accent = WideToUTF16(L"\u00c8");
+ const string16 a_with_acute_accent = WideToUTF16(L"\u00e1");
+
+ // Decomposed characters
+ const string16 e_with_acute_combining_mark = WideToUTF16(L"e\u0301");
+ const string16 E_with_acute_combining_mark = WideToUTF16(L"E\u0301");
+ const string16 e_with_grave_combining_mark = WideToUTF16(L"e\u0300");
+ const string16 E_with_grave_combining_mark = WideToUTF16(L"E\u0300");
+ const string16 a_with_acute_combining_mark = WideToUTF16(L"a\u0301");
+
+ std::string default_locale(uloc_getDefault());
+ bool locale_is_posix = (default_locale == "en_US_POSIX");
+ if (locale_is_posix)
+ SetICUDefaultLocale("en_US");
+
+ size_t index = 0;
+ size_t length = 0;
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ e_base, e_with_acute_accent, &index, &length));
+ EXPECT_EQ(0U, index);
+ EXPECT_EQ(e_with_acute_accent.size(), length);
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ e_with_acute_accent, e_base, &index, &length));
+ EXPECT_EQ(0U, index);
+ EXPECT_EQ(e_base.size(), length);
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ e_base, e_with_acute_combining_mark, &index, &length));
+ EXPECT_EQ(0U, index);
+ EXPECT_EQ(e_with_acute_combining_mark.size(), length);
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ e_with_acute_combining_mark, e_base, &index, &length));
+ EXPECT_EQ(0U, index);
+ EXPECT_EQ(e_base.size(), length);
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ e_with_acute_combining_mark, e_with_acute_accent,
+ &index, &length));
+ EXPECT_EQ(0U, index);
+ EXPECT_EQ(e_with_acute_accent.size(), length);
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ e_with_acute_accent, e_with_acute_combining_mark,
+ &index, &length));
+ EXPECT_EQ(0U, index);
+ EXPECT_EQ(e_with_acute_combining_mark.size(), length);
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ e_with_acute_combining_mark, e_with_grave_combining_mark,
+ &index, &length));
+ EXPECT_EQ(0U, index);
+ EXPECT_EQ(e_with_grave_combining_mark.size(), length);
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ e_with_grave_combining_mark, e_with_acute_combining_mark,
+ &index, &length));
+ EXPECT_EQ(0U, index);
+ EXPECT_EQ(e_with_acute_combining_mark.size(), length);
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ e_with_acute_combining_mark, e_with_grave_accent, &index, &length));
+ EXPECT_EQ(0U, index);
+ EXPECT_EQ(e_with_grave_accent.size(), length);
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ e_with_grave_accent, e_with_acute_combining_mark, &index, &length));
+ EXPECT_EQ(0U, index);
+ EXPECT_EQ(e_with_acute_combining_mark.size(), length);
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ E_with_acute_accent, e_with_acute_accent, &index, &length));
+ EXPECT_EQ(0U, index);
+ EXPECT_EQ(e_with_acute_accent.size(), length);
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ E_with_grave_accent, e_with_acute_accent, &index, &length));
+ EXPECT_EQ(0U, index);
+ EXPECT_EQ(e_with_acute_accent.size(), length);
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ E_with_acute_combining_mark, e_with_grave_accent, &index, &length));
+ EXPECT_EQ(0U, index);
+ EXPECT_EQ(e_with_grave_accent.size(), length);
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ E_with_grave_combining_mark, e_with_acute_accent, &index, &length));
+ EXPECT_EQ(0U, index);
+ EXPECT_EQ(e_with_acute_accent.size(), length);
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ E_base, e_with_grave_accent, &index, &length));
+ EXPECT_EQ(0U, index);
+ EXPECT_EQ(e_with_grave_accent.size(), length);
+
+ EXPECT_FALSE(StringSearchIgnoringCaseAndAccents(
+ a_with_acute_accent, e_with_acute_accent, &index, &length));
+
+ EXPECT_FALSE(StringSearchIgnoringCaseAndAccents(
+ a_with_acute_combining_mark, e_with_acute_combining_mark,
+ &index, &length));
+
+ if (locale_is_posix)
+ SetICUDefaultLocale(default_locale.data());
+}
+
+TEST(StringSearchTest, UnicodeLocaleDependent) {
+ // Base characters
+ const string16 a_base = WideToUTF16(L"a");
+
+ // Composed characters
+ const string16 a_with_ring = WideToUTF16(L"\u00e5");
+
+ EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(
+ a_base, a_with_ring, NULL, NULL));
+
+ const char* default_locale = uloc_getDefault();
+ SetICUDefaultLocale("da");
+
+ EXPECT_FALSE(StringSearchIgnoringCaseAndAccents(
+ a_base, a_with_ring, NULL, NULL));
+
+ SetICUDefaultLocale(default_locale);
+}
+
+} // namespace i18n
+} // namespace base
diff --git a/src/base/i18n/time_formatting.cc b/src/base/i18n/time_formatting.cc
new file mode 100644
index 0000000..9906dba
--- /dev/null
+++ b/src/base/i18n/time_formatting.cc
@@ -0,0 +1,164 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/time_formatting.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/utf_string_conversions.h"
+#include "base/time.h"
+#include "unicode/datefmt.h"
+#include "unicode/dtptngen.h"
+#include "unicode/smpdtfmt.h"
+
+using base::Time;
+
+namespace {
+
+string16 TimeFormat(const icu::DateFormat* formatter,
+ const Time& time) {
+ DCHECK(formatter);
+ icu::UnicodeString date_string;
+
+ formatter->format(static_cast<UDate>(time.ToDoubleT() * 1000), date_string);
+ return string16(date_string.getBuffer(),
+ static_cast<size_t>(date_string.length()));
+}
+
+string16 TimeFormatWithoutAmPm(const icu::DateFormat* formatter,
+ const Time& time) {
+ DCHECK(formatter);
+ icu::UnicodeString time_string;
+
+ icu::FieldPosition ampm_field(icu::DateFormat::kAmPmField);
+ formatter->format(
+ static_cast<UDate>(time.ToDoubleT() * 1000), time_string, ampm_field);
+ int ampm_length = ampm_field.getEndIndex() - ampm_field.getBeginIndex();
+ if (ampm_length) {
+ int begin = ampm_field.getBeginIndex();
+ // Doesn't include any spacing before the field.
+ if (begin)
+ begin--;
+ time_string.removeBetween(begin, ampm_field.getEndIndex());
+ }
+ return string16(time_string.getBuffer(),
+ static_cast<size_t>(time_string.length()));
+}
+
+} // namespace
+
+namespace base {
+
+string16 TimeFormatTimeOfDay(const Time& time) {
+ // We can omit the locale parameter because the default should match
+ // Chrome's application locale.
+ scoped_ptr<icu::DateFormat> formatter(
+ icu::DateFormat::createTimeInstance(icu::DateFormat::kShort));
+ return TimeFormat(formatter.get(), time);
+}
+
+string16 TimeFormatTimeOfDayWithHourClockType(const Time& time,
+ HourClockType type,
+ AmPmClockType ampm) {
+ // Just redirect to the normal function if the default type matches the
+ // given type.
+ HourClockType default_type = GetHourClockType();
+ if (default_type == type && (type == k24HourClock || ampm == kKeepAmPm)) {
+ return TimeFormatTimeOfDay(time);
+ }
+
+ // Generate a locale-dependent format pattern. The generator will take
+ // care of locale-dependent formatting issues like which separator to
+ // use (some locales use '.' instead of ':'), and where to put the am/pm
+ // marker.
+ UErrorCode status = U_ZERO_ERROR;
+ scoped_ptr<icu::DateTimePatternGenerator> generator(
+ icu::DateTimePatternGenerator::createInstance(status));
+ DCHECK(U_SUCCESS(status));
+ const char* base_pattern = (type == k12HourClock ? "ahm" : "Hm");
+ icu::UnicodeString generated_pattern =
+ generator->getBestPattern(icu::UnicodeString(base_pattern), status);
+ DCHECK(U_SUCCESS(status));
+
+ // Then, format the time using the generated pattern.
+ icu::SimpleDateFormat formatter(generated_pattern, status);
+ DCHECK(U_SUCCESS(status));
+ if (ampm == kKeepAmPm) {
+ return TimeFormat(&formatter, time);
+ } else {
+ return TimeFormatWithoutAmPm(&formatter, time);
+ }
+}
+
+string16 TimeFormatShortDate(const Time& time) {
+ scoped_ptr<icu::DateFormat> formatter(
+ icu::DateFormat::createDateInstance(icu::DateFormat::kMedium));
+ return TimeFormat(formatter.get(), time);
+}
+
+string16 TimeFormatShortDateNumeric(const Time& time) {
+ scoped_ptr<icu::DateFormat> formatter(
+ icu::DateFormat::createDateInstance(icu::DateFormat::kShort));
+ return TimeFormat(formatter.get(), time);
+}
+
+string16 TimeFormatShortDateAndTime(const Time& time) {
+ scoped_ptr<icu::DateFormat> formatter(
+ icu::DateFormat::createDateTimeInstance(icu::DateFormat::kShort));
+ return TimeFormat(formatter.get(), time);
+}
+
+string16 TimeFormatFriendlyDateAndTime(const Time& time) {
+ scoped_ptr<icu::DateFormat> formatter(
+ icu::DateFormat::createDateTimeInstance(icu::DateFormat::kFull));
+ return TimeFormat(formatter.get(), time);
+}
+
+string16 TimeFormatFriendlyDate(const Time& time) {
+ scoped_ptr<icu::DateFormat> formatter(icu::DateFormat::createDateInstance(
+ icu::DateFormat::kFull));
+ return TimeFormat(formatter.get(), time);
+}
+
+HourClockType GetHourClockType() {
+ // TODO(satorux,jshin): Rework this with ures_getByKeyWithFallback()
+ // once it becomes public. The short time format can be found at
+ // "calendar/gregorian/DateTimePatterns/3" in the resources.
+ scoped_ptr<icu::SimpleDateFormat> formatter(
+ static_cast<icu::SimpleDateFormat*>(
+ icu::DateFormat::createTimeInstance(icu::DateFormat::kShort)));
+ // Retrieve the short time format.
+ icu::UnicodeString pattern_unicode;
+ formatter->toPattern(pattern_unicode);
+
+ // Determine what hour clock type the current locale uses, by checking
+ // "a" (am/pm marker) in the short time format. This is reliable as "a"
+ // is used by all of 12-hour clock formats, but not any of 24-hour clock
+ // formats, as shown below.
+ //
+ // % grep -A4 DateTimePatterns third_party/icu/source/data/locales/*.txt |
+ // grep -B1 -- -- |grep -v -- '--' |
+ // perl -nle 'print $1 if /^\S+\s+"(.*)"/' |sort -u
+ //
+ // H.mm
+ // H:mm
+ // HH.mm
+ // HH:mm
+ // a h:mm
+ // ah:mm
+ // ahh:mm
+ // h-mm a
+ // h:mm a
+ // hh:mm a
+ //
+ // See http://userguide.icu-project.org/formatparse/datetime for details
+ // about the date/time format syntax.
+ if (pattern_unicode.indexOf('a') == -1) {
+ return k24HourClock;
+ } else {
+ return k12HourClock;
+ }
+}
+
+} // namespace base
diff --git a/src/base/i18n/time_formatting.h b/src/base/i18n/time_formatting.h
new file mode 100644
index 0000000..91b79c6
--- /dev/null
+++ b/src/base/i18n/time_formatting.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Basic time formatting methods. These methods use the current locale
+// formatting for displaying the time.
+
+#ifndef BASE_I18N_TIME_FORMATTING_H_
+#define BASE_I18N_TIME_FORMATTING_H_
+
+#include "base/i18n/base_i18n_export.h"
+#include "base/string16.h"
+
+namespace base {
+
+class Time;
+
+// Argument type used to specify the hour clock type.
+enum HourClockType {
+ k12HourClock, // Uses 1-12. e.g., "3:07 PM"
+ k24HourClock, // Uses 0-23. e.g., "15:07"
+};
+
+// Argument type used to specify whether or not to include AM/PM sign.
+enum AmPmClockType {
+ kDropAmPm, // Drops AM/PM sign. e.g., "3:07"
+ kKeepAmPm, // Keeps AM/PM sign. e.g., "3:07 PM"
+};
+
+// Returns the time of day, e.g., "3:07 PM".
+BASE_I18N_EXPORT string16 TimeFormatTimeOfDay(const Time& time);
+
+// Returns the time of day in the specified hour clock type. e.g.
+// "3:07 PM" (type == k12HourClock, ampm == kKeepAmPm).
+// "3:07" (type == k12HourClock, ampm == kDropAmPm).
+// "15:07" (type == k24HourClock).
+BASE_I18N_EXPORT string16 TimeFormatTimeOfDayWithHourClockType(
+ const Time& time,
+ HourClockType type,
+ AmPmClockType ampm);
+
+// Returns a shortened date, e.g. "Nov 7, 2007"
+BASE_I18N_EXPORT string16 TimeFormatShortDate(const Time& time);
+
+// Returns a numeric date such as 12/13/52.
+BASE_I18N_EXPORT string16 TimeFormatShortDateNumeric(const Time& time);
+
+// Returns a numeric date and time such as "12/13/52 2:44:30 PM".
+BASE_I18N_EXPORT string16 TimeFormatShortDateAndTime(const Time& time);
+
+// Formats a time in a friendly sentence format, e.g.
+// "Monday, March 6, 2008 2:44:30 PM".
+BASE_I18N_EXPORT string16 TimeFormatFriendlyDateAndTime(const Time& time);
+
+// Formats a time in a friendly sentence format, e.g.
+// "Monday, March 6, 2008".
+BASE_I18N_EXPORT string16 TimeFormatFriendlyDate(const Time& time);
+
+// Gets the hour clock type of the current locale. e.g.
+// k12HourClock (en-US).
+// k24HourClock (en-GB).
+BASE_I18N_EXPORT HourClockType GetHourClockType();
+
+} // namespace base
+
+#endif // BASE_I18N_TIME_FORMATTING_H_
diff --git a/src/base/i18n/time_formatting_unittest.cc b/src/base/i18n/time_formatting_unittest.cc
new file mode 100644
index 0000000..89b5c55
--- /dev/null
+++ b/src/base/i18n/time_formatting_unittest.cc
@@ -0,0 +1,181 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/time_formatting.h"
+
+#include "base/i18n/rtl.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+#if defined(__LB_SHELL__)
+
+// Some legacy platforms' local time calculations always use the current
+// Daylight Savings Time status when exploding/unexploding time ticks, no matter
+// what the time to explode is. On Starboard platforms, we use ICU instead as it
+// is platform-independent and considerably more correct.
+#define MAYBE_TimeFormatTimeOfDayDefault12h \
+ DISABLED_TimeFormatTimeOfDayDefault12h
+#define MAYBE_TimeFormatTimeOfDayDefault24h \
+ DISABLED_TimeFormatTimeOfDayDefault24h
+#define MAYBE_TimeFormatTimeOfDayJP \
+ DISABLED_TimeFormatTimeOfDayJP
+#define MAYBE_TimeFormatDateUS \
+ DISABLED_TimeFormatDateUS
+#define MAYBE_TimeFormatDateGB \
+ DISABLED_TimeFormatDateGB
+
+#else
+
+#define MAYBE_TimeFormatTimeOfDayDefault12h TimeFormatTimeOfDayDefault12h
+#define MAYBE_TimeFormatTimeOfDayDefault24h TimeFormatTimeOfDayDefault24h
+#define MAYBE_TimeFormatTimeOfDayJP TimeFormatTimeOfDayJP
+#define MAYBE_TimeFormatDateUS TimeFormatDateUS
+#define MAYBE_TimeFormatDateGB TimeFormatDateGB
+
+#endif
+
+const Time::Exploded kTestDateTimeExploded = {
+ 2011, 4, 6, 30, // Sat, Apr 30, 2011
+ 15, 42, 7, 0 // 15:42:07.000
+};
+
+TEST(TimeFormattingTest, MAYBE_TimeFormatTimeOfDayDefault12h) {
+ // Test for a locale defaulted to 12h clock.
+ // As an instance, we use third_party/icu/source/data/locales/en.txt.
+ i18n::SetICUDefaultLocale("en_US");
+
+ Time time(Time::FromLocalExploded(kTestDateTimeExploded));
+ string16 clock24h(ASCIIToUTF16("15:42"));
+ string16 clock12h_pm(ASCIIToUTF16("3:42 PM"));
+ string16 clock12h(ASCIIToUTF16("3:42"));
+
+ // The default is 12h clock.
+ EXPECT_EQ(clock12h_pm, TimeFormatTimeOfDay(time));
+ EXPECT_EQ(k12HourClock, GetHourClockType());
+ // k{Keep,Drop}AmPm should not affect for 24h clock.
+ EXPECT_EQ(clock24h,
+ TimeFormatTimeOfDayWithHourClockType(time,
+ k24HourClock,
+ kKeepAmPm));
+ EXPECT_EQ(clock24h,
+ TimeFormatTimeOfDayWithHourClockType(time,
+ k24HourClock,
+ kDropAmPm));
+ // k{Keep,Drop}AmPm affects for 12h clock.
+ EXPECT_EQ(clock12h_pm,
+ TimeFormatTimeOfDayWithHourClockType(time,
+ k12HourClock,
+ kKeepAmPm));
+ EXPECT_EQ(clock12h,
+ TimeFormatTimeOfDayWithHourClockType(time,
+ k12HourClock,
+ kDropAmPm));
+}
+
+TEST(TimeFormattingTest, MAYBE_TimeFormatTimeOfDayDefault24h) {
+ // Test for a locale defaulted to 24h clock.
+ // As an instance, we use third_party/icu/source/data/locales/en_GB.txt.
+ i18n::SetICUDefaultLocale("en_GB");
+
+ Time time(Time::FromLocalExploded(kTestDateTimeExploded));
+ string16 clock24h(ASCIIToUTF16("15:42"));
+ string16 clock12h_pm(ASCIIToUTF16("3:42 PM"));
+ string16 clock12h(ASCIIToUTF16("3:42"));
+
+ // The default is 24h clock.
+ EXPECT_EQ(clock24h, TimeFormatTimeOfDay(time));
+ EXPECT_EQ(k24HourClock, GetHourClockType());
+ // k{Keep,Drop}AmPm should not affect for 24h clock.
+ EXPECT_EQ(clock24h,
+ TimeFormatTimeOfDayWithHourClockType(time,
+ k24HourClock,
+ kKeepAmPm));
+ EXPECT_EQ(clock24h,
+ TimeFormatTimeOfDayWithHourClockType(time,
+ k24HourClock,
+ kDropAmPm));
+ // k{Keep,Drop}AmPm affects for 12h clock.
+ EXPECT_EQ(clock12h_pm,
+ TimeFormatTimeOfDayWithHourClockType(time,
+ k12HourClock,
+ kKeepAmPm));
+ EXPECT_EQ(clock12h,
+ TimeFormatTimeOfDayWithHourClockType(time,
+ k12HourClock,
+ kDropAmPm));
+}
+
+TEST(TimeFormattingTest, MAYBE_TimeFormatTimeOfDayJP) {
+ // Test for a locale that uses different mark than "AM" and "PM".
+ // As an instance, we use third_party/icu/source/data/locales/ja.txt.
+ i18n::SetICUDefaultLocale("ja_JP");
+
+ Time time(Time::FromLocalExploded(kTestDateTimeExploded));
+ string16 clock24h(ASCIIToUTF16("15:42"));
+ string16 clock12h_pm(WideToUTF16(L"\x5348\x5f8c" L"3:42"));
+ string16 clock12h(ASCIIToUTF16("3:42"));
+
+ // The default is 24h clock.
+ EXPECT_EQ(clock24h, TimeFormatTimeOfDay(time));
+ EXPECT_EQ(k24HourClock, GetHourClockType());
+ // k{Keep,Drop}AmPm should not affect for 24h clock.
+ EXPECT_EQ(clock24h,
+ TimeFormatTimeOfDayWithHourClockType(time,
+ k24HourClock,
+ kKeepAmPm));
+ EXPECT_EQ(clock24h,
+ TimeFormatTimeOfDayWithHourClockType(time,
+ k24HourClock,
+ kDropAmPm));
+ // k{Keep,Drop}AmPm affects for 12h clock.
+ EXPECT_EQ(clock12h_pm,
+ TimeFormatTimeOfDayWithHourClockType(time,
+ k12HourClock,
+ kKeepAmPm));
+ EXPECT_EQ(clock12h,
+ TimeFormatTimeOfDayWithHourClockType(time,
+ k12HourClock,
+ kDropAmPm));
+}
+
+TEST(TimeFormattingTest, MAYBE_TimeFormatDateUS) {
+ // See third_party/icu/source/data/locales/en.txt.
+ // The date patterns are "EEEE, MMMM d, y", "MMM d, y", and "M/d/yy".
+ i18n::SetICUDefaultLocale("en_US");
+
+ Time time(Time::FromLocalExploded(kTestDateTimeExploded));
+
+ EXPECT_EQ(ASCIIToUTF16("Apr 30, 2011"), TimeFormatShortDate(time));
+ EXPECT_EQ(ASCIIToUTF16("4/30/11"), TimeFormatShortDateNumeric(time));
+ EXPECT_EQ(ASCIIToUTF16("4/30/11 3:42:07 PM"),
+ TimeFormatShortDateAndTime(time));
+ EXPECT_EQ(ASCIIToUTF16("Saturday, April 30, 2011 3:42:07 PM"),
+ TimeFormatFriendlyDateAndTime(time));
+ EXPECT_EQ(ASCIIToUTF16("Saturday, April 30, 2011"),
+ TimeFormatFriendlyDate(time));
+}
+
+TEST(TimeFormattingTest, MAYBE_TimeFormatDateGB) {
+ // See third_party/icu/source/data/locales/en_GB.txt.
+ // The date patterns are "EEEE, d MMMM y", "d MMM y", and "dd/MM/yyyy".
+ i18n::SetICUDefaultLocale("en_GB");
+
+ Time time(Time::FromLocalExploded(kTestDateTimeExploded));
+
+ EXPECT_EQ(ASCIIToUTF16("30 Apr 2011"), TimeFormatShortDate(time));
+ EXPECT_EQ(ASCIIToUTF16("30/04/2011"), TimeFormatShortDateNumeric(time));
+ EXPECT_EQ(ASCIIToUTF16("30/04/2011 15:42:07"),
+ TimeFormatShortDateAndTime(time));
+ EXPECT_EQ(ASCIIToUTF16("Saturday, 30 April 2011 15:42:07"),
+ TimeFormatFriendlyDateAndTime(time));
+ EXPECT_EQ(ASCIIToUTF16("Saturday, 30 April 2011"),
+ TimeFormatFriendlyDate(time));
+}
+
+} // namespace
+} // namespace base
diff --git a/src/base/id_map.h b/src/base/id_map.h
new file mode 100644
index 0000000..d3fc7b8
--- /dev/null
+++ b/src/base/id_map.h
@@ -0,0 +1,257 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ID_MAP_H_
+#define BASE_ID_MAP_H_
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/hash_tables.h"
+#include "base/logging.h"
+#include "base/threading/non_thread_safe.h"
+
+// Ownership semantics - own pointer means the pointer is deleted in Remove()
+// & during destruction
+enum IDMapOwnershipSemantics {
+ IDMapExternalPointer,
+ IDMapOwnPointer
+};
+
+// This object maintains a list of IDs that can be quickly converted to
+// pointers to objects. It is implemented as a hash table, optimized for
+// relatively small data sets (in the common case, there will be exactly one
+// item in the list).
+//
+// Items can be inserted into the container with arbitrary ID, but the caller
+// must ensure they are unique. Inserting IDs and relying on automatically
+// generated ones is not allowed because they can collide.
+//
+// This class does not have a virtual destructor, do not inherit from it when
+// ownership semantics are set to own because pointers will leak.
+template<typename T, IDMapOwnershipSemantics OS = IDMapExternalPointer>
+class IDMap : public base::NonThreadSafe {
+ private:
+ typedef int32 KeyType;
+ typedef base::hash_map<KeyType, T*> HashTable;
+
+ public:
+ IDMap() : iteration_depth_(0), next_id_(1), check_on_null_data_(false) {
+ // A number of consumers of IDMap create it on one thread but always access
+ // it from a different, but consitent, thread post-construction.
+ DetachFromThread();
+ }
+
+ ~IDMap() {
+ // Many IDMap's are static, and hence will be destroyed on the main thread.
+ // However, all the accesses may take place on another thread, such as the
+ // IO thread. Detaching again to clean this up.
+ DetachFromThread();
+ Releaser<OS, 0>::release_all(&data_);
+ }
+
+ // Sets whether Add should CHECK if passed in NULL data. Default is false.
+ void set_check_on_null_data(bool value) { check_on_null_data_ = value; }
+
+ // Adds a view with an automatically generated unique ID. See AddWithID.
+ KeyType Add(T* data) {
+ DCHECK(CalledOnValidThread());
+ CHECK(!check_on_null_data_ || data);
+ KeyType this_id = next_id_;
+ DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item";
+ data_[this_id] = data;
+ next_id_++;
+ return this_id;
+ }
+
+ // Adds a new data member with the specified ID. The ID must not be in
+ // the list. The caller either must generate all unique IDs itself and use
+ // this function, or allow this object to generate IDs and call Add. These
+ // two methods may not be mixed, or duplicate IDs may be generated
+ void AddWithID(T* data, KeyType id) {
+ DCHECK(CalledOnValidThread());
+ CHECK(!check_on_null_data_ || data);
+ DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item";
+ data_[id] = data;
+ }
+
+ void Remove(KeyType id) {
+ DCHECK(CalledOnValidThread());
+ typename HashTable::iterator i = data_.find(id);
+ if (i == data_.end()) {
+ NOTREACHED() << "Attempting to remove an item not in the list";
+ return;
+ }
+
+ if (iteration_depth_ == 0) {
+ Releaser<OS, 0>::release(i->second);
+ data_.erase(i);
+ } else {
+ removed_ids_.insert(id);
+ }
+ }
+
+ void Clear() {
+ DCHECK(CalledOnValidThread());
+ if (iteration_depth_ == 0) {
+ Releaser<OS, 0>::release_all(&data_);
+ } else {
+ for (typename HashTable::iterator i = data_.begin();
+ i != data_.end(); ++i)
+ removed_ids_.insert(i->first);
+ }
+ }
+
+ bool IsEmpty() const {
+ DCHECK(CalledOnValidThread());
+ return size() == 0u;
+ }
+
+ T* Lookup(KeyType id) const {
+ DCHECK(CalledOnValidThread());
+ typename HashTable::const_iterator i = data_.find(id);
+ if (i == data_.end())
+ return NULL;
+ return i->second;
+ }
+
+ size_t size() const {
+ DCHECK(CalledOnValidThread());
+ return data_.size() - removed_ids_.size();
+ }
+
+#if defined(UNIT_TEST)
+ int iteration_depth() const {
+ return iteration_depth_;
+ }
+#endif // defined(UNIT_TEST)
+
+ // It is safe to remove elements from the map during iteration. All iterators
+ // will remain valid.
+ template<class ReturnType>
+ class Iterator {
+ public:
+ Iterator(IDMap<T, OS>* map)
+ : map_(map),
+ iter_(map_->data_.begin()) {
+ Init();
+ }
+
+ Iterator(const Iterator& iter)
+ : map_(iter.map_),
+ iter_(iter.iter_) {
+ Init();
+ }
+
+ const Iterator& operator=(const Iterator& iter) {
+ map_ = iter.map;
+ iter_ = iter.iter;
+ Init();
+ return *this;
+ }
+
+ ~Iterator() {
+ DCHECK(map_->CalledOnValidThread());
+
+ // We're going to decrement iteration depth. Make sure it's greater than
+ // zero so that it doesn't become negative.
+ DCHECK_LT(0, map_->iteration_depth_);
+
+ if (--map_->iteration_depth_ == 0)
+ map_->Compact();
+ }
+
+ bool IsAtEnd() const {
+ DCHECK(map_->CalledOnValidThread());
+ return iter_ == map_->data_.end();
+ }
+
+ KeyType GetCurrentKey() const {
+ DCHECK(map_->CalledOnValidThread());
+ return iter_->first;
+ }
+
+ ReturnType* GetCurrentValue() const {
+ DCHECK(map_->CalledOnValidThread());
+ return iter_->second;
+ }
+
+ void Advance() {
+ DCHECK(map_->CalledOnValidThread());
+ ++iter_;
+ SkipRemovedEntries();
+ }
+
+ private:
+ void Init() {
+ DCHECK(map_->CalledOnValidThread());
+ ++map_->iteration_depth_;
+ SkipRemovedEntries();
+ }
+
+ void SkipRemovedEntries() {
+ while (iter_ != map_->data_.end() &&
+ map_->removed_ids_.find(iter_->first) !=
+ map_->removed_ids_.end()) {
+ ++iter_;
+ }
+ }
+
+ IDMap<T, OS>* map_;
+ typename HashTable::const_iterator iter_;
+ };
+
+ typedef Iterator<T> iterator;
+ typedef Iterator<const T> const_iterator;
+
+ private:
+
+ // The dummy parameter is there because C++ standard does not allow
+ // explicitly specialized templates inside classes
+ template<IDMapOwnershipSemantics OI, int dummy> struct Releaser {
+ static inline void release(T* ptr) {}
+ static inline void release_all(HashTable* table) {}
+ };
+
+ template<int dummy> struct Releaser<IDMapOwnPointer, dummy> {
+ static inline void release(T* ptr) { delete ptr;}
+ static inline void release_all(HashTable* table) {
+ for (typename HashTable::iterator i = table->begin();
+ i != table->end(); ++i) {
+ delete i->second;
+ }
+ table->clear();
+ }
+ };
+
+ void Compact() {
+ DCHECK_EQ(0, iteration_depth_);
+ for (std::set<KeyType>::const_iterator i = removed_ids_.begin();
+ i != removed_ids_.end(); ++i) {
+ Remove(*i);
+ }
+ removed_ids_.clear();
+ }
+
+ // Keep track of how many iterators are currently iterating on us to safely
+ // handle removing items during iteration.
+ int iteration_depth_;
+
+ // Keep set of IDs that should be removed after the outermost iteration has
+ // finished. This way we manage to not invalidate the iterator when an element
+ // is removed.
+ std::set<KeyType> removed_ids_;
+
+ // The next ID that we will return from Add()
+ KeyType next_id_;
+
+ HashTable data_;
+
+ // See description above setter.
+ bool check_on_null_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(IDMap);
+};
+
+#endif // BASE_ID_MAP_H_
diff --git a/src/base/id_map_unittest.cc b/src/base/id_map_unittest.cc
new file mode 100644
index 0000000..53ca656
--- /dev/null
+++ b/src/base/id_map_unittest.cc
@@ -0,0 +1,341 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/id_map.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class TestObject {
+};
+
+class DestructorCounter {
+ public:
+ explicit DestructorCounter(int* counter) : counter_(counter) {}
+ ~DestructorCounter() { ++(*counter_); }
+
+ private:
+ int* counter_;
+};
+
+TEST(IDMapTest, Basic) {
+ IDMap<TestObject> map;
+ EXPECT_TRUE(map.IsEmpty());
+ EXPECT_EQ(0U, map.size());
+
+ TestObject obj1;
+ TestObject obj2;
+
+ int32 id1 = map.Add(&obj1);
+ EXPECT_FALSE(map.IsEmpty());
+ EXPECT_EQ(1U, map.size());
+ EXPECT_EQ(&obj1, map.Lookup(id1));
+
+ int32 id2 = map.Add(&obj2);
+ EXPECT_FALSE(map.IsEmpty());
+ EXPECT_EQ(2U, map.size());
+
+ EXPECT_EQ(&obj1, map.Lookup(id1));
+ EXPECT_EQ(&obj2, map.Lookup(id2));
+
+ map.Remove(id1);
+ EXPECT_FALSE(map.IsEmpty());
+ EXPECT_EQ(1U, map.size());
+
+ map.Remove(id2);
+ EXPECT_TRUE(map.IsEmpty());
+ EXPECT_EQ(0U, map.size());
+
+ map.AddWithID(&obj1, 1);
+ map.AddWithID(&obj2, 2);
+ EXPECT_EQ(&obj1, map.Lookup(1));
+ EXPECT_EQ(&obj2, map.Lookup(2));
+
+ EXPECT_EQ(0, map.iteration_depth());
+}
+
+TEST(IDMapTest, IteratorRemainsValidWhenRemovingCurrentElement) {
+ IDMap<TestObject> map;
+
+ TestObject obj1;
+ TestObject obj2;
+ TestObject obj3;
+
+ map.Add(&obj1);
+ map.Add(&obj2);
+ map.Add(&obj3);
+
+ {
+ IDMap<TestObject>::const_iterator iter(&map);
+
+ EXPECT_EQ(1, map.iteration_depth());
+
+ while (!iter.IsAtEnd()) {
+ map.Remove(iter.GetCurrentKey());
+ iter.Advance();
+ }
+
+ // Test that while an iterator is still in scope, we get the map emptiness
+ // right (http://crbug.com/35571).
+ EXPECT_TRUE(map.IsEmpty());
+ EXPECT_EQ(0U, map.size());
+ }
+
+ EXPECT_TRUE(map.IsEmpty());
+ EXPECT_EQ(0U, map.size());
+
+ EXPECT_EQ(0, map.iteration_depth());
+}
+
+TEST(IDMapTest, IteratorRemainsValidWhenRemovingOtherElements) {
+ IDMap<TestObject> map;
+
+ const int kCount = 5;
+ TestObject obj[kCount];
+ int32 ids[kCount];
+
+ for (int i = 0; i < kCount; i++)
+ ids[i] = map.Add(&obj[i]);
+
+ int counter = 0;
+ for (IDMap<TestObject>::const_iterator iter(&map);
+ !iter.IsAtEnd(); iter.Advance()) {
+ EXPECT_EQ(1, map.iteration_depth());
+
+ switch (counter) {
+ case 0:
+ EXPECT_EQ(ids[0], iter.GetCurrentKey());
+ EXPECT_EQ(&obj[0], iter.GetCurrentValue());
+ map.Remove(ids[1]);
+ break;
+ case 1:
+ EXPECT_EQ(ids[2], iter.GetCurrentKey());
+ EXPECT_EQ(&obj[2], iter.GetCurrentValue());
+ map.Remove(ids[3]);
+ break;
+ case 2:
+ EXPECT_EQ(ids[4], iter.GetCurrentKey());
+ EXPECT_EQ(&obj[4], iter.GetCurrentValue());
+ map.Remove(ids[0]);
+ break;
+ default:
+ FAIL() << "should not have that many elements";
+ break;
+ }
+
+ counter++;
+ }
+
+ EXPECT_EQ(0, map.iteration_depth());
+}
+
+TEST(IDMapTest, CopyIterator) {
+ IDMap<TestObject> map;
+
+ TestObject obj1;
+ TestObject obj2;
+ TestObject obj3;
+
+ map.Add(&obj1);
+ map.Add(&obj2);
+ map.Add(&obj3);
+
+ EXPECT_EQ(0, map.iteration_depth());
+
+ {
+ IDMap<TestObject>::const_iterator iter1(&map);
+ EXPECT_EQ(1, map.iteration_depth());
+
+ // Make sure that copying the iterator correctly increments
+ // map's iteration depth.
+ IDMap<TestObject>::const_iterator iter2(iter1);
+ EXPECT_EQ(2, map.iteration_depth());
+ }
+
+ // Make sure after destroying all iterators the map's iteration depth
+ // returns to initial state.
+ EXPECT_EQ(0, map.iteration_depth());
+}
+
+TEST(IDMapTest, AssignIterator) {
+ IDMap<TestObject> map;
+
+ TestObject obj1;
+ TestObject obj2;
+ TestObject obj3;
+
+ map.Add(&obj1);
+ map.Add(&obj2);
+ map.Add(&obj3);
+
+ EXPECT_EQ(0, map.iteration_depth());
+
+ {
+ IDMap<TestObject>::const_iterator iter1(&map);
+ EXPECT_EQ(1, map.iteration_depth());
+
+ IDMap<TestObject>::const_iterator iter2(&map);
+ EXPECT_EQ(2, map.iteration_depth());
+
+ // Make sure that assigning the iterator correctly updates
+ // map's iteration depth (-1 for destruction, +1 for assignment).
+ EXPECT_EQ(2, map.iteration_depth());
+ }
+
+ // Make sure after destroying all iterators the map's iteration depth
+ // returns to initial state.
+ EXPECT_EQ(0, map.iteration_depth());
+}
+
+// This test relies on specific ordering of items in a hash map to expect a
+// given ID at a given iteration point. This is a bad expectation for a
+// hash_map, and one that does not hold on lbshell platforms. This test
+// should be rewritten.
+#if !defined(__LB_SHELL__) && !defined(OS_STARBOARD)
+TEST(IDMapTest, IteratorRemainsValidWhenClearing) {
+ IDMap<TestObject> map;
+
+ const int kCount = 5;
+ TestObject obj[kCount];
+ int32 ids[kCount];
+
+ for (int i = 0; i < kCount; i++)
+ ids[i] = map.Add(&obj[i]);
+
+ int counter = 0;
+ for (IDMap<TestObject>::const_iterator iter(&map);
+ !iter.IsAtEnd(); iter.Advance()) {
+ switch (counter) {
+ case 0:
+ EXPECT_EQ(ids[0], iter.GetCurrentKey());
+ EXPECT_EQ(&obj[0], iter.GetCurrentValue());
+ break;
+ case 1:
+ EXPECT_EQ(ids[1], iter.GetCurrentKey());
+ EXPECT_EQ(&obj[1], iter.GetCurrentValue());
+ map.Clear();
+ EXPECT_TRUE(map.IsEmpty());
+ EXPECT_EQ(0U, map.size());
+ break;
+ default:
+ FAIL() << "should not have that many elements";
+ break;
+ }
+ counter++;
+ }
+
+ EXPECT_TRUE(map.IsEmpty());
+ EXPECT_EQ(0U, map.size());
+}
+#endif
+
+TEST(IDMapTest, OwningPointersDeletesThemOnRemove) {
+ const int kCount = 3;
+
+ int external_del_count = 0;
+ DestructorCounter* external_obj[kCount];
+ int map_external_ids[kCount];
+
+ int owned_del_count = 0;
+ DestructorCounter* owned_obj[kCount];
+ int map_owned_ids[kCount];
+
+ IDMap<DestructorCounter> map_external;
+ IDMap<DestructorCounter, IDMapOwnPointer> map_owned;
+
+ for (int i = 0; i < kCount; ++i) {
+ external_obj[i] = new DestructorCounter(&external_del_count);
+ map_external_ids[i] = map_external.Add(external_obj[i]);
+
+ owned_obj[i] = new DestructorCounter(&owned_del_count);
+ map_owned_ids[i] = map_owned.Add(owned_obj[i]);
+ }
+
+ for (int i = 0; i < kCount; ++i) {
+ EXPECT_EQ(external_del_count, 0);
+ EXPECT_EQ(owned_del_count, i);
+
+ map_external.Remove(map_external_ids[i]);
+ map_owned.Remove(map_owned_ids[i]);
+ }
+
+ for (int i = 0; i < kCount; ++i) {
+ delete external_obj[i];
+ }
+
+ EXPECT_EQ(external_del_count, kCount);
+ EXPECT_EQ(owned_del_count, kCount);
+}
+
+TEST(IDMapTest, OwningPointersDeletesThemOnClear) {
+ const int kCount = 3;
+
+ int external_del_count = 0;
+ DestructorCounter* external_obj[kCount];
+
+ int owned_del_count = 0;
+ DestructorCounter* owned_obj[kCount];
+
+ IDMap<DestructorCounter> map_external;
+ IDMap<DestructorCounter, IDMapOwnPointer> map_owned;
+
+ for (int i = 0; i < kCount; ++i) {
+ external_obj[i] = new DestructorCounter(&external_del_count);
+ map_external.Add(external_obj[i]);
+
+ owned_obj[i] = new DestructorCounter(&owned_del_count);
+ map_owned.Add(owned_obj[i]);
+ }
+
+ EXPECT_EQ(external_del_count, 0);
+ EXPECT_EQ(owned_del_count, 0);
+
+ map_external.Clear();
+ map_owned.Clear();
+
+ EXPECT_EQ(external_del_count, 0);
+ EXPECT_EQ(owned_del_count, kCount);
+
+ for (int i = 0; i < kCount; ++i) {
+ delete external_obj[i];
+ }
+
+ EXPECT_EQ(external_del_count, kCount);
+ EXPECT_EQ(owned_del_count, kCount);
+}
+
+TEST(IDMapTest, OwningPointersDeletesThemOnDestruct) {
+ const int kCount = 3;
+
+ int external_del_count = 0;
+ DestructorCounter* external_obj[kCount];
+
+ int owned_del_count = 0;
+ DestructorCounter* owned_obj[kCount];
+
+ {
+ IDMap<DestructorCounter> map_external;
+ IDMap<DestructorCounter, IDMapOwnPointer> map_owned;
+
+ for (int i = 0; i < kCount; ++i) {
+ external_obj[i] = new DestructorCounter(&external_del_count);
+ map_external.Add(external_obj[i]);
+
+ owned_obj[i] = new DestructorCounter(&owned_del_count);
+ map_owned.Add(owned_obj[i]);
+ }
+ }
+
+ EXPECT_EQ(external_del_count, 0);
+
+ for (int i = 0; i < kCount; ++i) {
+ delete external_obj[i];
+ }
+
+ EXPECT_EQ(external_del_count, kCount);
+ EXPECT_EQ(owned_del_count, kCount);
+}
+
+} // namespace
diff --git a/src/base/ios/device_util.h b/src/base/ios/device_util.h
new file mode 100644
index 0000000..fe4833d
--- /dev/null
+++ b/src/base/ios/device_util.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2012 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_IOS_DEVICE_UTIL_H_
+#define BASE_IOS_DEVICE_UTIL_H_
+
+#include <string>
+
+namespace ios {
+namespace device_util {
+
+// Returns the hardware version of the device the app is running on.
+//
+// The returned string is the string returned by sysctlbyname() with name
+// "hw.machine". Possible (known) values include:
+//
+// iPhone1,1 -> iPhone 1G
+// iPhone1,2 -> iPhone 3G
+// iPhone2,1 -> iPhone 3GS
+// iPhone3,1 -> iPhone 4/AT&T
+// iPhone3,2 -> iPhone 4/Other Carrier?
+// iPhone3,3 -> iPhone 4/Other Carrier?
+// iPhone4,1 -> iPhone 4S
+//
+// iPod1,1 -> iPod touch 1G
+// iPod2,1 -> iPod touch 2G
+// iPod2,2 -> ?
+// iPod3,1 -> iPod touch 3G
+// iPod4,1 -> iPod touch 4G
+// iPod5,1 -> ?
+//
+// iPad1,1 -> iPad 1G, WiFi
+// iPad1,? -> iPad 1G, 3G <- needs 3G owner to test
+// iPad2,1 -> iPad 2G, WiFi
+//
+// AppleTV2,1 -> AppleTV 2
+//
+// i386 -> Simulator
+// x86_64 -> Simulator
+std::string GetPlatform();
+
+// Returns true if the application is running on a high-ram device. (>=250M).
+bool IsRunningOnHighRamDevice();
+
+// Returns true if the device has only one core.
+bool IsSingleCoreDevice();
+
+// Returns the MAC address of the interface with name |interface_name|.
+std::string GetMacAddress(const std::string& interface_name);
+
+// Returns a random UUID.
+std::string GetRandomId();
+
+// Returns an identifier for the device, using the given |salt|. A global
+// identifier is generated the first time this method is called, and the salt
+// is used to be able to generate distinct identifiers for the same device. If
+// |salt| is NULL, a default value is used. Unless you are using this value for
+// something that should be anonymous, you should probably pass NULL.
+std::string GetDeviceIdentifier(const char* salt);
+
+} // namespace device_util
+} // namespace ios
+
+#endif // BASE_IOS_DEVICE_UTIL_H_
diff --git a/src/base/ios/device_util.mm b/src/base/ios/device_util.mm
new file mode 100644
index 0000000..b538ea8
--- /dev/null
+++ b/src/base/ios/device_util.mm
@@ -0,0 +1,151 @@
+// Copyright (c) 2012 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/ios/device_util.h"
+
+#include <CommonCrypto/CommonDigest.h>
+#import <UIKit/UIKit.h>
+
+#include <ifaddrs.h>
+#include <net/if_dl.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include "base/ios/ios_util.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/sys_string_conversions.h"
+
+namespace {
+
+// Client ID key in the user preferences.
+NSString* const kLegacyClientIdPreferenceKey = @"ChromiumClientID";
+NSString* const kClientIdPreferenceKey = @"ChromeClientID";
+// Default salt for device ids.
+const char kDefaultSalt[] = "Salt";
+// Zero UUID returned on buggy iOS devices.
+NSString* const kZeroUUID = @"00000000-0000-0000-0000-000000000000";
+
+NSString* GenerateClientId() {
+ NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+
+ // Try to migrate from legacy client id.
+ NSString* client_id = [defaults stringForKey:kLegacyClientIdPreferenceKey];
+
+ // Some iOS6 devices return a buggy identifierForVendor:
+ // http://openradar.appspot.com/12377282. If this is the case, revert to
+ // generating a new one.
+ if (!client_id || [client_id isEqualToString:kZeroUUID]) {
+ if (base::ios::IsRunningOnIOS6OrLater()) {
+ client_id = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
+ if ([client_id isEqualToString:kZeroUUID])
+ client_id = base::SysUTF8ToNSString(ios::device_util::GetRandomId());
+ } else {
+ client_id = base::SysUTF8ToNSString(ios::device_util::GetRandomId());
+ }
+ }
+ return client_id;
+}
+
+} // namespace
+
+namespace ios {
+namespace device_util {
+
+std::string GetPlatform() {
+ std::string platform;
+ size_t size = 0;
+ sysctlbyname("hw.machine", NULL, &size, NULL, 0);
+ sysctlbyname("hw.machine", WriteInto(&platform, size), &size, NULL, 0);
+ return platform;
+}
+
+bool IsRunningOnHighRamDevice() {
+ uint64_t memory_size = 0;
+ size_t size = sizeof(memory_size);
+ if (sysctlbyname("hw.memsize", &memory_size, &size, NULL, 0) == 0) {
+ // Anything >= 250M, call high ram.
+ return memory_size >= 250 * 1024 * 1024;
+ }
+ return false;
+}
+
+bool IsSingleCoreDevice() {
+ uint64_t cpu_number = 0;
+ size_t sizes = sizeof(cpu_number);
+ sysctlbyname("hw.physicalcpu", &cpu_number, &sizes, NULL, 0);
+ return cpu_number == 1;
+}
+
+std::string GetMacAddress(const std::string& interface_name) {
+ std::string mac_string;
+ struct ifaddrs* addresses;
+ if (getifaddrs(&addresses) == 0) {
+ for (struct ifaddrs* address = addresses; address;
+ address = address->ifa_next) {
+ if ((address->ifa_addr->sa_family == AF_LINK) &&
+ strcmp(interface_name.c_str(), address->ifa_name) == 0) {
+ const struct sockaddr_dl* found_address_struct =
+ reinterpret_cast<const struct sockaddr_dl*>(address->ifa_addr);
+
+ // |found_address_struct->sdl_data| contains the interface name followed
+ // by the interface address. The address part can be accessed based on
+ // the length of the name, that is, |found_address_struct->sdl_nlen|.
+ const unsigned char* found_address =
+ reinterpret_cast<const unsigned char*>(
+ &found_address_struct->sdl_data[
+ found_address_struct->sdl_nlen]);
+
+ int found_address_length = found_address_struct->sdl_alen;
+ for (int i = 0; i < found_address_length; ++i) {
+ if (i != 0)
+ mac_string.push_back(':');
+ base::StringAppendF(&mac_string, "%02X", found_address[i]);
+ }
+ break;
+ }
+ }
+ freeifaddrs(addresses);
+ }
+ return mac_string;
+}
+
+std::string GetRandomId() {
+ base::mac::ScopedCFTypeRef<CFUUIDRef>
+ uuid_object(CFUUIDCreate(kCFAllocatorDefault));
+ base::mac::ScopedCFTypeRef<CFStringRef> uuid_string(
+ CFUUIDCreateString(kCFAllocatorDefault, uuid_object));
+ return base::SysCFStringRefToUTF8(uuid_string);
+}
+
+std::string GetDeviceIdentifier(const char* salt) {
+ NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+ NSString* client_id = [defaults stringForKey:kClientIdPreferenceKey];
+
+ if (!client_id) {
+ client_id = GenerateClientId();
+ [defaults setObject:client_id forKey:kClientIdPreferenceKey];
+ [defaults synchronize];
+ }
+
+ NSData* hash_data = [[NSString stringWithFormat:@"%@%s", client_id,
+ salt ? salt : kDefaultSalt] dataUsingEncoding:NSUTF8StringEncoding];
+
+ unsigned char hash[CC_SHA256_DIGEST_LENGTH];
+ CC_SHA256([hash_data bytes], [hash_data length], hash);
+ CFUUIDBytes* uuid_bytes = reinterpret_cast<CFUUIDBytes*>(hash);
+
+ base::mac::ScopedCFTypeRef<CFUUIDRef>
+ uuid_object(CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, *uuid_bytes));
+ base::mac::ScopedCFTypeRef<CFStringRef> device_id(
+ CFUUIDCreateString(kCFAllocatorDefault, uuid_object));
+ return base::SysCFStringRefToUTF8(device_id);
+}
+
+} // namespace device_util
+} // namespace ios
diff --git a/src/base/ios/device_util_unittest.mm b/src/base/ios/device_util_unittest.mm
new file mode 100644
index 0000000..12bfe09
--- /dev/null
+++ b/src/base/ios/device_util_unittest.mm
@@ -0,0 +1,105 @@
+// Copyright (c) 2012 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.
+
+#import <UIKit/UIKit.h>
+
+#include "base/ios/device_util.h"
+#include "base/ios/ios_util.h"
+#include "base/sys_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+namespace {
+// The behavior of most of these utility functions depends on what they are run
+// on, so there is not much to unittest them. The APIs are run to make sure they
+// don't choke. Additional checks are added for particular APIs when needed.
+
+typedef PlatformTest DeviceUtilTest;
+
+void CleanNSUserDefaultsForDeviceId() {
+ NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+ [defaults removeObjectForKey:@"ChromeClientID"];
+ [defaults removeObjectForKey:@"ChromiumClientID"];
+ [defaults synchronize];
+}
+
+TEST_F(DeviceUtilTest, GetPlatform) {
+ GTEST_ASSERT_GT(ios::device_util::GetPlatform().length(), 0U);
+}
+
+TEST_F(DeviceUtilTest, IsRunningOnHighRamDevice) {
+ ios::device_util::IsRunningOnHighRamDevice();
+}
+
+TEST_F(DeviceUtilTest, IsSingleCoreDevice) {
+ ios::device_util::IsSingleCoreDevice();
+}
+
+TEST_F(DeviceUtilTest, GetMacAddress) {
+ GTEST_ASSERT_GT(ios::device_util::GetMacAddress("en0").length(), 0U);
+}
+
+TEST_F(DeviceUtilTest, GetRandomId) {
+ GTEST_ASSERT_GT(ios::device_util::GetRandomId().length(), 0U);
+}
+
+TEST_F(DeviceUtilTest, GetDeviceIdentifier) {
+ CleanNSUserDefaultsForDeviceId();
+
+ std::string default_id = ios::device_util::GetDeviceIdentifier(NULL);
+ std::string other_id = ios::device_util::GetDeviceIdentifier("ForTest");
+ EXPECT_NE(default_id, other_id);
+
+ CleanNSUserDefaultsForDeviceId();
+
+ std::string new_default_id = ios::device_util::GetDeviceIdentifier(NULL);
+ if (base::ios::IsRunningOnIOS6OrLater() &&
+ ![[[[UIDevice currentDevice] identifierForVendor] UUIDString]
+ isEqualToString:@"00000000-0000-0000-0000-000000000000"]) {
+ EXPECT_EQ(default_id, new_default_id);
+ } else {
+ EXPECT_NE(default_id, new_default_id);
+ }
+
+ CleanNSUserDefaultsForDeviceId();
+}
+
+TEST_F(DeviceUtilTest, CheckMigration) {
+ CleanNSUserDefaultsForDeviceId();
+
+ NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+ [defaults setObject:@"10000000-0000-0000-0000-000000000000"
+ forKey:@"ChromeClientID"];
+ [defaults synchronize];
+ std::string expected_id = ios::device_util::GetDeviceIdentifier(NULL);
+ [defaults removeObjectForKey:@"ChromeClientID"];
+ [defaults setObject:@"10000000-0000-0000-0000-000000000000"
+ forKey:@"ChromiumClientID"];
+ [defaults synchronize];
+ std::string new_id = ios::device_util::GetDeviceIdentifier(NULL);
+ EXPECT_EQ(expected_id, new_id);
+
+ CleanNSUserDefaultsForDeviceId();
+}
+
+TEST_F(DeviceUtilTest, CheckMigrationFromZero) {
+ CleanNSUserDefaultsForDeviceId();
+
+ NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+ [defaults setObject:@"00000000-0000-0000-0000-000000000000"
+ forKey:@"ChromeClientID"];
+ [defaults synchronize];
+ std::string zero_id = ios::device_util::GetDeviceIdentifier(NULL);
+ [defaults removeObjectForKey:@"ChromeClientID"];
+ [defaults setObject:@"00000000-0000-0000-0000-000000000000"
+ forKey:@"ChromiumClientID"];
+ [defaults synchronize];
+ std::string new_id = ios::device_util::GetDeviceIdentifier(NULL);
+ EXPECT_NE(zero_id, new_id);
+
+ CleanNSUserDefaultsForDeviceId();
+}
+
+} // namespace
diff --git a/src/base/ios/ios_util.h b/src/base/ios/ios_util.h
new file mode 100644
index 0000000..e8f9806
--- /dev/null
+++ b/src/base/ios/ios_util.h
@@ -0,0 +1,26 @@
+// Copyright 2012 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_IOS_IOS_UTIL_H_
+#define BASE_IOS_IOS_UTIL_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+namespace ios {
+
+// Returns whether the operation system is iOS 5 or later.
+BASE_EXPORT bool IsRunningOnIOS5OrLater();
+
+// Returns whether the operation system is iOS 6 or later.
+BASE_EXPORT bool IsRunningOnIOS6OrLater();
+
+// Returns whether the operation system is at the given version or later.
+BASE_EXPORT bool IsRunningOnOrLater(int32 major, int32 minor, int32 bug_fix);
+
+} // namespace ios
+} // namespace base
+
+#endif // BASE_IOS_IOS_UTIL_H_
diff --git a/src/base/ios/ios_util.mm b/src/base/ios/ios_util.mm
new file mode 100644
index 0000000..37f386d
--- /dev/null
+++ b/src/base/ios/ios_util.mm
@@ -0,0 +1,42 @@
+// Copyright 2012 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/ios/ios_util.h"
+
+#include "base/sys_info.h"
+
+namespace {
+// Return a 3 elements array containing the major, minor and bug fix version of
+// the OS.
+const int32* OSVersionAsArray() {
+ int32* digits = new int32[3];
+ base::SysInfo::OperatingSystemVersionNumbers(
+ &digits[0], &digits[1], &digits[2]);
+ return digits;
+}
+} // namespace
+
+namespace base {
+namespace ios {
+
+bool IsRunningOnIOS5OrLater() {
+ return IsRunningOnOrLater(5, 0, 0);
+}
+
+bool IsRunningOnIOS6OrLater() {
+ return IsRunningOnOrLater(6, 0, 0);
+}
+
+bool IsRunningOnOrLater(int32 major, int32 minor, int32 bug_fix) {
+ static const int32* current_version = OSVersionAsArray();
+ int32 version[] = { major, minor, bug_fix };
+ for (size_t i = 0; i < arraysize(version); i++) {
+ if (current_version[i] != version[i])
+ return current_version[i] > version[i];
+ }
+ return true;
+}
+
+} // namespace ios
+} // namespace base
diff --git a/src/base/ios/scoped_critical_action.h b/src/base/ios/scoped_critical_action.h
new file mode 100644
index 0000000..660a83a
--- /dev/null
+++ b/src/base/ios/scoped_critical_action.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2012 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_IOS_SCOPED_CRITICAL_ACTION_H_
+#define BASE_IOS_SCOPED_CRITICAL_ACTION_H_
+
+#include "base/synchronization/lock.h"
+
+namespace base {
+namespace ios {
+
+// This class attempts to allow the application to continue to run for a period
+// of time after it transitions to the background. The construction of an
+// instance of this class marks the beginning of a task that needs background
+// running time when the application is moved to the background and the
+// destruction marks the end of such a task.
+//
+// Note there is no guarantee that the task will continue to finish when the
+// application is moved to the background.
+//
+// This class should be used at times where leaving a task unfinished might be
+// detrimental to user experience. For example, it should be used to ensure that
+// the application has enough time to save important data or at least attempt to
+// save such data.
+class ScopedCriticalAction {
+ public:
+ ScopedCriticalAction();
+ ~ScopedCriticalAction();
+
+ private:
+ // Informs the OS that the background task has completed.
+ void EndBackgroundTask();
+
+ // |UIBackgroundTaskIdentifier| returned by
+ // |beginBackgroundTaskWithExpirationHandler:| when marking the beginning of
+ // a long-running background task. It is defined as an |unsigned int| instead
+ // of a |UIBackgroundTaskIdentifier| so this class can be used in .cc files.
+ unsigned int background_task_id_;
+ Lock background_task_id_lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedCriticalAction);
+};
+
+} // namespace ios
+} // namespace base
+
+#endif // BASE_IOS_SCOPED_CRITICAL_ACTION_H_
diff --git a/src/base/ios/scoped_critical_action.mm b/src/base/ios/scoped_critical_action.mm
new file mode 100644
index 0000000..734c0a2
--- /dev/null
+++ b/src/base/ios/scoped_critical_action.mm
@@ -0,0 +1,54 @@
+// Copyright (c) 2012 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/ios/scoped_critical_action.h"
+
+#import <UIKit/UIKit.h>
+
+#include "base/logging.h"
+#include "base/synchronization/lock.h"
+
+namespace base {
+namespace ios {
+
+// This implementation calls |beginBackgroundTaskWithExpirationHandler:| when
+// instantiated and |endBackgroundTask:| when destroyed, creating a scope whose
+// execution will continue (temporarily) even after the app is backgrounded.
+ScopedCriticalAction::ScopedCriticalAction() {
+ background_task_id_ = [[UIApplication sharedApplication]
+ beginBackgroundTaskWithExpirationHandler:^{
+ DLOG(WARNING) << "Background task with id " << background_task_id_
+ << " expired.";
+ // Note if |endBackgroundTask:| is not called for each task before time
+ // expires, the system kills the application.
+ EndBackgroundTask();
+ }];
+ if (background_task_id_ == UIBackgroundTaskInvalid) {
+ DLOG(WARNING) <<
+ "beginBackgroundTaskWithExpirationHandler: returned an invalid ID";
+ } else {
+ VLOG(3) << "Beginning background task with id " << background_task_id_;
+ }
+}
+
+ScopedCriticalAction::~ScopedCriticalAction() {
+ EndBackgroundTask();
+}
+
+void ScopedCriticalAction::EndBackgroundTask() {
+ UIBackgroundTaskIdentifier task_id;
+ {
+ AutoLock lock_scope(background_task_id_lock_);
+ if (background_task_id_ == UIBackgroundTaskInvalid)
+ return;
+ task_id = background_task_id_;
+ background_task_id_ = UIBackgroundTaskInvalid;
+ }
+
+ VLOG(3) << "Ending background task with id " << task_id;
+ [[UIApplication sharedApplication] endBackgroundTask:task_id];
+}
+
+} // namespace ios
+} // namespace base
diff --git a/src/base/json/json_file_value_serializer.cc b/src/base/json/json_file_value_serializer.cc
new file mode 100644
index 0000000..2fac451
--- /dev/null
+++ b/src/base/json/json_file_value_serializer.cc
@@ -0,0 +1,96 @@
+// Copyright (c) 2012 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/json/json_file_value_serializer.h"
+
+#include "base/file_util.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/logging.h"
+
+const char* JSONFileValueSerializer::kAccessDenied = "Access denied.";
+const char* JSONFileValueSerializer::kCannotReadFile = "Can't read file.";
+const char* JSONFileValueSerializer::kFileLocked = "File locked.";
+const char* JSONFileValueSerializer::kNoSuchFile = "File doesn't exist.";
+
+bool JSONFileValueSerializer::Serialize(const Value& root) {
+ return SerializeInternal(root, false);
+}
+
+bool JSONFileValueSerializer::SerializeAndOmitBinaryValues(const Value& root) {
+ return SerializeInternal(root, true);
+}
+
+bool JSONFileValueSerializer::SerializeInternal(const Value& root,
+ bool omit_binary_values) {
+ std::string json_string;
+ JSONStringValueSerializer serializer(&json_string);
+ serializer.set_pretty_print(true);
+ bool result = omit_binary_values ?
+ serializer.SerializeAndOmitBinaryValues(root) :
+ serializer.Serialize(root);
+ if (!result)
+ return false;
+
+ int data_size = static_cast<int>(json_string.size());
+ if (file_util::WriteFile(json_file_path_,
+ json_string.data(),
+ data_size) != data_size)
+ return false;
+
+ return true;
+}
+
+int JSONFileValueSerializer::ReadFileToString(std::string* json_string) {
+ DCHECK(json_string);
+ if (!file_util::ReadFileToString(json_file_path_, json_string)) {
+#if defined(OS_WIN)
+ int error = ::GetLastError();
+ if (error == ERROR_SHARING_VIOLATION || error == ERROR_LOCK_VIOLATION) {
+ return JSON_FILE_LOCKED;
+ } else if (error == ERROR_ACCESS_DENIED) {
+ return JSON_ACCESS_DENIED;
+ }
+#endif
+ if (!file_util::PathExists(json_file_path_))
+ return JSON_NO_SUCH_FILE;
+ else
+ return JSON_CANNOT_READ_FILE;
+ }
+ return JSON_NO_ERROR;
+}
+
+const char* JSONFileValueSerializer::GetErrorMessageForCode(int error_code) {
+ switch (error_code) {
+ case JSON_NO_ERROR:
+ return "";
+ case JSON_ACCESS_DENIED:
+ return kAccessDenied;
+ case JSON_CANNOT_READ_FILE:
+ return kCannotReadFile;
+ case JSON_FILE_LOCKED:
+ return kFileLocked;
+ case JSON_NO_SUCH_FILE:
+ return kNoSuchFile;
+ default:
+ NOTREACHED();
+ return "";
+ }
+}
+
+Value* JSONFileValueSerializer::Deserialize(int* error_code,
+ std::string* error_str) {
+ std::string json_string;
+ int error = ReadFileToString(&json_string);
+ if (error != JSON_NO_ERROR) {
+ if (error_code)
+ *error_code = error;
+ if (error_str)
+ *error_str = GetErrorMessageForCode(error);
+ return NULL;
+ }
+
+ JSONStringValueSerializer serializer(json_string);
+ serializer.set_allow_trailing_comma(allow_trailing_comma_);
+ return serializer.Deserialize(error_code, error_str);
+}
diff --git a/src/base/json/json_file_value_serializer.h b/src/base/json/json_file_value_serializer.h
new file mode 100644
index 0000000..633db5d
--- /dev/null
+++ b/src/base/json/json_file_value_serializer.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2012 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_JSON_JSON_FILE_VALUE_SERIALIZER_H_
+#define BASE_JSON_JSON_FILE_VALUE_SERIALIZER_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/values.h"
+
+class BASE_EXPORT JSONFileValueSerializer : public base::ValueSerializer {
+ public:
+ // json_file_patch is the path of a file that will be source of the
+ // deserialization or the destination of the serialization.
+ // When deserializing, the file should exist, but when serializing, the
+ // serializer will attempt to create the file at the specified location.
+ explicit JSONFileValueSerializer(const FilePath& json_file_path)
+ : json_file_path_(json_file_path),
+ allow_trailing_comma_(false) {}
+
+ virtual ~JSONFileValueSerializer() {}
+
+ // DO NOT USE except in unit tests to verify the file was written properly.
+ // We should never serialize directly to a file since this will block the
+ // thread. Instead, serialize to a string and write to the file you want on
+ // the file thread.
+ //
+ // Attempt to serialize the data structure represented by Value into
+ // JSON. If the return value is true, the result will have been written
+ // into the file whose name was passed into the constructor.
+ virtual bool Serialize(const Value& root) OVERRIDE;
+
+ // Equivalent to Serialize(root) except binary values are omitted from the
+ // output.
+ bool SerializeAndOmitBinaryValues(const Value& root);
+
+ // Attempt to deserialize the data structure encoded in the file passed
+ // in to the constructor into a structure of Value objects. If the return
+ // value is NULL, and if |error_code| is non-null, |error_code| will
+ // contain an integer error code (either JsonFileError or JsonParseError).
+ // If |error_message| is non-null, it will be filled in with a formatted
+ // error message including the location of the error if appropriate.
+ // The caller takes ownership of the returned value.
+ virtual Value* Deserialize(int* error_code,
+ std::string* error_message) OVERRIDE;
+
+ // This enum is designed to safely overlap with JSONReader::JsonParseError.
+ enum JsonFileError {
+ JSON_NO_ERROR = 0,
+ JSON_ACCESS_DENIED = 1000,
+ JSON_CANNOT_READ_FILE,
+ JSON_FILE_LOCKED,
+ JSON_NO_SUCH_FILE
+ };
+
+ // File-specific error messages that can be returned.
+ static const char* kAccessDenied;
+ static const char* kCannotReadFile;
+ static const char* kFileLocked;
+ static const char* kNoSuchFile;
+
+ // Convert an error code into an error message. |error_code| is assumed to
+ // be a JsonFileError.
+ static const char* GetErrorMessageForCode(int error_code);
+
+ void set_allow_trailing_comma(bool new_value) {
+ allow_trailing_comma_ = new_value;
+ }
+
+ private:
+ bool SerializeInternal(const Value& root, bool omit_binary_values);
+
+ FilePath json_file_path_;
+ bool allow_trailing_comma_;
+
+ // A wrapper for file_util::ReadFileToString which returns a non-zero
+ // JsonFileError if there were file errors.
+ int ReadFileToString(std::string* json_string);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSONFileValueSerializer);
+};
+
+#endif // BASE_JSON_JSON_FILE_VALUE_SERIALIZER_H_
+
diff --git a/src/base/json/json_parser.cc b/src/base/json/json_parser.cc
new file mode 100644
index 0000000..4c03a98
--- /dev/null
+++ b/src/base/json/json_parser.cc
@@ -0,0 +1,962 @@
+// Copyright (c) 2012 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/json/json_parser.h"
+
+#include "base/float_util.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_number_conversions.h"
+#include "base/string_piece.h"
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "base/third_party/icu/icu_utf.h"
+#include "base/utf_string_conversion_utils.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+
+namespace base {
+namespace internal {
+
+namespace {
+
+const int kStackMaxDepth = 100;
+
+const int32 kExtendedASCIIStart = 0x80;
+
+// This and the class below are used to own the JSON input string for when
+// string tokens are stored as StringPiece instead of std::string. This
+// optimization avoids about 2/3rds of string memory copies. The constructor
+// takes ownership of the input string. The real root value is Swap()ed into
+// the new instance.
+class DictionaryHiddenRootValue : public base::DictionaryValue {
+ public:
+ DictionaryHiddenRootValue(std::string* json, Value* root) : json_(json) {
+ DCHECK(root->IsType(Value::TYPE_DICTIONARY));
+ DictionaryValue::Swap(static_cast<DictionaryValue*>(root));
+ }
+
+ virtual void Swap(DictionaryValue* other) OVERRIDE {
+ DVLOG(1) << "Swap()ing a DictionaryValue inefficiently.";
+
+ // First deep copy to convert JSONStringValue to std::string and swap that
+ // copy with |other|, which contains the new contents of |this|.
+ scoped_ptr<base::DictionaryValue> copy(DeepCopy());
+ copy->Swap(other);
+
+ // Then erase the contents of the current dictionary and swap in the
+ // new contents, originally from |other|.
+ Clear();
+ json_.reset();
+ DictionaryValue::Swap(copy.get());
+ }
+
+ // Not overriding DictionaryValue::Remove because it just calls through to
+ // the method below.
+
+ virtual bool RemoveWithoutPathExpansion(const std::string& key,
+ Value** out) OVERRIDE {
+ // If the caller won't take ownership of the removed value, just call up.
+ if (!out)
+ return DictionaryValue::RemoveWithoutPathExpansion(key, out);
+
+ DVLOG(1) << "Remove()ing from a DictionaryValue inefficiently.";
+
+ // Otherwise, remove the value while its still "owned" by this and copy it
+ // to convert any JSONStringValues to std::string.
+ Value* out_owned = NULL;
+ if (!DictionaryValue::RemoveWithoutPathExpansion(key, &out_owned))
+ return false;
+
+ *out = out_owned->DeepCopy();
+ delete out_owned;
+
+ return true;
+ }
+
+ private:
+ scoped_ptr<std::string> json_;
+
+ DISALLOW_COPY_AND_ASSIGN(DictionaryHiddenRootValue);
+};
+
+class ListHiddenRootValue : public base::ListValue {
+ public:
+ ListHiddenRootValue(std::string* json, Value* root) : json_(json) {
+ DCHECK(root->IsType(Value::TYPE_LIST));
+ ListValue::Swap(static_cast<ListValue*>(root));
+ }
+
+ virtual void Swap(ListValue* other) OVERRIDE {
+ DVLOG(1) << "Swap()ing a ListValue inefficiently.";
+
+ // First deep copy to convert JSONStringValue to std::string and swap that
+ // copy with |other|, which contains the new contents of |this|.
+ scoped_ptr<base::ListValue> copy(DeepCopy());
+ copy->Swap(other);
+
+ // Then erase the contents of the current list and swap in the new contents,
+ // originally from |other|.
+ Clear();
+ json_.reset();
+ ListValue::Swap(copy.get());
+ }
+
+ virtual bool Remove(size_t index, Value** out) OVERRIDE {
+ // If the caller won't take ownership of the removed value, just call up.
+ if (!out)
+ return ListValue::Remove(index, out);
+
+ DVLOG(1) << "Remove()ing from a ListValue inefficiently.";
+
+ // Otherwise, remove the value while its still "owned" by this and copy it
+ // to convert any JSONStringValues to std::string.
+ Value* out_owned = NULL;
+ if (!ListValue::Remove(index, &out_owned))
+ return false;
+
+ *out = out_owned->DeepCopy();
+ delete out_owned;
+
+ return true;
+ }
+
+ private:
+ scoped_ptr<std::string> json_;
+
+ DISALLOW_COPY_AND_ASSIGN(ListHiddenRootValue);
+};
+
+// A variant on StringValue that uses StringPiece instead of copying the string
+// into the Value. This can only be stored in a child of hidden root (above),
+// otherwise the referenced string will not be guaranteed to outlive it.
+class JSONStringValue : public base::Value {
+ public:
+ explicit JSONStringValue(const base::StringPiece& piece)
+ : Value(TYPE_STRING),
+ string_piece_(piece) {
+ }
+
+ // Overridden from base::Value:
+ virtual bool GetAsString(std::string* out_value) const OVERRIDE {
+ string_piece_.CopyToString(out_value);
+ return true;
+ }
+ virtual bool GetAsString(string16* out_value) const OVERRIDE {
+ *out_value = UTF8ToUTF16(string_piece_);
+ return true;
+ }
+ virtual Value* DeepCopy() const OVERRIDE {
+ return new StringValue(string_piece_.as_string());
+ }
+ virtual bool Equals(const Value* other) const OVERRIDE {
+ std::string other_string;
+ return other->IsType(TYPE_STRING) && other->GetAsString(&other_string) &&
+ StringPiece(other_string) == string_piece_;
+ }
+
+ private:
+ // The location in the original input stream.
+ base::StringPiece string_piece_;
+
+ DISALLOW_COPY_AND_ASSIGN(JSONStringValue);
+};
+
+// Simple class that checks for maximum recursion/"stack overflow."
+class StackMarker {
+ public:
+ explicit StackMarker(int* depth) : depth_(depth) {
+ ++(*depth_);
+ DCHECK_LE(*depth_, kStackMaxDepth);
+ }
+ ~StackMarker() {
+ --(*depth_);
+ }
+
+ bool IsTooDeep() const {
+ return *depth_ >= kStackMaxDepth;
+ }
+
+ private:
+ int* const depth_;
+
+ DISALLOW_COPY_AND_ASSIGN(StackMarker);
+};
+
+} // namespace
+
+JSONParser::JSONParser(int options)
+ : options_(options),
+ start_pos_(NULL),
+ pos_(NULL),
+ end_pos_(NULL),
+ index_(0),
+ stack_depth_(0),
+ line_number_(0),
+ index_last_line_(0),
+ error_code_(JSONReader::JSON_NO_ERROR),
+ error_line_(0),
+ error_column_(0) {
+}
+
+JSONParser::~JSONParser() {
+}
+
+Value* JSONParser::Parse(const StringPiece& input) {
+ scoped_ptr<std::string> input_copy;
+ // If the children of a JSON root can be detached, then hidden roots cannot
+ // be used, so do not bother copying the input because StringPiece will not
+ // be used anywhere.
+ if (!(options_ & JSON_DETACHABLE_CHILDREN)) {
+ input_copy.reset(new std::string(input.as_string()));
+ start_pos_ = input_copy->data();
+ } else {
+ start_pos_ = input.data();
+ }
+ pos_ = start_pos_;
+ end_pos_ = start_pos_ + input.length();
+ index_ = 0;
+ line_number_ = 1;
+ index_last_line_ = 0;
+
+ error_code_ = JSONReader::JSON_NO_ERROR;
+ error_line_ = 0;
+ error_column_ = 0;
+
+ // When the input JSON string starts with a UTF-8 Byte-Order-Mark
+ // <0xEF 0xBB 0xBF>, advance the start position to avoid the
+ // ParseNextToken function mis-treating a Unicode BOM as an invalid
+ // character and returning NULL.
+ if (CanConsume(3) && static_cast<uint8>(*pos_) == 0xEF &&
+ static_cast<uint8>(*(pos_ + 1)) == 0xBB &&
+ static_cast<uint8>(*(pos_ + 2)) == 0xBF) {
+ NextNChars(3);
+ }
+
+ // Parse the first and any nested tokens.
+ scoped_ptr<Value> root(ParseNextToken());
+ if (!root.get())
+ return NULL;
+
+ // Make sure the input stream is at an end.
+ if (GetNextToken() != T_END_OF_INPUT) {
+ if (!CanConsume(1) || (NextChar() && GetNextToken() != T_END_OF_INPUT)) {
+ ReportError(JSONReader::JSON_UNEXPECTED_DATA_AFTER_ROOT, 1);
+ return NULL;
+ }
+ }
+
+ // Dictionaries and lists can contain JSONStringValues, so wrap them in a
+ // hidden root.
+ if (!(options_ & JSON_DETACHABLE_CHILDREN)) {
+ if (root->IsType(Value::TYPE_DICTIONARY)) {
+ return new DictionaryHiddenRootValue(input_copy.release(), root.get());
+ } else if (root->IsType(Value::TYPE_LIST)) {
+ return new ListHiddenRootValue(input_copy.release(), root.get());
+ } else if (root->IsType(Value::TYPE_STRING)) {
+ // A string type could be a JSONStringValue, but because there's no
+ // corresponding HiddenRootValue, the memory will be lost. Deep copy to
+ // preserve it.
+ return root->DeepCopy();
+ }
+ }
+
+ // All other values can be returned directly.
+ return root.release();
+}
+
+JSONReader::JsonParseError JSONParser::error_code() const {
+ return error_code_;
+}
+
+std::string JSONParser::GetErrorMessage() const {
+ return FormatErrorMessage(error_line_, error_column_,
+ JSONReader::ErrorCodeToString(error_code_));
+}
+
+// StringBuilder ///////////////////////////////////////////////////////////////
+
+JSONParser::StringBuilder::StringBuilder()
+ : pos_(NULL),
+ length_(0),
+ string_(NULL) {
+}
+
+JSONParser::StringBuilder::StringBuilder(const char* pos)
+ : pos_(pos),
+ length_(0),
+ string_(NULL) {
+}
+
+void JSONParser::StringBuilder::Swap(StringBuilder* other) {
+ std::swap(other->string_, string_);
+ std::swap(other->pos_, pos_);
+ std::swap(other->length_, length_);
+}
+
+JSONParser::StringBuilder::~StringBuilder() {
+ delete string_;
+}
+
+void JSONParser::StringBuilder::Append(const char& c) {
+ DCHECK_GE(c, 0);
+ DCHECK_LT(c, 128);
+
+ if (string_)
+ string_->push_back(c);
+ else
+ ++length_;
+}
+
+void JSONParser::StringBuilder::AppendString(const std::string& str) {
+ DCHECK(string_);
+ string_->append(str);
+}
+
+void JSONParser::StringBuilder::Convert() {
+ if (string_)
+ return;
+ string_ = new std::string(pos_, length_);
+}
+
+bool JSONParser::StringBuilder::CanBeStringPiece() const {
+ return !string_;
+}
+
+StringPiece JSONParser::StringBuilder::AsStringPiece() {
+ if (string_)
+ return StringPiece();
+ return StringPiece(pos_, length_);
+}
+
+const std::string& JSONParser::StringBuilder::AsString() {
+ if (!string_)
+ Convert();
+ return *string_;
+}
+
+// JSONParser private //////////////////////////////////////////////////////////
+
+inline bool JSONParser::CanConsume(int length) {
+ return pos_ + length <= end_pos_;
+}
+
+const char* JSONParser::NextChar() {
+ DCHECK(CanConsume(1));
+ ++index_;
+ ++pos_;
+ return pos_;
+}
+
+void JSONParser::NextNChars(int n) {
+ DCHECK(CanConsume(n));
+ index_ += n;
+ pos_ += n;
+}
+
+JSONParser::Token JSONParser::GetNextToken() {
+ EatWhitespaceAndComments();
+ if (!CanConsume(1))
+ return T_END_OF_INPUT;
+
+ switch (*pos_) {
+ case '{':
+ return T_OBJECT_BEGIN;
+ case '}':
+ return T_OBJECT_END;
+ case '[':
+ return T_ARRAY_BEGIN;
+ case ']':
+ return T_ARRAY_END;
+ case '"':
+ return T_STRING;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '-':
+ return T_NUMBER;
+ case 't':
+ return T_BOOL_TRUE;
+ case 'f':
+ return T_BOOL_FALSE;
+ case 'n':
+ return T_NULL;
+ case ',':
+ return T_LIST_SEPARATOR;
+ case ':':
+ return T_OBJECT_PAIR_SEPARATOR;
+ default:
+ return T_INVALID_TOKEN;
+ }
+}
+
+void JSONParser::EatWhitespaceAndComments() {
+ while (pos_ < end_pos_) {
+ switch (*pos_) {
+ case '\r':
+ case '\n':
+ index_last_line_ = index_;
+ ++line_number_;
+ // Fall through.
+ case ' ':
+ case '\t':
+ NextChar();
+ break;
+ case '/':
+ if (!EatComment())
+ return;
+ break;
+ default:
+ return;
+ }
+ }
+}
+
+bool JSONParser::EatComment() {
+ if (*pos_ != '/' || !CanConsume(1))
+ return false;
+
+ char next_char = *NextChar();
+ if (next_char == '/') {
+ // Single line comment, read to newline.
+ while (CanConsume(1)) {
+ char next_char = *NextChar();
+ if (next_char == '\n' || next_char == '\r')
+ return true;
+ }
+ } else if (next_char == '*') {
+ // Block comment, read until end marker.
+ while (CanConsume(2)) {
+ if (*NextChar() == '*' && *NextChar() == '/') {
+ // EatWhitespaceAndComments will inspect pos_, which will still be on
+ // the last / of the comment, so advance once more (which may also be
+ // end of input).
+ NextChar();
+ return true;
+ }
+ }
+
+ // If the comment is unterminated, GetNextToken will report T_END_OF_INPUT.
+ }
+
+ return false;
+}
+
+Value* JSONParser::ParseNextToken() {
+ return ParseToken(GetNextToken());
+}
+
+Value* JSONParser::ParseToken(Token token) {
+ switch (token) {
+ case T_OBJECT_BEGIN:
+ return ConsumeDictionary();
+ case T_ARRAY_BEGIN:
+ return ConsumeList();
+ case T_STRING:
+ return ConsumeString();
+ case T_NUMBER:
+ return ConsumeNumber();
+ case T_BOOL_TRUE:
+ case T_BOOL_FALSE:
+ case T_NULL:
+ return ConsumeLiteral();
+ default:
+ ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
+ return NULL;
+ }
+}
+
+Value* JSONParser::ConsumeDictionary() {
+ if (*pos_ != '{') {
+ ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
+ return NULL;
+ }
+
+ StackMarker depth_check(&stack_depth_);
+ if (depth_check.IsTooDeep()) {
+ ReportError(JSONReader::JSON_TOO_MUCH_NESTING, 1);
+ return NULL;
+ }
+
+ scoped_ptr<DictionaryValue> dict(new DictionaryValue);
+
+ NextChar();
+ Token token = GetNextToken();
+ while (token != T_OBJECT_END) {
+ if (token != T_STRING) {
+ ReportError(JSONReader::JSON_UNQUOTED_DICTIONARY_KEY, 1);
+ return NULL;
+ }
+
+ // First consume the key.
+ StringBuilder key;
+ if (!ConsumeStringRaw(&key)) {
+ return NULL;
+ }
+
+ // Read the separator.
+ NextChar();
+ token = GetNextToken();
+ if (token != T_OBJECT_PAIR_SEPARATOR) {
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
+ return NULL;
+ }
+
+ // The next token is the value. Ownership transfers to |dict|.
+ NextChar();
+ Value* value = ParseNextToken();
+ if (!value) {
+ // ReportError from deeper level.
+ return NULL;
+ }
+
+ dict->SetWithoutPathExpansion(key.AsString(), value);
+
+ NextChar();
+ token = GetNextToken();
+ if (token == T_LIST_SEPARATOR) {
+ NextChar();
+ token = GetNextToken();
+ if (token == T_OBJECT_END && !(options_ & JSON_ALLOW_TRAILING_COMMAS)) {
+ ReportError(JSONReader::JSON_TRAILING_COMMA, 1);
+ return NULL;
+ }
+ } else if (token != T_OBJECT_END) {
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 0);
+ return NULL;
+ }
+ }
+
+ return dict.release();
+}
+
+Value* JSONParser::ConsumeList() {
+ if (*pos_ != '[') {
+ ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
+ return NULL;
+ }
+
+ StackMarker depth_check(&stack_depth_);
+ if (depth_check.IsTooDeep()) {
+ ReportError(JSONReader::JSON_TOO_MUCH_NESTING, 1);
+ return NULL;
+ }
+
+ scoped_ptr<ListValue> list(new ListValue);
+
+ NextChar();
+ Token token = GetNextToken();
+ while (token != T_ARRAY_END) {
+ Value* item = ParseToken(token);
+ if (!item) {
+ // ReportError from deeper level.
+ return NULL;
+ }
+
+ list->Append(item);
+
+ NextChar();
+ token = GetNextToken();
+ if (token == T_LIST_SEPARATOR) {
+ NextChar();
+ token = GetNextToken();
+ if (token == T_ARRAY_END && !(options_ & JSON_ALLOW_TRAILING_COMMAS)) {
+ ReportError(JSONReader::JSON_TRAILING_COMMA, 1);
+ return NULL;
+ }
+ } else if (token != T_ARRAY_END) {
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
+ return NULL;
+ }
+ }
+
+ return list.release();
+}
+
+Value* JSONParser::ConsumeString() {
+ StringBuilder string;
+ if (!ConsumeStringRaw(&string))
+ return NULL;
+
+ // Create the Value representation, using a hidden root, if configured
+ // to do so, and if the string can be represented by StringPiece.
+ if (string.CanBeStringPiece() && !(options_ & JSON_DETACHABLE_CHILDREN)) {
+ return new JSONStringValue(string.AsStringPiece());
+ } else {
+ if (string.CanBeStringPiece())
+ string.Convert();
+ return new StringValue(string.AsString());
+ }
+}
+
+bool JSONParser::ConsumeStringRaw(StringBuilder* out) {
+ if (*pos_ != '"') {
+ ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
+ return false;
+ }
+
+ // StringBuilder will internally build a StringPiece unless a UTF-16
+ // conversion occurs, at which point it will perform a copy into a
+ // std::string.
+ StringBuilder string(NextChar());
+
+ int length = end_pos_ - start_pos_;
+ int32 next_char = 0;
+
+ while (CanConsume(1)) {
+ pos_ = start_pos_ + index_; // CBU8_NEXT is postcrement.
+ CBU8_NEXT(start_pos_, index_, length, next_char);
+ if (next_char < 0 || !IsValidCharacter(next_char)) {
+ ReportError(JSONReader::JSON_UNSUPPORTED_ENCODING, 1);
+ return false;
+ }
+
+ // If this character is an escape sequence...
+ if (next_char == '\\') {
+ // The input string will be adjusted (either by combining the two
+ // characters of an encoded escape sequence, or with a UTF conversion),
+ // so using StringPiece isn't possible -- force a conversion.
+ string.Convert();
+
+ if (!CanConsume(1)) {
+ ReportError(JSONReader::JSON_INVALID_ESCAPE, 0);
+ return false;
+ }
+
+ switch (*NextChar()) {
+ // Allowed esape sequences:
+ case 'x': { // UTF-8 sequence.
+ // UTF-8 \x escape sequences are not allowed in the spec, but they
+ // are supported here for backwards-compatiblity with the old parser.
+ if (!CanConsume(2)) {
+ ReportError(JSONReader::JSON_INVALID_ESCAPE, 1);
+ return false;
+ }
+
+ int hex_digit = 0;
+ if (!HexStringToInt(StringPiece(NextChar(), 2), &hex_digit)) {
+ ReportError(JSONReader::JSON_INVALID_ESCAPE, -1);
+ return false;
+ }
+ NextChar();
+
+ if (hex_digit < kExtendedASCIIStart)
+ string.Append(hex_digit);
+ else
+ DecodeUTF8(hex_digit, &string);
+ break;
+ }
+ case 'u': { // UTF-16 sequence.
+ // UTF units are of the form \uXXXX.
+ if (!CanConsume(5)) { // 5 being 'u' and four HEX digits.
+ ReportError(JSONReader::JSON_INVALID_ESCAPE, 0);
+ return false;
+ }
+
+ // Skip the 'u'.
+ NextChar();
+
+ std::string utf8_units;
+ if (!DecodeUTF16(&utf8_units)) {
+ ReportError(JSONReader::JSON_INVALID_ESCAPE, -1);
+ return false;
+ }
+
+ string.AppendString(utf8_units);
+ break;
+ }
+ case '"':
+ string.Append('"');
+ break;
+ case '\\':
+ string.Append('\\');
+ break;
+ case '/':
+ string.Append('/');
+ break;
+ case 'b':
+ string.Append('\b');
+ break;
+ case 'f':
+ string.Append('\f');
+ break;
+ case 'n':
+ string.Append('\n');
+ break;
+ case 'r':
+ string.Append('\r');
+ break;
+ case 't':
+ string.Append('\t');
+ break;
+ case 'v': // Not listed as valid escape sequence in the RFC.
+ string.Append('\v');
+ break;
+ // All other escape squences are illegal.
+ default:
+ ReportError(JSONReader::JSON_INVALID_ESCAPE, 0);
+ return false;
+ }
+ } else if (next_char == '"') {
+ --index_; // Rewind by one because of CBU8_NEXT.
+ out->Swap(&string);
+ return true;
+ } else {
+ if (next_char < kExtendedASCIIStart)
+ string.Append(next_char);
+ else
+ DecodeUTF8(next_char, &string);
+ }
+ }
+
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 0);
+ return false;
+}
+
+// Entry is at the first X in \uXXXX.
+bool JSONParser::DecodeUTF16(std::string* dest_string) {
+ if (!CanConsume(4))
+ return false;
+
+ // This is a 32-bit field because the shift operations in the
+ // conversion process below cause MSVC to error about "data loss."
+ // This only stores UTF-16 code units, though.
+ // Consume the UTF-16 code unit, which may be a high surrogate.
+ int code_unit16_high = 0;
+ if (!HexStringToInt(StringPiece(pos_, 4), &code_unit16_high))
+ return false;
+
+ // Only add 3, not 4, because at the end of this iteration, the parser has
+ // finished working with the last digit of the UTF sequence, meaning that
+ // the next iteration will advance to the next byte.
+ NextNChars(3);
+
+ // Used to convert the UTF-16 code units to a code point and then to a UTF-8
+ // code unit sequence.
+ char code_unit8[8] = { 0 };
+ size_t offset = 0;
+
+ // If this is a high surrogate, consume the next code unit to get the
+ // low surrogate.
+ if (CBU16_IS_SURROGATE(code_unit16_high)) {
+ // Make sure this is the high surrogate. If not, it's an encoding
+ // error.
+ if (!CBU16_IS_SURROGATE_LEAD(code_unit16_high))
+ return false;
+
+ // Make sure that the token has more characters to consume the
+ // lower surrogate.
+ if (!CanConsume(6)) // 6 being '\' 'u' and four HEX digits.
+ return false;
+ if (*NextChar() != '\\' || *NextChar() != 'u')
+ return false;
+
+ NextChar(); // Read past 'u'.
+ int code_unit16_low = 0;
+ if (!HexStringToInt(StringPiece(pos_, 4), &code_unit16_low))
+ return false;
+
+ NextNChars(3);
+
+ if (!CBU16_IS_TRAIL(code_unit16_low)) {
+ return false;
+ }
+
+ uint32 code_point = CBU16_GET_SUPPLEMENTARY(code_unit16_high,
+ code_unit16_low);
+ offset = 0;
+ CBU8_APPEND_UNSAFE(code_unit8, offset, code_point);
+ } else {
+ // Not a surrogate.
+ DCHECK(CBU16_IS_SINGLE(code_unit16_high));
+ CBU8_APPEND_UNSAFE(code_unit8, offset, code_unit16_high);
+ }
+
+ dest_string->append(code_unit8);
+ return true;
+}
+
+void JSONParser::DecodeUTF8(const int32& point, StringBuilder* dest) {
+ // Anything outside of the basic ASCII plane will need to be decoded from
+ // int32 to a multi-byte sequence.
+ if (point < kExtendedASCIIStart) {
+ dest->Append(point);
+ } else {
+ char utf8_units[4] = { 0 };
+ int offset = 0;
+ CBU8_APPEND_UNSAFE(utf8_units, offset, point);
+ dest->Convert();
+ // CBU8_APPEND_UNSAFE can overwrite up to 4 bytes, so utf8_units may not be
+ // zero terminated at this point. |offset| contains the correct length.
+ dest->AppendString(std::string(utf8_units, offset));
+ }
+}
+
+Value* JSONParser::ConsumeNumber() {
+ const char* num_start = pos_;
+ const int start_index = index_;
+ int end_index = start_index;
+
+ if (*pos_ == '-')
+ NextChar();
+
+ if (!ReadInt(false)) {
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
+ return NULL;
+ }
+ end_index = index_;
+
+ // The optional fraction part.
+ if (*pos_ == '.') {
+ if (!CanConsume(1)) {
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
+ return NULL;
+ }
+ NextChar();
+ if (!ReadInt(true)) {
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
+ return NULL;
+ }
+ end_index = index_;
+ }
+
+ // Optional exponent part.
+ if (*pos_ == 'e' || *pos_ == 'E') {
+ NextChar();
+ if (*pos_ == '-' || *pos_ == '+')
+ NextChar();
+ if (!ReadInt(true)) {
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
+ return NULL;
+ }
+ end_index = index_;
+ }
+
+ // ReadInt is greedy because numbers have no easily detectable sentinel,
+ // so save off where the parser should be on exit (see Consume invariant at
+ // the top of the header), then make sure the next token is one which is
+ // valid.
+ const char* exit_pos = pos_ - 1;
+ int exit_index = index_ - 1;
+
+ switch (GetNextToken()) {
+ case T_OBJECT_END:
+ case T_ARRAY_END:
+ case T_LIST_SEPARATOR:
+ case T_END_OF_INPUT:
+ break;
+ default:
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
+ return NULL;
+ }
+
+ pos_ = exit_pos;
+ index_ = exit_index;
+
+ StringPiece num_string(num_start, end_index - start_index);
+
+ int num_int;
+ if (StringToInt(num_string, &num_int))
+ return new FundamentalValue(num_int);
+
+ double num_double;
+ if (base::StringToDouble(num_string.as_string(), &num_double) &&
+ IsFinite(num_double)) {
+ return new FundamentalValue(num_double);
+ }
+
+ return NULL;
+}
+
+bool JSONParser::ReadInt(bool allow_leading_zeros) {
+ char first = *pos_;
+ int len = 0;
+
+ char c = first;
+ while (CanConsume(1) && IsAsciiDigit(c)) {
+ c = *NextChar();
+ ++len;
+ }
+
+ if (len == 0)
+ return false;
+
+ if (!allow_leading_zeros && len > 1 && first == '0')
+ return false;
+
+ return true;
+}
+
+Value* JSONParser::ConsumeLiteral() {
+ switch (*pos_) {
+ case 't': {
+ const char* kTrueLiteral = "true";
+ const int kTrueLen = static_cast<int>(strlen(kTrueLiteral));
+ if (!CanConsume(kTrueLen - 1) ||
+ !StringsAreEqual(pos_, kTrueLiteral, kTrueLen)) {
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
+ return NULL;
+ }
+ NextNChars(kTrueLen - 1);
+ return new FundamentalValue(true);
+ }
+ case 'f': {
+ const char* kFalseLiteral = "false";
+ const int kFalseLen = static_cast<int>(strlen(kFalseLiteral));
+ if (!CanConsume(kFalseLen - 1) ||
+ !StringsAreEqual(pos_, kFalseLiteral, kFalseLen)) {
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
+ return NULL;
+ }
+ NextNChars(kFalseLen - 1);
+ return new FundamentalValue(false);
+ }
+ case 'n': {
+ const char* kNullLiteral = "null";
+ const int kNullLen = static_cast<int>(strlen(kNullLiteral));
+ if (!CanConsume(kNullLen - 1) ||
+ !StringsAreEqual(pos_, kNullLiteral, kNullLen)) {
+ ReportError(JSONReader::JSON_SYNTAX_ERROR, 1);
+ return NULL;
+ }
+ NextNChars(kNullLen - 1);
+ return Value::CreateNullValue();
+ }
+ default:
+ ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1);
+ return NULL;
+ }
+}
+
+// static
+bool JSONParser::StringsAreEqual(const char* one, const char* two, size_t len) {
+ return strncmp(one, two, len) == 0;
+}
+
+void JSONParser::ReportError(JSONReader::JsonParseError code,
+ int column_adjust) {
+ error_code_ = code;
+ error_line_ = line_number_;
+ error_column_ = index_ - index_last_line_ + column_adjust;
+}
+
+// static
+std::string JSONParser::FormatErrorMessage(int line, int column,
+ const std::string& description) {
+ if (line || column) {
+ return StringPrintf("Line: %i, column: %i, %s",
+ line, column, description.c_str());
+ }
+ return description;
+}
+
+} // namespace internal
+} // namespace base
diff --git a/src/base/json/json_parser.h b/src/base/json/json_parser.h
new file mode 100644
index 0000000..020ac25
--- /dev/null
+++ b/src/base/json/json_parser.h
@@ -0,0 +1,271 @@
+// Copyright (c) 2012 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_JSON_JSON_PARSER_H_
+#define BASE_JSON_JSON_PARSER_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/json/json_reader.h"
+#include "base/string_piece.h"
+
+#if !defined(OS_CHROMEOS)
+#include "base/gtest_prod_util.h"
+#endif
+
+namespace base {
+class Value;
+}
+
+#if defined(OS_CHROMEOS)
+// Chromium and Chromium OS check out gtest to different places, so this is
+// unable to compile on both if gtest_prod.h is included here. Instead, include
+// its only contents -- this will need to be updated if the macro ever changes.
+#define FRIEND_TEST(test_case_name, test_name)\
+friend class test_case_name##_##test_name##_Test
+
+#define FRIEND_TEST_ALL_PREFIXES(test_case_name, test_name) \
+ FRIEND_TEST(test_case_name, test_name); \
+ FRIEND_TEST(test_case_name, DISABLED_##test_name); \
+ FRIEND_TEST(test_case_name, FLAKY_##test_name)
+#endif // OS_CHROMEOS
+
+namespace base {
+namespace internal {
+
+class JSONParserTest;
+
+// The implementation behind the JSONReader interface. This class is not meant
+// to be used directly; it encapsulates logic that need not be exposed publicly.
+//
+// This parser guarantees O(n) time through the input string. It also optimizes
+// base::StringValue by using StringPiece where possible when returning Value
+// objects by using "hidden roots," discussed in the implementation.
+//
+// Iteration happens on the byte level, with the functions CanConsume and
+// NextChar. The conversion from byte to JSON token happens without advancing
+// the parser in GetNextToken/ParseToken, that is tokenization operates on
+// the current parser position without advancing.
+//
+// Built on top of these are a family of Consume functions that iterate
+// internally. Invariant: on entry of a Consume function, the parser is wound
+// to the first byte of a valid JSON token. On exit, it is on the last byte
+// of a token, such that the next iteration of the parser will be at the byte
+// immediately following the token, which would likely be the first byte of the
+// next token.
+class BASE_EXPORT_PRIVATE JSONParser {
+ public:
+ explicit JSONParser(int options);
+ ~JSONParser();
+
+ // Parses the input string according to the set options and returns the
+ // result as a Value owned by the caller.
+ Value* Parse(const StringPiece& input);
+
+ // Returns the error code.
+ JSONReader::JsonParseError error_code() const;
+
+ // Returns the human-friendly error message.
+ std::string GetErrorMessage() const;
+
+ private:
+ enum Token {
+ T_OBJECT_BEGIN, // {
+ T_OBJECT_END, // }
+ T_ARRAY_BEGIN, // [
+ T_ARRAY_END, // ]
+ T_STRING,
+ T_NUMBER,
+ T_BOOL_TRUE, // true
+ T_BOOL_FALSE, // false
+ T_NULL, // null
+ T_LIST_SEPARATOR, // ,
+ T_OBJECT_PAIR_SEPARATOR, // :
+ T_END_OF_INPUT,
+ T_INVALID_TOKEN,
+ };
+
+ // A helper class used for parsing strings. One optimization performed is to
+ // create base::Value with a StringPiece to avoid unnecessary std::string
+ // copies. This is not possible if the input string needs to be decoded from
+ // UTF-16 to UTF-8, or if an escape sequence causes characters to be skipped.
+ // This class centralizes that logic.
+ class StringBuilder {
+ public:
+ // Empty constructor. Used for creating a builder with which to Swap().
+ StringBuilder();
+
+ // |pos| is the beginning of an input string, excluding the |"|.
+ explicit StringBuilder(const char* pos);
+
+ ~StringBuilder();
+
+ // Swaps the contents of |other| with this.
+ void Swap(StringBuilder* other);
+
+ // Either increases the |length_| of the string or copies the character if
+ // the StringBuilder has been converted. |c| must be in the basic ASCII
+ // plane; all other characters need to be in UTF-8 units, appended with
+ // AppendString below.
+ void Append(const char& c);
+
+ // Appends a string to the std::string. Must be Convert()ed to use.
+ void AppendString(const std::string& str);
+
+ // Converts the builder from its default StringPiece to a full std::string,
+ // performing a copy. Once a builder is converted, it cannot be made a
+ // StringPiece again.
+ void Convert();
+
+ // Returns whether the builder can be converted to a StringPiece.
+ bool CanBeStringPiece() const;
+
+ // Returns the StringPiece representation. Returns an empty piece if it
+ // cannot be converted.
+ StringPiece AsStringPiece();
+
+ // Returns the builder as a std::string.
+ const std::string& AsString();
+
+ private:
+ // The beginning of the input string.
+ const char* pos_;
+
+ // Number of bytes in |pos_| that make up the string being built.
+ size_t length_;
+
+ // The copied string representation. NULL until Convert() is called.
+ // Strong. scoped_ptr<T> has too much of an overhead here.
+ std::string* string_;
+ };
+
+ // Quick check that the stream has capacity to consume |length| more bytes.
+ bool CanConsume(int length);
+
+ // The basic way to consume a single character in the stream. Consumes one
+ // byte of the input stream and returns a pointer to the rest of it.
+ const char* NextChar();
+
+ // Performs the equivalent of NextChar N times.
+ void NextNChars(int n);
+
+ // Skips over whitespace and comments to find the next token in the stream.
+ // This does not advance the parser for non-whitespace or comment chars.
+ Token GetNextToken();
+
+ // Consumes whitespace characters and comments until the next non-that is
+ // encountered.
+ void EatWhitespaceAndComments();
+ // Helper function that consumes a comment, assuming that the parser is
+ // currently wound to a '/'.
+ bool EatComment();
+
+ // Calls GetNextToken() and then ParseToken(). Caller owns the result.
+ Value* ParseNextToken();
+
+ // Takes a token that represents the start of a Value ("a structural token"
+ // in RFC terms) and consumes it, returning the result as an object the
+ // caller owns.
+ Value* ParseToken(Token token);
+
+ // Assuming that the parser is currently wound to '{', this parses a JSON
+ // object into a DictionaryValue.
+ Value* ConsumeDictionary();
+
+ // Assuming that the parser is wound to '[', this parses a JSON list into a
+ // ListValue.
+ Value* ConsumeList();
+
+ // Calls through ConsumeStringRaw and wraps it in a value.
+ Value* ConsumeString();
+
+ // Assuming that the parser is wound to a double quote, this parses a string,
+ // decoding any escape sequences and converts UTF-16 to UTF-8. Returns true on
+ // success and Swap()s the result into |out|. Returns false on failure with
+ // error information set.
+ bool ConsumeStringRaw(StringBuilder* out);
+ // Helper function for ConsumeStringRaw() that consumes the next four or 10
+ // bytes (parser is wound to the first character of a HEX sequence, with the
+ // potential for consuming another \uXXXX for a surrogate). Returns true on
+ // success and places the UTF8 code units in |dest_string|, and false on
+ // failure.
+ bool DecodeUTF16(std::string* dest_string);
+ // Helper function for ConsumeStringRaw() that takes a single code point,
+ // decodes it into UTF-8 units, and appends it to the given builder. The
+ // point must be valid.
+ void DecodeUTF8(const int32& point, StringBuilder* dest);
+
+ // Assuming that the parser is wound to the start of a valid JSON number,
+ // this parses and converts it to either an int or double value.
+ Value* ConsumeNumber();
+ // Helper that reads characters that are ints. Returns true if a number was
+ // read and false on error.
+ bool ReadInt(bool allow_leading_zeros);
+
+ // Consumes the literal values of |true|, |false|, and |null|, assuming the
+ // parser is wound to the first character of any of those.
+ Value* ConsumeLiteral();
+
+ // Compares two string buffers of a given length.
+ static bool StringsAreEqual(const char* left, const char* right, size_t len);
+
+ // Sets the error information to |code| at the current column, based on
+ // |index_| and |index_last_line_|, with an optional positive/negative
+ // adjustment by |column_adjust|.
+ void ReportError(JSONReader::JsonParseError code, int column_adjust);
+
+ // Given the line and column number of an error, formats one of the error
+ // message contants from json_reader.h for human display.
+ static std::string FormatErrorMessage(int line, int column,
+ const std::string& description);
+
+ // base::JSONParserOptions that control parsing.
+ int options_;
+
+ // Pointer to the start of the input data.
+ const char* start_pos_;
+
+ // Pointer to the current position in the input data. Equivalent to
+ // |start_pos_ + index_|.
+ const char* pos_;
+
+ // Pointer to the last character of the input data.
+ const char* end_pos_;
+
+ // The index in the input stream to which the parser is wound.
+ int index_;
+
+ // The number of times the parser has recursed (current stack depth).
+ int stack_depth_;
+
+ // The line number that the parser is at currently.
+ int line_number_;
+
+ // The last value of |index_| on the previous line.
+ int index_last_line_;
+
+ // Error information.
+ JSONReader::JsonParseError error_code_;
+ int error_line_;
+ int error_column_;
+
+ friend class JSONParserTest;
+ FRIEND_TEST_ALL_PREFIXES(JSONParserTest, NextChar);
+ FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeDictionary);
+ FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeList);
+ FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeString);
+ FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeLiterals);
+ FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeNumbers);
+ FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ErrorMessages);
+
+ DISALLOW_COPY_AND_ASSIGN(JSONParser);
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_JSON_JSON_PARSER_H_
diff --git a/src/base/json/json_parser_unittest.cc b/src/base/json/json_parser_unittest.cc
new file mode 100644
index 0000000..8ee886b
--- /dev/null
+++ b/src/base/json/json_parser_unittest.cc
@@ -0,0 +1,306 @@
+// Copyright (c) 2012 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/json/json_parser.h"
+
+#include "base/json/json_reader.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace internal {
+
+class JSONParserTest : public testing::Test {
+ public:
+ JSONParser* NewTestParser(const std::string& input) {
+ JSONParser* parser = new JSONParser(JSON_PARSE_RFC);
+ parser->start_pos_ = input.data();
+ parser->pos_ = parser->start_pos_;
+ parser->end_pos_ = parser->start_pos_ + input.length();
+ return parser;
+ }
+
+ void TestLastThree(JSONParser* parser) {
+ EXPECT_EQ(',', *parser->NextChar());
+ EXPECT_EQ('|', *parser->NextChar());
+ EXPECT_EQ('\0', *parser->NextChar());
+ EXPECT_EQ(parser->end_pos_, parser->pos_);
+ }
+};
+
+TEST_F(JSONParserTest, NextChar) {
+ std::string input("Hello world");
+ scoped_ptr<JSONParser> parser(NewTestParser(input));
+
+ EXPECT_EQ('H', *parser->pos_);
+ for (size_t i = 1; i < input.length(); ++i) {
+ EXPECT_EQ(input[i], *parser->NextChar());
+ }
+ EXPECT_EQ(parser->end_pos_, parser->NextChar());
+}
+
+TEST_F(JSONParserTest, ConsumeString) {
+ std::string input("\"test\",|");
+ scoped_ptr<JSONParser> parser(NewTestParser(input));
+ scoped_ptr<Value> value(parser->ConsumeString());
+ EXPECT_EQ('"', *parser->pos_);
+
+ TestLastThree(parser.get());
+
+ ASSERT_TRUE(value.get());
+ std::string str;
+ EXPECT_TRUE(value->GetAsString(&str));
+ EXPECT_EQ("test", str);
+}
+
+TEST_F(JSONParserTest, ConsumeList) {
+ std::string input("[true, false],|");
+ scoped_ptr<JSONParser> parser(NewTestParser(input));
+ scoped_ptr<Value> value(parser->ConsumeList());
+ EXPECT_EQ(']', *parser->pos_);
+
+ TestLastThree(parser.get());
+
+ ASSERT_TRUE(value.get());
+ base::ListValue* list;
+ EXPECT_TRUE(value->GetAsList(&list));
+ EXPECT_EQ(2u, list->GetSize());
+}
+
+TEST_F(JSONParserTest, ConsumeDictionary) {
+ std::string input("{\"abc\":\"def\"},|");
+ scoped_ptr<JSONParser> parser(NewTestParser(input));
+ scoped_ptr<Value> value(parser->ConsumeDictionary());
+ EXPECT_EQ('}', *parser->pos_);
+
+ TestLastThree(parser.get());
+
+ ASSERT_TRUE(value.get());
+ base::DictionaryValue* dict;
+ EXPECT_TRUE(value->GetAsDictionary(&dict));
+ std::string str;
+ EXPECT_TRUE(dict->GetString("abc", &str));
+ EXPECT_EQ("def", str);
+}
+
+TEST_F(JSONParserTest, ConsumeLiterals) {
+ // Literal |true|.
+ std::string input("true,|");
+ scoped_ptr<JSONParser> parser(NewTestParser(input));
+ scoped_ptr<Value> value(parser->ConsumeLiteral());
+ EXPECT_EQ('e', *parser->pos_);
+
+ TestLastThree(parser.get());
+
+ ASSERT_TRUE(value.get());
+ bool bool_value = false;
+ EXPECT_TRUE(value->GetAsBoolean(&bool_value));
+ EXPECT_TRUE(bool_value);
+
+ // Literal |false|.
+ input = "false,|";
+ parser.reset(NewTestParser(input));
+ value.reset(parser->ConsumeLiteral());
+ EXPECT_EQ('e', *parser->pos_);
+
+ TestLastThree(parser.get());
+
+ ASSERT_TRUE(value.get());
+ EXPECT_TRUE(value->GetAsBoolean(&bool_value));
+ EXPECT_FALSE(bool_value);
+
+ // Literal |null|.
+ input = "null,|";
+ parser.reset(NewTestParser(input));
+ value.reset(parser->ConsumeLiteral());
+ EXPECT_EQ('l', *parser->pos_);
+
+ TestLastThree(parser.get());
+
+ ASSERT_TRUE(value.get());
+ EXPECT_TRUE(value->IsType(Value::TYPE_NULL));
+}
+
+TEST_F(JSONParserTest, ConsumeNumbers) {
+ // Integer.
+ std::string input("1234,|");
+ scoped_ptr<JSONParser> parser(NewTestParser(input));
+ scoped_ptr<Value> value(parser->ConsumeNumber());
+ EXPECT_EQ('4', *parser->pos_);
+
+ TestLastThree(parser.get());
+
+ ASSERT_TRUE(value.get());
+ int number_i;
+ EXPECT_TRUE(value->GetAsInteger(&number_i));
+ EXPECT_EQ(1234, number_i);
+
+ // Negative integer.
+ input = "-1234,|";
+ parser.reset(NewTestParser(input));
+ value.reset(parser->ConsumeNumber());
+ EXPECT_EQ('4', *parser->pos_);
+
+ TestLastThree(parser.get());
+
+ ASSERT_TRUE(value.get());
+ EXPECT_TRUE(value->GetAsInteger(&number_i));
+ EXPECT_EQ(-1234, number_i);
+
+ // Double.
+ input = "12.34,|";
+ parser.reset(NewTestParser(input));
+ value.reset(parser->ConsumeNumber());
+ EXPECT_EQ('4', *parser->pos_);
+
+ TestLastThree(parser.get());
+
+ ASSERT_TRUE(value.get());
+ double number_d;
+ EXPECT_TRUE(value->GetAsDouble(&number_d));
+ EXPECT_EQ(12.34, number_d);
+
+ // Scientific.
+ input = "42e3,|";
+ parser.reset(NewTestParser(input));
+ value.reset(parser->ConsumeNumber());
+ EXPECT_EQ('3', *parser->pos_);
+
+ TestLastThree(parser.get());
+
+ ASSERT_TRUE(value.get());
+ EXPECT_TRUE(value->GetAsDouble(&number_d));
+ EXPECT_EQ(42000, number_d);
+
+ // Negative scientific.
+ input = "314159e-5,|";
+ parser.reset(NewTestParser(input));
+ value.reset(parser->ConsumeNumber());
+ EXPECT_EQ('5', *parser->pos_);
+
+ TestLastThree(parser.get());
+
+ ASSERT_TRUE(value.get());
+ EXPECT_TRUE(value->GetAsDouble(&number_d));
+ EXPECT_EQ(3.14159, number_d);
+
+ // Positive scientific.
+ input = "0.42e+3,|";
+ parser.reset(NewTestParser(input));
+ value.reset(parser->ConsumeNumber());
+ EXPECT_EQ('3', *parser->pos_);
+
+ TestLastThree(parser.get());
+
+ ASSERT_TRUE(value.get());
+ EXPECT_TRUE(value->GetAsDouble(&number_d));
+ EXPECT_EQ(420, number_d);
+}
+
+TEST_F(JSONParserTest, ErrorMessages) {
+ // Error strings should not be modified in case of success.
+ std::string error_message;
+ int error_code = 0;
+ scoped_ptr<Value> root;
+ root.reset(JSONReader::ReadAndReturnError("[42]", JSON_PARSE_RFC,
+ &error_code, &error_message));
+ EXPECT_TRUE(error_message.empty());
+ EXPECT_EQ(0, error_code);
+
+ // Test line and column counting
+ const char* big_json = "[\n0,\n1,\n2,\n3,4,5,6 7,\n8,\n9\n]";
+ // error here ---------------------------------^
+ root.reset(JSONReader::ReadAndReturnError(big_json, JSON_PARSE_RFC,
+ &error_code, &error_message));
+ EXPECT_FALSE(root.get());
+ EXPECT_EQ(JSONParser::FormatErrorMessage(5, 10, JSONReader::kSyntaxError),
+ error_message);
+ EXPECT_EQ(JSONReader::JSON_SYNTAX_ERROR, error_code);
+
+ // Test each of the error conditions
+ root.reset(JSONReader::ReadAndReturnError("{},{}", JSON_PARSE_RFC,
+ &error_code, &error_message));
+ EXPECT_FALSE(root.get());
+ EXPECT_EQ(JSONParser::FormatErrorMessage(1, 3,
+ JSONReader::kUnexpectedDataAfterRoot), error_message);
+ EXPECT_EQ(JSONReader::JSON_UNEXPECTED_DATA_AFTER_ROOT, error_code);
+
+ std::string nested_json;
+ for (int i = 0; i < 101; ++i) {
+ nested_json.insert(nested_json.begin(), '[');
+ nested_json.append(1, ']');
+ }
+ root.reset(JSONReader::ReadAndReturnError(nested_json, JSON_PARSE_RFC,
+ &error_code, &error_message));
+ EXPECT_FALSE(root.get());
+ EXPECT_EQ(JSONParser::FormatErrorMessage(1, 100, JSONReader::kTooMuchNesting),
+ error_message);
+ EXPECT_EQ(JSONReader::JSON_TOO_MUCH_NESTING, error_code);
+
+ root.reset(JSONReader::ReadAndReturnError("[1,]", JSON_PARSE_RFC,
+ &error_code, &error_message));
+ EXPECT_FALSE(root.get());
+ EXPECT_EQ(JSONParser::FormatErrorMessage(1, 4, JSONReader::kTrailingComma),
+ error_message);
+ EXPECT_EQ(JSONReader::JSON_TRAILING_COMMA, error_code);
+
+ root.reset(JSONReader::ReadAndReturnError("{foo:\"bar\"}", JSON_PARSE_RFC,
+ &error_code, &error_message));
+ EXPECT_FALSE(root.get());
+ EXPECT_EQ(JSONParser::FormatErrorMessage(1, 2,
+ JSONReader::kUnquotedDictionaryKey), error_message);
+ EXPECT_EQ(JSONReader::JSON_UNQUOTED_DICTIONARY_KEY, error_code);
+
+ root.reset(JSONReader::ReadAndReturnError("{\"foo\":\"bar\",}",
+ JSON_PARSE_RFC,
+ &error_code, &error_message));
+ EXPECT_FALSE(root.get());
+ EXPECT_EQ(JSONParser::FormatErrorMessage(1, 14, JSONReader::kTrailingComma),
+ error_message);
+
+ root.reset(JSONReader::ReadAndReturnError("[nu]", JSON_PARSE_RFC,
+ &error_code, &error_message));
+ EXPECT_FALSE(root.get());
+ EXPECT_EQ(JSONParser::FormatErrorMessage(1, 2, JSONReader::kSyntaxError),
+ error_message);
+ EXPECT_EQ(JSONReader::JSON_SYNTAX_ERROR, error_code);
+
+ root.reset(JSONReader::ReadAndReturnError("[\"xxx\\xq\"]", JSON_PARSE_RFC,
+ &error_code, &error_message));
+ EXPECT_FALSE(root.get());
+ EXPECT_EQ(JSONParser::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape),
+ error_message);
+ EXPECT_EQ(JSONReader::JSON_INVALID_ESCAPE, error_code);
+
+ root.reset(JSONReader::ReadAndReturnError("[\"xxx\\uq\"]", JSON_PARSE_RFC,
+ &error_code, &error_message));
+ EXPECT_FALSE(root.get());
+ EXPECT_EQ(JSONParser::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape),
+ error_message);
+ EXPECT_EQ(JSONReader::JSON_INVALID_ESCAPE, error_code);
+
+ root.reset(JSONReader::ReadAndReturnError("[\"xxx\\q\"]", JSON_PARSE_RFC,
+ &error_code, &error_message));
+ EXPECT_FALSE(root.get());
+ EXPECT_EQ(JSONParser::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape),
+ error_message);
+ EXPECT_EQ(JSONReader::JSON_INVALID_ESCAPE, error_code);
+}
+
+TEST_F(JSONParserTest, Decode4ByteUtf8Char) {
+ // This test strings contains a 4 byte unicode character (a smiley!) that the
+ // reader should be able to handle (the character is \xf0\x9f\x98\x87).
+ const char kUtf8Data[] =
+ "[\"😇\",[],[],[],{\"google:suggesttype\":[]}]";
+ std::string error_message;
+ int error_code = 0;
+ scoped_ptr<Value> root(
+ JSONReader::ReadAndReturnError(kUtf8Data, JSON_PARSE_RFC, &error_code,
+ &error_message));
+ EXPECT_TRUE(root.get()) << error_message;
+}
+
+} // namespace internal
+} // namespace base
diff --git a/src/base/json/json_reader.cc b/src/base/json/json_reader.cc
new file mode 100644
index 0000000..593273e
--- /dev/null
+++ b/src/base/json/json_reader.cc
@@ -0,0 +1,110 @@
+// Copyright (c) 2012 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/json/json_reader.h"
+
+#include "base/json/json_parser.h"
+#include "base/logging.h"
+
+namespace base {
+
+const char* JSONReader::kInvalidEscape =
+ "Invalid escape sequence.";
+const char* JSONReader::kSyntaxError =
+ "Syntax error.";
+const char* JSONReader::kUnexpectedToken =
+ "Unexpected token.";
+const char* JSONReader::kTrailingComma =
+ "Trailing comma not allowed.";
+const char* JSONReader::kTooMuchNesting =
+ "Too much nesting.";
+const char* JSONReader::kUnexpectedDataAfterRoot =
+ "Unexpected data after root element.";
+const char* JSONReader::kUnsupportedEncoding =
+ "Unsupported encoding. JSON must be UTF-8.";
+const char* JSONReader::kUnquotedDictionaryKey =
+ "Dictionary keys must be quoted.";
+
+JSONReader::JSONReader()
+ : parser_(new internal::JSONParser(JSON_PARSE_RFC)) {
+}
+
+JSONReader::JSONReader(int options)
+ : parser_(new internal::JSONParser(options)) {
+}
+
+JSONReader::~JSONReader() {
+}
+
+// static
+Value* JSONReader::Read(const StringPiece& json) {
+ internal::JSONParser parser(JSON_PARSE_RFC);
+ return parser.Parse(json);
+}
+
+// static
+Value* JSONReader::Read(const StringPiece& json,
+ int options) {
+ internal::JSONParser parser(options);
+ return parser.Parse(json);
+}
+
+// static
+Value* JSONReader::ReadAndReturnError(const StringPiece& json,
+ int options,
+ int* error_code_out,
+ std::string* error_msg_out) {
+ internal::JSONParser parser(options);
+ Value* root = parser.Parse(json);
+ if (root)
+ return root;
+
+ if (error_code_out)
+ *error_code_out = parser.error_code();
+ if (error_msg_out)
+ *error_msg_out = parser.GetErrorMessage();
+
+ return NULL;
+}
+
+// static
+std::string JSONReader::ErrorCodeToString(JsonParseError error_code) {
+ switch (error_code) {
+ case JSON_NO_ERROR:
+ return std::string();
+ case JSON_INVALID_ESCAPE:
+ return kInvalidEscape;
+ case JSON_SYNTAX_ERROR:
+ return kSyntaxError;
+ case JSON_UNEXPECTED_TOKEN:
+ return kUnexpectedToken;
+ case JSON_TRAILING_COMMA:
+ return kTrailingComma;
+ case JSON_TOO_MUCH_NESTING:
+ return kTooMuchNesting;
+ case JSON_UNEXPECTED_DATA_AFTER_ROOT:
+ return kUnexpectedDataAfterRoot;
+ case JSON_UNSUPPORTED_ENCODING:
+ return kUnsupportedEncoding;
+ case JSON_UNQUOTED_DICTIONARY_KEY:
+ return kUnquotedDictionaryKey;
+ default:
+ NOTREACHED();
+ return std::string();
+ }
+}
+
+Value* JSONReader::ReadToValue(const std::string& json) {
+ return parser_->Parse(json);
+}
+
+JSONReader::JsonParseError JSONReader::error_code() const {
+ return parser_->error_code();
+}
+
+std::string JSONReader::GetErrorMessage() const {
+ return parser_->GetErrorMessage();
+}
+
+} // namespace base
diff --git a/src/base/json/json_reader.h b/src/base/json/json_reader.h
new file mode 100644
index 0000000..86b2612
--- /dev/null
+++ b/src/base/json/json_reader.h
@@ -0,0 +1,135 @@
+// Copyright (c) 2012 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.
+//
+// A JSON parser. Converts strings of JSON into a Value object (see
+// base/values.h).
+// http://www.ietf.org/rfc/rfc4627.txt?number=4627
+//
+// Known limitations/deviations from the RFC:
+// - Only knows how to parse ints within the range of a signed 32 bit int and
+// decimal numbers within a double.
+// - Assumes input is encoded as UTF8. The spec says we should allow UTF-16
+// (BE or LE) and UTF-32 (BE or LE) as well.
+// - We limit nesting to 100 levels to prevent stack overflow (this is allowed
+// by the RFC).
+// - A Unicode FAQ ("http://unicode.org/faq/utf_bom.html") writes a data
+// stream may start with a Unicode Byte-Order-Mark (U+FEFF), i.e. the input
+// UTF-8 string for the JSONReader::JsonToValue() function may start with a
+// UTF-8 BOM (0xEF, 0xBB, 0xBF).
+// To avoid the function from mis-treating a UTF-8 BOM as an invalid
+// character, the function skips a Unicode BOM at the beginning of the
+// Unicode string (converted from the input UTF-8 string) before parsing it.
+//
+// TODO(tc): Add a parsing option to to relax object keys being wrapped in
+// double quotes
+// TODO(tc): Add an option to disable comment stripping
+
+#ifndef BASE_JSON_JSON_READER_H_
+#define BASE_JSON_JSON_READER_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/string_piece.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace base {
+class Value;
+
+namespace internal {
+class JSONParser;
+}
+}
+
+namespace base {
+
+enum JSONParserOptions {
+ // Parses the input strictly according to RFC 4627, except for where noted
+ // above.
+ JSON_PARSE_RFC = 0,
+
+ // Allows commas to exist after the last element in structures.
+ JSON_ALLOW_TRAILING_COMMAS = 1 << 0,
+
+ // The parser can perform optimizations by placing hidden data in the root of
+ // the JSON object, which speeds up certain operations on children. However,
+ // if the child is Remove()d from root, it would result in use-after-free
+ // unless it is DeepCopy()ed or this option is used.
+ JSON_DETACHABLE_CHILDREN = 1 << 1,
+};
+
+class BASE_EXPORT JSONReader {
+ public:
+ // Error codes during parsing.
+ enum JsonParseError {
+ JSON_NO_ERROR = 0,
+ JSON_INVALID_ESCAPE,
+ JSON_SYNTAX_ERROR,
+ JSON_UNEXPECTED_TOKEN,
+ JSON_TRAILING_COMMA,
+ JSON_TOO_MUCH_NESTING,
+ JSON_UNEXPECTED_DATA_AFTER_ROOT,
+ JSON_UNSUPPORTED_ENCODING,
+ JSON_UNQUOTED_DICTIONARY_KEY,
+ };
+
+ // String versions of parse error codes.
+ static const char* kInvalidEscape;
+ static const char* kSyntaxError;
+ static const char* kUnexpectedToken;
+ static const char* kTrailingComma;
+ static const char* kTooMuchNesting;
+ static const char* kUnexpectedDataAfterRoot;
+ static const char* kUnsupportedEncoding;
+ static const char* kUnquotedDictionaryKey;
+
+ // Constructs a reader with the default options, JSON_PARSE_RFC.
+ JSONReader();
+
+ // Constructs a reader with custom options.
+ explicit JSONReader(int options);
+
+ ~JSONReader();
+
+ // Reads and parses |json|, returning a Value. The caller owns the returned
+ // instance. If |json| is not a properly formed JSON string, returns NULL.
+ static Value* Read(const StringPiece& json);
+
+ // Reads and parses |json|, returning a Value owned by the caller. The
+ // parser respects the given |options|. If the input is not properly formed,
+ // returns NULL.
+ static Value* Read(const StringPiece& json, int options);
+
+ // Reads and parses |json| like Read(). |error_code_out| and |error_msg_out|
+ // are optional. If specified and NULL is returned, they will be populated
+ // an error code and a formatted error message (including error location if
+ // appropriate). Otherwise, they will be unmodified.
+ static Value* ReadAndReturnError(const StringPiece& json,
+ int options, // JSONParserOptions
+ int* error_code_out,
+ std::string* error_msg_out);
+
+ // Converts a JSON parse error code into a human readable message.
+ // Returns an empty string if error_code is JSON_NO_ERROR.
+ static std::string ErrorCodeToString(JsonParseError error_code);
+
+ // Parses an input string into a Value that is owned by the caller.
+ Value* ReadToValue(const std::string& json);
+
+ // Returns the error code if the last call to ReadToValue() failed.
+ // Returns JSON_NO_ERROR otherwise.
+ JsonParseError error_code() const;
+
+ // Converts error_code_ to a human-readable string, including line and column
+ // numbers if appropriate.
+ std::string GetErrorMessage() const;
+
+ private:
+ scoped_ptr<internal::JSONParser> parser_;
+};
+
+} // namespace base
+
+#endif // BASE_JSON_JSON_READER_H_
diff --git a/src/base/json/json_reader_unittest.cc b/src/base/json/json_reader_unittest.cc
new file mode 100644
index 0000000..38bf590
--- /dev/null
+++ b/src/base/json/json_reader_unittest.cc
@@ -0,0 +1,648 @@
+// Copyright (c) 2012 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/json/json_reader.h"
+
+#include "base/base_paths.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/path_service.h"
+#include "base/string_piece.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(JSONReaderTest, Reading) {
+ // some whitespace checking
+ scoped_ptr<Value> root;
+ root.reset(JSONReader().ReadToValue(" null "));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_NULL));
+
+ // Invalid JSON string
+ root.reset(JSONReader().ReadToValue("nu"));
+ EXPECT_FALSE(root.get());
+
+ // Simple bool
+ root.reset(JSONReader().ReadToValue("true "));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_BOOLEAN));
+
+ // Embedded comment
+ root.reset(JSONReader().ReadToValue("/* comment */null"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_NULL));
+ root.reset(JSONReader().ReadToValue("40 /* comment */"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_INTEGER));
+ root.reset(JSONReader().ReadToValue("true // comment"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_BOOLEAN));
+ root.reset(JSONReader().ReadToValue("/* comment */\"sample string\""));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
+ std::string value;
+ EXPECT_TRUE(root->GetAsString(&value));
+ EXPECT_EQ("sample string", value);
+ root.reset(JSONReader().ReadToValue("[1, /* comment, 2 ] */ \n 3]"));
+ ASSERT_TRUE(root.get());
+ ListValue* list = static_cast<ListValue*>(root.get());
+ EXPECT_EQ(2u, list->GetSize());
+ int int_val = 0;
+ EXPECT_TRUE(list->GetInteger(0, &int_val));
+ EXPECT_EQ(1, int_val);
+ EXPECT_TRUE(list->GetInteger(1, &int_val));
+ EXPECT_EQ(3, int_val);
+ root.reset(JSONReader().ReadToValue("[1, /*a*/2, 3]"));
+ ASSERT_TRUE(root.get());
+ list = static_cast<ListValue*>(root.get());
+ EXPECT_EQ(3u, list->GetSize());
+
+ // Test number formats
+ root.reset(JSONReader().ReadToValue("43"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_INTEGER));
+ EXPECT_TRUE(root->GetAsInteger(&int_val));
+ EXPECT_EQ(43, int_val);
+
+ // According to RFC4627, oct, hex, and leading zeros are invalid JSON.
+ root.reset(JSONReader().ReadToValue("043"));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader().ReadToValue("0x43"));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader().ReadToValue("00"));
+ EXPECT_FALSE(root.get());
+
+ // Test 0 (which needs to be special cased because of the leading zero
+ // clause).
+ root.reset(JSONReader().ReadToValue("0"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_INTEGER));
+ int_val = 1;
+ EXPECT_TRUE(root->GetAsInteger(&int_val));
+ EXPECT_EQ(0, int_val);
+
+ // Numbers that overflow ints should succeed, being internally promoted to
+ // storage as doubles
+ root.reset(JSONReader().ReadToValue("2147483648"));
+ ASSERT_TRUE(root.get());
+ double double_val;
+ EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
+ double_val = 0.0;
+ EXPECT_TRUE(root->GetAsDouble(&double_val));
+ EXPECT_DOUBLE_EQ(2147483648.0, double_val);
+ root.reset(JSONReader().ReadToValue("-2147483649"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
+ double_val = 0.0;
+ EXPECT_TRUE(root->GetAsDouble(&double_val));
+ EXPECT_DOUBLE_EQ(-2147483649.0, double_val);
+
+ // Parse a double
+ root.reset(JSONReader().ReadToValue("43.1"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
+ double_val = 0.0;
+ EXPECT_TRUE(root->GetAsDouble(&double_val));
+ EXPECT_DOUBLE_EQ(43.1, double_val);
+
+ root.reset(JSONReader().ReadToValue("4.3e-1"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
+ double_val = 0.0;
+ EXPECT_TRUE(root->GetAsDouble(&double_val));
+ EXPECT_DOUBLE_EQ(.43, double_val);
+
+ root.reset(JSONReader().ReadToValue("2.1e0"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
+ double_val = 0.0;
+ EXPECT_TRUE(root->GetAsDouble(&double_val));
+ EXPECT_DOUBLE_EQ(2.1, double_val);
+
+ root.reset(JSONReader().ReadToValue("2.1e+0001"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
+ double_val = 0.0;
+ EXPECT_TRUE(root->GetAsDouble(&double_val));
+ EXPECT_DOUBLE_EQ(21.0, double_val);
+
+ root.reset(JSONReader().ReadToValue("0.01"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
+ double_val = 0.0;
+ EXPECT_TRUE(root->GetAsDouble(&double_val));
+ EXPECT_DOUBLE_EQ(0.01, double_val);
+
+ root.reset(JSONReader().ReadToValue("1.00"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_DOUBLE));
+ double_val = 0.0;
+ EXPECT_TRUE(root->GetAsDouble(&double_val));
+ EXPECT_DOUBLE_EQ(1.0, double_val);
+
+ // Fractional parts must have a digit before and after the decimal point.
+ root.reset(JSONReader().ReadToValue("1."));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader().ReadToValue(".1"));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader().ReadToValue("1.e10"));
+ EXPECT_FALSE(root.get());
+
+ // Exponent must have a digit following the 'e'.
+ root.reset(JSONReader().ReadToValue("1e"));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader().ReadToValue("1E"));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader().ReadToValue("1e1."));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader().ReadToValue("1e1.0"));
+ EXPECT_FALSE(root.get());
+
+ // INF/-INF/NaN are not valid
+ root.reset(JSONReader().ReadToValue("1e1000"));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader().ReadToValue("-1e1000"));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader().ReadToValue("NaN"));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader().ReadToValue("nan"));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader().ReadToValue("inf"));
+ EXPECT_FALSE(root.get());
+
+ // Invalid number formats
+ root.reset(JSONReader().ReadToValue("4.3.1"));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader().ReadToValue("4e3.1"));
+ EXPECT_FALSE(root.get());
+
+ // Test string parser
+ root.reset(JSONReader().ReadToValue("\"hello world\""));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
+ std::string str_val;
+ EXPECT_TRUE(root->GetAsString(&str_val));
+ EXPECT_EQ("hello world", str_val);
+
+ // Empty string
+ root.reset(JSONReader().ReadToValue("\"\""));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
+ str_val.clear();
+ EXPECT_TRUE(root->GetAsString(&str_val));
+ EXPECT_EQ("", str_val);
+
+ // Test basic string escapes
+ root.reset(JSONReader().ReadToValue("\" \\\"\\\\\\/\\b\\f\\n\\r\\t\\v\""));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
+ str_val.clear();
+ EXPECT_TRUE(root->GetAsString(&str_val));
+ EXPECT_EQ(" \"\\/\b\f\n\r\t\v", str_val);
+
+ // Test hex and unicode escapes including the null character.
+ root.reset(JSONReader().ReadToValue("\"\\x41\\x00\\u1234\""));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
+ str_val.clear();
+ EXPECT_TRUE(root->GetAsString(&str_val));
+ EXPECT_EQ(std::wstring(L"A\0\x1234", 3), UTF8ToWide(str_val));
+
+ // Test invalid strings
+ root.reset(JSONReader().ReadToValue("\"no closing quote"));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader().ReadToValue("\"\\z invalid escape char\""));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader().ReadToValue("\"\\xAQ invalid hex code\""));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader().ReadToValue("not enough hex chars\\x1\""));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader().ReadToValue("\"not enough escape chars\\u123\""));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader().ReadToValue("\"extra backslash at end of input\\\""));
+ EXPECT_FALSE(root.get());
+
+ // Basic array
+ root.reset(JSONReader::Read("[true, false, null]"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_LIST));
+ list = static_cast<ListValue*>(root.get());
+ EXPECT_EQ(3U, list->GetSize());
+
+ // Test with trailing comma. Should be parsed the same as above.
+ scoped_ptr<Value> root2;
+ root2.reset(JSONReader::Read("[true, false, null, ]",
+ JSON_ALLOW_TRAILING_COMMAS));
+ EXPECT_TRUE(root->Equals(root2.get()));
+
+ // Empty array
+ root.reset(JSONReader::Read("[]"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_LIST));
+ list = static_cast<ListValue*>(root.get());
+ EXPECT_EQ(0U, list->GetSize());
+
+ // Nested arrays
+ root.reset(JSONReader::Read("[[true], [], [false, [], [null]], null]"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_LIST));
+ list = static_cast<ListValue*>(root.get());
+ EXPECT_EQ(4U, list->GetSize());
+
+ // Lots of trailing commas.
+ root2.reset(JSONReader::Read("[[true], [], [false, [], [null, ] , ], null,]",
+ JSON_ALLOW_TRAILING_COMMAS));
+ EXPECT_TRUE(root->Equals(root2.get()));
+
+ // Invalid, missing close brace.
+ root.reset(JSONReader::Read("[[true], [], [false, [], [null]], null"));
+ EXPECT_FALSE(root.get());
+
+ // Invalid, too many commas
+ root.reset(JSONReader::Read("[true,, null]"));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader::Read("[true,, null]", JSON_ALLOW_TRAILING_COMMAS));
+ EXPECT_FALSE(root.get());
+
+ // Invalid, no commas
+ root.reset(JSONReader::Read("[true null]"));
+ EXPECT_FALSE(root.get());
+
+ // Invalid, trailing comma
+ root.reset(JSONReader::Read("[true,]"));
+ EXPECT_FALSE(root.get());
+
+ // Valid if we set |allow_trailing_comma| to true.
+ root.reset(JSONReader::Read("[true,]", JSON_ALLOW_TRAILING_COMMAS));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_LIST));
+ list = static_cast<ListValue*>(root.get());
+ EXPECT_EQ(1U, list->GetSize());
+ Value* tmp_value = NULL;
+ ASSERT_TRUE(list->Get(0, &tmp_value));
+ EXPECT_TRUE(tmp_value->IsType(Value::TYPE_BOOLEAN));
+ bool bool_value = false;
+ EXPECT_TRUE(tmp_value->GetAsBoolean(&bool_value));
+ EXPECT_TRUE(bool_value);
+
+ // Don't allow empty elements, even if |allow_trailing_comma| is
+ // true.
+ root.reset(JSONReader::Read("[,]", JSON_ALLOW_TRAILING_COMMAS));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader::Read("[true,,]", JSON_ALLOW_TRAILING_COMMAS));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader::Read("[,true,]", JSON_ALLOW_TRAILING_COMMAS));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader::Read("[true,,false]", JSON_ALLOW_TRAILING_COMMAS));
+ EXPECT_FALSE(root.get());
+
+ // Test objects
+ root.reset(JSONReader::Read("{}"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
+
+ root.reset(JSONReader::Read(
+ "{\"number\":9.87654321, \"null\":null , \"\\x53\" : \"str\" }"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
+ DictionaryValue* dict_val = static_cast<DictionaryValue*>(root.get());
+ double_val = 0.0;
+ EXPECT_TRUE(dict_val->GetDouble("number", &double_val));
+ EXPECT_DOUBLE_EQ(9.87654321, double_val);
+ Value* null_val = NULL;
+ ASSERT_TRUE(dict_val->Get("null", &null_val));
+ EXPECT_TRUE(null_val->IsType(Value::TYPE_NULL));
+ str_val.clear();
+ EXPECT_TRUE(dict_val->GetString("S", &str_val));
+ EXPECT_EQ("str", str_val);
+
+ root2.reset(JSONReader::Read(
+ "{\"number\":9.87654321, \"null\":null , \"\\x53\" : \"str\", }",
+ JSON_ALLOW_TRAILING_COMMAS));
+ ASSERT_TRUE(root2.get());
+ EXPECT_TRUE(root->Equals(root2.get()));
+
+ // Test newline equivalence.
+ root2.reset(JSONReader::Read(
+ "{\n"
+ " \"number\":9.87654321,\n"
+ " \"null\":null,\n"
+ " \"\\x53\":\"str\",\n"
+ "}\n", JSON_ALLOW_TRAILING_COMMAS));
+ ASSERT_TRUE(root2.get());
+ EXPECT_TRUE(root->Equals(root2.get()));
+
+ root2.reset(JSONReader::Read(
+ "{\r\n"
+ " \"number\":9.87654321,\r\n"
+ " \"null\":null,\r\n"
+ " \"\\x53\":\"str\",\r\n"
+ "}\r\n", JSON_ALLOW_TRAILING_COMMAS));
+ ASSERT_TRUE(root2.get());
+ EXPECT_TRUE(root->Equals(root2.get()));
+
+ // Test nesting
+ root.reset(JSONReader::Read(
+ "{\"inner\":{\"array\":[true]},\"false\":false,\"d\":{}}"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
+ dict_val = static_cast<DictionaryValue*>(root.get());
+ DictionaryValue* inner_dict = NULL;
+ ASSERT_TRUE(dict_val->GetDictionary("inner", &inner_dict));
+ ListValue* inner_array = NULL;
+ ASSERT_TRUE(inner_dict->GetList("array", &inner_array));
+ EXPECT_EQ(1U, inner_array->GetSize());
+ bool_value = true;
+ EXPECT_TRUE(dict_val->GetBoolean("false", &bool_value));
+ EXPECT_FALSE(bool_value);
+ inner_dict = NULL;
+ EXPECT_TRUE(dict_val->GetDictionary("d", &inner_dict));
+
+ root2.reset(JSONReader::Read(
+ "{\"inner\": {\"array\":[true] , },\"false\":false,\"d\":{},}",
+ JSON_ALLOW_TRAILING_COMMAS));
+ EXPECT_TRUE(root->Equals(root2.get()));
+
+ // Test keys with periods
+ root.reset(JSONReader::Read(
+ "{\"a.b\":3,\"c\":2,\"d.e.f\":{\"g.h.i.j\":1}}"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
+ dict_val = static_cast<DictionaryValue*>(root.get());
+ int integer_value = 0;
+ EXPECT_TRUE(dict_val->GetIntegerWithoutPathExpansion("a.b", &integer_value));
+ EXPECT_EQ(3, integer_value);
+ EXPECT_TRUE(dict_val->GetIntegerWithoutPathExpansion("c", &integer_value));
+ EXPECT_EQ(2, integer_value);
+ inner_dict = NULL;
+ ASSERT_TRUE(dict_val->GetDictionaryWithoutPathExpansion("d.e.f",
+ &inner_dict));
+ EXPECT_EQ(1U, inner_dict->size());
+ EXPECT_TRUE(inner_dict->GetIntegerWithoutPathExpansion("g.h.i.j",
+ &integer_value));
+ EXPECT_EQ(1, integer_value);
+
+ root.reset(JSONReader::Read("{\"a\":{\"b\":2},\"a.b\":1}"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
+ dict_val = static_cast<DictionaryValue*>(root.get());
+ EXPECT_TRUE(dict_val->GetInteger("a.b", &integer_value));
+ EXPECT_EQ(2, integer_value);
+ EXPECT_TRUE(dict_val->GetIntegerWithoutPathExpansion("a.b", &integer_value));
+ EXPECT_EQ(1, integer_value);
+
+ // Invalid, no closing brace
+ root.reset(JSONReader::Read("{\"a\": true"));
+ EXPECT_FALSE(root.get());
+
+ // Invalid, keys must be quoted
+ root.reset(JSONReader::Read("{foo:true}"));
+ EXPECT_FALSE(root.get());
+
+ // Invalid, trailing comma
+ root.reset(JSONReader::Read("{\"a\":true,}"));
+ EXPECT_FALSE(root.get());
+
+ // Invalid, too many commas
+ root.reset(JSONReader::Read("{\"a\":true,,\"b\":false}"));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader::Read("{\"a\":true,,\"b\":false}",
+ JSON_ALLOW_TRAILING_COMMAS));
+ EXPECT_FALSE(root.get());
+
+ // Invalid, no separator
+ root.reset(JSONReader::Read("{\"a\" \"b\"}"));
+ EXPECT_FALSE(root.get());
+
+ // Invalid, lone comma.
+ root.reset(JSONReader::Read("{,}"));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader::Read("{,}", JSON_ALLOW_TRAILING_COMMAS));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader::Read("{\"a\":true,,}", JSON_ALLOW_TRAILING_COMMAS));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader::Read("{,\"a\":true}", JSON_ALLOW_TRAILING_COMMAS));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader::Read("{\"a\":true,,\"b\":false}",
+ JSON_ALLOW_TRAILING_COMMAS));
+ EXPECT_FALSE(root.get());
+
+ // Test stack overflow
+ std::string evil(1000000, '[');
+ evil.append(std::string(1000000, ']'));
+ root.reset(JSONReader::Read(evil));
+ EXPECT_FALSE(root.get());
+
+ // A few thousand adjacent lists is fine.
+ std::string not_evil("[");
+ not_evil.reserve(15010);
+ for (int i = 0; i < 5000; ++i) {
+ not_evil.append("[],");
+ }
+ not_evil.append("[]]");
+ root.reset(JSONReader::Read(not_evil));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_LIST));
+ list = static_cast<ListValue*>(root.get());
+ EXPECT_EQ(5001U, list->GetSize());
+
+ // Test utf8 encoded input
+ root.reset(JSONReader().ReadToValue("\"\xe7\xbd\x91\xe9\xa1\xb5\""));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
+ str_val.clear();
+ EXPECT_TRUE(root->GetAsString(&str_val));
+ EXPECT_EQ(L"\x7f51\x9875", UTF8ToWide(str_val));
+
+ root.reset(JSONReader().ReadToValue(
+ "{\"path\": \"/tmp/\xc3\xa0\xc3\xa8\xc3\xb2.png\"}"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
+ EXPECT_TRUE(root->GetAsDictionary(&dict_val));
+ EXPECT_TRUE(dict_val->GetString("path", &str_val));
+ EXPECT_EQ("/tmp/\xC3\xA0\xC3\xA8\xC3\xB2.png", str_val);
+
+ // Test invalid utf8 encoded input
+ root.reset(JSONReader().ReadToValue("\"345\xb0\xa1\xb0\xa2\""));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader().ReadToValue("\"123\xc0\x81\""));
+ EXPECT_FALSE(root.get());
+ root.reset(JSONReader().ReadToValue("\"abc\xc0\xae\""));
+ EXPECT_FALSE(root.get());
+
+ // Test utf16 encoded strings.
+ root.reset(JSONReader().ReadToValue("\"\\u20ac3,14\""));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
+ str_val.clear();
+ EXPECT_TRUE(root->GetAsString(&str_val));
+ EXPECT_EQ("\xe2\x82\xac""3,14", str_val);
+
+ root.reset(JSONReader().ReadToValue("\"\\ud83d\\udca9\\ud83d\\udc6c\""));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->IsType(Value::TYPE_STRING));
+ str_val.clear();
+ EXPECT_TRUE(root->GetAsString(&str_val));
+ EXPECT_EQ("\xf0\x9f\x92\xa9\xf0\x9f\x91\xac", str_val);
+
+ // Test invalid utf16 strings.
+ const char* cases[] = {
+ "\"\\u123\"", // Invalid scalar.
+ "\"\\ud83d\"", // Invalid scalar.
+ "\"\\u$%@!\"", // Invalid scalar.
+ "\"\\uzz89\"", // Invalid scalar.
+ "\"\\ud83d\\udca\"", // Invalid lower surrogate.
+ "\"\\ud83d\\ud83d\"", // Invalid lower surrogate.
+ "\"\\ud83foo\"", // No lower surrogate.
+ "\"\\ud83\\foo\"" // No lower surrogate.
+ };
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ root.reset(JSONReader().ReadToValue(cases[i]));
+ EXPECT_FALSE(root.get()) << cases[i];
+ }
+
+ // Test literal root objects.
+ root.reset(JSONReader::Read("null"));
+ EXPECT_TRUE(root->IsType(Value::TYPE_NULL));
+
+ root.reset(JSONReader::Read("true"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->GetAsBoolean(&bool_value));
+ EXPECT_TRUE(bool_value);
+
+ root.reset(JSONReader::Read("10"));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->GetAsInteger(&integer_value));
+ EXPECT_EQ(10, integer_value);
+
+ root.reset(JSONReader::Read("\"root\""));
+ ASSERT_TRUE(root.get());
+ EXPECT_TRUE(root->GetAsString(&str_val));
+ EXPECT_EQ("root", str_val);
+}
+
+TEST(JSONReaderTest, ReadFromFile) {
+ FilePath path;
+ ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &path));
+ path = path.Append(FILE_PATH_LITERAL("base"))
+ .Append(FILE_PATH_LITERAL("data"))
+ .Append(FILE_PATH_LITERAL("json"));
+
+ std::string input;
+ ASSERT_TRUE(file_util::ReadFileToString(
+ path.Append(FILE_PATH_LITERAL("bom_feff.json")), &input));
+
+ JSONReader reader;
+ scoped_ptr<Value> root(reader.ReadToValue(input));
+ ASSERT_TRUE(root.get()) << reader.GetErrorMessage();
+ EXPECT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
+}
+
+// Tests that the root of a JSON object can be deleted safely while its
+// children outlive it.
+TEST(JSONReaderTest, StringOptimizations) {
+ Value* dict_literals[2] = {0};
+ Value* dict_strings[2] = {0};
+ Value* list_values[2] = {0};
+
+ {
+ scoped_ptr<Value> root(JSONReader::Read(
+ "{"
+ " \"test\": {"
+ " \"foo\": true,"
+ " \"bar\": 3.14,"
+ " \"baz\": \"bat\","
+ " \"moo\": \"cow\""
+ " },"
+ " \"list\": ["
+ " \"a\","
+ " \"b\""
+ " ]"
+ "}", JSON_DETACHABLE_CHILDREN));
+ ASSERT_TRUE(root.get());
+
+ DictionaryValue* root_dict = NULL;
+ ASSERT_TRUE(root->GetAsDictionary(&root_dict));
+
+ DictionaryValue* dict = NULL;
+ ListValue* list = NULL;
+
+ ASSERT_TRUE(root_dict->GetDictionary("test", &dict));
+ ASSERT_TRUE(root_dict->GetList("list", &list));
+
+ EXPECT_TRUE(dict->Remove("foo", &dict_literals[0]));
+ EXPECT_TRUE(dict->Remove("bar", &dict_literals[1]));
+ EXPECT_TRUE(dict->Remove("baz", &dict_strings[0]));
+ EXPECT_TRUE(dict->Remove("moo", &dict_strings[1]));
+
+ ASSERT_EQ(2u, list->GetSize());
+ EXPECT_TRUE(list->Remove(0, &list_values[0]));
+ EXPECT_TRUE(list->Remove(0, &list_values[1]));
+ }
+
+ bool b = false;
+ double d = 0;
+ std::string s;
+
+ EXPECT_TRUE(dict_literals[0]->GetAsBoolean(&b));
+ EXPECT_TRUE(b);
+
+ EXPECT_TRUE(dict_literals[1]->GetAsDouble(&d));
+ EXPECT_EQ(3.14, d);
+
+ EXPECT_TRUE(dict_strings[0]->GetAsString(&s));
+ EXPECT_EQ("bat", s);
+
+ EXPECT_TRUE(dict_strings[1]->GetAsString(&s));
+ EXPECT_EQ("cow", s);
+
+ EXPECT_TRUE(list_values[0]->GetAsString(&s));
+ EXPECT_EQ("a", s);
+ EXPECT_TRUE(list_values[1]->GetAsString(&s));
+ EXPECT_EQ("b", s);
+
+ delete dict_literals[0];
+ delete dict_literals[1];
+ delete dict_strings[0];
+ delete dict_strings[1];
+ delete list_values[0];
+ delete list_values[1];
+}
+
+// A smattering of invalid JSON designed to test specific portions of the
+// parser implementation against buffer overflow. Best run with DCHECKs so
+// that the one in NextChar fires.
+TEST(JSONReaderTest, InvalidSanity) {
+ const char* invalid_json[] = {
+ "/* test *",
+ "{\"foo\"",
+ "{\"foo\":",
+ " [",
+ "\"\\u123g\"",
+ "{\n\"eh:\n}",
+ };
+
+ for (size_t i = 0; i < arraysize(invalid_json); ++i) {
+ JSONReader reader;
+ LOG(INFO) << "Sanity test " << i << ": <" << invalid_json[i] << ">";
+ EXPECT_FALSE(reader.ReadToValue(invalid_json[i]));
+ EXPECT_NE(JSONReader::JSON_NO_ERROR, reader.error_code());
+ EXPECT_NE("", reader.GetErrorMessage());
+ }
+}
+
+TEST(JSONReaderTest, IllegalTrailingNull) {
+ const char json[] = { '"', 'n', 'u', 'l', 'l', '"', '\0' };
+ std::string json_string(json, sizeof(json));
+ JSONReader reader;
+ EXPECT_FALSE(reader.ReadToValue(json_string));
+ EXPECT_EQ(JSONReader::JSON_UNEXPECTED_DATA_AFTER_ROOT, reader.error_code());
+}
+
+} // namespace base
diff --git a/src/base/json/json_string_value_serializer.cc b/src/base/json/json_string_value_serializer.cc
new file mode 100644
index 0000000..59c0765
--- /dev/null
+++ b/src/base/json/json_string_value_serializer.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2012 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/json/json_string_value_serializer.h"
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+
+JSONStringValueSerializer::~JSONStringValueSerializer() {}
+
+bool JSONStringValueSerializer::Serialize(const Value& root) {
+ return SerializeInternal(root, false);
+}
+
+bool JSONStringValueSerializer::SerializeAndOmitBinaryValues(
+ const Value& root) {
+ return SerializeInternal(root, true);
+}
+
+bool JSONStringValueSerializer::SerializeInternal(const Value& root,
+ bool omit_binary_values) {
+ if (!json_string_ || initialized_with_const_string_)
+ return false;
+
+ int options = 0;
+ if (omit_binary_values)
+ options |= base::JSONWriter::OPTIONS_OMIT_BINARY_VALUES;
+ if (pretty_print_)
+ options |= base::JSONWriter::OPTIONS_PRETTY_PRINT;
+
+ base::JSONWriter::WriteWithOptions(&root, options, json_string_);
+ return true;
+}
+
+Value* JSONStringValueSerializer::Deserialize(int* error_code,
+ std::string* error_str) {
+ if (!json_string_)
+ return NULL;
+
+ return base::JSONReader::ReadAndReturnError(*json_string_,
+ allow_trailing_comma_ ? base::JSON_ALLOW_TRAILING_COMMAS :
+ base::JSON_PARSE_RFC,
+ error_code, error_str);
+}
diff --git a/src/base/json/json_string_value_serializer.h b/src/base/json/json_string_value_serializer.h
new file mode 100644
index 0000000..e333dc6
--- /dev/null
+++ b/src/base/json/json_string_value_serializer.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2012 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_JSON_JSON_STRING_VALUE_SERIALIZER_H_
+#define BASE_JSON_JSON_STRING_VALUE_SERIALIZER_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/values.h"
+
+class BASE_EXPORT JSONStringValueSerializer : public base::ValueSerializer {
+ public:
+ // json_string is the string that will be source of the deserialization
+ // or the destination of the serialization. The caller of the constructor
+ // retains ownership of the string.
+ explicit JSONStringValueSerializer(std::string* json_string)
+ : json_string_(json_string),
+ initialized_with_const_string_(false),
+ pretty_print_(false),
+ allow_trailing_comma_(false) {
+ }
+
+ // This version allows initialization with a const string reference for
+ // deserialization only.
+ explicit JSONStringValueSerializer(const std::string& json_string)
+ : json_string_(&const_cast<std::string&>(json_string)),
+ initialized_with_const_string_(true),
+ pretty_print_(false),
+ allow_trailing_comma_(false) {
+ }
+
+ virtual ~JSONStringValueSerializer();
+
+ // Attempt to serialize the data structure represented by Value into
+ // JSON. If the return value is true, the result will have been written
+ // into the string passed into the constructor.
+ virtual bool Serialize(const Value& root) OVERRIDE;
+
+ // Equivalent to Serialize(root) except binary values are omitted from the
+ // output.
+ bool SerializeAndOmitBinaryValues(const Value& root);
+
+ // Attempt to deserialize the data structure encoded in the string passed
+ // in to the constructor into a structure of Value objects. If the return
+ // value is NULL, and if |error_code| is non-null, |error_code| will
+ // contain an integer error code (either JsonFileError or JsonParseError).
+ // If |error_message| is non-null, it will be filled in with a formatted
+ // error message including the location of the error if appropriate.
+ // The caller takes ownership of the returned value.
+ virtual Value* Deserialize(int* error_code,
+ std::string* error_message) OVERRIDE;
+
+ void set_pretty_print(bool new_value) { pretty_print_ = new_value; }
+ bool pretty_print() { return pretty_print_; }
+
+ void set_allow_trailing_comma(bool new_value) {
+ allow_trailing_comma_ = new_value;
+ }
+
+ private:
+ bool SerializeInternal(const Value& root, bool omit_binary_values);
+
+ std::string* json_string_;
+ bool initialized_with_const_string_;
+ bool pretty_print_; // If true, serialization will span multiple lines.
+ // If true, deserialization will allow trailing commas.
+ bool allow_trailing_comma_;
+
+ DISALLOW_COPY_AND_ASSIGN(JSONStringValueSerializer);
+};
+
+#endif // BASE_JSON_JSON_STRING_VALUE_SERIALIZER_H_
+
diff --git a/src/base/json/json_value_converter.h b/src/base/json/json_value_converter.h
new file mode 100644
index 0000000..69da0d8
--- /dev/null
+++ b/src/base/json/json_value_converter.h
@@ -0,0 +1,527 @@
+// Copyright (c) 2012 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_JSON_JSON_VALUE_CONVERTER_H_
+#define BASE_JSON_JSON_VALUE_CONVERTER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/stl_util.h"
+#include "base/string16.h"
+#include "base/string_piece.h"
+#include "base/values.h"
+
+// JSONValueConverter converts a JSON value into a C++ struct in a
+// lightweight way.
+//
+// Usage:
+// For real examples, you may want to refer to _unittest.cc file.
+//
+// Assume that you have a struct like this:
+// struct Message {
+// int foo;
+// std::string bar;
+// static void RegisterJSONConverter(
+// JSONValueConverter<Message>* converter);
+// };
+//
+// And you want to parse a json data into this struct. First, you
+// need to declare RegisterJSONConverter() method in your struct.
+// // static
+// void Message::RegisterJSONConverter(
+// JSONValueConverter<Message>* converter) {
+// converter->RegisterIntField("foo", &Message::foo);
+// converter->RegisterStringField("bar", &Message::bar);
+// }
+//
+// Then, you just instantiate your JSONValueConverter of your type and call
+// Convert() method.
+// Message message;
+// JSONValueConverter<Message> converter;
+// converter.Convert(json, &message);
+//
+// Convert() returns false when it fails. Here "fail" means that the value is
+// structurally different from expected, such like a string value appears
+// for an int field. Do not report failures for missing fields.
+// Also note that Convert() will modify the passed |message| even when it
+// fails for performance reason.
+//
+// For nested field, the internal message also has to implement the registration
+// method. Then, just use RegisterNestedField() from the containing struct's
+// RegisterJSONConverter method.
+// struct Nested {
+// Message foo;
+// static void RegisterJSONConverter(...) {
+// ...
+// converter->RegisterNestedField("foo", &Nested::foo);
+// }
+// };
+//
+// For repeated field, we just assume ScopedVector for its container
+// and you can put RegisterRepeatedInt or some other types. Use
+// RegisterRepeatedMessage for nested repeated fields.
+//
+// Sometimes JSON format uses string representations for other types such
+// like enum, timestamp, or URL. You can use RegisterCustomField method
+// and specify a function to convert a StringPiece to your type.
+// bool ConvertFunc(const StringPiece& s, YourEnum* result) {
+// // do something and return true if succeed...
+// }
+// struct Message {
+// YourEnum ye;
+// ...
+// static void RegisterJSONConverter(...) {
+// ...
+// converter->RegsiterCustomField<YourEnum>(
+// "your_enum", &Message::ye, &ConvertFunc);
+// }
+// };
+
+namespace base {
+
+template <typename StructType>
+class JSONValueConverter;
+
+namespace internal {
+
+template<typename StructType>
+class FieldConverterBase {
+ public:
+ explicit FieldConverterBase(const std::string& path) : field_path_(path) {}
+ virtual ~FieldConverterBase() {}
+ virtual bool ConvertField(const base::Value& value, StructType* obj)
+ const = 0;
+ const std::string& field_path() const { return field_path_; }
+
+ private:
+ std::string field_path_;
+ DISALLOW_COPY_AND_ASSIGN(FieldConverterBase);
+};
+
+template <typename FieldType>
+class ValueConverter {
+ public:
+ virtual ~ValueConverter() {}
+ virtual bool Convert(const base::Value& value, FieldType* field) const = 0;
+};
+
+template <typename StructType, typename FieldType>
+class FieldConverter : public FieldConverterBase<StructType> {
+ public:
+ explicit FieldConverter(const std::string& path,
+ FieldType StructType::* field,
+ ValueConverter<FieldType>* converter)
+ : FieldConverterBase<StructType>(path),
+ field_pointer_(field),
+ value_converter_(converter) {
+ }
+
+ virtual bool ConvertField(
+ const base::Value& value, StructType* dst) const OVERRIDE {
+ return value_converter_->Convert(value, &(dst->*field_pointer_));
+ }
+
+ private:
+ FieldType StructType::* field_pointer_;
+ scoped_ptr<ValueConverter<FieldType> > value_converter_;
+ DISALLOW_COPY_AND_ASSIGN(FieldConverter);
+};
+
+template <typename FieldType>
+class BasicValueConverter;
+
+template <>
+class BasicValueConverter<int> : public ValueConverter<int> {
+ public:
+ BasicValueConverter() {}
+
+ virtual bool Convert(const base::Value& value, int* field) const OVERRIDE {
+ return value.GetAsInteger(field);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
+};
+
+template <>
+class BasicValueConverter<std::string> : public ValueConverter<std::string> {
+ public:
+ BasicValueConverter() {}
+
+ virtual bool Convert(
+ const base::Value& value, std::string* field) const OVERRIDE {
+ return value.GetAsString(field);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
+};
+
+template <>
+class BasicValueConverter<string16> : public ValueConverter<string16> {
+ public:
+ BasicValueConverter() {}
+
+ virtual bool Convert(
+ const base::Value& value, string16* field) const OVERRIDE {
+ return value.GetAsString(field);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
+};
+
+template <>
+class BasicValueConverter<double> : public ValueConverter<double> {
+ public:
+ BasicValueConverter() {}
+
+ virtual bool Convert(const base::Value& value, double* field) const OVERRIDE {
+ return value.GetAsDouble(field);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
+};
+
+template <>
+class BasicValueConverter<bool> : public ValueConverter<bool> {
+ public:
+ BasicValueConverter() {}
+
+ virtual bool Convert(const base::Value& value, bool* field) const OVERRIDE {
+ return value.GetAsBoolean(field);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicValueConverter);
+};
+
+template <typename FieldType>
+class ValueFieldConverter : public ValueConverter<FieldType> {
+ public:
+ typedef bool(*ConvertFunc)(const base::Value* value, FieldType* field);
+
+ ValueFieldConverter(ConvertFunc convert_func)
+ : convert_func_(convert_func) {}
+
+ virtual bool Convert(const base::Value& value,
+ FieldType* field) const OVERRIDE {
+ return convert_func_(&value, field);
+ }
+
+ private:
+ ConvertFunc convert_func_;
+
+ DISALLOW_COPY_AND_ASSIGN(ValueFieldConverter);
+};
+
+template <typename FieldType>
+class CustomFieldConverter : public ValueConverter<FieldType> {
+ public:
+ typedef bool(*ConvertFunc)(const StringPiece& value, FieldType* field);
+
+ CustomFieldConverter(ConvertFunc convert_func)
+ : convert_func_(convert_func) {}
+
+ virtual bool Convert(const base::Value& value,
+ FieldType* field) const OVERRIDE {
+ std::string string_value;
+ return value.GetAsString(&string_value) &&
+ convert_func_(string_value, field);
+ }
+
+ private:
+ ConvertFunc convert_func_;
+
+ DISALLOW_COPY_AND_ASSIGN(CustomFieldConverter);
+};
+
+template <typename NestedType>
+class NestedValueConverter : public ValueConverter<NestedType> {
+ public:
+ NestedValueConverter() {}
+
+ virtual bool Convert(
+ const base::Value& value, NestedType* field) const OVERRIDE {
+ return converter_.Convert(value, field);
+ }
+
+ private:
+ JSONValueConverter<NestedType> converter_;
+ DISALLOW_COPY_AND_ASSIGN(NestedValueConverter);
+};
+
+template <typename Element>
+class RepeatedValueConverter : public ValueConverter<ScopedVector<Element> > {
+ public:
+ RepeatedValueConverter() {}
+
+ virtual bool Convert(
+ const base::Value& value, ScopedVector<Element>* field) const OVERRIDE {
+ const base::ListValue* list = NULL;
+ if (!value.GetAsList(&list)) {
+ // The field is not a list.
+ return false;
+ }
+
+ field->reserve(list->GetSize());
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ const base::Value* element = NULL;
+ if (!list->Get(i, &element))
+ continue;
+
+ scoped_ptr<Element> e(new Element);
+ if (basic_converter_.Convert(*element, e.get())) {
+ field->push_back(e.release());
+ } else {
+ DVLOG(1) << "failure at " << i << "-th element";
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ BasicValueConverter<Element> basic_converter_;
+ DISALLOW_COPY_AND_ASSIGN(RepeatedValueConverter);
+};
+
+template <typename NestedType>
+class RepeatedMessageConverter
+ : public ValueConverter<ScopedVector<NestedType> > {
+ public:
+ RepeatedMessageConverter() {}
+
+ virtual bool Convert(const base::Value& value,
+ ScopedVector<NestedType>* field) const OVERRIDE {
+ const base::ListValue* list = NULL;
+ if (!value.GetAsList(&list))
+ return false;
+
+ field->reserve(list->GetSize());
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ const base::Value* element = NULL;
+ if (!list->Get(i, &element))
+ continue;
+
+ scoped_ptr<NestedType> nested(new NestedType);
+ if (converter_.Convert(*element, nested.get())) {
+ field->push_back(nested.release());
+ } else {
+ DVLOG(1) << "failure at " << i << "-th element";
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ JSONValueConverter<NestedType> converter_;
+ DISALLOW_COPY_AND_ASSIGN(RepeatedMessageConverter);
+};
+
+template <typename NestedType>
+class RepeatedCustomValueConverter
+ : public ValueConverter<ScopedVector<NestedType> > {
+ public:
+ typedef bool(*ConvertFunc)(const base::Value* value, NestedType* field);
+
+ RepeatedCustomValueConverter(ConvertFunc convert_func)
+ : convert_func_(convert_func) {}
+
+ virtual bool Convert(const base::Value& value,
+ ScopedVector<NestedType>* field) const OVERRIDE {
+ const base::ListValue* list = NULL;
+ if (!value.GetAsList(&list))
+ return false;
+
+ field->reserve(list->GetSize());
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ const base::Value* element = NULL;
+ if (!list->Get(i, &element))
+ continue;
+
+ scoped_ptr<NestedType> nested(new NestedType);
+ if ((*convert_func_)(element, nested.get())) {
+ field->push_back(nested.release());
+ } else {
+ DVLOG(1) << "failure at " << i << "-th element";
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ ConvertFunc convert_func_;
+ DISALLOW_COPY_AND_ASSIGN(RepeatedCustomValueConverter);
+};
+
+
+} // namespace internal
+
+template <class StructType>
+class JSONValueConverter {
+ public:
+ JSONValueConverter() {
+ StructType::RegisterJSONConverter(this);
+ }
+
+ void RegisterIntField(const std::string& field_name,
+ int StructType::* field) {
+ fields_.push_back(new internal::FieldConverter<StructType, int>(
+ field_name, field, new internal::BasicValueConverter<int>));
+ }
+
+ void RegisterStringField(const std::string& field_name,
+ std::string StructType::* field) {
+ fields_.push_back(new internal::FieldConverter<StructType, std::string>(
+ field_name, field, new internal::BasicValueConverter<std::string>));
+ }
+
+ void RegisterStringField(const std::string& field_name,
+ string16 StructType::* field) {
+ fields_.push_back(new internal::FieldConverter<StructType, string16>(
+ field_name, field, new internal::BasicValueConverter<string16>));
+ }
+
+ void RegisterBoolField(const std::string& field_name,
+ bool StructType::* field) {
+ fields_.push_back(new internal::FieldConverter<StructType, bool>(
+ field_name, field, new internal::BasicValueConverter<bool>));
+ }
+
+ void RegisterDoubleField(const std::string& field_name,
+ double StructType::* field) {
+ fields_.push_back(new internal::FieldConverter<StructType, double>(
+ field_name, field, new internal::BasicValueConverter<double>));
+ }
+
+ template <class NestedType>
+ void RegisterNestedField(
+ const std::string& field_name, NestedType StructType::* field) {
+ fields_.push_back(new internal::FieldConverter<StructType, NestedType>(
+ field_name,
+ field,
+ new internal::NestedValueConverter<NestedType>));
+ }
+
+ template <typename FieldType>
+ void RegisterCustomField(
+ const std::string& field_name,
+ FieldType StructType::* field,
+ bool (*convert_func)(const StringPiece&, FieldType*)) {
+ fields_.push_back(new internal::FieldConverter<StructType, FieldType>(
+ field_name,
+ field,
+ new internal::CustomFieldConverter<FieldType>(convert_func)));
+ }
+
+ template <typename FieldType>
+ void RegisterCustomValueField(
+ const std::string& field_name,
+ FieldType StructType::* field,
+ bool (*convert_func)(const base::Value*, FieldType*)) {
+ fields_.push_back(new internal::FieldConverter<StructType, FieldType>(
+ field_name,
+ field,
+ new internal::ValueFieldConverter<FieldType>(convert_func)));
+ }
+
+ void RegisterRepeatedInt(const std::string& field_name,
+ ScopedVector<int> StructType::* field) {
+ fields_.push_back(
+ new internal::FieldConverter<StructType, ScopedVector<int> >(
+ field_name, field, new internal::RepeatedValueConverter<int>));
+ }
+
+ void RegisterRepeatedString(const std::string& field_name,
+ ScopedVector<std::string> StructType::* field) {
+ fields_.push_back(
+ new internal::FieldConverter<StructType, ScopedVector<std::string> >(
+ field_name,
+ field,
+ new internal::RepeatedValueConverter<std::string>));
+ }
+
+ void RegisterRepeatedString(const std::string& field_name,
+ ScopedVector<string16> StructType::* field) {
+ fields_.push_back(
+ new internal::FieldConverter<StructType, ScopedVector<string16> >(
+ field_name,
+ field,
+ new internal::RepeatedValueConverter<string16>));
+ }
+
+ void RegisterRepeatedDouble(const std::string& field_name,
+ ScopedVector<double> StructType::* field) {
+ fields_.push_back(
+ new internal::FieldConverter<StructType, ScopedVector<double> >(
+ field_name, field, new internal::RepeatedValueConverter<double>));
+ }
+
+ void RegisterRepeatedBool(const std::string& field_name,
+ ScopedVector<bool> StructType::* field) {
+ fields_.push_back(
+ new internal::FieldConverter<StructType, ScopedVector<bool> >(
+ field_name, field, new internal::RepeatedValueConverter<bool>));
+ }
+
+ template <class NestedType>
+ void RegisterRepeatedCustomValue(
+ const std::string& field_name,
+ ScopedVector<NestedType> StructType::* field,
+ bool (*convert_func)(const base::Value*, NestedType*)) {
+ fields_.push_back(
+ new internal::FieldConverter<StructType, ScopedVector<NestedType> >(
+ field_name,
+ field,
+ new internal::RepeatedCustomValueConverter<NestedType>(
+ convert_func)));
+ }
+
+ template <class NestedType>
+ void RegisterRepeatedMessage(const std::string& field_name,
+ ScopedVector<NestedType> StructType::* field) {
+ fields_.push_back(
+ new internal::FieldConverter<StructType, ScopedVector<NestedType> >(
+ field_name,
+ field,
+ new internal::RepeatedMessageConverter<NestedType>));
+ }
+
+ bool Convert(const base::Value& value, StructType* output) const {
+ const DictionaryValue* dictionary_value = NULL;
+ if (!value.GetAsDictionary(&dictionary_value))
+ return false;
+
+ for(size_t i = 0; i < fields_.size(); ++i) {
+ const internal::FieldConverterBase<StructType>* field_converter =
+ fields_[i];
+ const base::Value* field = NULL;
+ if (dictionary_value->Get(field_converter->field_path(), &field)) {
+ if (!field_converter->ConvertField(*field, output)) {
+ DVLOG(1) << "failure at field " << field_converter->field_path();
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private:
+ ScopedVector<internal::FieldConverterBase<StructType> > fields_;
+
+ DISALLOW_COPY_AND_ASSIGN(JSONValueConverter);
+};
+
+} // namespace base
+
+#endif // BASE_JSON_JSON_VALUE_CONVERTER_H_
diff --git a/src/base/json/json_value_converter_unittest.cc b/src/base/json/json_value_converter_unittest.cc
new file mode 100644
index 0000000..e5ad289
--- /dev/null
+++ b/src/base/json/json_value_converter_unittest.cc
@@ -0,0 +1,256 @@
+// Copyright (c) 2012 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/json/json_value_converter.h"
+
+#include <string>
+#include <vector>
+
+#include "base/values.h"
+#include "base/json/json_reader.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/string_piece.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+// Very simple messages.
+struct SimpleMessage {
+ enum SimpleEnum {
+ FOO, BAR,
+ };
+ int foo;
+ std::string bar;
+ bool baz;
+ bool bstruct;
+ SimpleEnum simple_enum;
+ ScopedVector<int> ints;
+ ScopedVector<std::string> string_values;
+ SimpleMessage() : foo(0), baz(false), bstruct(false), simple_enum(FOO) {}
+
+ static bool ParseSimpleEnum(const StringPiece& value, SimpleEnum* field) {
+ if (value == "foo") {
+ *field = FOO;
+ return true;
+ } else if (value == "bar") {
+ *field = BAR;
+ return true;
+ }
+ return false;
+ }
+
+ static bool HasFieldPresent(const base::Value* value, bool* result) {
+ *result = value != NULL;
+ return true;
+ }
+
+ static bool GetValueString(const base::Value* value, std::string* result) {
+ const base::DictionaryValue* dict = NULL;
+ if (!value->GetAsDictionary(&dict))
+ return false;
+
+ if (!dict->GetString("val", result))
+ return false;
+
+ return true;
+ }
+
+ static void RegisterJSONConverter(
+ base::JSONValueConverter<SimpleMessage>* converter) {
+ converter->RegisterIntField("foo", &SimpleMessage::foo);
+ converter->RegisterStringField("bar", &SimpleMessage::bar);
+ converter->RegisterBoolField("baz", &SimpleMessage::baz);
+ converter->RegisterCustomField<SimpleEnum>(
+ "simple_enum", &SimpleMessage::simple_enum, &ParseSimpleEnum);
+ converter->RegisterRepeatedInt("ints", &SimpleMessage::ints);
+ converter->RegisterCustomValueField<bool>("bstruct",
+ &SimpleMessage::bstruct,
+ &HasFieldPresent);
+ converter->RegisterRepeatedCustomValue<std::string>(
+ "string_values",
+ &SimpleMessage::string_values,
+ &GetValueString);
+ }
+};
+
+// For nested messages.
+struct NestedMessage {
+ double foo;
+ SimpleMessage child;
+ ScopedVector<SimpleMessage> children;
+
+ NestedMessage() : foo(0) {}
+
+ static void RegisterJSONConverter(
+ base::JSONValueConverter<NestedMessage>* converter) {
+ converter->RegisterDoubleField("foo", &NestedMessage::foo);
+ converter->RegisterNestedField("child", &NestedMessage::child);
+ converter->RegisterRepeatedMessage("children", &NestedMessage::children);
+ }
+};
+
+} // namespace
+
+TEST(JSONValueConverterTest, ParseSimpleMessage) {
+ const char normal_data[] =
+ "{\n"
+ " \"foo\": 1,\n"
+ " \"bar\": \"bar\",\n"
+ " \"baz\": true,\n"
+ " \"bstruct\": {},\n"
+ " \"string_values\": [{\"val\": \"value_1\"}, {\"val\": \"value_2\"}],"
+ " \"simple_enum\": \"foo\","
+ " \"ints\": [1, 2]"
+ "}\n";
+
+ scoped_ptr<Value> value(base::JSONReader::Read(normal_data));
+ SimpleMessage message;
+ base::JSONValueConverter<SimpleMessage> converter;
+ EXPECT_TRUE(converter.Convert(*value.get(), &message));
+
+ EXPECT_EQ(1, message.foo);
+ EXPECT_EQ("bar", message.bar);
+ EXPECT_TRUE(message.baz);
+ EXPECT_EQ(SimpleMessage::FOO, message.simple_enum);
+ EXPECT_EQ(2, static_cast<int>(message.ints.size()));
+ ASSERT_EQ(2U, message.string_values.size());
+ EXPECT_EQ("value_1", *message.string_values[0]);
+ EXPECT_EQ("value_2", *message.string_values[1]);
+ EXPECT_EQ(1, *(message.ints[0]));
+ EXPECT_EQ(2, *(message.ints[1]));
+}
+
+TEST(JSONValueConverterTest, ParseNestedMessage) {
+ const char normal_data[] =
+ "{\n"
+ " \"foo\": 1.0,\n"
+ " \"child\": {\n"
+ " \"foo\": 1,\n"
+ " \"bar\": \"bar\",\n"
+ " \"bstruct\": {},\n"
+ " \"string_values\": [{\"val\": \"value_1\"}, {\"val\": \"value_2\"}],"
+ " \"baz\": true\n"
+ " },\n"
+ " \"children\": [{\n"
+ " \"foo\": 2,\n"
+ " \"bar\": \"foobar\",\n"
+ " \"bstruct\": \"\",\n"
+ " \"string_values\": [{\"val\": \"value_1\"}],"
+ " \"baz\": true\n"
+ " },\n"
+ " {\n"
+ " \"foo\": 3,\n"
+ " \"bar\": \"barbaz\",\n"
+ " \"baz\": false\n"
+ " }]\n"
+ "}\n";
+
+ scoped_ptr<Value> value(base::JSONReader::Read(normal_data));
+ NestedMessage message;
+ base::JSONValueConverter<NestedMessage> converter;
+ EXPECT_TRUE(converter.Convert(*value.get(), &message));
+
+ EXPECT_EQ(1.0, message.foo);
+ EXPECT_EQ(1, message.child.foo);
+ EXPECT_EQ("bar", message.child.bar);
+ EXPECT_TRUE(message.child.baz);
+ EXPECT_TRUE(message.child.bstruct);
+ ASSERT_EQ(2U, message.child.string_values.size());
+ EXPECT_EQ("value_1", *message.child.string_values[0]);
+ EXPECT_EQ("value_2", *message.child.string_values[1]);
+
+ EXPECT_EQ(2, static_cast<int>(message.children.size()));
+ const SimpleMessage* first_child = message.children[0];
+ ASSERT_TRUE(first_child);
+ EXPECT_EQ(2, first_child->foo);
+ EXPECT_EQ("foobar", first_child->bar);
+ EXPECT_TRUE(first_child->baz);
+ EXPECT_TRUE(first_child->bstruct);
+ ASSERT_EQ(1U, first_child->string_values.size());
+ EXPECT_EQ("value_1", *first_child->string_values[0]);
+
+ const SimpleMessage* second_child = message.children[1];
+ ASSERT_TRUE(second_child);
+ EXPECT_EQ(3, second_child->foo);
+ EXPECT_EQ("barbaz", second_child->bar);
+ EXPECT_FALSE(second_child->baz);
+ EXPECT_FALSE(second_child->bstruct);
+ EXPECT_EQ(0U, second_child->string_values.size());
+}
+
+TEST(JSONValueConverterTest, ParseFailures) {
+ const char normal_data[] =
+ "{\n"
+ " \"foo\": 1,\n"
+ " \"bar\": 2,\n" // "bar" is an integer here.
+ " \"baz\": true,\n"
+ " \"ints\": [1, 2]"
+ "}\n";
+
+ scoped_ptr<Value> value(base::JSONReader::Read(normal_data));
+ SimpleMessage message;
+ base::JSONValueConverter<SimpleMessage> converter;
+ EXPECT_FALSE(converter.Convert(*value.get(), &message));
+ // Do not check the values below. |message| may be modified during
+ // Convert() even it fails.
+}
+
+TEST(JSONValueConverterTest, ParseWithMissingFields) {
+ const char normal_data[] =
+ "{\n"
+ " \"foo\": 1,\n"
+ " \"baz\": true,\n"
+ " \"ints\": [1, 2]"
+ "}\n";
+
+ scoped_ptr<Value> value(base::JSONReader::Read(normal_data));
+ SimpleMessage message;
+ base::JSONValueConverter<SimpleMessage> converter;
+ // Convert() still succeeds even if the input doesn't have "bar" field.
+ EXPECT_TRUE(converter.Convert(*value.get(), &message));
+
+ EXPECT_EQ(1, message.foo);
+ EXPECT_TRUE(message.baz);
+ EXPECT_EQ(2, static_cast<int>(message.ints.size()));
+ EXPECT_EQ(1, *(message.ints[0]));
+ EXPECT_EQ(2, *(message.ints[1]));
+}
+
+TEST(JSONValueConverterTest, EnumParserFails) {
+ const char normal_data[] =
+ "{\n"
+ " \"foo\": 1,\n"
+ " \"bar\": \"bar\",\n"
+ " \"baz\": true,\n"
+ " \"simple_enum\": \"baz\","
+ " \"ints\": [1, 2]"
+ "}\n";
+
+ scoped_ptr<Value> value(base::JSONReader::Read(normal_data));
+ SimpleMessage message;
+ base::JSONValueConverter<SimpleMessage> converter;
+ EXPECT_FALSE(converter.Convert(*value.get(), &message));
+ // No check the values as mentioned above.
+}
+
+TEST(JSONValueConverterTest, RepeatedValueErrorInTheMiddle) {
+ const char normal_data[] =
+ "{\n"
+ " \"foo\": 1,\n"
+ " \"bar\": \"bar\",\n"
+ " \"baz\": true,\n"
+ " \"simple_enum\": \"baz\","
+ " \"ints\": [1, false]"
+ "}\n";
+
+ scoped_ptr<Value> value(base::JSONReader::Read(normal_data));
+ SimpleMessage message;
+ base::JSONValueConverter<SimpleMessage> converter;
+ EXPECT_FALSE(converter.Convert(*value.get(), &message));
+ // No check the values as mentioned above.
+}
+
+} // namespace base
diff --git a/src/base/json/json_value_serializer_unittest.cc b/src/base/json/json_value_serializer_unittest.cc
new file mode 100644
index 0000000..dc103dd
--- /dev/null
+++ b/src/base/json/json_value_serializer_unittest.cc
@@ -0,0 +1,156 @@
+// Copyright (c) 2012 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/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+// Some proper JSON to test with:
+const char kProperJSON[] =
+ "{\n"
+ " \"compound\": {\n"
+ " \"a\": 1,\n"
+ " \"b\": 2\n"
+ " },\n"
+ " \"some_String\": \"1337\",\n"
+ " \"some_int\": 42,\n"
+ " \"the_list\": [ \"val1\", \"val2\" ]\n"
+ "}\n";
+
+// Some proper JSON with trailing commas:
+const char kProperJSONWithCommas[] =
+ "{\n"
+ "\t\"some_int\": 42,\n"
+ "\t\"some_String\": \"1337\",\n"
+ "\t\"the_list\": [\"val1\", \"val2\", ],\n"
+ "\t\"compound\": { \"a\": 1, \"b\": 2, },\n"
+ "}\n";
+
+const char kWinLineEnds[] = "\r\n";
+const char kLinuxLineEnds[] = "\n";
+
+// Verifies the generated JSON against the expected output.
+void CheckJSONIsStillTheSame(Value& value) {
+ // Serialize back the output.
+ std::string serialized_json;
+ JSONStringValueSerializer str_serializer(&serialized_json);
+ str_serializer.set_pretty_print(true);
+ ASSERT_TRUE(str_serializer.Serialize(value));
+ // Unify line endings between platforms.
+ ReplaceSubstringsAfterOffset(&serialized_json, 0,
+ kWinLineEnds, kLinuxLineEnds);
+ // Now compare the input with the output.
+ ASSERT_EQ(kProperJSON, serialized_json);
+}
+
+// Test proper JSON [de]serialization from string is working.
+TEST(JSONValueSerializerTest, ReadProperJSONFromString) {
+ // Try to deserialize it through the serializer.
+ std::string proper_json(kProperJSON);
+ JSONStringValueSerializer str_deserializer(proper_json);
+
+ int error_code = 0;
+ std::string error_message;
+ scoped_ptr<Value> value(
+ str_deserializer.Deserialize(&error_code, &error_message));
+ ASSERT_TRUE(value.get());
+ ASSERT_EQ(0, error_code);
+ ASSERT_TRUE(error_message.empty());
+ // Verify if the same JSON is still there.
+ CheckJSONIsStillTheSame(*value);
+}
+
+// Test that trialing commas are only properly deserialized from string when
+// the proper flag for that is set.
+TEST(JSONValueSerializerTest, ReadJSONWithTrailingCommasFromString) {
+ // Try to deserialize it through the serializer.
+ std::string proper_json(kProperJSONWithCommas);
+ JSONStringValueSerializer str_deserializer(proper_json);
+
+ int error_code = 0;
+ std::string error_message;
+ scoped_ptr<Value> value(
+ str_deserializer.Deserialize(&error_code, &error_message));
+ ASSERT_FALSE(value.get());
+ ASSERT_NE(0, error_code);
+ ASSERT_FALSE(error_message.empty());
+ // Now the flag is set and it must pass.
+ str_deserializer.set_allow_trailing_comma(true);
+ value.reset(str_deserializer.Deserialize(&error_code, &error_message));
+ ASSERT_TRUE(value.get());
+ ASSERT_EQ(JSONReader::JSON_TRAILING_COMMA, error_code);
+ // Verify if the same JSON is still there.
+ CheckJSONIsStillTheSame(*value);
+}
+
+// Test proper JSON [de]serialization from file is working.
+TEST(JSONValueSerializerTest, ReadProperJSONFromFile) {
+ ScopedTempDir tempdir;
+ ASSERT_TRUE(tempdir.CreateUniqueTempDir());
+ // Write it down in the file.
+ FilePath temp_file(tempdir.path().AppendASCII("test.json"));
+ ASSERT_EQ(static_cast<int>(strlen(kProperJSON)),
+ file_util::WriteFile(temp_file, kProperJSON, strlen(kProperJSON)));
+
+ // Try to deserialize it through the serializer.
+ JSONFileValueSerializer file_deserializer(temp_file);
+
+ int error_code = 0;
+ std::string error_message;
+ scoped_ptr<Value> value(
+ file_deserializer.Deserialize(&error_code, &error_message));
+ ASSERT_TRUE(value.get());
+ ASSERT_EQ(0, error_code);
+ ASSERT_TRUE(error_message.empty());
+ // Verify if the same JSON is still there.
+ CheckJSONIsStillTheSame(*value);
+}
+
+// Test that trialing commas are only properly deserialized from file when
+// the proper flag for that is set.
+TEST(JSONValueSerializerTest, ReadJSONWithCommasFromFile) {
+ ScopedTempDir tempdir;
+ ASSERT_TRUE(tempdir.CreateUniqueTempDir());
+ // Write it down in the file.
+ FilePath temp_file(tempdir.path().AppendASCII("test.json"));
+ ASSERT_EQ(static_cast<int>(strlen(kProperJSONWithCommas)),
+ file_util::WriteFile(temp_file,
+ kProperJSONWithCommas,
+ strlen(kProperJSONWithCommas)));
+
+ // Try to deserialize it through the serializer.
+ JSONFileValueSerializer file_deserializer(temp_file);
+ // This must fail without the proper flag.
+ int error_code = 0;
+ std::string error_message;
+ scoped_ptr<Value> value(
+ file_deserializer.Deserialize(&error_code, &error_message));
+ ASSERT_FALSE(value.get());
+ ASSERT_NE(0, error_code);
+ ASSERT_FALSE(error_message.empty());
+ // Now the flag is set and it must pass.
+ file_deserializer.set_allow_trailing_comma(true);
+ value.reset(file_deserializer.Deserialize(&error_code, &error_message));
+ ASSERT_TRUE(value.get());
+ ASSERT_EQ(JSONReader::JSON_TRAILING_COMMA, error_code);
+ // Verify if the same JSON is still there.
+ CheckJSONIsStillTheSame(*value);
+}
+
+} // namespace
+
+} // namespace base
+
diff --git a/src/base/json/json_writer.cc b/src/base/json/json_writer.cc
new file mode 100644
index 0000000..e8cf9ac
--- /dev/null
+++ b/src/base/json/json_writer.cc
@@ -0,0 +1,236 @@
+// Copyright (c) 2012 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/json/json_writer.h"
+
+#include <cmath>
+
+#include "base/json/string_escape.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "base/string_number_conversions.h"
+#include "base/values.h"
+#include "base/utf_string_conversions.h"
+
+namespace base {
+
+#if defined(OS_WIN)
+static const char kPrettyPrintLineEnding[] = "\r\n";
+#else
+static const char kPrettyPrintLineEnding[] = "\n";
+#endif
+
+/* static */
+const char* JSONWriter::kEmptyArray = "[]";
+
+/* static */
+void JSONWriter::Write(const Value* const node, std::string* json) {
+ WriteWithOptions(node, 0, json);
+}
+
+/* static */
+void JSONWriter::WriteWithOptions(const Value* const node, int options,
+ std::string* json) {
+ json->clear();
+ // Is there a better way to estimate the size of the output?
+ json->reserve(1024);
+
+ bool escape = !(options & OPTIONS_DO_NOT_ESCAPE);
+ bool omit_binary_values = !!(options & OPTIONS_OMIT_BINARY_VALUES);
+ bool omit_double_type_preservation =
+ !!(options & OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION);
+ bool pretty_print = !!(options & OPTIONS_PRETTY_PRINT);
+
+ JSONWriter writer(escape, omit_binary_values, omit_double_type_preservation,
+ pretty_print, json);
+ writer.BuildJSONString(node, 0);
+
+ if (pretty_print)
+ json->append(kPrettyPrintLineEnding);
+}
+
+JSONWriter::JSONWriter(bool escape, bool omit_binary_values,
+ bool omit_double_type_preservation, bool pretty_print,
+ std::string* json)
+ : escape_(escape),
+ omit_binary_values_(omit_binary_values),
+ omit_double_type_preservation_(omit_double_type_preservation),
+ pretty_print_(pretty_print),
+ json_string_(json) {
+ DCHECK(json);
+}
+
+void JSONWriter::BuildJSONString(const Value* const node, int depth) {
+ switch (node->GetType()) {
+ case Value::TYPE_NULL:
+ json_string_->append("null");
+ break;
+
+ case Value::TYPE_BOOLEAN:
+ {
+ bool value;
+ bool result = node->GetAsBoolean(&value);
+ DCHECK(result);
+ json_string_->append(value ? "true" : "false");
+ break;
+ }
+
+ case Value::TYPE_INTEGER:
+ {
+ int value;
+ bool result = node->GetAsInteger(&value);
+ DCHECK(result);
+ base::StringAppendF(json_string_, "%d", value);
+ break;
+ }
+
+ case Value::TYPE_DOUBLE:
+ {
+ double value;
+ bool result = node->GetAsDouble(&value);
+ DCHECK(result);
+ if (omit_double_type_preservation_ &&
+ value <= kint64max &&
+ value >= kint64min &&
+ std::floor(value) == value) {
+ json_string_->append(Int64ToString(static_cast<int64>(value)));
+ break;
+ }
+ std::string real = DoubleToString(value);
+ // Ensure that the number has a .0 if there's no decimal or 'e'. This
+ // makes sure that when we read the JSON back, it's interpreted as a
+ // real rather than an int.
+ if (real.find('.') == std::string::npos &&
+ real.find('e') == std::string::npos &&
+ real.find('E') == std::string::npos) {
+ real.append(".0");
+ }
+ // The JSON spec requires that non-integer values in the range (-1,1)
+ // have a zero before the decimal point - ".52" is not valid, "0.52" is.
+ if (real[0] == '.') {
+ real.insert(0, "0");
+ } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') {
+ // "-.1" bad "-0.1" good
+ real.insert(1, "0");
+ }
+ json_string_->append(real);
+ break;
+ }
+
+ case Value::TYPE_STRING:
+ {
+ std::string value;
+ bool result = node->GetAsString(&value);
+ DCHECK(result);
+ if (escape_) {
+ JsonDoubleQuote(UTF8ToUTF16(value), true, json_string_);
+ } else {
+ JsonDoubleQuote(value, true, json_string_);
+ }
+ break;
+ }
+
+ case Value::TYPE_LIST:
+ {
+ json_string_->append("[");
+ if (pretty_print_)
+ json_string_->append(" ");
+
+ const ListValue* list = static_cast<const ListValue*>(node);
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ const Value* value = NULL;
+ bool result = list->Get(i, &value);
+ DCHECK(result);
+
+ if (omit_binary_values_ && value->GetType() == Value::TYPE_BINARY) {
+ continue;
+ }
+
+ if (i != 0) {
+ json_string_->append(",");
+ if (pretty_print_)
+ json_string_->append(" ");
+ }
+
+ BuildJSONString(value, depth);
+ }
+
+ if (pretty_print_)
+ json_string_->append(" ");
+ json_string_->append("]");
+ break;
+ }
+
+ case Value::TYPE_DICTIONARY:
+ {
+ json_string_->append("{");
+ if (pretty_print_)
+ json_string_->append(kPrettyPrintLineEnding);
+
+ const DictionaryValue* dict =
+ static_cast<const DictionaryValue*>(node);
+ for (DictionaryValue::key_iterator key_itr = dict->begin_keys();
+ key_itr != dict->end_keys();
+ ++key_itr) {
+ const Value* value = NULL;
+ bool result = dict->GetWithoutPathExpansion(*key_itr, &value);
+ DCHECK(result);
+
+ if (omit_binary_values_ && value->GetType() == Value::TYPE_BINARY) {
+ continue;
+ }
+
+ if (key_itr != dict->begin_keys()) {
+ json_string_->append(",");
+ if (pretty_print_)
+ json_string_->append(kPrettyPrintLineEnding);
+ }
+
+ if (pretty_print_)
+ IndentLine(depth + 1);
+ AppendQuotedString(*key_itr);
+ if (pretty_print_) {
+ json_string_->append(": ");
+ } else {
+ json_string_->append(":");
+ }
+ BuildJSONString(value, depth + 1);
+ }
+
+ if (pretty_print_) {
+ json_string_->append(kPrettyPrintLineEnding);
+ IndentLine(depth);
+ json_string_->append("}");
+ } else {
+ json_string_->append("}");
+ }
+ break;
+ }
+
+ case Value::TYPE_BINARY:
+ {
+ if (!omit_binary_values_) {
+ NOTREACHED() << "Cannot serialize binary value.";
+ }
+ break;
+ }
+
+ default:
+ NOTREACHED() << "unknown json type";
+ }
+}
+
+void JSONWriter::AppendQuotedString(const std::string& str) {
+ // TODO(viettrungluu): |str| is UTF-8, not ASCII, so to properly escape it we
+ // have to convert it to UTF-16. This round-trip is suboptimal.
+ JsonDoubleQuote(UTF8ToUTF16(str), true, json_string_);
+}
+
+void JSONWriter::IndentLine(int depth) {
+ // It may be faster to keep an indent string so we don't have to keep
+ // reallocating.
+ json_string_->append(std::string(depth * 3, ' '));
+}
+
+} // namespace base
diff --git a/src/base/json/json_writer.h b/src/base/json/json_writer.h
new file mode 100644
index 0000000..94052c8
--- /dev/null
+++ b/src/base/json/json_writer.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2012 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_JSON_JSON_WRITER_H_
+#define BASE_JSON_JSON_WRITER_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+
+class Value;
+
+class BASE_EXPORT JSONWriter {
+ public:
+ enum Options {
+ // Do not escape the string, preserving its UTF8 characters. It is useful
+ // if you can pass the resulting string to the JSON parser in binary form
+ // (as UTF8).
+ OPTIONS_DO_NOT_ESCAPE = 1 << 0,
+
+ // For values of binary type, the value (and key if within a dictionary)
+ // will be omitted from the output.
+ OPTIONS_OMIT_BINARY_VALUES = 1 << 1,
+
+ // This option instructs the writer to write doubles that have no fractional
+ // part as a normal integer (i.e., without using exponential notation
+ // or appending a '.0') as long as the value is within the range of a
+ // 64-bit int.
+ OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION = 1 << 2,
+
+ // Return a slightly nicer formatted json string (pads with whitespace to
+ // help with readability).
+ OPTIONS_PRETTY_PRINT = 1 << 3
+ };
+
+ // Given a root node, generates a JSON string and puts it into |json|.
+ // TODO(tc): Should we generate json if it would be invalid json (e.g.,
+ // |node| is not a DictionaryValue/ListValue or if there are inf/-inf float
+ // values)?
+ static void Write(const Value* const node, std::string* json);
+
+ // Same as above but with |options| which is a bunch of JSONWriter::Options
+ // bitwise ORed together.
+ static void WriteWithOptions(const Value* const node, int options,
+ std::string* json);
+
+ // A static, constant JSON string representing an empty array. Useful
+ // for empty JSON argument passing.
+ static const char* kEmptyArray;
+
+ private:
+ JSONWriter(bool escape, bool omit_binary_values,
+ bool omit_double_type_preservation, bool pretty_print,
+ std::string* json);
+
+ // Called recursively to build the JSON string. Whe completed, value is
+ // json_string_ will contain the JSON.
+ void BuildJSONString(const Value* const node, int depth);
+
+ // Appends a quoted, escaped, version of (UTF-8) str to json_string_.
+ void AppendQuotedString(const std::string& str);
+
+ // Adds space to json_string_ for the indent level.
+ void IndentLine(int depth);
+
+ bool escape_;
+ bool omit_binary_values_;
+ bool omit_double_type_preservation_;
+ bool pretty_print_;
+
+ // Where we write JSON data as we generate it.
+ std::string* json_string_;
+
+ DISALLOW_COPY_AND_ASSIGN(JSONWriter);
+};
+
+} // namespace base
+
+#endif // BASE_JSON_JSON_WRITER_H_
diff --git a/src/base/json/json_writer_unittest.cc b/src/base/json/json_writer_unittest.cc
new file mode 100644
index 0000000..7ddd7b4
--- /dev/null
+++ b/src/base/json/json_writer_unittest.cc
@@ -0,0 +1,131 @@
+// Copyright (c) 2012 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/json/json_writer.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(JSONWriterTest, Writing) {
+ // Test null
+ Value* root = Value::CreateNullValue();
+ std::string output_js;
+ JSONWriter::Write(root, &output_js);
+ ASSERT_EQ("null", output_js);
+ delete root;
+
+ // Test empty dict
+ root = new DictionaryValue;
+ JSONWriter::Write(root, &output_js);
+ ASSERT_EQ("{}", output_js);
+ delete root;
+
+ // Test empty list
+ root = new ListValue;
+ JSONWriter::Write(root, &output_js);
+ ASSERT_EQ("[]", output_js);
+ delete root;
+
+ // Test Real values should always have a decimal or an 'e'.
+ root = new FundamentalValue(1.0);
+ JSONWriter::Write(root, &output_js);
+ ASSERT_EQ("1.0", output_js);
+ delete root;
+
+ // Test Real values in the the range (-1, 1) must have leading zeros
+ root = new FundamentalValue(0.2);
+ JSONWriter::Write(root, &output_js);
+ ASSERT_EQ("0.2", output_js);
+ delete root;
+
+ // Test Real values in the the range (-1, 1) must have leading zeros
+ root = new FundamentalValue(-0.8);
+ JSONWriter::Write(root, &output_js);
+ ASSERT_EQ("-0.8", output_js);
+ delete root;
+
+ // Writer unittests like empty list/dict nesting,
+ // list list nesting, etc.
+ DictionaryValue root_dict;
+ ListValue* list = new ListValue;
+ root_dict.Set("list", list);
+ DictionaryValue* inner_dict = new DictionaryValue;
+ list->Append(inner_dict);
+ inner_dict->SetInteger("inner int", 10);
+ ListValue* inner_list = new ListValue;
+ list->Append(inner_list);
+ list->Append(new FundamentalValue(true));
+
+ // Test the pretty-printer.
+ JSONWriter::Write(&root_dict, &output_js);
+ ASSERT_EQ("{\"list\":[{\"inner int\":10},[],true]}", output_js);
+ JSONWriter::WriteWithOptions(&root_dict, JSONWriter::OPTIONS_PRETTY_PRINT,
+ &output_js);
+ // The pretty-printer uses a different newline style on Windows than on
+ // other platforms.
+#if defined(OS_WIN)
+#define JSON_NEWLINE "\r\n"
+#else
+#define JSON_NEWLINE "\n"
+#endif
+ ASSERT_EQ("{" JSON_NEWLINE
+ " \"list\": [ {" JSON_NEWLINE
+ " \"inner int\": 10" JSON_NEWLINE
+ " }, [ ], true ]" JSON_NEWLINE
+ "}" JSON_NEWLINE,
+ output_js);
+#undef JSON_NEWLINE
+
+ // Test keys with periods
+ DictionaryValue period_dict;
+ period_dict.SetWithoutPathExpansion("a.b", new FundamentalValue(3));
+ period_dict.SetWithoutPathExpansion("c", new FundamentalValue(2));
+ DictionaryValue* period_dict2 = new DictionaryValue;
+ period_dict2->SetWithoutPathExpansion("g.h.i.j", new FundamentalValue(1));
+ period_dict.SetWithoutPathExpansion("d.e.f", period_dict2);
+ JSONWriter::Write(&period_dict, &output_js);
+ ASSERT_EQ("{\"a.b\":3,\"c\":2,\"d.e.f\":{\"g.h.i.j\":1}}", output_js);
+
+ DictionaryValue period_dict3;
+ period_dict3.Set("a.b", new FundamentalValue(2));
+ period_dict3.SetWithoutPathExpansion("a.b", new FundamentalValue(1));
+ JSONWriter::Write(&period_dict3, &output_js);
+ ASSERT_EQ("{\"a\":{\"b\":2},\"a.b\":1}", output_js);
+
+ // Test omitting binary values.
+ root = BinaryValue::CreateWithCopiedBuffer("asdf", 4);
+ JSONWriter::WriteWithOptions(root, JSONWriter::OPTIONS_OMIT_BINARY_VALUES,
+ &output_js);
+ ASSERT_TRUE(output_js.empty());
+ delete root;
+
+ ListValue binary_list;
+ binary_list.Append(new FundamentalValue(5));
+ binary_list.Append(BinaryValue::CreateWithCopiedBuffer("asdf", 4));
+ binary_list.Append(new FundamentalValue(2));
+ JSONWriter::WriteWithOptions(&binary_list,
+ JSONWriter::OPTIONS_OMIT_BINARY_VALUES,
+ &output_js);
+ ASSERT_EQ("[5,2]", output_js);
+
+ DictionaryValue binary_dict;
+ binary_dict.Set("a", new FundamentalValue(5));
+ binary_dict.Set("b", BinaryValue::CreateWithCopiedBuffer("asdf", 4));
+ binary_dict.Set("c", new FundamentalValue(2));
+ JSONWriter::WriteWithOptions(&binary_dict,
+ JSONWriter::OPTIONS_OMIT_BINARY_VALUES,
+ &output_js);
+ ASSERT_EQ("{\"a\":5,\"c\":2}", output_js);
+
+ // Test allowing a double with no fractional part to be written as an integer.
+ FundamentalValue double_value(1e10);
+ JSONWriter::WriteWithOptions(
+ &double_value,
+ JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION,
+ &output_js);
+ ASSERT_EQ("10000000000", output_js);
+}
+
+} // namespace base
diff --git a/src/base/json/string_escape.cc b/src/base/json/string_escape.cc
new file mode 100644
index 0000000..b4415b8
--- /dev/null
+++ b/src/base/json/string_escape.cc
@@ -0,0 +1,105 @@
+// 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/json/string_escape.h"
+
+#include <string>
+
+#include "base/stringprintf.h"
+#include "base/string_util.h"
+
+namespace base {
+
+namespace {
+
+// Try to escape |c| as a "SingleEscapeCharacter" (\n, etc). If successful,
+// returns true and appends the escape sequence to |dst|. This isn't required
+// by the spec, but it's more readable by humans than the \uXXXX alternatives.
+template<typename CHAR>
+static bool JsonSingleEscapeChar(const CHAR c, std::string* dst) {
+ // WARNING: if you add a new case here, you need to update the reader as well.
+ // Note: \v is in the reader, but not here since the JSON spec doesn't
+ // allow it.
+ switch (c) {
+ case '\b':
+ dst->append("\\b");
+ break;
+ case '\f':
+ dst->append("\\f");
+ break;
+ case '\n':
+ dst->append("\\n");
+ break;
+ case '\r':
+ dst->append("\\r");
+ break;
+ case '\t':
+ dst->append("\\t");
+ break;
+ case '\\':
+ dst->append("\\\\");
+ break;
+ case '"':
+ dst->append("\\\"");
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+template <class STR>
+void JsonDoubleQuoteT(const STR& str,
+ bool put_in_quotes,
+ std::string* dst) {
+ if (put_in_quotes)
+ dst->push_back('"');
+
+ for (typename STR::const_iterator it = str.begin(); it != str.end(); ++it) {
+ typename ToUnsigned<typename STR::value_type>::Unsigned c = *it;
+ if (!JsonSingleEscapeChar(c, dst)) {
+ if (c < 32 || c > 126 || c == '<' || c == '>') {
+ // 1. Escaping <, > to prevent script execution.
+ // 2. Technically, we could also pass through c > 126 as UTF8, but this
+ // is also optional. It would also be a pain to implement here.
+ unsigned int as_uint = static_cast<unsigned int>(c);
+ base::StringAppendF(dst, "\\u%04X", as_uint);
+ } else {
+ unsigned char ascii = static_cast<unsigned char>(*it);
+ dst->push_back(ascii);
+ }
+ }
+ }
+
+ if (put_in_quotes)
+ dst->push_back('"');
+}
+
+} // namespace
+
+void JsonDoubleQuote(const std::string& str,
+ bool put_in_quotes,
+ std::string* dst) {
+ JsonDoubleQuoteT(str, put_in_quotes, dst);
+}
+
+std::string GetDoubleQuotedJson(const std::string& str) {
+ std::string dst;
+ JsonDoubleQuote(str, true, &dst);
+ return dst;
+}
+
+void JsonDoubleQuote(const string16& str,
+ bool put_in_quotes,
+ std::string* dst) {
+ JsonDoubleQuoteT(str, put_in_quotes, dst);
+}
+
+std::string GetDoubleQuotedJson(const string16& str) {
+ std::string dst;
+ JsonDoubleQuote(str, true, &dst);
+ return dst;
+}
+
+} // namespace base
diff --git a/src/base/json/string_escape.h b/src/base/json/string_escape.h
new file mode 100644
index 0000000..088db62
--- /dev/null
+++ b/src/base/json/string_escape.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file defines utility functions for escaping strings.
+
+#ifndef BASE_JSON_STRING_ESCAPE_H_
+#define BASE_JSON_STRING_ESCAPE_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/string16.h"
+
+namespace base {
+
+// Escape |str| appropriately for a JSON string literal, _appending_ the
+// result to |dst|. This will create unicode escape sequences (\uXXXX).
+// If |put_in_quotes| is true, the result will be surrounded in double quotes.
+// The outputted literal, when interpreted by the browser, should result in a
+// javascript string that is identical and the same length as the input |str|.
+BASE_EXPORT void JsonDoubleQuote(const std::string& str,
+ bool put_in_quotes,
+ std::string* dst);
+
+// Same as above, but always returns the result double quoted.
+BASE_EXPORT std::string GetDoubleQuotedJson(const std::string& str);
+
+BASE_EXPORT void JsonDoubleQuote(const string16& str,
+ bool put_in_quotes,
+ std::string* dst);
+
+// Same as above, but always returns the result double quoted.
+BASE_EXPORT std::string GetDoubleQuotedJson(const string16& str);
+
+} // namespace base
+
+#endif // BASE_JSON_STRING_ESCAPE_H_
diff --git a/src/base/json/string_escape_unittest.cc b/src/base/json/string_escape_unittest.cc
new file mode 100644
index 0000000..c550ca3
--- /dev/null
+++ b/src/base/json/string_escape_unittest.cc
@@ -0,0 +1,100 @@
+// 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/json/string_escape.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+const struct json_narrow_test_data {
+ const char* to_escape;
+ const char* escaped;
+} json_narrow_cases[] = {
+ {"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"},
+ {"a\b\f\n\r\t\v\1\\.\"z",
+ "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"},
+ {"b\x0f\x7f\xf0\xff!", "b\\u000F\\u007F\\u00F0\\u00FF!"},
+ {"c<>d", "c\\u003C\\u003Ed"},
+};
+
+} // namespace
+
+TEST(StringEscapeTest, JsonDoubleQuoteNarrow) {
+ for (size_t i = 0; i < arraysize(json_narrow_cases); ++i) {
+ std::string in = json_narrow_cases[i].to_escape;
+ std::string out;
+ JsonDoubleQuote(in, false, &out);
+ EXPECT_EQ(std::string(json_narrow_cases[i].escaped), out);
+ }
+
+ std::string in = json_narrow_cases[0].to_escape;
+ std::string out;
+ JsonDoubleQuote(in, false, &out);
+
+ // test quoting
+ std::string out_quoted;
+ JsonDoubleQuote(in, true, &out_quoted);
+ EXPECT_EQ(out.length() + 2, out_quoted.length());
+ EXPECT_EQ(out_quoted.find(out), 1U);
+
+ // now try with a NULL in the string
+ std::string null_prepend = "test";
+ null_prepend.push_back(0);
+ in = null_prepend + in;
+ std::string expected = "test\\u0000";
+ expected += json_narrow_cases[0].escaped;
+ out.clear();
+ JsonDoubleQuote(in, false, &out);
+ EXPECT_EQ(expected, out);
+}
+
+namespace {
+
+const struct json_wide_test_data {
+ const wchar_t* to_escape;
+ const char* escaped;
+} json_wide_cases[] = {
+ {L"b\uffb1\u00ff", "b\\uFFB1\\u00FF"},
+ {L"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"},
+ {L"a\b\f\n\r\t\v\1\\.\"z",
+ "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"},
+ {L"b\x0f\x7f\xf0\xff!", "b\\u000F\\u007F\\u00F0\\u00FF!"},
+ {L"c<>d", "c\\u003C\\u003Ed"},
+};
+
+} // namespace
+
+TEST(StringEscapeTest, JsonDoubleQuoteWide) {
+ for (size_t i = 0; i < arraysize(json_wide_cases); ++i) {
+ std::string out;
+ string16 in = WideToUTF16(json_wide_cases[i].to_escape);
+ JsonDoubleQuote(in, false, &out);
+ EXPECT_EQ(std::string(json_wide_cases[i].escaped), out);
+ }
+
+ string16 in = WideToUTF16(json_wide_cases[0].to_escape);
+ std::string out;
+ JsonDoubleQuote(in, false, &out);
+
+ // test quoting
+ std::string out_quoted;
+ JsonDoubleQuote(in, true, &out_quoted);
+ EXPECT_EQ(out.length() + 2, out_quoted.length());
+ EXPECT_EQ(out_quoted.find(out), 1U);
+
+ // now try with a NULL in the string
+ string16 null_prepend = WideToUTF16(L"test");
+ null_prepend.push_back(0);
+ in = null_prepend + in;
+ std::string expected = "test\\u0000";
+ expected += json_wide_cases[0].escaped;
+ out.clear();
+ JsonDoubleQuote(in, false, &out);
+ EXPECT_EQ(expected, out);
+}
+
+} // namespace base
diff --git a/src/base/lazy_instance.cc b/src/base/lazy_instance.cc
new file mode 100644
index 0000000..a81cb8c
--- /dev/null
+++ b/src/base/lazy_instance.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/lazy_instance.h"
+
+#include "base/at_exit.h"
+#include "base/atomicops.h"
+#include "base/basictypes.h"
+#include "base/threading/platform_thread.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+
+namespace base {
+namespace internal {
+
+// TODO(joth): This function could be shared with Singleton, in place of its
+// WaitForInstance() call.
+bool NeedsLazyInstance(subtle::AtomicWord* state) {
+ // Try to create the instance, if we're the first, will go from 0 to
+ // kLazyInstanceStateCreating, otherwise we've already been beaten here.
+ // The memory access has no memory ordering as state 0 and
+ // kLazyInstanceStateCreating have no associated data (memory barriers are
+ // all about ordering of memory accesses to *associated* data).
+ if (subtle::NoBarrier_CompareAndSwap(state, 0,
+ kLazyInstanceStateCreating) == 0)
+ // Caller must create instance
+ return true;
+
+ // It's either in the process of being created, or already created. Spin.
+ // The load has acquire memory ordering as a thread which sees
+ // state_ == STATE_CREATED needs to acquire visibility over
+ // the associated data (buf_). Pairing Release_Store is in
+ // CompleteLazyInstance().
+ while (subtle::Acquire_Load(state) == kLazyInstanceStateCreating) {
+ PlatformThread::YieldCurrentThread();
+ }
+ // Someone else created the instance.
+ return false;
+}
+
+void CompleteLazyInstance(subtle::AtomicWord* state,
+ subtle::AtomicWord new_instance,
+ void* lazy_instance,
+ void (*dtor)(void*)) {
+ // See the comment to the corresponding HAPPENS_AFTER in Pointer().
+ ANNOTATE_HAPPENS_BEFORE(state);
+
+ // Instance is created, go from CREATING to CREATED.
+ // Releases visibility over private_buf_ to readers. Pairing Acquire_Load's
+ // are in NeedsInstance() and Pointer().
+ subtle::Release_Store(state, new_instance);
+
+ // Make sure that the lazily instantiated object will get destroyed at exit.
+ if (dtor)
+ AtExitManager::RegisterCallback(dtor, lazy_instance);
+}
+
+} // namespace internal
+} // namespace base
diff --git a/src/base/lazy_instance.h b/src/base/lazy_instance.h
new file mode 100644
index 0000000..6e833bf
--- /dev/null
+++ b/src/base/lazy_instance.h
@@ -0,0 +1,209 @@
+// Copyright (c) 2012 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.
+
+// The LazyInstance<Type, Traits> class manages a single instance of Type,
+// which will be lazily created on the first time it's accessed. This class is
+// useful for places you would normally use a function-level static, but you
+// need to have guaranteed thread-safety. The Type constructor will only ever
+// be called once, even if two threads are racing to create the object. Get()
+// and Pointer() will always return the same, completely initialized instance.
+// When the instance is constructed it is registered with AtExitManager. The
+// destructor will be called on program exit.
+//
+// LazyInstance is completely thread safe, assuming that you create it safely.
+// The class was designed to be POD initialized, so it shouldn't require a
+// static constructor. It really only makes sense to declare a LazyInstance as
+// a global variable using the LAZY_INSTANCE_INITIALIZER initializer.
+//
+// LazyInstance is similar to Singleton, except it does not have the singleton
+// property. You can have multiple LazyInstance's of the same type, and each
+// will manage a unique instance. It also preallocates the space for Type, as
+// to avoid allocating the Type instance on the heap. This may help with the
+// performance of creating the instance, and reducing heap fragmentation. This
+// requires that Type be a complete type so we can determine the size.
+//
+// Example usage:
+// static LazyInstance<MyClass> my_instance = LAZY_INSTANCE_INITIALIZER;
+// void SomeMethod() {
+// my_instance.Get().SomeMethod(); // MyClass::SomeMethod()
+//
+// MyClass* ptr = my_instance.Pointer();
+// ptr->DoDoDo(); // MyClass::DoDoDo
+// }
+
+#ifndef BASE_LAZY_INSTANCE_H_
+#define BASE_LAZY_INSTANCE_H_
+
+#include <new> // For placement new.
+
+#include "base/atomicops.h"
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/aligned_memory.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/threading/thread_restrictions.h"
+
+// LazyInstance uses its own struct initializer-list style static
+// initialization, as base's LINKER_INITIALIZED requires a constructor and on
+// some compilers (notably gcc 4.4) this still ends up needing runtime
+// initialization.
+#define LAZY_INSTANCE_INITIALIZER {0}
+
+namespace base {
+
+template <typename Type>
+struct DefaultLazyInstanceTraits {
+ static const bool kRegisterOnExit = true;
+ static const bool kAllowedToAccessOnNonjoinableThread = false;
+
+ static Type* New(void* instance) {
+ DCHECK_EQ(reinterpret_cast<uintptr_t>(instance) & (ALIGNOF(Type) - 1), 0u)
+ << ": Bad boy, the buffer passed to placement new is not aligned!\n"
+ "This may break some stuff like SSE-based optimizations assuming the "
+ "<Type> objects are word aligned.";
+ // Use placement new to initialize our instance in our preallocated space.
+ // The parenthesis is very important here to force POD type initialization.
+ return new (instance) Type();
+ }
+ static void Delete(Type* instance) {
+ // Explicitly call the destructor.
+ instance->~Type();
+ }
+};
+
+// We pull out some of the functionality into non-templated functions, so we
+// can implement the more complicated pieces out of line in the .cc file.
+namespace internal {
+
+// Use LazyInstance<T>::Leaky for a less-verbose call-site typedef; e.g.:
+// base::LazyInstance<T>::Leaky my_leaky_lazy_instance;
+// instead of:
+// base::LazyInstance<T, base::internal::LeakyLazyInstanceTraits<T> >
+// my_leaky_lazy_instance;
+// (especially when T is MyLongTypeNameImplClientHolderFactory).
+// Only use this internal::-qualified verbose form to extend this traits class
+// (depending on its implementation details).
+template <typename Type>
+struct LeakyLazyInstanceTraits {
+ static const bool kRegisterOnExit = false;
+ static const bool kAllowedToAccessOnNonjoinableThread = true;
+
+ static Type* New(void* instance) {
+ return DefaultLazyInstanceTraits<Type>::New(instance);
+ }
+ static void Delete(Type* /* instance */) {}
+};
+
+// Our AtomicWord doubles as a spinlock, where a value of
+// kBeingCreatedMarker means the spinlock is being held for creation.
+static const subtle::AtomicWord kLazyInstanceStateCreating = 1;
+
+// Check if instance needs to be created. If so return true otherwise
+// if another thread has beat us, wait for instance to be created and
+// return false.
+BASE_EXPORT bool NeedsLazyInstance(subtle::AtomicWord* state);
+
+// After creating an instance, call this to register the dtor to be called
+// at program exit and to update the atomic state to hold the |new_instance|
+BASE_EXPORT void CompleteLazyInstance(subtle::AtomicWord* state,
+ subtle::AtomicWord new_instance,
+ void* lazy_instance,
+ void (*dtor)(void*));
+
+} // namespace internal
+
+template <typename Type, typename Traits = DefaultLazyInstanceTraits<Type> >
+class LazyInstance {
+ public:
+ // Do not define a destructor, as doing so makes LazyInstance a
+ // non-POD-struct. We don't want that because then a static initializer will
+ // be created to register the (empty) destructor with atexit() under MSVC, for
+ // example. We handle destruction of the contained Type class explicitly via
+ // the OnExit member function, where needed.
+ // ~LazyInstance() {}
+
+ // Convenience typedef to avoid having to repeat Type for leaky lazy
+ // instances.
+ typedef LazyInstance<Type, internal::LeakyLazyInstanceTraits<Type> > Leaky;
+
+ Type& Get() {
+ return *Pointer();
+ }
+
+ Type* Pointer() {
+#ifndef NDEBUG
+ // Avoid making TLS lookup on release builds.
+ if (!Traits::kAllowedToAccessOnNonjoinableThread)
+ ThreadRestrictions::AssertSingletonAllowed();
+#endif
+ // If any bit in the created mask is true, the instance has already been
+ // fully constructed.
+ static const subtle::AtomicWord kLazyInstanceCreatedMask =
+ ~internal::kLazyInstanceStateCreating;
+
+ // We will hopefully have fast access when the instance is already created.
+ // Since a thread sees private_instance_ == 0 or kLazyInstanceStateCreating
+ // at most once, the load is taken out of NeedsInstance() as a fast-path.
+ // The load has acquire memory ordering as a thread which sees
+ // private_instance_ > creating needs to acquire visibility over
+ // the associated data (private_buf_). Pairing Release_Store is in
+ // CompleteLazyInstance().
+ subtle::AtomicWord value = subtle::Acquire_Load(&private_instance_);
+ if (!(value & kLazyInstanceCreatedMask) &&
+ internal::NeedsLazyInstance(&private_instance_)) {
+ // Create the instance in the space provided by |private_buf_|.
+ value = reinterpret_cast<subtle::AtomicWord>(
+ Traits::New(private_buf_.void_data()));
+ internal::CompleteLazyInstance(&private_instance_, value, this,
+ Traits::kRegisterOnExit ? OnExit : NULL);
+ }
+
+ // This annotation helps race detectors recognize correct lock-less
+ // synchronization between different threads calling Pointer().
+ // We suggest dynamic race detection tool that "Traits::New" above
+ // and CompleteLazyInstance(...) happens before "return instance()" below.
+ // See the corresponding HAPPENS_BEFORE in CompleteLazyInstance(...).
+ ANNOTATE_HAPPENS_AFTER(&private_instance_);
+ return instance();
+ }
+
+ bool operator==(Type* p) {
+ switch (subtle::NoBarrier_Load(&private_instance_)) {
+ case 0:
+ return p == NULL;
+ case internal::kLazyInstanceStateCreating:
+ return static_cast<void*>(p) == private_buf_.void_data();
+ default:
+ return p == instance();
+ }
+ }
+
+ // Effectively private: member data is only public to allow the linker to
+ // statically initialize it and to maintain a POD class. DO NOT USE FROM
+ // OUTSIDE THIS CLASS.
+
+ subtle::AtomicWord private_instance_;
+ // Preallocated space for the Type instance.
+ base::AlignedMemory<sizeof(Type), ALIGNOF(Type)> private_buf_;
+
+ private:
+ Type* instance() {
+ return reinterpret_cast<Type*>(subtle::NoBarrier_Load(&private_instance_));
+ }
+
+ // Adapter function for use with AtExit. This should be called single
+ // threaded, so don't synchronize across threads.
+ // Calling OnExit while the instance is in use by other threads is a mistake.
+ static void OnExit(void* lazy_instance) {
+ LazyInstance<Type, Traits>* me =
+ reinterpret_cast<LazyInstance<Type, Traits>*>(lazy_instance);
+ Traits::Delete(me->instance());
+ subtle::NoBarrier_Store(&me->private_instance_, 0);
+ }
+};
+
+} // namespace base
+
+#endif // BASE_LAZY_INSTANCE_H_
diff --git a/src/base/lazy_instance_unittest.cc b/src/base/lazy_instance_unittest.cc
new file mode 100644
index 0000000..e25366e
--- /dev/null
+++ b/src/base/lazy_instance_unittest.cc
@@ -0,0 +1,172 @@
+// Copyright (c) 2012 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/atomic_sequence_num.h"
+#include "base/lazy_instance.h"
+#include "base/memory/aligned_memory.h"
+#include "base/threading/simple_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+base::StaticAtomicSequenceNumber constructed_seq_;
+base::StaticAtomicSequenceNumber destructed_seq_;
+
+class ConstructAndDestructLogger {
+ public:
+ ConstructAndDestructLogger() {
+ constructed_seq_.GetNext();
+ }
+ ~ConstructAndDestructLogger() {
+ destructed_seq_.GetNext();
+ }
+};
+
+class SlowConstructor {
+ public:
+ SlowConstructor() : some_int_(0) {
+ // Sleep for 1 second to try to cause a race.
+ base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
+ ++constructed;
+ some_int_ = 12;
+ }
+ int some_int() const { return some_int_; }
+
+ static int constructed;
+ private:
+ int some_int_;
+};
+
+int SlowConstructor::constructed = 0;
+
+class SlowDelegate : public base::DelegateSimpleThread::Delegate {
+ public:
+ explicit SlowDelegate(base::LazyInstance<SlowConstructor>* lazy)
+ : lazy_(lazy) {}
+
+ virtual void Run() OVERRIDE {
+ EXPECT_EQ(12, lazy_->Get().some_int());
+ EXPECT_EQ(12, lazy_->Pointer()->some_int());
+ }
+
+ private:
+ base::LazyInstance<SlowConstructor>* lazy_;
+};
+
+} // namespace
+
+static base::LazyInstance<ConstructAndDestructLogger> lazy_logger =
+ LAZY_INSTANCE_INITIALIZER;
+
+TEST(LazyInstanceTest, Basic) {
+ {
+ base::ShadowingAtExitManager shadow;
+
+ EXPECT_EQ(0, constructed_seq_.GetNext());
+ EXPECT_EQ(0, destructed_seq_.GetNext());
+
+ lazy_logger.Get();
+ EXPECT_EQ(2, constructed_seq_.GetNext());
+ EXPECT_EQ(1, destructed_seq_.GetNext());
+
+ lazy_logger.Pointer();
+ EXPECT_EQ(3, constructed_seq_.GetNext());
+ EXPECT_EQ(2, destructed_seq_.GetNext());
+ }
+ EXPECT_EQ(4, constructed_seq_.GetNext());
+ EXPECT_EQ(4, destructed_seq_.GetNext());
+}
+
+static base::LazyInstance<SlowConstructor> lazy_slow =
+ LAZY_INSTANCE_INITIALIZER;
+
+TEST(LazyInstanceTest, ConstructorThreadSafety) {
+ {
+ base::ShadowingAtExitManager shadow;
+
+ SlowDelegate delegate(&lazy_slow);
+ EXPECT_EQ(0, SlowConstructor::constructed);
+
+ base::DelegateSimpleThreadPool pool("lazy_instance_cons", 5);
+ pool.AddWork(&delegate, 20);
+ EXPECT_EQ(0, SlowConstructor::constructed);
+
+ pool.Start();
+ pool.JoinAll();
+ EXPECT_EQ(1, SlowConstructor::constructed);
+ }
+}
+
+namespace {
+
+// DeleteLogger is an object which sets a flag when it's destroyed.
+// It accepts a bool* and sets the bool to true when the dtor runs.
+class DeleteLogger {
+ public:
+ DeleteLogger() : deleted_(NULL) {}
+ ~DeleteLogger() { *deleted_ = true; }
+
+ void SetDeletedPtr(bool* deleted) {
+ deleted_ = deleted;
+ }
+
+ private:
+ bool* deleted_;
+};
+
+} // anonymous namespace
+
+TEST(LazyInstanceTest, LeakyLazyInstance) {
+ // Check that using a plain LazyInstance causes the dtor to run
+ // when the AtExitManager finishes.
+ bool deleted1 = false;
+ {
+ base::ShadowingAtExitManager shadow;
+ static base::LazyInstance<DeleteLogger> test = LAZY_INSTANCE_INITIALIZER;
+ test.Get().SetDeletedPtr(&deleted1);
+ }
+ EXPECT_TRUE(deleted1);
+
+ // Check that using a *leaky* LazyInstance makes the dtor not run
+ // when the AtExitManager finishes.
+ bool deleted2 = false;
+ {
+ base::ShadowingAtExitManager shadow;
+ static base::LazyInstance<DeleteLogger>::Leaky
+ test = LAZY_INSTANCE_INITIALIZER;
+ test.Get().SetDeletedPtr(&deleted2);
+ }
+ EXPECT_FALSE(deleted2);
+}
+
+namespace {
+
+template <size_t alignment>
+class AlignedData {
+ public:
+ AlignedData() {}
+ ~AlignedData() {}
+ base::AlignedMemory<alignment, alignment> data_;
+};
+
+} // anonymous namespace
+
+#define EXPECT_ALIGNED(ptr, align) \
+ EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(ptr) & (align - 1))
+
+TEST(LazyInstanceTest, Alignment) {
+ using base::LazyInstance;
+
+ // Create some static instances with increasing sizes and alignment
+ // requirements. By ordering this way, the linker will need to do some work to
+ // ensure proper alignment of the static data.
+ static LazyInstance<AlignedData<4> > align4 = LAZY_INSTANCE_INITIALIZER;
+ static LazyInstance<AlignedData<32> > align32 = LAZY_INSTANCE_INITIALIZER;
+ static LazyInstance<AlignedData<4096> > align4096 = LAZY_INSTANCE_INITIALIZER;
+
+ EXPECT_ALIGNED(align4.Pointer(), 4);
+ EXPECT_ALIGNED(align32.Pointer(), 32);
+ EXPECT_ALIGNED(align4096.Pointer(), 4096);
+}
diff --git a/src/base/linux_util.cc b/src/base/linux_util.cc
new file mode 100644
index 0000000..49df773
--- /dev/null
+++ b/src/base/linux_util.cc
@@ -0,0 +1,304 @@
+// Copyright (c) 2012 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/linux_util.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "base/string_util.h"
+#include "base/synchronization/lock.h"
+
+namespace {
+
+// Not needed for OS_CHROMEOS.
+#if defined(OS_LINUX)
+enum LinuxDistroState {
+ STATE_DID_NOT_CHECK = 0,
+ STATE_CHECK_STARTED = 1,
+ STATE_CHECK_FINISHED = 2,
+};
+
+// Helper class for GetLinuxDistro().
+class LinuxDistroHelper {
+ public:
+ // Retrieves the Singleton.
+ static LinuxDistroHelper* GetInstance() {
+ return Singleton<LinuxDistroHelper>::get();
+ }
+
+ // The simple state machine goes from:
+ // STATE_DID_NOT_CHECK -> STATE_CHECK_STARTED -> STATE_CHECK_FINISHED.
+ LinuxDistroHelper() : state_(STATE_DID_NOT_CHECK) {}
+ ~LinuxDistroHelper() {}
+
+ // Retrieve the current state, if we're in STATE_DID_NOT_CHECK,
+ // we automatically move to STATE_CHECK_STARTED so nobody else will
+ // do the check.
+ LinuxDistroState State() {
+ base::AutoLock scoped_lock(lock_);
+ if (STATE_DID_NOT_CHECK == state_) {
+ state_ = STATE_CHECK_STARTED;
+ return STATE_DID_NOT_CHECK;
+ }
+ return state_;
+ }
+
+ // Indicate the check finished, move to STATE_CHECK_FINISHED.
+ void CheckFinished() {
+ base::AutoLock scoped_lock(lock_);
+ DCHECK_EQ(STATE_CHECK_STARTED, state_);
+ state_ = STATE_CHECK_FINISHED;
+ }
+
+ private:
+ base::Lock lock_;
+ LinuxDistroState state_;
+};
+#endif // if defined(OS_LINUX)
+
+// expected prefix of the target of the /proc/self/fd/%d link for a socket
+const char kSocketLinkPrefix[] = "socket:[";
+
+// Parse a symlink in /proc/pid/fd/$x and return the inode number of the
+// socket.
+// inode_out: (output) set to the inode number on success
+// path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor)
+// log: if true, log messages about failure details
+bool ProcPathGetInode(ino_t* inode_out, const char* path, bool log = false) {
+ DCHECK(inode_out);
+ DCHECK(path);
+
+ char buf[256];
+ const ssize_t n = readlink(path, buf, sizeof(buf) - 1);
+ if (n == -1) {
+ if (log) {
+ DLOG(WARNING) << "Failed to read the inode number for a socket from /proc"
+ "(" << errno << ")";
+ }
+ return false;
+ }
+ buf[n] = 0;
+
+ if (memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) {
+ if (log) {
+ DLOG(WARNING) << "The descriptor passed from the crashing process wasn't "
+ " a UNIX domain socket.";
+ }
+ return false;
+ }
+
+ char* endptr;
+ const unsigned long long int inode_ul =
+ strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10);
+ if (*endptr != ']')
+ return false;
+
+ if (inode_ul == ULLONG_MAX) {
+ if (log) {
+ DLOG(WARNING) << "Failed to parse a socket's inode number: the number "
+ "was too large. Please report this bug: " << buf;
+ }
+ return false;
+ }
+
+ *inode_out = inode_ul;
+ return true;
+}
+
+} // namespace
+
+namespace base {
+
+const char kFindInodeSwitch[] = "--find-inode";
+
+// Account for the terminating null character.
+static const int kDistroSize = 128 + 1;
+
+// We use this static string to hold the Linux distro info. If we
+// crash, the crash handler code will send this in the crash dump.
+char g_linux_distro[kDistroSize] =
+#if defined(OS_CHROMEOS)
+ "CrOS";
+#elif defined(OS_ANDROID)
+ "Android";
+#else // if defined(OS_LINUX)
+ "Unknown";
+#endif
+
+std::string GetLinuxDistro() {
+#if defined(OS_CHROMEOS) || defined(OS_ANDROID)
+ return g_linux_distro;
+#elif defined(OS_LINUX)
+ LinuxDistroHelper* distro_state_singleton = LinuxDistroHelper::GetInstance();
+ LinuxDistroState state = distro_state_singleton->State();
+ if (STATE_CHECK_FINISHED == state)
+ return g_linux_distro;
+ if (STATE_CHECK_STARTED == state)
+ return "Unknown"; // Don't wait for other thread to finish.
+ DCHECK_EQ(state, STATE_DID_NOT_CHECK);
+ // We do this check only once per process. If it fails, there's
+ // little reason to believe it will work if we attempt to run
+ // lsb_release again.
+ std::vector<std::string> argv;
+ argv.push_back("lsb_release");
+ argv.push_back("-d");
+ std::string output;
+ base::GetAppOutput(CommandLine(argv), &output);
+ if (output.length() > 0) {
+ // lsb_release -d should return: Description:<tab>Distro Info
+ const char field[] = "Description:\t";
+ if (output.compare(0, strlen(field), field) == 0) {
+ SetLinuxDistro(output.substr(strlen(field)));
+ }
+ }
+ distro_state_singleton->CheckFinished();
+ return g_linux_distro;
+#else
+ NOTIMPLEMENTED();
+ return "Unknown";
+#endif
+}
+
+void SetLinuxDistro(const std::string& distro) {
+ std::string trimmed_distro;
+ TrimWhitespaceASCII(distro, TRIM_ALL, &trimmed_distro);
+ base::strlcpy(g_linux_distro, trimmed_distro.c_str(), kDistroSize);
+}
+
+bool FileDescriptorGetInode(ino_t* inode_out, int fd) {
+ DCHECK(inode_out);
+
+ struct stat buf;
+ if (fstat(fd, &buf) < 0)
+ return false;
+
+ if (!S_ISSOCK(buf.st_mode))
+ return false;
+
+ *inode_out = buf.st_ino;
+ return true;
+}
+
+bool FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode) {
+ DCHECK(pid_out);
+ bool already_found = false;
+
+ DIR* proc = opendir("/proc");
+ if (!proc) {
+ DLOG(WARNING) << "Cannot open /proc";
+ return false;
+ }
+
+ std::vector<pid_t> pids;
+
+ struct dirent* dent;
+ while ((dent = readdir(proc))) {
+ char* endptr;
+ const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10);
+ if (pid_ul == ULONG_MAX || *endptr)
+ continue;
+ pids.push_back(pid_ul);
+ }
+ closedir(proc);
+
+ for (std::vector<pid_t>::const_iterator
+ i = pids.begin(); i != pids.end(); ++i) {
+ const pid_t current_pid = *i;
+ char buf[256];
+ snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid);
+ DIR* fd = opendir(buf);
+ if (!fd)
+ continue;
+
+ while ((dent = readdir(fd))) {
+ if (snprintf(buf, sizeof(buf), "/proc/%d/fd/%s", current_pid,
+ dent->d_name) >= static_cast<int>(sizeof(buf))) {
+ continue;
+ }
+
+ ino_t fd_inode;
+ if (ProcPathGetInode(&fd_inode, buf)) {
+ if (fd_inode == socket_inode) {
+ if (already_found) {
+ closedir(fd);
+ return false;
+ }
+
+ already_found = true;
+ *pid_out = current_pid;
+ break;
+ }
+ }
+ }
+
+ closedir(fd);
+ }
+
+ return already_found;
+}
+
+pid_t FindThreadIDWithSyscall(pid_t pid, const std::string& expected_data,
+ bool* syscall_supported) {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "/proc/%d/task", pid);
+
+ if (syscall_supported != NULL)
+ *syscall_supported = false;
+
+ DIR* task = opendir(buf);
+ if (!task) {
+ DLOG(WARNING) << "Cannot open " << buf;
+ return -1;
+ }
+
+ std::vector<pid_t> tids;
+ struct dirent* dent;
+ while ((dent = readdir(task))) {
+ char* endptr;
+ const unsigned long int tid_ul = strtoul(dent->d_name, &endptr, 10);
+ if (tid_ul == ULONG_MAX || *endptr)
+ continue;
+ tids.push_back(tid_ul);
+ }
+ closedir(task);
+
+ scoped_array<char> syscall_data(new char[expected_data.length()]);
+ for (std::vector<pid_t>::const_iterator
+ i = tids.begin(); i != tids.end(); ++i) {
+ const pid_t current_tid = *i;
+ snprintf(buf, sizeof(buf), "/proc/%d/task/%d/syscall", pid, current_tid);
+ int fd = open(buf, O_RDONLY);
+ if (fd < 0)
+ continue;
+ if (syscall_supported != NULL)
+ *syscall_supported = true;
+ bool read_ret =
+ file_util::ReadFromFD(fd, syscall_data.get(), expected_data.length());
+ close(fd);
+ if (!read_ret)
+ continue;
+
+ if (0 == strncmp(expected_data.c_str(), syscall_data.get(),
+ expected_data.length())) {
+ return current_tid;
+ }
+ }
+ return -1;
+}
+
+} // namespace base
diff --git a/src/base/linux_util.h b/src/base/linux_util.h
new file mode 100644
index 0000000..b9ba56d
--- /dev/null
+++ b/src/base/linux_util.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2012 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_LINUX_UTIL_H_
+#define BASE_LINUX_UTIL_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "base/base_export.h"
+
+namespace base {
+
+BASE_EXPORT extern const char kFindInodeSwitch[];
+
+// This is declared here so the crash reporter can access the memory directly
+// in compromised context without going through the standard library.
+BASE_EXPORT extern char g_linux_distro[];
+
+// Get the Linux Distro if we can, or return "Unknown".
+BASE_EXPORT std::string GetLinuxDistro();
+
+// Set the Linux Distro string.
+BASE_EXPORT void SetLinuxDistro(const std::string& distro);
+
+// Return the inode number for the UNIX domain socket |fd|.
+BASE_EXPORT bool FileDescriptorGetInode(ino_t* inode_out, int fd);
+
+// Find the process which holds the given socket, named by inode number. If
+// multiple processes hold the socket, this function returns false.
+BASE_EXPORT bool FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode);
+
+// For a given process |pid|, look through all its threads and find the first
+// thread with /proc/[pid]/task/[thread_id]/syscall whose first N bytes matches
+// |expected_data|, where N is the length of |expected_data|.
+// Returns the thread id or -1 on error. If |syscall_supported| is
+// set to false the kernel does not support syscall in procfs.
+BASE_EXPORT pid_t FindThreadIDWithSyscall(pid_t pid,
+ const std::string& expected_data,
+ bool* syscall_supported);
+
+} // namespace base
+
+#endif // BASE_LINUX_UTIL_H_
diff --git a/src/base/location.cc b/src/base/location.cc
new file mode 100644
index 0000000..b6ded2c
--- /dev/null
+++ b/src/base/location.cc
@@ -0,0 +1,102 @@
+// Copyright (c) 2012 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"
+
+#if defined(COMPILER_MSVC)
+// MSDN says to #include <intrin.h>, but that breaks the VS2005 build.
+extern "C" {
+ void* _ReturnAddress();
+}
+#endif
+
+#include "base/location.h"
+#include "base/string_number_conversions.h"
+#include "base/stringprintf.h"
+
+namespace tracked_objects {
+
+Location::Location(const char* function_name,
+ const char* file_name,
+ int line_number,
+ const void* program_counter)
+ : function_name_(function_name),
+ file_name_(file_name),
+ line_number_(line_number),
+ program_counter_(program_counter) {
+}
+
+Location::Location()
+ : function_name_("Unknown"),
+ file_name_("Unknown"),
+ line_number_(-1),
+ program_counter_(NULL) {
+}
+
+std::string Location::ToString() const {
+ return std::string(function_name_) + "@" + file_name_ + ":" +
+ base::IntToString(line_number_);
+}
+
+void Location::Write(bool display_filename, bool display_function_name,
+ std::string* output) const {
+ base::StringAppendF(output, "%s[%d] ",
+ display_filename ? file_name_ : "line",
+ line_number_);
+
+ if (display_function_name) {
+ WriteFunctionName(output);
+ output->push_back(' ');
+ }
+}
+
+void Location::WriteFunctionName(std::string* output) const {
+ // Translate "<" to "<" for HTML safety.
+ // TODO(jar): Support ASCII or html for logging in ASCII.
+ for (const char *p = function_name_; *p; p++) {
+ switch (*p) {
+ case '<':
+ output->append("<");
+ break;
+
+ case '>':
+ output->append(">");
+ break;
+
+ default:
+ output->push_back(*p);
+ break;
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+LocationSnapshot::LocationSnapshot() : line_number(-1) {
+}
+
+LocationSnapshot::LocationSnapshot(
+ const tracked_objects::Location& location)
+ : file_name(location.file_name()),
+ function_name(location.function_name()),
+ line_number(location.line_number()) {
+}
+
+LocationSnapshot::~LocationSnapshot() {
+}
+
+//------------------------------------------------------------------------------
+#if defined(COMPILER_MSVC)
+__declspec(noinline)
+#endif
+BASE_EXPORT const void* GetProgramCounter() {
+#if defined(COMPILER_MSVC)
+ return _ReturnAddress();
+#elif defined(COMPILER_GCC) && !defined(__LB_PS3__) && !defined(__LB_WIIU__)
+ return __builtin_extract_return_addr(__builtin_return_address(0));
+#endif // COMPILER_GCC
+
+ return NULL;
+}
+
+} // namespace tracked_objects
diff --git a/src/base/location.h b/src/base/location.h
new file mode 100644
index 0000000..05a4f66
--- /dev/null
+++ b/src/base/location.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2012 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_LOCATION_H_
+#define BASE_LOCATION_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace tracked_objects {
+
+// Location provides basic info where of an object was constructed, or was
+// significantly brought to life.
+class BASE_EXPORT Location {
+ public:
+ // Constructor should be called with a long-lived char*, such as __FILE__.
+ // It assumes the provided value will persist as a global constant, and it
+ // will not make a copy of it.
+ Location(const char* function_name,
+ const char* file_name,
+ int line_number,
+ const void* program_counter);
+
+ // Provide a default constructor for easy of debugging.
+ Location();
+
+ // Comparison operator for insertion into a std::map<> hash tables.
+ // All we need is *some* (any) hashing distinction. Strings should already
+ // be unique, so we don't bother with strcmp or such.
+ // Use line number as the primary key (because it is fast, and usually gets us
+ // a difference), and then pointers as secondary keys (just to get some
+ // distinctions).
+ bool operator < (const Location& other) const {
+ if (line_number_ != other.line_number_)
+ return line_number_ < other.line_number_;
+ if (file_name_ != other.file_name_)
+ return file_name_ < other.file_name_;
+ return function_name_ < other.function_name_;
+ }
+
+ const char* function_name() const { return function_name_; }
+ const char* file_name() const { return file_name_; }
+ int line_number() const { return line_number_; }
+ const void* program_counter() const { return program_counter_; }
+
+ std::string ToString() const;
+
+ // Translate the some of the state in this instance into a human readable
+ // string with HTML characters in the function names escaped, and append that
+ // string to |output|. Inclusion of the file_name_ and function_name_ are
+ // optional, and controlled by the boolean arguments.
+ void Write(bool display_filename, bool display_function_name,
+ std::string* output) const;
+
+ // Write function_name_ in HTML with '<' and '>' properly encoded.
+ void WriteFunctionName(std::string* output) const;
+
+ private:
+ const char* function_name_;
+ const char* file_name_;
+ int line_number_;
+ const void* program_counter_;
+};
+
+// A "snapshotted" representation of the Location class that can safely be
+// passed across process boundaries.
+struct BASE_EXPORT LocationSnapshot {
+ // The default constructor is exposed to support the IPC serialization macros.
+ LocationSnapshot();
+ explicit LocationSnapshot(const tracked_objects::Location& location);
+ ~LocationSnapshot();
+
+ std::string file_name;
+ std::string function_name;
+ int line_number;
+};
+
+BASE_EXPORT const void* GetProgramCounter();
+
+// Define a macro to record the current source location.
+#define FROM_HERE FROM_HERE_WITH_EXPLICIT_FUNCTION(__FUNCTION__)
+
+#define FROM_HERE_WITH_EXPLICIT_FUNCTION(function_name) \
+ ::tracked_objects::Location(function_name, \
+ __FILE__, \
+ __LINE__, \
+ ::tracked_objects::GetProgramCounter())
+
+} // namespace tracked_objects
+
+#endif // BASE_LOCATION_H_
diff --git a/src/base/logging.cc b/src/base/logging.cc
new file mode 100644
index 0000000..6a135ea
--- /dev/null
+++ b/src/base/logging.cc
@@ -0,0 +1,995 @@
+// Copyright (c) 2012 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"
+
+#if defined(OS_WIN)
+#include <io.h>
+#include <windows.h>
+typedef HANDLE FileHandle;
+typedef HANDLE MutexHandle;
+// Windows warns on using write(). It prefers _write().
+#define write(fd, buf, count) _write(fd, buf, static_cast<unsigned int>(count))
+// Windows doesn't define STDERR_FILENO. Define it here.
+#define STDERR_FILENO 2
+#elif defined(OS_MACOSX)
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <mach-o/dyld.h>
+#elif defined(OS_POSIX)
+#if defined(OS_NACL)
+#include <sys/time.h> // timespec doesn't seem to be in <time.h>
+#else
+#include <sys/syscall.h>
+#endif
+#include <time.h>
+#elif defined(OS_STARBOARD)
+#include "starboard/client_porting/eztime/eztime.h"
+#include "starboard/file.h"
+#include "starboard/log.h"
+#include "starboard/mutex.h"
+#include "starboard/system.h"
+#include "starboard/time.h"
+typedef SbFile FileHandle;
+typedef SbMutex MutexHandle;
+#endif
+
+#if defined(__LB_SHELL__)
+// To implement TickCount.
+#include "lb_platform.h"
+#endif
+
+#if defined(OS_POSIX)
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#define MAX_PATH PATH_MAX
+typedef FILE* FileHandle;
+typedef pthread_mutex_t* MutexHandle;
+#endif
+
+#include <algorithm>
+#include <cstring>
+#include <ctime>
+#include <iomanip>
+#include <ostream>
+
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/debug/alias.h"
+#include "base/debug/debugger.h"
+#include "base/debug/stack_trace.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/string_piece.h"
+#include "base/synchronization/lock_impl.h"
+#include "base/threading/platform_thread.h"
+#include "base/utf_string_conversions.h"
+#include "base/vlog.h"
+#if defined(OS_POSIX)
+#include "base/safe_strerror_posix.h"
+#endif
+
+#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
+#include <android/log.h>
+#endif
+
+#if defined(__LB_XB1__) || defined(__LB_XB360__)
+#undef MAX_PATH
+#include <Windows.h>
+#undef MAX_PATH
+#endif
+
+namespace logging {
+
+DcheckState g_dcheck_state = DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS;
+
+namespace {
+
+VlogInfo* g_vlog_info = NULL;
+VlogInfo* g_vlog_info_prev = NULL;
+
+const char* const log_severity_names[LOG_NUM_SEVERITIES] = {
+ "INFO", "WARNING", "ERROR", "ERROR_REPORT", "FATAL" };
+
+int min_log_level = 0;
+
+// The default set here for logging_destination will only be used if
+// InitLogging is not called. On Windows, use a file next to the exe;
+// on POSIX platforms, where it may not even be possible to locate the
+// executable on disk, use stderr.
+#if defined(OS_WIN)
+LoggingDestination logging_destination = LOG_ONLY_TO_FILE;
+#elif defined(OS_POSIX)
+LoggingDestination logging_destination = LOG_ONLY_TO_SYSTEM_DEBUG_LOG;
+#elif defined(OS_STARBOARD)
+LoggingDestination logging_destination = LOG_ONLY_TO_SYSTEM_DEBUG_LOG;
+#endif
+
+// For LOG_ERROR and above, always print to stderr.
+const int kAlwaysPrintErrorLevel = LOG_ERROR;
+
+// Which log file to use? This is initialized by InitLogging or
+// will be lazily initialized to the default value when it is
+// first needed.
+#if defined(OS_WIN)
+typedef std::wstring PathString;
+#else
+typedef std::string PathString;
+#endif
+PathString* log_file_name = NULL;
+
+// this file is lazily opened and the handle may be NULL
+FileHandle log_file = NULL;
+
+// what should be prepended to each message?
+bool log_process_id = false;
+bool log_thread_id = false;
+bool log_timestamp = true;
+bool log_tickcount = false;
+
+// Should we pop up fatal debug messages in a dialog?
+bool show_error_dialogs = false;
+
+// An assert handler override specified by the client to be called instead of
+// the debug message dialog and process termination.
+LogAssertHandlerFunction log_assert_handler = NULL;
+// An report handler override specified by the client to be called instead of
+// the debug message dialog.
+LogReportHandlerFunction log_report_handler = NULL;
+// A log message handler that gets notified of every log message we process.
+LogMessageHandlerFunction log_message_handler = NULL;
+
+// Helper functions to wrap platform differences.
+
+int32 CurrentProcessId() {
+#if defined(OS_WIN)
+ return GetCurrentProcessId();
+#elif defined(__LB_SHELL__) || defined(OS_STARBOARD)
+ // This should never be reached, because logging PIDs should never
+ // be enabled for LB shell.
+ return 0;
+#elif defined(OS_POSIX)
+ return getpid();
+#endif
+}
+
+uint64 TickCount() {
+#if defined(OS_WIN)
+ return GetTickCount();
+#elif defined(OS_MACOSX)
+ return mach_absolute_time();
+#elif defined(OS_NACL)
+ // NaCl sadly does not have _POSIX_TIMERS enabled in sys/features.h
+ // So we have to use clock() for now.
+ return clock();
+#elif defined(OS_STARBOARD)
+ return static_cast<uint64>(SbTimeGetMonotonicNow());
+#elif defined(__LB_SHELL__)
+ return LB::Platform::TickCount();
+#elif defined(OS_POSIX)
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ uint64 absolute_micro =
+ static_cast<int64>(ts.tv_sec) * 1000000 +
+ static_cast<int64>(ts.tv_nsec) / 1000;
+
+ return absolute_micro;
+#endif
+}
+
+void CloseFile(FileHandle log) {
+#if defined(OS_WIN)
+ CloseHandle(log);
+#elif defined(OS_STARBOARD)
+ SbFileClose(log);
+#else
+ fclose(log);
+#endif
+}
+
+void DeleteFilePath(const PathString& log_name) {
+#if defined(OS_WIN)
+ DeleteFile(log_name.c_str());
+#elif defined(OS_STARBOARD)
+ SbFileDelete(log_name.c_str());
+#else
+ unlink(log_name.c_str());
+#endif
+}
+
+PathString GetDefaultLogFile() {
+#if defined(OS_WIN)
+ // On Windows we use the same path as the exe.
+ wchar_t module_name[MAX_PATH];
+ GetModuleFileName(NULL, module_name, MAX_PATH);
+
+ PathString log_file = module_name;
+ PathString::size_type last_backslash =
+ log_file.rfind('\\', log_file.size());
+ if (last_backslash != PathString::npos)
+ log_file.erase(last_backslash + 1);
+ log_file += L"debug.log";
+ return log_file;
+#elif defined(OS_STARBOARD)
+ // On Starboard, we politely ask for the log directory, like a civilized
+ // platform.
+ char path[SB_FILE_MAX_PATH + 1];
+ SbSystemGetPath(kSbSystemPathDebugOutputDirectory, path,
+ SB_ARRAY_SIZE_INT(path));
+ PathString log_file = path;
+ log_file += SB_FILE_SEP_STRING "debug.log";
+ return log_file;
+#elif defined(OS_POSIX)
+ // On other platforms we just use the current directory.
+ return PathString("debug.log");
+#endif
+}
+
+// This class acts as a wrapper for locking the logging files.
+// LoggingLock::Init() should be called from the main thread before any logging
+// is done. Then whenever logging, be sure to have a local LoggingLock
+// instance on the stack. This will ensure that the lock is unlocked upon
+// exiting the frame.
+// LoggingLocks can not be nested.
+class LoggingLock {
+ public:
+ LoggingLock() {
+ LockLogging();
+ }
+
+ ~LoggingLock() {
+ UnlockLogging();
+ }
+
+ static void Init(LogLockingState lock_log, const PathChar* new_log_file) {
+ if (initialized)
+ return;
+ lock_log_file = lock_log;
+ if (lock_log_file == LOCK_LOG_FILE) {
+#if defined(OS_WIN)
+ if (!log_mutex) {
+ std::wstring safe_name;
+ if (new_log_file)
+ safe_name = new_log_file;
+ else
+ safe_name = GetDefaultLogFile();
+ // \ is not a legal character in mutex names so we replace \ with /
+ std::replace(safe_name.begin(), safe_name.end(), '\\', '/');
+ std::wstring t(L"Global\\");
+ t.append(safe_name);
+ log_mutex = ::CreateMutex(NULL, FALSE, t.c_str());
+
+ if (log_mutex == NULL) {
+#if DEBUG
+ // Keep the error code for debugging
+ int error = GetLastError(); // NOLINT
+ base::debug::BreakDebugger();
+#endif
+ // Return nicely without putting initialized to true.
+ return;
+ }
+ }
+#endif
+ } else {
+ log_lock = new base::internal::LockImpl();
+ }
+ initialized = true;
+ }
+
+ private:
+ static void LockLogging() {
+ if (lock_log_file == LOCK_LOG_FILE) {
+#if defined(OS_WIN)
+ ::WaitForSingleObject(log_mutex, INFINITE);
+ // WaitForSingleObject could have returned WAIT_ABANDONED. We don't
+ // abort the process here. UI tests might be crashy sometimes,
+ // and aborting the test binary only makes the problem worse.
+ // We also don't use LOG macros because that might lead to an infinite
+ // loop. For more info see http://crbug.com/18028.
+#elif defined(OS_POSIX)
+ pthread_mutex_lock(&log_mutex);
+#elif defined(OS_STARBOARD)
+ SbMutexAcquire(&log_mutex);
+#endif
+ } else {
+ // use the lock
+ log_lock->Lock();
+ }
+ }
+
+ static void UnlockLogging() {
+ if (lock_log_file == LOCK_LOG_FILE) {
+#if defined(OS_WIN)
+ ReleaseMutex(log_mutex);
+#elif defined(OS_POSIX)
+ pthread_mutex_unlock(&log_mutex);
+#elif defined(OS_STARBOARD)
+ SbMutexRelease(&log_mutex);
+#endif
+ } else {
+ log_lock->Unlock();
+ }
+ }
+
+ // The lock is used if log file locking is false. It helps us avoid problems
+ // with multiple threads writing to the log file at the same time. Use
+ // LockImpl directly instead of using Lock, because Lock makes logging calls.
+ static base::internal::LockImpl* log_lock;
+
+ // When we don't use a lock, we are using a global mutex. We need to do this
+ // because LockFileEx is not thread safe.
+#if defined(OS_WIN)
+ static MutexHandle log_mutex;
+#elif defined(OS_POSIX)
+ static pthread_mutex_t log_mutex;
+#elif defined(OS_STARBOARD)
+ static SbMutex log_mutex;
+#endif
+
+ static bool initialized;
+ static LogLockingState lock_log_file;
+};
+
+// static
+bool LoggingLock::initialized = false;
+// static
+base::internal::LockImpl* LoggingLock::log_lock = NULL;
+// static
+LogLockingState LoggingLock::lock_log_file = LOCK_LOG_FILE;
+
+#if defined(OS_WIN)
+// static
+MutexHandle LoggingLock::log_mutex = NULL;
+#elif defined(OS_POSIX)
+pthread_mutex_t LoggingLock::log_mutex = PTHREAD_MUTEX_INITIALIZER;
+#elif defined(OS_STARBOARD)
+SbMutex LoggingLock::log_mutex = SB_MUTEX_INITIALIZER;
+#endif
+
+// Called by logging functions to ensure that debug_file is initialized
+// and can be used for writing. Returns false if the file could not be
+// initialized. debug_file will be NULL in this case.
+bool InitializeLogFileHandle() {
+ if (log_file)
+ return true;
+
+ if (!log_file_name) {
+ // Nobody has called InitLogging to specify a debug log file, so here we
+ // initialize the log file name to a default.
+ log_file_name = new PathString(GetDefaultLogFile());
+ }
+
+#if defined(COBALT)
+ // This seems to get called a lot with an empty filename, at least in
+ // base_unittests.
+ if (log_file_name->empty()) {
+ return false;
+ }
+#endif
+
+ if (logging_destination == LOG_ONLY_TO_FILE ||
+ logging_destination == LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG) {
+#if defined(OS_WIN)
+ log_file = CreateFile(log_file_name->c_str(), GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) {
+ // try the current directory
+ log_file = CreateFile(L".\\debug.log", GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) {
+ log_file = NULL;
+ return false;
+ }
+ }
+ SetFilePointer(log_file, 0, 0, FILE_END);
+#elif defined(OS_POSIX)
+ log_file = fopen(log_file_name->c_str(), "a");
+ if (log_file == NULL)
+ return false;
+#elif defined(OS_STARBOARD)
+ log_file = SbFileOpen(log_file_name->c_str(),
+ kSbFileOpenAlways | kSbFileWrite, NULL, NULL);
+ if (!SbFileIsValid(log_file))
+ return false;
+
+ SbFileSeek(log_file, kSbFileFromEnd, 0);
+#endif
+ }
+
+ return true;
+}
+
+} // namespace
+
+
+bool BaseInitLoggingImpl(const PathChar* new_log_file,
+ LoggingDestination logging_dest,
+ LogLockingState lock_log,
+ OldFileDeletionState delete_old,
+ DcheckState dcheck_state) {
+ g_dcheck_state = dcheck_state;
+// TODO(bbudge) Hook this up to NaCl logging.
+#if !defined(OS_NACL)
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ // Don't bother initializing g_vlog_info unless we use one of the
+ // vlog switches.
+ if (command_line->HasSwitch(switches::kV) ||
+ command_line->HasSwitch(switches::kVModule)) {
+ // NOTE: If g_vlog_info has already been initialized, it might be in use
+ // by another thread. Don't delete the old VLogInfo, just create a second
+ // one. We keep track of both to avoid memory leak warnings.
+ CHECK(!g_vlog_info_prev);
+ g_vlog_info_prev = g_vlog_info;
+
+ g_vlog_info =
+ new VlogInfo(command_line->GetSwitchValueASCII(switches::kV),
+ command_line->GetSwitchValueASCII(switches::kVModule),
+ &min_log_level);
+ }
+
+ LoggingLock::Init(lock_log, new_log_file);
+
+ LoggingLock logging_lock;
+
+ if (log_file) {
+ // calling InitLogging twice or after some log call has already opened the
+ // default log file will re-initialize to the new options
+ CloseFile(log_file);
+ log_file = NULL;
+ }
+
+ logging_destination = logging_dest;
+
+ // ignore file options if logging is disabled or only to system
+ if (logging_destination == LOG_NONE ||
+ logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG)
+ return true;
+
+ if (!log_file_name)
+ log_file_name = new PathString();
+ *log_file_name = new_log_file;
+ if (delete_old == DELETE_OLD_LOG_FILE)
+ DeleteFilePath(*log_file_name);
+
+ return InitializeLogFileHandle();
+#else
+ (void) g_vlog_info_prev;
+ return true;
+#endif // !defined(OS_NACL)
+}
+
+void SetMinLogLevel(int level) {
+ min_log_level = std::min(LOG_ERROR_REPORT, level);
+}
+
+int GetMinLogLevel() {
+#if LOGGING_IS_OFFICIAL_BUILD
+ return LOG_FATAL;
+#else
+ return min_log_level;
+#endif
+}
+
+int GetVlogVerbosity() {
+ return std::max(-1, LOG_INFO - GetMinLogLevel());
+}
+
+int GetVlogLevelHelper(const char* file, size_t N) {
+ DCHECK_GT(N, 0U);
+ // Note: g_vlog_info may change on a different thread during startup
+ // (but will always be valid or NULL).
+ VlogInfo* vlog_info = g_vlog_info;
+ return vlog_info ?
+ vlog_info->GetVlogLevel(base::StringPiece(file, N - 1)) :
+ GetVlogVerbosity();
+}
+
+void SetLogItems(bool enable_process_id, bool enable_thread_id,
+ bool enable_timestamp, bool enable_tickcount) {
+ log_process_id = enable_process_id;
+ log_thread_id = enable_thread_id;
+ log_timestamp = enable_timestamp;
+ log_tickcount = enable_tickcount;
+}
+
+void SetShowErrorDialogs(bool enable_dialogs) {
+ show_error_dialogs = enable_dialogs;
+}
+
+void SetLogAssertHandler(LogAssertHandlerFunction handler) {
+ log_assert_handler = handler;
+}
+
+void SetLogReportHandler(LogReportHandlerFunction handler) {
+ log_report_handler = handler;
+}
+
+void SetLogMessageHandler(LogMessageHandlerFunction handler) {
+ log_message_handler = handler;
+}
+
+LogMessageHandlerFunction GetLogMessageHandler() {
+ return log_message_handler;
+}
+
+// MSVC doesn't like complex extern templates and DLLs.
+#if !defined(COMPILER_MSVC)
+// Explicit instantiations for commonly used comparisons.
+template std::string* MakeCheckOpString<int, int>(
+ const int&, const int&, const char* names);
+template std::string* MakeCheckOpString<unsigned long, unsigned long>(
+ const unsigned long&, const unsigned long&, const char* names);
+template std::string* MakeCheckOpString<unsigned long, unsigned int>(
+ const unsigned long&, const unsigned int&, const char* names);
+template std::string* MakeCheckOpString<unsigned int, unsigned long>(
+ const unsigned int&, const unsigned long&, const char* names);
+template std::string* MakeCheckOpString<std::string, std::string>(
+ const std::string&, const std::string&, const char* name);
+#endif
+
+// Displays a message box to the user with the error message in it.
+// Used for fatal messages, where we close the app simultaneously.
+// This is for developers only; we don't use this in circumstances
+// (like release builds) where users could see it, since users don't
+// understand these messages anyway.
+void DisplayDebugMessageInDialog(const std::string& str) {
+ if (str.empty())
+ return;
+
+ if (!show_error_dialogs)
+ return;
+
+#if defined(OS_WIN)
+ // For Windows programs, it's possible that the message loop is
+ // messed up on a fatal error, and creating a MessageBox will cause
+ // that message loop to be run. Instead, we try to spawn another
+ // process that displays its command line. We look for "Debug
+ // Message.exe" in the same directory as the application. If it
+ // exists, we use it, otherwise, we use a regular message box.
+ wchar_t prog_name[MAX_PATH];
+ GetModuleFileNameW(NULL, prog_name, MAX_PATH);
+ wchar_t* backslash = wcsrchr(prog_name, '\\');
+ if (backslash)
+ backslash[1] = 0;
+ wcscat_s(prog_name, MAX_PATH, L"debug_message.exe");
+
+ std::wstring cmdline = UTF8ToWide(str);
+ if (cmdline.empty())
+ return;
+
+ STARTUPINFO startup_info;
+ memset(&startup_info, 0, sizeof(startup_info));
+ startup_info.cb = sizeof(startup_info);
+
+ PROCESS_INFORMATION process_info;
+ if (CreateProcessW(prog_name, &cmdline[0], NULL, NULL, false, 0, NULL,
+ NULL, &startup_info, &process_info)) {
+ WaitForSingleObject(process_info.hProcess, INFINITE);
+ CloseHandle(process_info.hThread);
+ CloseHandle(process_info.hProcess);
+ } else {
+ // debug process broken, let's just do a message box
+ MessageBoxW(NULL, &cmdline[0], L"Fatal error",
+ MB_OK | MB_ICONHAND | MB_TOPMOST);
+ }
+#else
+ // We intentionally don't implement a dialog on other platforms.
+ // You can just look at stderr.
+#endif
+}
+
+#if defined(OS_WIN)
+LogMessage::SaveLastError::SaveLastError() : last_error_(::GetLastError()) {
+}
+
+LogMessage::SaveLastError::~SaveLastError() {
+ ::SetLastError(last_error_);
+}
+#endif // defined(OS_WIN)
+
+LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
+ int ctr)
+ : severity_(severity), file_(file), line_(line) {
+ Init(file, line);
+}
+
+LogMessage::LogMessage(const char* file, int line)
+ : severity_(LOG_INFO), file_(file), line_(line) {
+ Init(file, line);
+}
+
+LogMessage::LogMessage(const char* file, int line, LogSeverity severity)
+ : severity_(severity), file_(file), line_(line) {
+ Init(file, line);
+}
+
+LogMessage::LogMessage(const char* file, int line, std::string* result)
+ : severity_(LOG_FATAL), file_(file), line_(line) {
+ Init(file, line);
+ stream_ << "Check failed: " << *result;
+ delete result;
+}
+
+LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
+ std::string* result)
+ : severity_(severity), file_(file), line_(line) {
+ Init(file, line);
+ stream_ << "Check failed: " << *result;
+ delete result;
+}
+
+LogMessage::~LogMessage() {
+ // TODO(port): enable stacktrace generation on LOG_FATAL once backtrace are
+ // working in Android.
+#if (!defined(NDEBUG) || defined(__LB_SHELL__FORCE_LOGGING__)) && \
+ !defined(OS_ANDROID) && !defined(OS_NACL)
+ if (severity_ == LOG_FATAL) {
+ // Include a stack trace on a fatal.
+ base::debug::StackTrace trace;
+ stream_ << std::endl; // Newline to separate from log message.
+ trace.OutputToStream(&stream_);
+ }
+#endif
+ stream_ << std::endl;
+ std::string str_newline(stream_.str());
+
+ // Give any log message handler first dibs on the message.
+ if (log_message_handler && log_message_handler(severity_, file_, line_,
+ message_start_, str_newline)) {
+ // The handler took care of it, no further processing.
+ return;
+ }
+
+ if (logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG ||
+ logging_destination == LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG) {
+#if defined(OS_STARBOARD)
+ SbLogPriority priority = kSbLogPriorityUnknown;
+ switch (severity_) {
+ case LOG_INFO:
+ priority = kSbLogPriorityInfo;
+ break;
+ case LOG_WARNING:
+ priority = kSbLogPriorityWarning;
+ break;
+ case LOG_ERROR:
+ case LOG_ERROR_REPORT:
+ priority = kSbLogPriorityError;
+ break;
+ case LOG_FATAL:
+ priority = kSbLogPriorityFatal;
+ break;
+ }
+ SbLog(priority, str_newline.c_str());
+#elif defined(OS_WIN) || defined(COBALT_WIN)
+ OutputDebugStringA(str_newline.c_str());
+#elif defined(OS_ANDROID) || defined(__LB_ANDROID__)
+ android_LogPriority priority = ANDROID_LOG_UNKNOWN;
+ switch (severity_) {
+ case LOG_INFO:
+ priority = ANDROID_LOG_INFO;
+ break;
+ case LOG_WARNING:
+ priority = ANDROID_LOG_WARN;
+ break;
+ case LOG_ERROR:
+ case LOG_ERROR_REPORT:
+ priority = ANDROID_LOG_ERROR;
+ break;
+ case LOG_FATAL:
+ priority = ANDROID_LOG_FATAL;
+ break;
+ }
+ __android_log_write(priority, "chromium", str_newline.c_str());
+#endif
+#if !defined(OS_STARBOARD)
+ fprintf(stderr, "%s", str_newline.c_str());
+ fflush(stderr);
+#endif
+ } else if (severity_ >= kAlwaysPrintErrorLevel) {
+ // When we're only outputting to a log file, above a certain log level, we
+ // should still output to stderr so that we can better detect and diagnose
+ // problems with unit tests, especially on the buildbots.
+ fprintf(stderr, "%s", str_newline.c_str());
+ fflush(stderr);
+ }
+
+ // We can have multiple threads and/or processes, so try to prevent them
+ // from clobbering each other's writes.
+ // If the client app did not call InitLogging, and the lock has not
+ // been created do it now. We do this on demand, but if two threads try
+ // to do this at the same time, there will be a race condition to create
+ // the lock. This is why InitLogging should be called from the main
+ // thread at the beginning of execution.
+ LoggingLock::Init(LOCK_LOG_FILE, NULL);
+ // write to log file
+ if (logging_destination != LOG_NONE &&
+ logging_destination != LOG_ONLY_TO_SYSTEM_DEBUG_LOG) {
+ LoggingLock logging_lock;
+ if (InitializeLogFileHandle()) {
+#if defined(OS_WIN)
+ SetFilePointer(log_file, 0, 0, SEEK_END);
+ DWORD num_written;
+ WriteFile(log_file,
+ static_cast<const void*>(str_newline.c_str()),
+ static_cast<DWORD>(str_newline.length()),
+ &num_written,
+ NULL);
+#elif defined(OS_STARBOARD)
+ SbFileSeek(log_file, kSbFileFromEnd, 0);
+ int written = 0;
+ while (written < str_newline.length()) {
+ int result = SbFileWrite(log_file, &(str_newline.c_str()[written]),
+ str_newline.length() - written);
+ if (result < 0) {
+ break;
+ }
+ written += result;
+ }
+#else
+ fprintf(log_file, "%s", str_newline.c_str());
+ fflush(log_file);
+#endif
+ }
+ }
+
+ if (severity_ == LOG_FATAL) {
+ // Ensure the first characters of the string are on the stack so they
+ // are contained in minidumps for diagnostic purposes.
+ char str_stack[1024];
+ str_newline.copy(str_stack, arraysize(str_stack));
+ base::debug::Alias(str_stack);
+
+ // display a message or break into the debugger on a fatal error
+ if (base::debug::BeingDebugged()) {
+ base::debug::BreakDebugger();
+ } else {
+ if (log_assert_handler) {
+ // make a copy of the string for the handler out of paranoia
+ log_assert_handler(std::string(stream_.str()));
+ } else {
+ // Don't use the string with the newline, get a fresh version to send to
+ // the debug message process. We also don't display assertions to the
+ // user in release mode. The enduser can't do anything with this
+ // information, and displaying message boxes when the application is
+ // hosed can cause additional problems.
+#ifndef NDEBUG
+ DisplayDebugMessageInDialog(stream_.str());
+#endif
+ // Crash the process to generate a dump.
+ base::debug::BreakDebugger();
+ }
+ }
+ } else if (severity_ == LOG_ERROR_REPORT) {
+ // We are here only if the user runs with --enable-dcheck in release mode.
+ if (log_report_handler) {
+ log_report_handler(std::string(stream_.str()));
+ } else {
+ DisplayDebugMessageInDialog(stream_.str());
+ }
+ }
+}
+
+// writes the common header info to the stream
+void LogMessage::Init(const char* file, int line) {
+ base::StringPiece filename(file);
+ size_t last_slash_pos = filename.find_last_of("\\/");
+ if (last_slash_pos != base::StringPiece::npos)
+ filename.remove_prefix(last_slash_pos + 1);
+
+ // TODO(darin): It might be nice if the columns were fixed width.
+
+ stream_ << '[';
+ if (log_process_id)
+ stream_ << CurrentProcessId() << ':';
+ if (log_thread_id)
+ stream_ << base::PlatformThread::CurrentId() << ':';
+ if (log_timestamp) {
+#if defined(OS_STARBOARD)
+ EzTimeT t = EzTimeTGetNow(NULL);
+ struct EzTimeExploded local_time = {0};
+ EzTimeTExplodeLocal(&t, &local_time);
+ struct EzTimeExploded* tm_time = &local_time;
+#else
+ time_t t = time(NULL);
+ struct tm local_time = {0};
+#if _MSC_VER >= 1400
+ localtime_s(&local_time, &t);
+#else
+ localtime_r(&t, &local_time);
+#endif
+ struct tm* tm_time = &local_time;
+#endif
+ stream_ << std::setfill('0')
+ << std::setw(2) << 1 + tm_time->tm_mon
+ << std::setw(2) << tm_time->tm_mday
+ << '/'
+ << std::setw(2) << tm_time->tm_hour
+ << std::setw(2) << tm_time->tm_min
+ << std::setw(2) << tm_time->tm_sec
+ << ':';
+ }
+ if (log_tickcount)
+ stream_ << TickCount() << ':';
+ if (severity_ >= 0)
+ stream_ << log_severity_names[severity_];
+ else
+ stream_ << "VERBOSE" << -severity_;
+
+ stream_ << ":" << filename << "(" << line << ")] ";
+
+ message_start_ = stream_.tellp();
+}
+
+#if defined(OS_WIN)
+// This has already been defined in the header, but defining it again as DWORD
+// ensures that the type used in the header is equivalent to DWORD. If not,
+// the redefinition is a compile error.
+typedef DWORD SystemErrorCode;
+#endif
+
+SystemErrorCode GetLastSystemErrorCode() {
+#if defined(OS_WIN)
+ return ::GetLastError();
+#elif defined(OS_POSIX)
+ return errno;
+#elif defined(OS_STARBOARD)
+ // Not implemented yet
+ return SbSystemGetLastError();
+#else
+#error Not implemented
+#endif
+}
+
+#if defined(OS_WIN)
+Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err,
+ const char* module)
+ : err_(err),
+ module_(module),
+ log_message_(file, line, severity) {
+}
+
+Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err)
+ : err_(err),
+ module_(NULL),
+ log_message_(file, line, severity) {
+}
+
+Win32ErrorLogMessage::~Win32ErrorLogMessage() {
+ const int error_message_buffer_size = 256;
+ char msgbuf[error_message_buffer_size];
+ DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
+ HMODULE hmod;
+ if (module_) {
+ hmod = GetModuleHandleA(module_);
+ if (hmod) {
+ flags |= FORMAT_MESSAGE_FROM_HMODULE;
+ } else {
+ // This makes a nested Win32ErrorLogMessage. It will have module_ of NULL
+ // so it will not call GetModuleHandle, so recursive errors are
+ // impossible.
+ DPLOG(WARNING) << "Couldn't open module " << module_
+ << " for error message query";
+ }
+ } else {
+ hmod = NULL;
+ }
+ DWORD len = FormatMessageA(flags,
+ hmod,
+ err_,
+ 0,
+ msgbuf,
+ sizeof(msgbuf) / sizeof(msgbuf[0]),
+ NULL);
+ if (len) {
+ while ((len > 0) &&
+ isspace(static_cast<unsigned char>(msgbuf[len - 1]))) {
+ msgbuf[--len] = 0;
+ }
+ stream() << ": " << msgbuf;
+ } else {
+ stream() << ": Error " << GetLastError() << " while retrieving error "
+ << err_;
+ }
+ // We're about to crash (CHECK). Put |err_| on the stack (by placing it in a
+ // field) and use Alias in hopes that it makes it into crash dumps.
+ DWORD last_error = err_;
+ base::debug::Alias(&last_error);
+}
+#elif defined(OS_POSIX)
+ErrnoLogMessage::ErrnoLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err)
+ : err_(err),
+ log_message_(file, line, severity) {
+}
+
+ErrnoLogMessage::~ErrnoLogMessage() {
+ stream() << ": " << safe_strerror(err_);
+}
+#elif defined(OS_STARBOARD)
+StarboardLogMessage::StarboardLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err)
+ : err_(err),
+ log_message_(file, line, severity) {
+}
+
+StarboardLogMessage::~StarboardLogMessage() {
+ char message[512];
+ SbSystemGetErrorString(err_, message, SB_ARRAY_SIZE_INT(message));
+ stream() << ": " << message;
+}
+#endif // OS_WIN
+
+void CloseLogFile() {
+ LoggingLock logging_lock;
+
+ if (!log_file)
+ return;
+
+ CloseFile(log_file);
+ log_file = NULL;
+}
+
+void RawLog(int level, const char* message) {
+ if (level >= min_log_level) {
+#if defined(OS_STARBOARD)
+ SbLogRaw(message);
+ const size_t message_len = strlen(message);
+ if (message_len > 0 && message[message_len - 1] != '\n') {
+ SbLogRaw("\n");
+ }
+#else
+ size_t bytes_written = 0;
+ const size_t message_len = strlen(message);
+ int rv;
+ while (bytes_written < message_len) {
+ rv = HANDLE_EINTR(
+ write(STDERR_FILENO, message + bytes_written,
+ message_len - bytes_written));
+ if (rv < 0) {
+ // Give up, nothing we can do now.
+ break;
+ }
+ bytes_written += rv;
+ }
+
+ if (message_len > 0 && message[message_len - 1] != '\n') {
+ do {
+ rv = HANDLE_EINTR(write(STDERR_FILENO, "\n", 1));
+ if (rv < 0) {
+ // Give up, nothing we can do now.
+ break;
+ }
+ } while (rv != 1);
+ }
+#endif
+ }
+
+ if (level == LOG_FATAL)
+ base::debug::BreakDebugger();
+}
+
+// This was defined at the beginning of this file.
+#undef write
+
+} // namespace logging
+
+std::ostream& operator<<(std::ostream& out, const wchar_t* wstr) {
+ return out << WideToUTF8(std::wstring(wstr));
+}
diff --git a/src/base/logging.h b/src/base/logging.h
new file mode 100644
index 0000000..2fb626e
--- /dev/null
+++ b/src/base/logging.h
@@ -0,0 +1,1092 @@
+// Copyright (c) 2012 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_LOGGING_H_
+#define BASE_LOGGING_H_
+
+#include <cassert>
+#include <string>
+#include <cstring>
+#include <sstream>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/debug/debugger.h"
+#include "build/build_config.h"
+
+#if defined(OS_STARBOARD)
+#include "starboard/system.h"
+#endif
+
+//
+// Optional message capabilities
+// -----------------------------
+// Assertion failed messages and fatal errors are displayed in a dialog box
+// before the application exits. However, running this UI creates a message
+// loop, which causes application messages to be processed and potentially
+// dispatched to existing application windows. Since the application is in a
+// bad state when this assertion dialog is displayed, these messages may not
+// get processed and hang the dialog, or the application might go crazy.
+//
+// Therefore, it can be beneficial to display the error dialog in a separate
+// process from the main application. When the logging system needs to display
+// a fatal error dialog box, it will look for a program called
+// "DebugMessage.exe" in the same directory as the application executable. It
+// will run this application with the message as the command line, and will
+// not include the name of the application as is traditional for easier
+// parsing.
+//
+// The code for DebugMessage.exe is only one line. In WinMain, do:
+// MessageBox(NULL, GetCommandLineW(), L"Fatal Error", 0);
+//
+// If DebugMessage.exe is not found, the logging code will use a normal
+// MessageBox, potentially causing the problems discussed above.
+
+
+// Instructions
+// ------------
+//
+// Make a bunch of macros for logging. The way to log things is to stream
+// things to LOG(<a particular severity level>). E.g.,
+//
+// LOG(INFO) << "Found " << num_cookies << " cookies";
+//
+// You can also do conditional logging:
+//
+// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// The above will cause log messages to be output on the 1st, 11th, 21st, ...
+// times it is executed. Note that the special COUNTER value is used to
+// identify which repetition is happening.
+//
+// The CHECK(condition) macro is active in both debug and release builds and
+// effectively performs a LOG(FATAL) which terminates the process and
+// generates a crashdump unless a debugger is attached.
+//
+// There are also "debug mode" logging macros like the ones above:
+//
+// DLOG(INFO) << "Found cookies";
+//
+// DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// All "debug mode" logging is compiled away to nothing for non-debug mode
+// compiles. LOG_IF and development flags also work well together
+// because the code can be compiled away sometimes.
+//
+// We also have
+//
+// LOG_ASSERT(assertion);
+// DLOG_ASSERT(assertion);
+//
+// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion;
+//
+// There are "verbose level" logging macros. They look like
+//
+// VLOG(1) << "I'm printed when you run the program with --v=1 or more";
+// VLOG(2) << "I'm printed when you run the program with --v=2 or more";
+//
+// These always log at the INFO log level (when they log at all).
+// The verbose logging can also be turned on module-by-module. For instance,
+// --vmodule=profile=2,icon_loader=1,browser_*=3,*/chromeos/*=4 --v=0
+// will cause:
+// a. VLOG(2) and lower messages to be printed from profile.{h,cc}
+// b. VLOG(1) and lower messages to be printed from icon_loader.{h,cc}
+// c. VLOG(3) and lower messages to be printed from files prefixed with
+// "browser"
+// d. VLOG(4) and lower messages to be printed from files under a
+// "chromeos" directory.
+// e. VLOG(0) and lower messages to be printed from elsewhere
+//
+// The wildcarding functionality shown by (c) supports both '*' (match
+// 0 or more characters) and '?' (match any single character)
+// wildcards. Any pattern containing a forward or backward slash will
+// be tested against the whole pathname and not just the module.
+// E.g., "*/foo/bar/*=2" would change the logging level for all code
+// in source files under a "foo/bar" directory.
+//
+// There's also VLOG_IS_ON(n) "verbose level" condition macro. To be used as
+//
+// if (VLOG_IS_ON(2)) {
+// // do some logging preparation and logging
+// // that can't be accomplished with just VLOG(2) << ...;
+// }
+//
+// There is also a VLOG_IF "verbose level" condition macro for sample
+// cases, when some extra computation and preparation for logs is not
+// needed.
+//
+// VLOG_IF(1, (size > 1024))
+// << "I'm printed when size is more than 1024 and when you run the "
+// "program with --v=1 or more";
+//
+// We also override the standard 'assert' to use 'DLOG_ASSERT'.
+//
+// Lastly, there is:
+//
+// PLOG(ERROR) << "Couldn't do foo";
+// DPLOG(ERROR) << "Couldn't do foo";
+// PLOG_IF(ERROR, cond) << "Couldn't do foo";
+// DPLOG_IF(ERROR, cond) << "Couldn't do foo";
+// PCHECK(condition) << "Couldn't do foo";
+// DPCHECK(condition) << "Couldn't do foo";
+//
+// which append the last system error to the message in string form (taken from
+// GetLastError() on Windows and errno on POSIX).
+//
+// The supported severity levels for macros that allow you to specify one
+// are (in increasing order of severity) INFO, WARNING, ERROR, ERROR_REPORT,
+// and FATAL.
+//
+// Very important: logging a message at the FATAL severity level causes
+// the program to terminate (after the message is logged).
+//
+// Note the special severity of ERROR_REPORT only available/relevant in normal
+// mode, which displays error dialog without terminating the program. There is
+// no error dialog for severity ERROR or below in normal mode.
+//
+// There is also the special severity of DFATAL, which logs FATAL in
+// debug mode, ERROR in normal mode.
+
+namespace logging {
+
+// Where to record logging output? A flat file and/or system debug log via
+// OutputDebugString. Defaults on Windows to LOG_ONLY_TO_FILE, and on
+// POSIX to LOG_ONLY_TO_SYSTEM_DEBUG_LOG (aka stderr).
+enum LoggingDestination { LOG_NONE,
+ LOG_ONLY_TO_FILE,
+ LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
+ LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG };
+
+// Indicates that the log file should be locked when being written to.
+// Often, there is no locking, which is fine for a single threaded program.
+// If logging is being done from multiple threads or there can be more than
+// one process doing the logging, the file should be locked during writes to
+// make each log outut atomic. Other writers will block.
+//
+// All processes writing to the log file must have their locking set for it to
+// work properly. Defaults to DONT_LOCK_LOG_FILE.
+enum LogLockingState { LOCK_LOG_FILE, DONT_LOCK_LOG_FILE };
+
+// On startup, should we delete or append to an existing log file (if any)?
+// Defaults to APPEND_TO_OLD_LOG_FILE.
+enum OldFileDeletionState { DELETE_OLD_LOG_FILE, APPEND_TO_OLD_LOG_FILE };
+
+enum DcheckState {
+ DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS,
+ ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS
+};
+
+// TODO(avi): do we want to do a unification of character types here?
+#if defined(OS_WIN)
+typedef wchar_t PathChar;
+#else
+typedef char PathChar;
+#endif
+
+// Define different names for the BaseInitLoggingImpl() function depending on
+// whether NDEBUG is defined or not so that we'll fail to link if someone tries
+// to compile logging.cc with NDEBUG but includes logging.h without defining it,
+// or vice versa.
+#if defined(NDEBUG) && !defined(__LB_SHELL__FORCE_LOGGING__)
+#define BaseInitLoggingImpl BaseInitLoggingImpl_built_with_NDEBUG
+#else
+#define BaseInitLoggingImpl BaseInitLoggingImpl_built_without_NDEBUG
+#endif
+
+// Implementation of the InitLogging() method declared below. We use a
+// more-specific name so we can #define it above without affecting other code
+// that has named stuff "InitLogging".
+BASE_EXPORT bool BaseInitLoggingImpl(const PathChar* log_file,
+ LoggingDestination logging_dest,
+ LogLockingState lock_log,
+ OldFileDeletionState delete_old,
+ DcheckState dcheck_state);
+
+// Sets the log file name and other global logging state. Calling this function
+// is recommended, and is normally done at the beginning of application init.
+// If you don't call it, all the flags will be initialized to their default
+// values, and there is a race condition that may leak a critical section
+// object if two threads try to do the first log at the same time.
+// See the definition of the enums above for descriptions and default values.
+//
+// The default log file is initialized to "debug.log" in the application
+// directory. You probably don't want this, especially since the program
+// directory may not be writable on an enduser's system.
+//
+// This function may be called a second time to re-direct logging (e.g after
+// loging in to a user partition), however it should never be called more than
+// twice.
+inline bool InitLogging(const PathChar* log_file,
+ LoggingDestination logging_dest,
+ LogLockingState lock_log,
+ OldFileDeletionState delete_old,
+ DcheckState dcheck_state) {
+ return BaseInitLoggingImpl(log_file, logging_dest, lock_log,
+ delete_old, dcheck_state);
+}
+
+// Sets the log level. Anything at or above this level will be written to the
+// log file/displayed to the user (if applicable). Anything below this level
+// will be silently ignored. The log level defaults to 0 (everything is logged
+// up to level INFO) if this function is not called.
+// Note that log messages for VLOG(x) are logged at level -x, so setting
+// the min log level to negative values enables verbose logging.
+BASE_EXPORT void SetMinLogLevel(int level);
+
+// Gets the current log level.
+BASE_EXPORT int GetMinLogLevel();
+
+// Gets the VLOG default verbosity level.
+BASE_EXPORT int GetVlogVerbosity();
+
+// Gets the current vlog level for the given file (usually taken from
+// __FILE__).
+
+// Note that |N| is the size *with* the null terminator.
+BASE_EXPORT int GetVlogLevelHelper(const char* file_start, size_t N);
+
+template <size_t N>
+int GetVlogLevel(const char (&file)[N]) {
+ return GetVlogLevelHelper(file, N);
+}
+
+// Sets the common items you want to be prepended to each log message.
+// process and thread IDs default to off, the timestamp defaults to on.
+// If this function is not called, logging defaults to writing the timestamp
+// only.
+BASE_EXPORT void SetLogItems(bool enable_process_id, bool enable_thread_id,
+ bool enable_timestamp, bool enable_tickcount);
+
+// Sets whether or not you'd like to see fatal debug messages popped up in
+// a dialog box or not.
+// Dialogs are not shown by default.
+BASE_EXPORT void SetShowErrorDialogs(bool enable_dialogs);
+
+// Sets the Log Assert Handler that will be used to notify of check failures.
+// The default handler shows a dialog box and then terminate the process,
+// however clients can use this function to override with their own handling
+// (e.g. a silent one for Unit Tests)
+typedef void (*LogAssertHandlerFunction)(const std::string& str);
+BASE_EXPORT void SetLogAssertHandler(LogAssertHandlerFunction handler);
+
+// Sets the Log Report Handler that will be used to notify of check failures
+// in non-debug mode. The default handler shows a dialog box and continues
+// the execution, however clients can use this function to override with their
+// own handling.
+typedef void (*LogReportHandlerFunction)(const std::string& str);
+BASE_EXPORT void SetLogReportHandler(LogReportHandlerFunction handler);
+
+// Sets the Log Message Handler that gets passed every log message before
+// it's sent to other log destinations (if any).
+// Returns true to signal that it handled the message and the message
+// should not be sent to other log destinations.
+typedef bool (*LogMessageHandlerFunction)(int severity,
+ const char* file, int line, size_t message_start, const std::string& str);
+BASE_EXPORT void SetLogMessageHandler(LogMessageHandlerFunction handler);
+BASE_EXPORT LogMessageHandlerFunction GetLogMessageHandler();
+
+typedef int LogSeverity;
+const LogSeverity LOG_VERBOSE = -1; // This is level 1 verbosity
+// Note: the log severities are used to index into the array of names,
+// see log_severity_names.
+const LogSeverity LOG_INFO = 0;
+const LogSeverity LOG_WARNING = 1;
+const LogSeverity LOG_ERROR = 2;
+const LogSeverity LOG_ERROR_REPORT = 3;
+const LogSeverity LOG_FATAL = 4;
+const LogSeverity LOG_NUM_SEVERITIES = 5;
+
+// LOG_DFATAL is LOG_FATAL in debug mode, ERROR in normal mode
+#if defined(NDEBUG) && !defined(__LB_SHELL__FORCE_LOGGING__)
+const LogSeverity LOG_DFATAL = LOG_ERROR;
+#else
+const LogSeverity LOG_DFATAL = LOG_FATAL;
+#endif
+
+// A few definitions of macros that don't generate much code. These are used
+// by LOG() and LOG_IF, etc. Since these are used all over our code, it's
+// better to have compact code for these operations.
+#define COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...) \
+ logging::ClassName(__FILE__, __LINE__, logging::LOG_INFO , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...) \
+ logging::ClassName(__FILE__, __LINE__, logging::LOG_WARNING , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ...) \
+ logging::ClassName(__FILE__, __LINE__, logging::LOG_ERROR , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(ClassName, ...) \
+ logging::ClassName(__FILE__, __LINE__, \
+ logging::LOG_ERROR_REPORT , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ...) \
+ logging::ClassName(__FILE__, __LINE__, logging::LOG_FATAL , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...) \
+ logging::ClassName(__FILE__, __LINE__, logging::LOG_DFATAL , ##__VA_ARGS__)
+
+#define COMPACT_GOOGLE_LOG_INFO \
+ COMPACT_GOOGLE_LOG_EX_INFO(LogMessage)
+#define COMPACT_GOOGLE_LOG_WARNING \
+ COMPACT_GOOGLE_LOG_EX_WARNING(LogMessage)
+#define COMPACT_GOOGLE_LOG_ERROR \
+ COMPACT_GOOGLE_LOG_EX_ERROR(LogMessage)
+#define COMPACT_GOOGLE_LOG_ERROR_REPORT \
+ COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(LogMessage)
+#define COMPACT_GOOGLE_LOG_FATAL \
+ COMPACT_GOOGLE_LOG_EX_FATAL(LogMessage)
+#define COMPACT_GOOGLE_LOG_DFATAL \
+ COMPACT_GOOGLE_LOG_EX_DFATAL(LogMessage)
+
+// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets
+// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us
+// to keep using this syntax, we define this macro to do the same thing
+// as COMPACT_GOOGLE_LOG_ERROR, and also define ERROR the same way that
+// the Windows SDK does for consistency.
+#define ERROR 0
+#define COMPACT_GOOGLE_LOG_EX_0(ClassName, ...) \
+ COMPACT_GOOGLE_LOG_EX_ERROR(ClassName , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_0 COMPACT_GOOGLE_LOG_ERROR
+// Needed for LOG_IS_ON(ERROR).
+const LogSeverity LOG_0 = LOG_ERROR;
+
+// As special cases, we can assume that LOG_IS_ON(ERROR_REPORT) and
+// LOG_IS_ON(FATAL) always hold. Also, LOG_IS_ON(DFATAL) always holds
+// in debug mode. In particular, CHECK()s will always fire if they
+// fail.
+#define LOG_IS_ON(severity) \
+ ((::logging::LOG_ ## severity) >= ::logging::GetMinLogLevel())
+
+// We can't do any caching tricks with VLOG_IS_ON() like the
+// google-glog version since it requires GCC extensions. This means
+// that using the v-logging functions in conjunction with --vmodule
+// may be slow.
+#define VLOG_IS_ON(verboselevel) \
+ ((verboselevel) <= ::logging::GetVlogLevel(__FILE__))
+
+// Helper macro which avoids evaluating the arguments to a stream if
+// the condition doesn't hold.
+#define LAZY_STREAM(stream, condition) \
+ !(condition) ? (void) 0 : ::logging::LogMessageVoidify() & (stream)
+
+// We use the preprocessor's merging operator, "##", so that, e.g.,
+// LOG(INFO) becomes the token COMPACT_GOOGLE_LOG_INFO. There's some funny
+// subtle difference between ostream member streaming functions (e.g.,
+// ostream::operator<<(int) and ostream non-member streaming functions
+// (e.g., ::operator<<(ostream&, string&): it turns out that it's
+// impossible to stream something like a string directly to an unnamed
+// ostream. We employ a neat hack by calling the stream() member
+// function of LogMessage which seems to avoid the problem.
+#define LOG_STREAM(severity) COMPACT_GOOGLE_LOG_ ## severity.stream()
+
+#define LOG(severity) LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity))
+#define LOG_IF(severity, condition) \
+ LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity) && (condition))
+
+#define SYSLOG(severity) LOG(severity)
+#define SYSLOG_IF(severity, condition) LOG_IF(severity, condition)
+
+// The VLOG macros log with negative verbosities.
+#define VLOG_STREAM(verbose_level) \
+ logging::LogMessage(__FILE__, __LINE__, -verbose_level).stream()
+
+#define VLOG(verbose_level) \
+ LAZY_STREAM(VLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level))
+
+#define VLOG_IF(verbose_level, condition) \
+ LAZY_STREAM(VLOG_STREAM(verbose_level), \
+ VLOG_IS_ON(verbose_level) && (condition))
+
+#if defined (OS_WIN)
+#define VPLOG_STREAM(verbose_level) \
+ logging::Win32ErrorLogMessage(__FILE__, __LINE__, -verbose_level, \
+ ::logging::GetLastSystemErrorCode()).stream()
+#elif defined(OS_POSIX)
+#define VPLOG_STREAM(verbose_level) \
+ logging::ErrnoLogMessage(__FILE__, __LINE__, -verbose_level, \
+ ::logging::GetLastSystemErrorCode()).stream()
+#elif defined(OS_STARBOARD)
+#define VPLOG_STREAM(verbose_level) \
+ logging::StarboardLogMessage(__FILE__, __LINE__, -verbose_level, \
+ ::logging::GetLastSystemErrorCode()).stream()
+#endif
+
+#define VPLOG(verbose_level) \
+ LAZY_STREAM(VPLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level))
+
+#define VPLOG_IF(verbose_level, condition) \
+ LAZY_STREAM(VPLOG_STREAM(verbose_level), \
+ VLOG_IS_ON(verbose_level) && (condition))
+
+// TODO(akalin): Add more VLOG variants, e.g. VPLOG.
+
+#define LOG_ASSERT(condition) \
+ LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". "
+#define SYSLOG_ASSERT(condition) \
+ SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". "
+
+#if defined(OS_WIN)
+#define LOG_GETLASTERROR_STREAM(severity) \
+ COMPACT_GOOGLE_LOG_EX_ ## severity(Win32ErrorLogMessage, \
+ ::logging::GetLastSystemErrorCode()).stream()
+#define LOG_GETLASTERROR(severity) \
+ LAZY_STREAM(LOG_GETLASTERROR_STREAM(severity), LOG_IS_ON(severity))
+#define LOG_GETLASTERROR_MODULE_STREAM(severity, module) \
+ COMPACT_GOOGLE_LOG_EX_ ## severity(Win32ErrorLogMessage, \
+ ::logging::GetLastSystemErrorCode(), module).stream()
+#define LOG_GETLASTERROR_MODULE(severity, module) \
+ LAZY_STREAM(LOG_GETLASTERROR_STREAM(severity, module), \
+ LOG_IS_ON(severity))
+// PLOG_STREAM is used by PLOG, which is the usual error logging macro
+// for each platform.
+#define PLOG_STREAM(severity) LOG_GETLASTERROR_STREAM(severity)
+#elif defined(OS_POSIX)
+#define LOG_ERRNO_STREAM(severity) \
+ COMPACT_GOOGLE_LOG_EX_ ## severity(ErrnoLogMessage, \
+ ::logging::GetLastSystemErrorCode()).stream()
+#define LOG_ERRNO(severity) \
+ LAZY_STREAM(LOG_ERRNO_STREAM(severity), LOG_IS_ON(severity))
+// PLOG_STREAM is used by PLOG, which is the usual error logging macro
+// for each platform.
+#define PLOG_STREAM(severity) LOG_ERRNO_STREAM(severity)
+#elif defined(OS_STARBOARD)
+#define LOG_SB_ERROR_STREAM(severity) \
+ COMPACT_GOOGLE_LOG_EX_ ## severity(StarboardLogMessage, \
+ ::logging::GetLastSystemErrorCode()).stream()
+#define LOG_SB_ERROR(severity) \
+ LAZY_STREAM(LOG_SB_ERROR_STREAM(severity), LOG_IS_ON(severity))
+// PLOG_STREAM is used by PLOG, which is the usual error logging macro
+// for each platform.
+#define PLOG_STREAM(severity) LOG_SB_ERROR_STREAM(severity)
+#endif
+
+#define PLOG(severity) \
+ LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity))
+
+#define PLOG_IF(severity, condition) \
+ LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity) && (condition))
+
+// http://crbug.com/16512 is open for a real fix for this. For now, Windows
+// uses OFFICIAL_BUILD and other platforms use the branding flag when NDEBUG is
+// defined.
+#if ( defined(OS_WIN) && defined(OFFICIAL_BUILD)) || \
+ (!defined(OS_WIN) && defined(NDEBUG) && defined(GOOGLE_CHROME_BUILD)) || \
+ defined(__LB_SHELL__FOR_RELEASE__)
+#define LOGGING_IS_OFFICIAL_BUILD 1
+#else
+#define LOGGING_IS_OFFICIAL_BUILD 0
+#endif
+
+// The actual stream used isn't important.
+#define EAT_STREAM_PARAMETERS \
+ true ? (void) 0 : ::logging::LogMessageVoidify() & LOG_STREAM(FATAL)
+
+// CHECK dies with a fatal error if condition is not true. It is *not*
+// controlled by NDEBUG, so the check will be executed regardless of
+// compilation mode.
+//
+// We make sure CHECK et al. always evaluates their arguments, as
+// doing CHECK(FunctionWithSideEffect()) is a common idiom.
+
+#if LOGGING_IS_OFFICIAL_BUILD
+
+// Make all CHECK functions discard their log strings to reduce code
+// bloat for official builds.
+
+// TODO(akalin): This would be more valuable if there were some way to
+// remove BreakDebugger() from the backtrace, perhaps by turning it
+// into a macro (like __debugbreak() on Windows).
+#define CHECK(condition) \
+ !(condition) ? ::base::debug::BreakDebugger() : EAT_STREAM_PARAMETERS
+
+#define PCHECK(condition) CHECK(condition)
+
+#define CHECK_OP(name, op, val1, val2) CHECK((val1) op (val2))
+
+#else
+
+// Help Microsoft code analysis tool to understand that if CHECK()/PCHECK()
+// did not terminate the execution, the condition they checked must be true.
+// Note that the code under _PREFAST_ macro is not actually executed, only
+// analyzed.
+#if defined(COBALT) && defined(_PREFAST_)
+#define CHECK(condition) \
+ __analysis_assume(condition), EAT_STREAM_PARAMETERS
+
+#define PCHECK(condition) \
+ __analysis_assume(condition), EAT_STREAM_PARAMETERS
+#else
+#define CHECK(condition) \
+ LAZY_STREAM(LOG_STREAM(FATAL), !(condition)) \
+ << "Check failed: " #condition ". "
+
+#define PCHECK(condition) \
+ LAZY_STREAM(PLOG_STREAM(FATAL), !(condition)) \
+ << "Check failed: " #condition ". "
+#endif
+
+// Helper macro for binary operators.
+// Don't use this macro directly in your code, use CHECK_EQ et al below.
+//
+// TODO(akalin): Rewrite this so that constructs like if (...)
+// CHECK_EQ(...) else { ... } work properly.
+#define CHECK_OP(name, op, val1, val2) \
+ if (std::string* _result = \
+ logging::Check##name##Impl((val1), (val2), \
+ #val1 " " #op " " #val2)) \
+ logging::LogMessage(__FILE__, __LINE__, _result).stream()
+
+#endif
+
+// Build the error message string. This is separate from the "Impl"
+// function template because it is not performance critical and so can
+// be out of line, while the "Impl" code should be inline. Caller
+// takes ownership of the returned string.
+template<class t1, class t2>
+std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) {
+ std::ostringstream ss;
+ ss << names << " (" << v1 << " vs. " << v2 << ")";
+ std::string* msg = new std::string(ss.str());
+ return msg;
+}
+
+// MSVC doesn't like complex extern templates and DLLs.
+#if !defined(COMPILER_MSVC) && !defined(COMPILER_GHS)
+// Commonly used instantiations of MakeCheckOpString<>. Explicitly instantiated
+// in logging.cc.
+extern template BASE_EXPORT std::string* MakeCheckOpString<int, int>(
+ const int&, const int&, const char* names);
+extern template BASE_EXPORT
+std::string* MakeCheckOpString<unsigned long, unsigned long>(
+ const unsigned long&, const unsigned long&, const char* names);
+extern template BASE_EXPORT
+std::string* MakeCheckOpString<unsigned long, unsigned int>(
+ const unsigned long&, const unsigned int&, const char* names);
+extern template BASE_EXPORT
+std::string* MakeCheckOpString<unsigned int, unsigned long>(
+ const unsigned int&, const unsigned long&, const char* names);
+extern template BASE_EXPORT
+std::string* MakeCheckOpString<std::string, std::string>(
+ const std::string&, const std::string&, const char* name);
+#endif
+
+// Helper functions for CHECK_OP macro.
+// The (int, int) specialization works around the issue that the compiler
+// will not instantiate the template version of the function on values of
+// unnamed enum type - see comment below.
+#define DEFINE_CHECK_OP_IMPL(name, op) \
+ template <class t1, class t2> \
+ inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \
+ const char* names) { \
+ if (v1 op v2) return NULL; \
+ else return MakeCheckOpString(v1, v2, names); \
+ } \
+ inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \
+ if (v1 op v2) return NULL; \
+ else return MakeCheckOpString(v1, v2, names); \
+ }
+
+// Turn off sign-conversion warnings for DCHECK_EQ. This is for
+// consistency with EXPECT_EQ and ASSERT_EQ in gtest.
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4389)
+#elif defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wsign-compare"
+#endif
+
+DEFINE_CHECK_OP_IMPL(EQ, ==)
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#elif defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+DEFINE_CHECK_OP_IMPL(NE, !=)
+DEFINE_CHECK_OP_IMPL(LE, <=)
+DEFINE_CHECK_OP_IMPL(LT, < )
+DEFINE_CHECK_OP_IMPL(GE, >=)
+DEFINE_CHECK_OP_IMPL(GT, > )
+#undef DEFINE_CHECK_OP_IMPL
+
+#define CHECK_EQ(val1, val2) CHECK_OP(EQ, ==, val1, val2)
+#define CHECK_NE(val1, val2) CHECK_OP(NE, !=, val1, val2)
+#define CHECK_LE(val1, val2) CHECK_OP(LE, <=, val1, val2)
+#define CHECK_LT(val1, val2) CHECK_OP(LT, < , val1, val2)
+#define CHECK_GE(val1, val2) CHECK_OP(GE, >=, val1, val2)
+#define CHECK_GT(val1, val2) CHECK_OP(GT, > , val1, val2)
+
+#if LOGGING_IS_OFFICIAL_BUILD || \
+ (defined(__LB_SHELL__FOR_QA__) && !defined(__LB_SHELL__FORCE_LOGGING__))
+// In order to have optimized code for official builds, remove DLOGs and
+// DCHECKs.
+#define ENABLE_DLOG 0
+#define ENABLE_DCHECK 0
+
+#elif defined(NDEBUG) && !defined(__LB_SHELL__FORCE_LOGGING__)
+// Otherwise, if we're a release build, remove DLOGs but not DCHECKs
+// (since those can still be turned on via a command-line flag).
+#define ENABLE_DLOG 0
+#define ENABLE_DCHECK 1
+
+#else
+// Otherwise, we're a debug build so enable DLOGs and DCHECKs.
+#define ENABLE_DLOG 1
+#define ENABLE_DCHECK 1
+#endif
+
+// Definitions for DLOG et al.
+
+#if ENABLE_DLOG
+
+#define DLOG_IS_ON(severity) LOG_IS_ON(severity)
+#define DLOG_IF(severity, condition) LOG_IF(severity, condition)
+#define DLOG_ASSERT(condition) LOG_ASSERT(condition)
+#define DPLOG_IF(severity, condition) PLOG_IF(severity, condition)
+#define DVLOG_IF(verboselevel, condition) VLOG_IF(verboselevel, condition)
+#define DVPLOG_IF(verboselevel, condition) VPLOG_IF(verboselevel, condition)
+
+#else // ENABLE_DLOG
+
+// If ENABLE_DLOG is off, we want to avoid emitting any references to
+// |condition| (which may reference a variable defined only if NDEBUG
+// is not defined). Contrast this with DCHECK et al., which has
+// different behavior.
+
+#define DLOG_IS_ON(severity) false
+#define DLOG_IF(severity, condition) EAT_STREAM_PARAMETERS
+#define DLOG_ASSERT(condition) EAT_STREAM_PARAMETERS
+#define DPLOG_IF(severity, condition) EAT_STREAM_PARAMETERS
+#define DVLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS
+#define DVPLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS
+
+#endif // ENABLE_DLOG
+
+// DEBUG_MODE is for uses like
+// if (DEBUG_MODE) foo.CheckThatFoo();
+// instead of
+// #ifndef NDEBUG
+// foo.CheckThatFoo();
+// #endif
+//
+// We tie its state to ENABLE_DLOG.
+enum { DEBUG_MODE = ENABLE_DLOG };
+
+#undef ENABLE_DLOG
+
+#define DLOG(severity) \
+ LAZY_STREAM(LOG_STREAM(severity), DLOG_IS_ON(severity))
+
+#if defined(OS_WIN)
+#define DLOG_GETLASTERROR(severity) \
+ LAZY_STREAM(LOG_GETLASTERROR_STREAM(severity), DLOG_IS_ON(severity))
+#define DLOG_GETLASTERROR_MODULE(severity, module) \
+ LAZY_STREAM(LOG_GETLASTERROR_STREAM(severity, module), \
+ DLOG_IS_ON(severity))
+#elif defined(OS_POSIX)
+#define DLOG_ERRNO(severity) \
+ LAZY_STREAM(LOG_ERRNO_STREAM(severity), DLOG_IS_ON(severity))
+#endif
+
+#define DPLOG(severity) \
+ LAZY_STREAM(PLOG_STREAM(severity), DLOG_IS_ON(severity))
+
+#define DVLOG(verboselevel) DVLOG_IF(verboselevel, VLOG_IS_ON(verboselevel))
+
+#define DVPLOG(verboselevel) DVPLOG_IF(verboselevel, VLOG_IS_ON(verboselevel))
+
+// Definitions for DCHECK et al.
+
+#if ENABLE_DCHECK
+
+#if defined(NDEBUG) && !defined(__LB_SHELL__FORCE_LOGGING__)
+
+BASE_EXPORT extern DcheckState g_dcheck_state;
+
+#if defined(DCHECK_ALWAYS_ON)
+
+#define DCHECK_IS_ON() true
+#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \
+ COMPACT_GOOGLE_LOG_EX_FATAL(ClassName , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_FATAL
+const LogSeverity LOG_DCHECK = LOG_FATAL;
+
+#else
+
+#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \
+ COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(ClassName , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_ERROR_REPORT
+const LogSeverity LOG_DCHECK = LOG_ERROR_REPORT;
+#define DCHECK_IS_ON() \
+ ((::logging::g_dcheck_state == \
+ ::logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS) && \
+ LOG_IS_ON(DCHECK))
+
+#endif // defined(DCHECK_ALWAYS_ON)
+
+#else // defined(NDEBUG) && !defined(__LB_SHELL__FORCE_LOGGING__)
+
+#if defined(NDEBUG) && defined(__LB_SHELL__FORCE_LOGGING__)
+BASE_EXPORT extern DcheckState g_dcheck_state;
+#endif
+
+// On a regular debug build, we want to have DCHECKs enabled.
+#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \
+ COMPACT_GOOGLE_LOG_EX_FATAL(ClassName , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_FATAL
+const LogSeverity LOG_DCHECK = LOG_FATAL;
+#define DCHECK_IS_ON() true
+
+#endif // defined(NDEBUG) && !defined(__LB_SHELL__FORCE_LOGGING__)
+
+#else // ENABLE_DCHECK
+
+// These are just dummy values since DCHECK_IS_ON() is always false in
+// this case.
+#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \
+ COMPACT_GOOGLE_LOG_EX_INFO(ClassName , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_INFO
+const LogSeverity LOG_DCHECK = LOG_INFO;
+#define DCHECK_IS_ON() false
+
+#endif // ENABLE_DCHECK
+#undef ENABLE_DCHECK
+
+// DCHECK et al. make sure to reference |condition| regardless of
+// whether DCHECKs are enabled; this is so that we don't get unused
+// variable warnings if the only use of a variable is in a DCHECK.
+// This behavior is different from DLOG_IF et al.
+
+// Help Microsoft code analysis tool to understand that if DCHECK()/DPCHECK()
+// did not terminate the execution, the condition they checked must be true.
+// Note that the code under _PREFAST_ macro is not actually executed, only
+// analyzed.
+// Use !!(condition) to work around error C2088:
+// '__assume': illegal for class
+#if defined(COBALT) && defined(_PREFAST_)
+#define DCHECK(condition) \
+ __analysis_assume(!!(condition)), EAT_STREAM_PARAMETERS
+
+#define DPCHECK(condition) \
+ __analysis_assume(!!(condition)), EAT_STREAM_PARAMETERS
+#else
+#define DCHECK(condition) \
+ LAZY_STREAM(LOG_STREAM(DCHECK), DCHECK_IS_ON() ? !(condition) : false) \
+ << "Check failed: " #condition ". "
+
+#define DPCHECK(condition) \
+ LAZY_STREAM(PLOG_STREAM(DCHECK), DCHECK_IS_ON() ? !(condition) : false) \
+ << "Check failed: " #condition ". "
+#endif
+
+// Helper macro for binary operators.
+// Don't use this macro directly in your code, use DCHECK_EQ et al below.
+#define DCHECK_OP(name, op, val1, val2) \
+ if (DCHECK_IS_ON()) \
+ if (std::string* _result = \
+ logging::Check##name##Impl((val1), (val2), \
+ #val1 " " #op " " #val2)) \
+ logging::LogMessage( \
+ __FILE__, __LINE__, ::logging::LOG_DCHECK, \
+ _result).stream()
+
+// Equality/Inequality checks - compare two values, and log a
+// LOG_DCHECK message including the two values when the result is not
+// as expected. The values must have operator<<(ostream, ...)
+// defined.
+//
+// You may append to the error message like so:
+// DCHECK_NE(1, 2) << ": The world must be ending!";
+//
+// We are very careful to ensure that each argument is evaluated exactly
+// once, and that anything which is legal to pass as a function argument is
+// legal here. In particular, the arguments may be temporary expressions
+// which will end up being destroyed at the end of the apparent statement,
+// for example:
+// DCHECK_EQ(string("abc")[1], 'b');
+//
+// WARNING: These may not compile correctly if one of the arguments is a pointer
+// and the other is NULL. To work around this, simply static_cast NULL to the
+// type of the desired pointer.
+
+#define DCHECK_EQ(val1, val2) DCHECK_OP(EQ, ==, val1, val2)
+#define DCHECK_NE(val1, val2) DCHECK_OP(NE, !=, val1, val2)
+#define DCHECK_LE(val1, val2) DCHECK_OP(LE, <=, val1, val2)
+#define DCHECK_LT(val1, val2) DCHECK_OP(LT, < , val1, val2)
+#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2)
+#define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2)
+
+#define NOTREACHED() DCHECK(false)
+
+// Redefine the standard assert to use our nice log files
+#undef assert
+#define assert(x) DLOG_ASSERT(x)
+
+// This class more or less represents a particular log message. You
+// create an instance of LogMessage and then stream stuff to it.
+// When you finish streaming to it, ~LogMessage is called and the
+// full message gets streamed to the appropriate destination.
+//
+// You shouldn't actually use LogMessage's constructor to log things,
+// though. You should use the LOG() macro (and variants thereof)
+// above.
+class BASE_EXPORT LogMessage {
+ public:
+ LogMessage(const char* file, int line, LogSeverity severity, int ctr);
+
+ // Two special constructors that generate reduced amounts of code at
+ // LOG call sites for common cases.
+ //
+ // Used for LOG(INFO): Implied are:
+ // severity = LOG_INFO, ctr = 0
+ //
+ // Using this constructor instead of the more complex constructor above
+ // saves a couple of bytes per call site.
+ LogMessage(const char* file, int line);
+
+ // Used for LOG(severity) where severity != INFO. Implied
+ // are: ctr = 0
+ //
+ // Using this constructor instead of the more complex constructor above
+ // saves a couple of bytes per call site.
+ LogMessage(const char* file, int line, LogSeverity severity);
+
+ // A special constructor used for check failures. Takes ownership
+ // of the given string.
+ // Implied severity = LOG_FATAL
+ LogMessage(const char* file, int line, std::string* result);
+
+ // A special constructor used for check failures, with the option to
+ // specify severity. Takes ownership of the given string.
+ LogMessage(const char* file, int line, LogSeverity severity,
+ std::string* result);
+
+ ~LogMessage();
+
+ std::ostream& stream() { return stream_; }
+
+ private:
+ void Init(const char* file, int line);
+
+ LogSeverity severity_;
+ std::ostringstream stream_;
+ size_t message_start_; // Offset of the start of the message (past prefix
+ // info).
+ // The file and line information passed in to the constructor.
+ const char* file_;
+ const int line_;
+
+#if defined(OS_WIN)
+ // Stores the current value of GetLastError in the constructor and restores
+ // it in the destructor by calling SetLastError.
+ // This is useful since the LogMessage class uses a lot of Win32 calls
+ // that will lose the value of GLE and the code that called the log function
+ // will have lost the thread error value when the log call returns.
+ class SaveLastError {
+ public:
+ SaveLastError();
+ ~SaveLastError();
+
+ unsigned long get_error() const { return last_error_; }
+
+ protected:
+ unsigned long last_error_;
+ };
+
+ SaveLastError last_error_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(LogMessage);
+};
+
+// A non-macro interface to the log facility; (useful
+// when the logging level is not a compile-time constant).
+inline void LogAtLevel(int const log_level, std::string const &msg) {
+ LogMessage(__FILE__, __LINE__, log_level).stream() << msg;
+}
+
+// This class is used to explicitly ignore values in the conditional
+// logging macros. This avoids compiler warnings like "value computed
+// is not used" and "statement has no effect".
+class LogMessageVoidify {
+ public:
+ LogMessageVoidify() { }
+ // This has to be an operator with a precedence lower than << but
+ // higher than ?:
+ void operator&(std::ostream&) { }
+};
+
+#if defined(OS_WIN)
+typedef unsigned long SystemErrorCode;
+#elif defined(OS_POSIX)
+typedef int SystemErrorCode;
+#elif defined(OS_STARBOARD)
+typedef SbSystemError SystemErrorCode;
+#endif
+
+// Alias for ::GetLastError() on Windows and errno on POSIX. Avoids having to
+// pull in windows.h just for GetLastError() and DWORD.
+BASE_EXPORT SystemErrorCode GetLastSystemErrorCode();
+
+#if defined(OS_WIN)
+// Appends a formatted system message of the GetLastError() type.
+class BASE_EXPORT Win32ErrorLogMessage {
+ public:
+ Win32ErrorLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err,
+ const char* module);
+
+ Win32ErrorLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err);
+
+ // Appends the error message before destructing the encapsulated class.
+ ~Win32ErrorLogMessage();
+
+ std::ostream& stream() { return log_message_.stream(); }
+
+ private:
+ SystemErrorCode err_;
+ // Optional name of the module defining the error.
+ const char* module_;
+ LogMessage log_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(Win32ErrorLogMessage);
+};
+#elif defined(OS_POSIX)
+// Appends a formatted system message of the errno type
+class BASE_EXPORT ErrnoLogMessage {
+ public:
+ ErrnoLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err);
+
+ // Appends the error message before destructing the encapsulated class.
+ ~ErrnoLogMessage();
+
+ std::ostream& stream() { return log_message_.stream(); }
+
+ private:
+ SystemErrorCode err_;
+ LogMessage log_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(ErrnoLogMessage);
+};
+#elif defined(OS_STARBOARD)
+// Appends a formatted system message of the errno type
+class BASE_EXPORT StarboardLogMessage {
+ public:
+ StarboardLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err);
+
+ // Appends the error message before destructing the encapsulated class.
+ ~StarboardLogMessage();
+
+ std::ostream& stream() { return log_message_.stream(); }
+
+ private:
+ SystemErrorCode err_;
+ LogMessage log_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(StarboardLogMessage);
+};
+#endif // OS_WIN
+
+// Closes the log file explicitly if open.
+// NOTE: Since the log file is opened as necessary by the action of logging
+// statements, there's no guarantee that it will stay closed
+// after this call.
+BASE_EXPORT void CloseLogFile();
+
+// Async signal safe logging mechanism.
+BASE_EXPORT void RawLog(int level, const char* message);
+
+#define RAW_LOG(level, message) logging::RawLog(logging::LOG_ ## level, message)
+
+#define RAW_CHECK(condition) \
+ do { \
+ if (!(condition)) \
+ logging::RawLog(logging::LOG_FATAL, "Check failed: " #condition "\n"); \
+ } while (0)
+
+} // namespace logging
+
+// These functions are provided as a convenience for logging, which is where we
+// use streams (it is against Google style to use streams in other places). It
+// is designed to allow you to emit non-ASCII Unicode strings to the log file,
+// which is normally ASCII. It is relatively slow, so try not to use it for
+// common cases. Non-ASCII characters will be converted to UTF-8 by these
+// operators.
+BASE_EXPORT std::ostream& operator<<(std::ostream& out, const wchar_t* wstr);
+inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) {
+ return out << wstr.c_str();
+}
+
+#if defined(__LB_SHELL__) || defined(COBALT)
+#if defined(__cplusplus_winrt)
+// Support for logging C++/CX strings. This function must be inlined because
+// the Chromium library is not compiled with C++/CX extensions enabled.
+inline std::ostream& operator<<(std::ostream& out, ::Platform::String^ str) {
+ return out << std::wstring(str->Begin(), str->End());
+}
+#endif
+#endif
+
+// The NOTIMPLEMENTED() macro annotates codepaths which have
+// not been implemented yet.
+//
+// The implementation of this macro is controlled by NOTIMPLEMENTED_POLICY:
+// 0 -- Do nothing (stripped by compiler)
+// 1 -- Warn at compile time
+// 2 -- Fail at compile time
+// 3 -- Fail at runtime (DCHECK)
+// 4 -- [default] LOG(ERROR) at runtime
+// 5 -- LOG(ERROR) at runtime, only once per call-site
+
+#ifndef NOTIMPLEMENTED_POLICY
+#if (defined(OS_ANDROID) && defined(OFFICIAL_BUILD)) || \
+ defined(__LB_SHELL__FOR_RELEASE__)
+#define NOTIMPLEMENTED_POLICY 0
+#elif defined (__LB_SHELL__) || defined(COBALT)
+ // Only print each message once.
+#define NOTIMPLEMENTED_POLICY 5
+#else
+ // Select default policy: LOG(ERROR)
+#define NOTIMPLEMENTED_POLICY 4
+#endif
+#endif
+
+#if defined(COMPILER_GCC)
+// On Linux, with GCC, we can use __PRETTY_FUNCTION__ to get the demangled name
+// of the current function in the NOTIMPLEMENTED message.
+#define NOTIMPLEMENTED_MSG "Not implemented reached in " << __PRETTY_FUNCTION__
+#elif defined(__LB_SHELL__) || defined(COBALT)
+#define NOTIMPLEMENTED_MSG "Not implemented reached in " << __FUNCTION__
+#else
+#define NOTIMPLEMENTED_MSG "NOT IMPLEMENTED"
+#endif
+
+#if NOTIMPLEMENTED_POLICY == 0
+#define NOTIMPLEMENTED() EAT_STREAM_PARAMETERS
+#elif NOTIMPLEMENTED_POLICY == 1
+// TODO, figure out how to generate a warning
+#define NOTIMPLEMENTED() COMPILE_ASSERT(false, NOT_IMPLEMENTED)
+#elif NOTIMPLEMENTED_POLICY == 2
+#define NOTIMPLEMENTED() COMPILE_ASSERT(false, NOT_IMPLEMENTED)
+#elif NOTIMPLEMENTED_POLICY == 3
+#define NOTIMPLEMENTED() NOTREACHED()
+#elif NOTIMPLEMENTED_POLICY == 4
+#define NOTIMPLEMENTED() LOG(ERROR) << NOTIMPLEMENTED_MSG
+#elif NOTIMPLEMENTED_POLICY == 5
+#define NOTIMPLEMENTED() do {\
+ static int count = 0;\
+ LOG_IF(ERROR, 0 == count++) << NOTIMPLEMENTED_MSG;\
+} while(0);\
+EAT_STREAM_PARAMETERS
+#endif
+
+#endif // BASE_LOGGING_H_
diff --git a/src/base/logging_unittest.cc b/src/base/logging_unittest.cc
new file mode 100644
index 0000000..f0e9f56
--- /dev/null
+++ b/src/base/logging_unittest.cc
@@ -0,0 +1,279 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace logging {
+
+namespace {
+
+using ::testing::Return;
+
+// Needs to be global since log assert handlers can't maintain state.
+int log_sink_call_count = 0;
+
+void LogSink(const std::string& str) {
+ ++log_sink_call_count;
+}
+
+// Class to make sure any manipulations we do to the min log level are
+// contained (i.e., do not affect other unit tests).
+class LogStateSaver {
+ public:
+ LogStateSaver() : old_min_log_level_(GetMinLogLevel()) {}
+
+ ~LogStateSaver() {
+ SetMinLogLevel(old_min_log_level_);
+ SetLogAssertHandler(NULL);
+ SetLogReportHandler(NULL);
+ log_sink_call_count = 0;
+ }
+
+ private:
+ int old_min_log_level_;
+
+ DISALLOW_COPY_AND_ASSIGN(LogStateSaver);
+};
+
+class LoggingTest : public testing::Test {
+ private:
+ LogStateSaver log_state_saver_;
+};
+
+class MockLogSource {
+ public:
+ MOCK_METHOD0(Log, const char*());
+};
+
+TEST_F(LoggingTest, BasicLogging) {
+ MockLogSource mock_log_source;
+ const int kExpectedDebugOrReleaseCalls = 6;
+ const int kExpectedDebugCalls = 6;
+ const int kExpectedCalls =
+ kExpectedDebugOrReleaseCalls + (DEBUG_MODE ? kExpectedDebugCalls : 0);
+ EXPECT_CALL(mock_log_source, Log()).Times(kExpectedCalls).
+ WillRepeatedly(Return("log message"));
+
+ SetMinLogLevel(LOG_INFO);
+
+ EXPECT_TRUE(LOG_IS_ON(INFO));
+ // As of g++-4.5, the first argument to EXPECT_EQ cannot be a
+ // constant expression.
+ const bool kIsDebugMode = (DEBUG_MODE != 0);
+ EXPECT_EQ(kIsDebugMode, DLOG_IS_ON(INFO));
+ EXPECT_TRUE(VLOG_IS_ON(0));
+
+ LOG(INFO) << mock_log_source.Log();
+ LOG_IF(INFO, true) << mock_log_source.Log();
+ PLOG(INFO) << mock_log_source.Log();
+ PLOG_IF(INFO, true) << mock_log_source.Log();
+ VLOG(0) << mock_log_source.Log();
+ VLOG_IF(0, true) << mock_log_source.Log();
+
+ DLOG(INFO) << mock_log_source.Log();
+ DLOG_IF(INFO, true) << mock_log_source.Log();
+ DPLOG(INFO) << mock_log_source.Log();
+ DPLOG_IF(INFO, true) << mock_log_source.Log();
+ DVLOG(0) << mock_log_source.Log();
+ DVLOG_IF(0, true) << mock_log_source.Log();
+}
+
+TEST_F(LoggingTest, LogIsOn) {
+#if defined(NDEBUG) && !defined(__LB_SHELL__FORCE_LOGGING__)
+ const bool kDfatalIsFatal = false;
+#else // defined(NDEBUG)
+ const bool kDfatalIsFatal = true;
+#endif // defined(NDEBUG)
+
+ SetMinLogLevel(LOG_INFO);
+ EXPECT_TRUE(LOG_IS_ON(INFO));
+ EXPECT_TRUE(LOG_IS_ON(WARNING));
+ EXPECT_TRUE(LOG_IS_ON(ERROR));
+ EXPECT_TRUE(LOG_IS_ON(ERROR_REPORT));
+ EXPECT_TRUE(LOG_IS_ON(FATAL));
+ EXPECT_TRUE(LOG_IS_ON(DFATAL));
+
+ SetMinLogLevel(LOG_WARNING);
+ EXPECT_FALSE(LOG_IS_ON(INFO));
+ EXPECT_TRUE(LOG_IS_ON(WARNING));
+ EXPECT_TRUE(LOG_IS_ON(ERROR));
+ EXPECT_TRUE(LOG_IS_ON(ERROR_REPORT));
+ EXPECT_TRUE(LOG_IS_ON(FATAL));
+ EXPECT_TRUE(LOG_IS_ON(DFATAL));
+
+ SetMinLogLevel(LOG_ERROR);
+ EXPECT_FALSE(LOG_IS_ON(INFO));
+ EXPECT_FALSE(LOG_IS_ON(WARNING));
+ EXPECT_TRUE(LOG_IS_ON(ERROR));
+ EXPECT_TRUE(LOG_IS_ON(ERROR_REPORT));
+ EXPECT_TRUE(LOG_IS_ON(FATAL));
+ EXPECT_TRUE(LOG_IS_ON(DFATAL));
+
+ SetMinLogLevel(LOG_ERROR_REPORT);
+ EXPECT_FALSE(LOG_IS_ON(INFO));
+ EXPECT_FALSE(LOG_IS_ON(WARNING));
+ EXPECT_FALSE(LOG_IS_ON(ERROR));
+ EXPECT_TRUE(LOG_IS_ON(ERROR_REPORT));
+ EXPECT_TRUE(LOG_IS_ON(FATAL));
+ EXPECT_EQ(kDfatalIsFatal, LOG_IS_ON(DFATAL));
+
+ // LOG_IS_ON(ERROR_REPORT) should always be true.
+ SetMinLogLevel(LOG_FATAL);
+ EXPECT_FALSE(LOG_IS_ON(INFO));
+ EXPECT_FALSE(LOG_IS_ON(WARNING));
+ EXPECT_FALSE(LOG_IS_ON(ERROR));
+ EXPECT_TRUE(LOG_IS_ON(ERROR_REPORT));
+ EXPECT_TRUE(LOG_IS_ON(FATAL));
+ EXPECT_EQ(kDfatalIsFatal, LOG_IS_ON(DFATAL));
+
+ // So should LOG_IS_ON(FATAL).
+ SetMinLogLevel(LOG_FATAL + 1);
+ EXPECT_FALSE(LOG_IS_ON(INFO));
+ EXPECT_FALSE(LOG_IS_ON(WARNING));
+ EXPECT_FALSE(LOG_IS_ON(ERROR));
+ EXPECT_TRUE(LOG_IS_ON(ERROR_REPORT));
+ EXPECT_TRUE(LOG_IS_ON(FATAL));
+ EXPECT_EQ(kDfatalIsFatal, LOG_IS_ON(DFATAL));
+}
+
+TEST_F(LoggingTest, LoggingIsLazy) {
+ MockLogSource mock_log_source;
+ EXPECT_CALL(mock_log_source, Log()).Times(0);
+
+ SetMinLogLevel(LOG_WARNING);
+
+ EXPECT_FALSE(LOG_IS_ON(INFO));
+ EXPECT_FALSE(DLOG_IS_ON(INFO));
+ EXPECT_FALSE(VLOG_IS_ON(1));
+
+ LOG(INFO) << mock_log_source.Log();
+ LOG_IF(INFO, false) << mock_log_source.Log();
+ PLOG(INFO) << mock_log_source.Log();
+ PLOG_IF(INFO, false) << mock_log_source.Log();
+ VLOG(1) << mock_log_source.Log();
+ VLOG_IF(1, true) << mock_log_source.Log();
+
+ DLOG(INFO) << mock_log_source.Log();
+ DLOG_IF(INFO, true) << mock_log_source.Log();
+ DPLOG(INFO) << mock_log_source.Log();
+ DPLOG_IF(INFO, true) << mock_log_source.Log();
+ DVLOG(1) << mock_log_source.Log();
+ DVLOG_IF(1, true) << mock_log_source.Log();
+}
+
+// Official builds have CHECKs directly call BreakDebugger.
+#if !defined(LOGGING_IS_OFFICIAL_BUILD)
+
+TEST_F(LoggingTest, CheckStreamsAreLazy) {
+ MockLogSource mock_log_source, uncalled_mock_log_source;
+ EXPECT_CALL(mock_log_source, Log()).Times(8).
+ WillRepeatedly(Return("check message"));
+ EXPECT_CALL(uncalled_mock_log_source, Log()).Times(0);
+
+ SetLogAssertHandler(&LogSink);
+
+ CHECK(mock_log_source.Log()) << uncalled_mock_log_source.Log();
+ PCHECK(!mock_log_source.Log()) << mock_log_source.Log();
+ CHECK_EQ(mock_log_source.Log(), mock_log_source.Log())
+ << uncalled_mock_log_source.Log();
+ CHECK_NE(mock_log_source.Log(), mock_log_source.Log())
+ << mock_log_source.Log();
+}
+
+#endif
+
+TEST_F(LoggingTest, DebugLoggingReleaseBehavior) {
+#if !defined(NDEBUG) || defined(__LB_SHELL__FORCE_LOGGING__)
+ int debug_only_variable = 1;
+#endif
+ // These should avoid emitting references to |debug_only_variable|
+ // in release mode.
+ DLOG_IF(INFO, debug_only_variable) << "test";
+ DLOG_ASSERT(debug_only_variable) << "test";
+ DPLOG_IF(INFO, debug_only_variable) << "test";
+ DVLOG_IF(1, debug_only_variable) << "test";
+}
+
+TEST_F(LoggingTest, DcheckStreamsAreLazy) {
+ MockLogSource mock_log_source;
+ EXPECT_CALL(mock_log_source, Log()).Times(0);
+#if !defined(LOGGING_IS_OFFICIAL_BUILD) && defined(NDEBUG) && \
+ !defined(DCHECK_ALWAYS_ON)
+ // Unofficial release build without dcheck enabled.
+ g_dcheck_state = DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS;
+ DCHECK(mock_log_source.Log()) << mock_log_source.Log();
+ DPCHECK(mock_log_source.Log()) << mock_log_source.Log();
+ DCHECK_EQ(0, 0) << mock_log_source.Log();
+ DCHECK_EQ(mock_log_source.Log(), static_cast<const char*>(NULL))
+ << mock_log_source.Log();
+#endif
+}
+
+TEST_F(LoggingTest, Dcheck) {
+#if LOGGING_IS_OFFICIAL_BUILD
+ // Official build.
+ EXPECT_FALSE(DCHECK_IS_ON());
+ EXPECT_FALSE(DLOG_IS_ON(DCHECK));
+#elif defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) && !defined(__LB_SHELL__FORCE_LOGGING__)
+ // Unofficial release build.
+ g_dcheck_state = ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS;
+ SetLogReportHandler(&LogSink);
+ EXPECT_TRUE(DCHECK_IS_ON());
+ EXPECT_FALSE(DLOG_IS_ON(DCHECK));
+#elif defined(NDEBUG) && defined(DCHECK_ALWAYS_ON)
+ // Unofficial release build with real DCHECKS.
+ g_dcheck_state = ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS;
+ SetLogAssertHandler(&LogSink);
+ EXPECT_TRUE(DCHECK_IS_ON());
+ EXPECT_FALSE(DLOG_IS_ON(DCHECK));
+#elif defined(NDEBUG) && defined(__LB_SHELL__FORCE_LOGGING__)
+ // Unofficial release build with real DCHECKS.
+ g_dcheck_state = ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS;
+ SetLogAssertHandler(&LogSink);
+ EXPECT_TRUE(DCHECK_IS_ON());
+ // FORCE_LOGGING implies DLOG is on.
+ EXPECT_TRUE(DLOG_IS_ON(DCHECK));
+#else
+ // Unofficial debug build.
+ SetLogAssertHandler(&LogSink);
+ EXPECT_TRUE(DCHECK_IS_ON());
+ EXPECT_TRUE(DLOG_IS_ON(DCHECK));
+#endif // defined(LOGGING_IS_OFFICIAL_BUILD)
+
+#if !defined(OS_STARBOARD)
+#if defined(__LB_SHELL__)
+ // These only break when the Logging class thinks a debugger is attached.
+ // Unfortunately, LB_SHELL assumes a debugger is attached as long as
+ // NDEBUG is not defined for some platforms (see debugger_posix.cc:BeingDebugged()).
+ if (!base::debug::BeingDebugged()) {
+#endif
+ EXPECT_EQ(0, log_sink_call_count);
+ DCHECK(false);
+ EXPECT_EQ(DCHECK_IS_ON() ? 1 : 0, log_sink_call_count);
+ DPCHECK(false);
+ EXPECT_EQ(DCHECK_IS_ON() ? 2 : 0, log_sink_call_count);
+ DCHECK_EQ(0, 1);
+ EXPECT_EQ(DCHECK_IS_ON() ? 3 : 0, log_sink_call_count);
+#if defined(__LB_SHELL__)
+ }
+#endif
+#endif
+}
+
+TEST_F(LoggingTest, DcheckReleaseBehavior) {
+ int some_variable = 1;
+ // These should still reference |some_variable| so we don't get
+ // unused variable warnings.
+ DCHECK(some_variable) << "test";
+ DPCHECK(some_variable) << "test";
+ DCHECK_EQ(some_variable, 1) << "test";
+}
+
+} // namespace
+
+} // namespace logging
diff --git a/src/base/logging_win.cc b/src/base/logging_win.cc
new file mode 100644
index 0000000..a714665
--- /dev/null
+++ b/src/base/logging_win.cc
@@ -0,0 +1,139 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging_win.h"
+#include "base/memory/singleton.h"
+#include <initguid.h> // NOLINT
+
+namespace logging {
+
+using base::win::EtwEventLevel;
+using base::win::EtwMofEvent;
+
+DEFINE_GUID(kLogEventId,
+ 0x7fe69228, 0x633e, 0x4f06, 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7);
+
+LogEventProvider::LogEventProvider() : old_log_level_(LOG_NONE) {
+}
+
+LogEventProvider* LogEventProvider::GetInstance() {
+ return Singleton<LogEventProvider,
+ StaticMemorySingletonTraits<LogEventProvider> >::get();
+}
+
+bool LogEventProvider::LogMessage(logging::LogSeverity severity,
+ const char* file, int line, size_t message_start,
+ const std::string& message) {
+ EtwEventLevel level = TRACE_LEVEL_NONE;
+
+ // Convert the log severity to the most appropriate ETW trace level.
+ if (severity >= 0) {
+ switch (severity) {
+ case LOG_INFO:
+ level = TRACE_LEVEL_INFORMATION;
+ break;
+ case LOG_WARNING:
+ level = TRACE_LEVEL_WARNING;
+ break;
+ case LOG_ERROR:
+ case LOG_ERROR_REPORT:
+ level = TRACE_LEVEL_ERROR;
+ break;
+ case LOG_FATAL:
+ level = TRACE_LEVEL_FATAL;
+ break;
+ }
+ } else { // severity < 0 is VLOG verbosity levels.
+ level = TRACE_LEVEL_INFORMATION - severity;
+ }
+
+ // Bail if we're not logging, not at that level,
+ // or if we're post-atexit handling.
+ LogEventProvider* provider = LogEventProvider::GetInstance();
+ if (provider == NULL || level > provider->enable_level())
+ return false;
+
+ // And now log the event.
+ if (provider->enable_flags() & ENABLE_LOG_MESSAGE_ONLY) {
+ EtwMofEvent<1> event(kLogEventId, LOG_MESSAGE, level);
+ event.SetField(0, message.length() + 1 - message_start,
+ message.c_str() + message_start);
+
+ provider->Log(event.get());
+ } else {
+ const size_t kMaxBacktraceDepth = 32;
+ void* backtrace[kMaxBacktraceDepth];
+ DWORD depth = 0;
+
+ // Capture a stack trace if one is requested.
+ // requested per our enable flags.
+ if (provider->enable_flags() & ENABLE_STACK_TRACE_CAPTURE)
+ depth = CaptureStackBackTrace(2, kMaxBacktraceDepth, backtrace, NULL);
+
+ EtwMofEvent<5> event(kLogEventId, LOG_MESSAGE_FULL, level);
+ if (file == NULL)
+ file = "";
+
+ // Add the stack trace.
+ event.SetField(0, sizeof(depth), &depth);
+ event.SetField(1, sizeof(backtrace[0]) * depth, &backtrace);
+ // The line.
+ event.SetField(2, sizeof(line), &line);
+ // The file.
+ event.SetField(3, strlen(file) + 1, file);
+ // And finally the message.
+ event.SetField(4, message.length() + 1 - message_start,
+ message.c_str() + message_start);
+
+ provider->Log(event.get());
+ }
+
+ // Don't increase verbosity in other log destinations.
+ if (severity < provider->old_log_level_)
+ return true;
+
+ return false;
+}
+
+void LogEventProvider::Initialize(const GUID& provider_name) {
+ LogEventProvider* provider = LogEventProvider::GetInstance();
+
+ provider->set_provider_name(provider_name);
+ provider->Register();
+
+ // Register our message handler with logging.
+ SetLogMessageHandler(LogMessage);
+}
+
+void LogEventProvider::Uninitialize() {
+ LogEventProvider::GetInstance()->Unregister();
+}
+
+void LogEventProvider::OnEventsEnabled() {
+ // Grab the old log level so we can restore it later.
+ old_log_level_ = GetMinLogLevel();
+
+ // Convert the new trace level to a logging severity
+ // and enable logging at that level.
+ EtwEventLevel level = enable_level();
+ if (level == TRACE_LEVEL_NONE || level == TRACE_LEVEL_FATAL) {
+ SetMinLogLevel(LOG_FATAL);
+ } else if (level == TRACE_LEVEL_ERROR) {
+ SetMinLogLevel(LOG_ERROR);
+ } else if (level == TRACE_LEVEL_WARNING) {
+ SetMinLogLevel(LOG_WARNING);
+ } else if (level == TRACE_LEVEL_INFORMATION) {
+ SetMinLogLevel(LOG_INFO);
+ } else if (level >= TRACE_LEVEL_VERBOSE) {
+ // Above INFO, we enable verbose levels with negative severities.
+ SetMinLogLevel(TRACE_LEVEL_INFORMATION - level);
+ }
+}
+
+void LogEventProvider::OnEventsDisabled() {
+ // Restore the old log level.
+ SetMinLogLevel(old_log_level_);
+}
+
+} // namespace logging
diff --git a/src/base/logging_win.h b/src/base/logging_win.h
new file mode 100644
index 0000000..0720231
--- /dev/null
+++ b/src/base/logging_win.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2012 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_LOGGING_WIN_H_
+#define BASE_LOGGING_WIN_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/win/event_trace_provider.h"
+#include "base/logging.h"
+
+template <typename Type>
+struct StaticMemorySingletonTraits;
+
+namespace logging {
+
+// Event ID for the log messages we generate.
+BASE_EXPORT extern const GUID kLogEventId;
+
+// Feature enable mask for LogEventProvider.
+enum LogEnableMask {
+ // If this bit is set in our provider enable mask, we will include
+ // a stack trace with every log message.
+ ENABLE_STACK_TRACE_CAPTURE = 0x0001,
+ // If this bit is set in our provider enable mask, the provider will log
+ // a LOG message with only the textual content of the message, and no
+ // stack trace.
+ ENABLE_LOG_MESSAGE_ONLY = 0x0002,
+};
+
+// The message types our log event provider generates.
+// ETW likes user message types to start at 10.
+enum LogMessageTypes {
+ // A textual only log message, contains a zero-terminated string.
+ LOG_MESSAGE = 10,
+ // A message with a stack trace, followed by the zero-terminated
+ // message text.
+ LOG_MESSAGE_WITH_STACKTRACE = 11,
+ // A message with:
+ // a stack trace,
+ // the line number as a four byte integer,
+ // the file as a zero terminated UTF8 string,
+ // the zero-terminated UTF8 message text.
+ LOG_MESSAGE_FULL = 12,
+};
+
+// Trace provider class to drive log control and transport
+// with Event Tracing for Windows.
+class BASE_EXPORT LogEventProvider : public base::win::EtwTraceProvider {
+ public:
+ static LogEventProvider* GetInstance();
+
+ static bool LogMessage(logging::LogSeverity severity, const char* file,
+ int line, size_t message_start, const std::string& str);
+
+ static void Initialize(const GUID& provider_name);
+ static void Uninitialize();
+
+ protected:
+ // Overridden to manipulate the log level on ETW control callbacks.
+ virtual void OnEventsEnabled();
+ virtual void OnEventsDisabled();
+
+ private:
+ LogEventProvider();
+
+ // The log severity prior to OnEventsEnabled,
+ // restored in OnEventsDisabled.
+ logging::LogSeverity old_log_level_;
+
+ friend struct StaticMemorySingletonTraits<LogEventProvider>;
+ DISALLOW_COPY_AND_ASSIGN(LogEventProvider);
+};
+
+} // namespace logging
+
+#endif // BASE_LOGGING_WIN_H_
diff --git a/src/base/mac/authorization_util.h b/src/base/mac/authorization_util.h
new file mode 100644
index 0000000..b34348d
--- /dev/null
+++ b/src/base/mac/authorization_util.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 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_MAC_AUTHORIZATION_UTIL_H_
+#define BASE_MAC_AUTHORIZATION_UTIL_H_
+
+// AuthorizationExecuteWithPrivileges fork()s and exec()s the tool, but it
+// does not wait() for it. It also doesn't provide the caller with access to
+// the forked pid. If used irresponsibly, zombie processes will accumulate.
+//
+// Apple's really gotten us between a rock and a hard place, here.
+//
+// Fortunately, AuthorizationExecuteWithPrivileges does give access to the
+// tool's stdout (and stdin) via a FILE* pipe. The tool can output its pid
+// to this pipe, and the main program can read it, and then have something
+// that it can wait() for.
+//
+// The contract is that any tool executed by the wrappers declared in this
+// file must print its pid to stdout on a line by itself before doing anything
+// else.
+//
+// http://developer.apple.com/library/mac/#samplecode/BetterAuthorizationSample/Listings/BetterAuthorizationSampleLib_c.html
+// (Look for "What's This About Zombies?")
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Authorization.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "base/base_export.h"
+
+namespace base {
+namespace mac {
+
+// Obtains an AuthorizationRef that can be used to run commands as root. If
+// necessary, prompts the user for authentication. If the user is prompted,
+// |prompt| will be used as the prompt string and an icon appropriate for the
+// application will be displayed in a prompt dialog. Note that the system
+// appends its own text to the prompt string. Returns NULL on failure.
+BASE_EXPORT
+AuthorizationRef AuthorizationCreateToRunAsRoot(CFStringRef prompt);
+
+// Calls straight through to AuthorizationExecuteWithPrivileges. If that
+// call succeeds, |pid| will be set to the pid of the executed tool. If the
+// pid can't be determined, |pid| will be set to -1. |pid| must not be NULL.
+// |pipe| may be NULL, but the tool will always be executed with a pipe in
+// order to read the pid from its stdout.
+BASE_EXPORT
+OSStatus ExecuteWithPrivilegesAndGetPID(AuthorizationRef authorization,
+ const char* tool_path,
+ AuthorizationFlags options,
+ const char** arguments,
+ FILE** pipe,
+ pid_t* pid);
+
+// Calls ExecuteWithPrivilegesAndGetPID, and if that call succeeds, calls
+// waitpid() to wait for the process to exit. If waitpid() succeeds, the
+// exit status is placed in |exit_status|, otherwise, -1 is stored.
+// |exit_status| may be NULL and this function will still wait for the process
+// to exit.
+BASE_EXPORT
+OSStatus ExecuteWithPrivilegesAndWait(AuthorizationRef authorization,
+ const char* tool_path,
+ AuthorizationFlags options,
+ const char** arguments,
+ FILE** pipe,
+ int* exit_status);
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_AUTHORIZATION_UTIL_H_
diff --git a/src/base/mac/authorization_util.mm b/src/base/mac/authorization_util.mm
new file mode 100644
index 0000000..62a2074
--- /dev/null
+++ b/src/base/mac/authorization_util.mm
@@ -0,0 +1,187 @@
+// Copyright (c) 2012 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/mac/authorization_util.h"
+
+#import <Foundation/Foundation.h>
+#include <sys/wait.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/mac/bundle_locations.h"
+#include "base/mac/mac_logging.h"
+#import "base/mac/mac_util.h"
+#include "base/mac/scoped_authorizationref.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+
+namespace base {
+namespace mac {
+
+AuthorizationRef AuthorizationCreateToRunAsRoot(CFStringRef prompt) {
+ // Create an empty AuthorizationRef.
+ ScopedAuthorizationRef authorization;
+ OSStatus status = AuthorizationCreate(NULL,
+ kAuthorizationEmptyEnvironment,
+ kAuthorizationFlagDefaults,
+ &authorization);
+ if (status != errAuthorizationSuccess) {
+ OSSTATUS_LOG(ERROR, status) << "AuthorizationCreate";
+ return NULL;
+ }
+
+ // Specify the "system.privilege.admin" right, which allows
+ // AuthorizationExecuteWithPrivileges to run commands as root.
+ AuthorizationItem right_items[] = {
+ {kAuthorizationRightExecute, 0, NULL, 0}
+ };
+ AuthorizationRights rights = {arraysize(right_items), right_items};
+
+ // product_logo_32.png is used instead of app.icns because Authorization
+ // Services can't deal with .icns files.
+ NSString* icon_path =
+ [base::mac::FrameworkBundle() pathForResource:@"product_logo_32"
+ ofType:@"png"];
+ const char* icon_path_c = [icon_path fileSystemRepresentation];
+ size_t icon_path_length = icon_path_c ? strlen(icon_path_c) : 0;
+
+ // The OS will append " Type an administrator's name and password to allow
+ // <CFBundleDisplayName> to make changes."
+ NSString* prompt_ns = base::mac::CFToNSCast(prompt);
+ const char* prompt_c = [prompt_ns UTF8String];
+ size_t prompt_length = prompt_c ? strlen(prompt_c) : 0;
+
+ AuthorizationItem environment_items[] = {
+ {kAuthorizationEnvironmentIcon, icon_path_length, (void*)icon_path_c, 0},
+ {kAuthorizationEnvironmentPrompt, prompt_length, (void*)prompt_c, 0}
+ };
+
+ AuthorizationEnvironment environment = {arraysize(environment_items),
+ environment_items};
+
+ AuthorizationFlags flags = kAuthorizationFlagDefaults |
+ kAuthorizationFlagInteractionAllowed |
+ kAuthorizationFlagExtendRights |
+ kAuthorizationFlagPreAuthorize;
+
+ status = AuthorizationCopyRights(authorization,
+ &rights,
+ &environment,
+ flags,
+ NULL);
+ if (status != errAuthorizationSuccess) {
+ if (status != errAuthorizationCanceled) {
+ OSSTATUS_LOG(ERROR, status) << "AuthorizationCopyRights";
+ }
+ return NULL;
+ }
+
+ return authorization.release();
+}
+
+OSStatus ExecuteWithPrivilegesAndGetPID(AuthorizationRef authorization,
+ const char* tool_path,
+ AuthorizationFlags options,
+ const char** arguments,
+ FILE** pipe,
+ pid_t* pid) {
+ // pipe may be NULL, but this function needs one. In that case, use a local
+ // pipe.
+ FILE* local_pipe;
+ FILE** pipe_pointer;
+ if (pipe) {
+ pipe_pointer = pipe;
+ } else {
+ pipe_pointer = &local_pipe;
+ }
+
+ // AuthorizationExecuteWithPrivileges wants |char* const*| for |arguments|,
+ // but it doesn't actually modify the arguments, and that type is kind of
+ // silly and callers probably aren't dealing with that. Put the cast here
+ // to make things a little easier on callers.
+ OSStatus status = AuthorizationExecuteWithPrivileges(authorization,
+ tool_path,
+ options,
+ (char* const*)arguments,
+ pipe_pointer);
+ if (status != errAuthorizationSuccess) {
+ return status;
+ }
+
+ int line_pid = -1;
+ size_t line_length = 0;
+ char* line_c = fgetln(*pipe_pointer, &line_length);
+ if (line_c) {
+ if (line_length > 0 && line_c[line_length - 1] == '\n') {
+ // line_c + line_length is the start of the next line if there is one.
+ // Back up one character.
+ --line_length;
+ }
+ std::string line(line_c, line_length);
+ if (!base::StringToInt(line, &line_pid)) {
+ // StringToInt may have set line_pid to something, but if the conversion
+ // was imperfect, use -1.
+ LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: funny line: " << line;
+ line_pid = -1;
+ }
+ } else {
+ LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: no line";
+ }
+
+ if (!pipe) {
+ fclose(*pipe_pointer);
+ }
+
+ if (pid) {
+ *pid = line_pid;
+ }
+
+ return status;
+}
+
+OSStatus ExecuteWithPrivilegesAndWait(AuthorizationRef authorization,
+ const char* tool_path,
+ AuthorizationFlags options,
+ const char** arguments,
+ FILE** pipe,
+ int* exit_status) {
+ pid_t pid;
+ OSStatus status = ExecuteWithPrivilegesAndGetPID(authorization,
+ tool_path,
+ options,
+ arguments,
+ pipe,
+ &pid);
+ if (status != errAuthorizationSuccess) {
+ return status;
+ }
+
+ // exit_status may be NULL, but this function needs it. In that case, use a
+ // local version.
+ int local_exit_status;
+ int* exit_status_pointer;
+ if (exit_status) {
+ exit_status_pointer = exit_status;
+ } else {
+ exit_status_pointer = &local_exit_status;
+ }
+
+ if (pid != -1) {
+ pid_t wait_result = HANDLE_EINTR(waitpid(pid, exit_status_pointer, 0));
+ if (wait_result != pid) {
+ PLOG(ERROR) << "waitpid";
+ *exit_status_pointer = -1;
+ }
+ } else {
+ *exit_status_pointer = -1;
+ }
+
+ return status;
+}
+
+} // namespace mac
+} // namespace base
diff --git a/src/base/mac/bind_objc_block.h b/src/base/mac/bind_objc_block.h
new file mode 100644
index 0000000..2d48c7d
--- /dev/null
+++ b/src/base/mac/bind_objc_block.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2012 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_MAC_BIND_OBJC_BLOCK_H_
+#define BASE_MAC_BIND_OBJC_BLOCK_H_
+
+#include "base/base_export.h"
+#include "base/callback_forward.h"
+
+namespace base {
+
+// Construct a closure from an objective-C block.
+BASE_EXPORT base::Closure BindBlock(void(^block)());
+
+} // namespace base
+
+#endif // BASE_MAC_BIND_OBJC_BLOCK_H_
diff --git a/src/base/mac/bind_objc_block.mm b/src/base/mac/bind_objc_block.mm
new file mode 100644
index 0000000..81472d0
--- /dev/null
+++ b/src/base/mac/bind_objc_block.mm
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 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.
+
+#import "base/mac/bind_objc_block.h"
+
+#include "base/bind.h"
+#include "base/memory/scoped_nsobject.h"
+
+namespace {
+
+// Run the block contained in the parameter.
+void RunBlock(scoped_nsobject<id> block) {
+ void(^extracted_block)() = block.get();
+ extracted_block();
+}
+
+} // namespace
+
+namespace base {
+
+base::Closure BindBlock(void(^block)()) {
+ return base::Bind(&RunBlock, scoped_nsobject<id>([block copy]));
+}
+
+} // namespace base
diff --git a/src/base/mac/bind_objc_block_unittest.mm b/src/base/mac/bind_objc_block_unittest.mm
new file mode 100644
index 0000000..17fbe6e
--- /dev/null
+++ b/src/base/mac/bind_objc_block_unittest.mm
@@ -0,0 +1,42 @@
+// Copyright (c) 2012 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.
+
+#import "base/mac/bind_objc_block.h"
+
+#include "base/callback.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+TEST(BindObjcBlockTest, TestScopedClosureRunnerExitScope) {
+ int run_count = 0;
+ int* ptr = &run_count;
+ {
+ base::ScopedClosureRunner runner(base::BindBlock(^{
+ (*ptr)++;
+ }));
+ EXPECT_EQ(0, run_count);
+ }
+ EXPECT_EQ(1, run_count);
+}
+
+TEST(BindObjcBlockTest, TestScopedClosureRunnerRelease) {
+ int run_count = 0;
+ int* ptr = &run_count;
+ base::Closure c;
+ {
+ base::ScopedClosureRunner runner(base::BindBlock(^{
+ (*ptr)++;
+ }));
+ c = runner.Release();
+ EXPECT_EQ(0, run_count);
+ }
+ EXPECT_EQ(0, run_count);
+ c.Run();
+ EXPECT_EQ(1, run_count);
+}
+
+} // namespace
diff --git a/src/base/mac/bundle_locations.h b/src/base/mac/bundle_locations.h
new file mode 100644
index 0000000..dd84b59
--- /dev/null
+++ b/src/base/mac/bundle_locations.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2012 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_MAC_BUNDLE_LOCATIONS_H_
+#define BASE_MAC_BUNDLE_LOCATIONS_H_
+
+#include "base/base_export.h"
+#include "base/file_path.h"
+
+#if defined(__OBJC__)
+#import <Foundation/Foundation.h>
+#else // __OBJC__
+class NSBundle;
+class NSString;
+#endif // __OBJC__
+
+class FilePath;
+
+namespace base {
+namespace mac {
+
+// This file provides several functions to explicitly request the various
+// component bundles of Chrome. Please use these methods rather than calling
+// +[NSBundle mainBundle] or CFBundleGetMainBundle().
+//
+// Terminology
+// - "Outer Bundle" - This is the main bundle for Chrome; it's what
+// +[NSBundle mainBundle] returns when Chrome is launched normally.
+//
+// - "Main Bundle" - This is the bundle from which Chrome was launched.
+// This will be the same as the outer bundle except when Chrome is launched
+// via an app shortcut, in which case this will return the app shortcut's
+// bundle rather than the main Chrome bundle.
+//
+// - "Framework Bundle" - This is the bundle corresponding to the Chrome
+// framework.
+//
+// Guidelines for use:
+// - To access a resource, the Framework bundle should be used.
+// - If the choice is between the Outer or Main bundles then please choose
+// carefully. Most often the Outer bundle will be the right choice, but for
+// cases such as adding an app to the "launch on startup" list, the Main
+// bundle is probably the one to use.
+
+// Methods for retrieving the various bundles.
+BASE_EXPORT NSBundle* MainBundle();
+BASE_EXPORT FilePath MainBundlePath();
+BASE_EXPORT NSBundle* OuterBundle();
+BASE_EXPORT FilePath OuterBundlePath();
+BASE_EXPORT NSBundle* FrameworkBundle();
+BASE_EXPORT FilePath FrameworkBundlePath();
+
+// Set the bundle that the preceding functions will return, overriding the
+// default values. Restore the default by passing in |nil|.
+BASE_EXPORT void SetOverrideOuterBundle(NSBundle* bundle);
+BASE_EXPORT void SetOverrideFrameworkBundle(NSBundle* bundle);
+
+// Same as above but accepting a FilePath argument.
+BASE_EXPORT void SetOverrideOuterBundlePath(const FilePath& file_path);
+BASE_EXPORT void SetOverrideFrameworkBundlePath(const FilePath& file_path);
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_BUNDLE_LOCATIONS_H_
diff --git a/src/base/mac/bundle_locations.mm b/src/base/mac/bundle_locations.mm
new file mode 100644
index 0000000..363b7ea
--- /dev/null
+++ b/src/base/mac/bundle_locations.mm
@@ -0,0 +1,83 @@
+// Copyright (c) 2012 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/mac/bundle_locations.h"
+
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/sys_string_conversions.h"
+
+namespace base {
+namespace mac {
+
+// NSBundle isn't threadsafe, all functions in this file must be called on the
+// main thread.
+static NSBundle* g_override_framework_bundle = nil;
+static NSBundle* g_override_outer_bundle = nil;
+
+NSBundle* MainBundle() {
+ return [NSBundle mainBundle];
+}
+
+FilePath MainBundlePath() {
+ NSBundle* bundle = MainBundle();
+ return NSStringToFilePath([bundle bundlePath]);
+}
+
+NSBundle* OuterBundle() {
+ if (g_override_outer_bundle)
+ return g_override_outer_bundle;
+ return [NSBundle mainBundle];
+}
+
+FilePath OuterBundlePath() {
+ NSBundle* bundle = OuterBundle();
+ return NSStringToFilePath([bundle bundlePath]);
+}
+
+NSBundle* FrameworkBundle() {
+ if (g_override_framework_bundle)
+ return g_override_framework_bundle;
+ return [NSBundle mainBundle];
+}
+
+FilePath FrameworkBundlePath() {
+ NSBundle* bundle = FrameworkBundle();
+ return NSStringToFilePath([bundle bundlePath]);
+}
+
+static void AssignOverrideBundle(NSBundle* new_bundle,
+ NSBundle** override_bundle) {
+ if (new_bundle != *override_bundle) {
+ [*override_bundle release];
+ *override_bundle = [new_bundle retain];
+ }
+}
+
+static void AssignOverridePath(const FilePath& file_path,
+ NSBundle** override_bundle) {
+ NSString* path = base::SysUTF8ToNSString(file_path.value());
+ NSBundle* new_bundle = [NSBundle bundleWithPath:path];
+ DCHECK(new_bundle) << "Failed to load the bundle at " << file_path.value();
+ AssignOverrideBundle(new_bundle, override_bundle);
+}
+
+void SetOverrideOuterBundle(NSBundle* bundle) {
+ AssignOverrideBundle(bundle, &g_override_outer_bundle);
+}
+
+void SetOverrideFrameworkBundle(NSBundle* bundle) {
+ AssignOverrideBundle(bundle, &g_override_framework_bundle);
+}
+
+void SetOverrideOuterBundlePath(const FilePath& file_path) {
+ AssignOverridePath(file_path, &g_override_outer_bundle);
+}
+
+void SetOverrideFrameworkBundlePath(const FilePath& file_path) {
+ AssignOverridePath(file_path, &g_override_framework_bundle);
+}
+
+} // namespace mac
+} // namespace base
diff --git a/src/base/mac/cocoa_protocols.h b/src/base/mac/cocoa_protocols.h
new file mode 100644
index 0000000..314e7fa
--- /dev/null
+++ b/src/base/mac/cocoa_protocols.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 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_COCOA_PROTOCOLS_MAC_H_
+#define BASE_COCOA_PROTOCOLS_MAC_H_
+
+#import <Cocoa/Cocoa.h>
+
+// GTM also maintinas a list of empty protocols, but only the ones the library
+// requires. Augment that below.
+#import "third_party/GTM/GTMDefines.h"
+
+// New Mac OS X SDKs introduce new protocols used for delegates. These
+// protocol defintions aren't not present in earlier releases of the Mac OS X
+// SDK. In order to support building against the new SDK, which requires
+// delegates to conform to these protocols, and earlier SDKs, which do not
+// define these protocols at all, this file will provide empty protocol
+// definitions when used with earlier SDK versions.
+
+#define DEFINE_EMPTY_PROTOCOL(p) \
+@protocol p \
+@end
+
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+
+DEFINE_EMPTY_PROTOCOL(NSDraggingDestination)
+
+#endif // MAC_OS_X_VERSION_10_7
+
+#if !defined(MAC_OS_X_VERSION_10_8) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8
+
+DEFINE_EMPTY_PROTOCOL(NSUserNotificationCenterDelegate)
+
+#endif // MAC_OS_X_VERSION_10_8
+
+#undef DEFINE_EMPTY_PROTOCOL
+
+#endif // BASE_COCOA_PROTOCOLS_MAC_H_
diff --git a/src/base/mac/crash_logging.h b/src/base/mac/crash_logging.h
new file mode 100644
index 0000000..7e83eae
--- /dev/null
+++ b/src/base/mac/crash_logging.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 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_MAC_CRASH_LOGGING_H_
+#define BASE_MAC_CRASH_LOGGING_H_
+
+#include "base/base_export.h"
+
+#if __OBJC__
+#import "base/memory/scoped_nsobject.h"
+
+@class NSString;
+#else
+class NSString;
+#endif
+
+namespace base {
+namespace mac {
+
+typedef void (*SetCrashKeyValueFuncPtr)(NSString*, NSString*);
+typedef void (*ClearCrashKeyValueFuncPtr)(NSString*);
+
+// Set the low level functions used to supply crash keys to Breakpad.
+BASE_EXPORT void SetCrashKeyFunctions(SetCrashKeyValueFuncPtr set_key_func,
+ ClearCrashKeyValueFuncPtr clear_key_func);
+
+// Set and clear meta information for Minidump.
+// IMPORTANT: On OS X, the key/value pairs are sent to the crash server
+// out of bounds and not recorded on disk in the minidump, this means
+// that if you look at the minidump file locally you won't see them!
+BASE_EXPORT void SetCrashKeyValue(NSString* key, NSString* val);
+BASE_EXPORT void ClearCrashKey(NSString* key);
+
+// Format |count| items from |addresses| using %p, and set the
+// resulting string as value for crash key |key|. A maximum of 23
+// items will be encoded, since breakpad limits values to 255 bytes.
+BASE_EXPORT void SetCrashKeyFromAddresses(NSString* key,
+ const void* const* addresses,
+ size_t count);
+
+#if __OBJC__
+
+class BASE_EXPORT ScopedCrashKey {
+ public:
+ ScopedCrashKey(NSString* key, NSString* value);
+ ~ScopedCrashKey();
+ private:
+ scoped_nsobject<NSString> crash_key_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedCrashKey);
+};
+
+#endif // __OBJC__
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_CRASH_LOGGING_H_
diff --git a/src/base/mac/crash_logging.mm b/src/base/mac/crash_logging.mm
new file mode 100644
index 0000000..e18a3fb
--- /dev/null
+++ b/src/base/mac/crash_logging.mm
@@ -0,0 +1,69 @@
+// Copyright (c) 2012 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/mac/crash_logging.h"
+
+#import <Foundation/Foundation.h>
+
+#include "base/logging.h"
+
+namespace base {
+namespace mac {
+
+static SetCrashKeyValueFuncPtr g_set_key_func;
+static ClearCrashKeyValueFuncPtr g_clear_key_func;
+
+void SetCrashKeyFunctions(SetCrashKeyValueFuncPtr set_key_func,
+ ClearCrashKeyValueFuncPtr clear_key_func) {
+ g_set_key_func = set_key_func;
+ g_clear_key_func = clear_key_func;
+}
+
+void SetCrashKeyValue(NSString* key, NSString* val) {
+ if (g_set_key_func)
+ g_set_key_func(key, val);
+}
+
+void ClearCrashKey(NSString* key) {
+ if (g_clear_key_func)
+ g_clear_key_func(key);
+}
+
+void SetCrashKeyFromAddresses(NSString* key,
+ const void* const* addresses,
+ size_t count) {
+ NSString* value = @"<null>";
+ if (addresses && count) {
+ const size_t kBreakpadValueMax = 255;
+
+ NSMutableArray* hexBacktrace = [NSMutableArray arrayWithCapacity:count];
+ size_t length = 0;
+ for (size_t i = 0; i < count; ++i) {
+ NSString* s = [NSString stringWithFormat:@"%p", addresses[i]];
+ length += 1 + [s length];
+ if (length > kBreakpadValueMax)
+ break;
+ [hexBacktrace addObject:s];
+ }
+ value = [hexBacktrace componentsJoinedByString:@" "];
+
+ // Warn someone if this exceeds the breakpad limits.
+ DCHECK_LE(strlen([value UTF8String]), kBreakpadValueMax);
+ }
+ base::mac::SetCrashKeyValue(key, value);
+}
+
+ScopedCrashKey::ScopedCrashKey(NSString* key, NSString* value)
+ : crash_key_([key retain]) {
+ if (g_set_key_func)
+ g_set_key_func(crash_key_, value);
+}
+
+ScopedCrashKey::~ScopedCrashKey() {
+ if (g_clear_key_func)
+ g_clear_key_func(crash_key_);
+}
+
+} // namespace mac
+} // namespace base
diff --git a/src/base/mac/foundation_util.h b/src/base/mac/foundation_util.h
new file mode 100644
index 0000000..e6ad784
--- /dev/null
+++ b/src/base/mac/foundation_util.h
@@ -0,0 +1,358 @@
+// Copyright (c) 2012 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_MAC_FOUNDATION_UTIL_H_
+#define BASE_MAC_FOUNDATION_UTIL_H_
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/logging.h"
+#include "base/mac/scoped_cftyperef.h"
+
+#if defined(__OBJC__)
+#import <Foundation/Foundation.h>
+#else // __OBJC__
+#include <CoreFoundation/CoreFoundation.h>
+class NSBundle;
+class NSString;
+#endif // __OBJC__
+
+#if defined(OS_IOS)
+#include <CoreText/CoreText.h>
+#else
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+class FilePath;
+
+// Adapted from NSPathUtilities.h and NSObjCRuntime.h.
+#if __LP64__ || NS_BUILD_32_LIKE_64
+typedef unsigned long NSSearchPathDirectory;
+typedef unsigned long NSSearchPathDomainMask;
+#else
+typedef unsigned int NSSearchPathDirectory;
+typedef unsigned int NSSearchPathDomainMask;
+#endif
+
+typedef struct OpaqueSecTrustRef* SecACLRef;
+typedef struct OpaqueSecTrustedApplicationRef* SecTrustedApplicationRef;
+
+namespace base {
+namespace mac {
+
+// Returns true if the application is running from a bundle
+BASE_EXPORT bool AmIBundled();
+BASE_EXPORT void SetOverrideAmIBundled(bool value);
+
+// Returns true if this process is marked as a "Background only process".
+BASE_EXPORT bool IsBackgroundOnlyProcess();
+
+// Returns the path to a resource within the framework bundle.
+BASE_EXPORT FilePath PathForFrameworkBundleResource(CFStringRef resourceName);
+
+// Returns the creator code associated with the CFBundleRef at bundle.
+OSType CreatorCodeForCFBundleRef(CFBundleRef bundle);
+
+// Returns the creator code associated with this application, by calling
+// CreatorCodeForCFBundleRef for the application's main bundle. If this
+// information cannot be determined, returns kUnknownType ('????'). This
+// does not respect the override app bundle because it's based on CFBundle
+// instead of NSBundle, and because callers probably don't want the override
+// app bundle's creator code anyway.
+BASE_EXPORT OSType CreatorCodeForApplication();
+
+// Searches for directories for the given key in only the given |domain_mask|.
+// If found, fills result (which must always be non-NULL) with the
+// first found directory and returns true. Otherwise, returns false.
+BASE_EXPORT bool GetSearchPathDirectory(NSSearchPathDirectory directory,
+ NSSearchPathDomainMask domain_mask,
+ FilePath* result);
+
+// Searches for directories for the given key in only the local domain.
+// If found, fills result (which must always be non-NULL) with the
+// first found directory and returns true. Otherwise, returns false.
+BASE_EXPORT bool GetLocalDirectory(NSSearchPathDirectory directory,
+ FilePath* result);
+
+// Searches for directories for the given key in only the user domain.
+// If found, fills result (which must always be non-NULL) with the
+// first found directory and returns true. Otherwise, returns false.
+BASE_EXPORT bool GetUserDirectory(NSSearchPathDirectory directory,
+ FilePath* result);
+
+// Returns the ~/Library directory.
+BASE_EXPORT FilePath GetUserLibraryPath();
+
+// Takes a path to an (executable) binary and tries to provide the path to an
+// application bundle containing it. It takes the outermost bundle that it can
+// find (so for "/Foo/Bar.app/.../Baz.app/..." it produces "/Foo/Bar.app").
+// |exec_name| - path to the binary
+// returns - path to the application bundle, or empty on error
+BASE_EXPORT FilePath GetAppBundlePath(const FilePath& exec_name);
+
+#define TYPE_NAME_FOR_CF_TYPE_DECL(TypeCF) \
+BASE_EXPORT std::string TypeNameForCFType(TypeCF##Ref);
+
+TYPE_NAME_FOR_CF_TYPE_DECL(CFArray);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFBag);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFBoolean);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFData);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFDate);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFDictionary);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFNull);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFNumber);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFSet);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFString);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFURL);
+TYPE_NAME_FOR_CF_TYPE_DECL(CFUUID);
+
+TYPE_NAME_FOR_CF_TYPE_DECL(CGColor);
+
+TYPE_NAME_FOR_CF_TYPE_DECL(CTFont);
+TYPE_NAME_FOR_CF_TYPE_DECL(CTRun);
+
+#undef TYPE_NAME_FOR_CF_TYPE_DECL
+
+// Retain/release calls for memory management in C++.
+BASE_EXPORT void NSObjectRetain(void* obj);
+BASE_EXPORT void NSObjectRelease(void* obj);
+
+// CFTypeRefToNSObjectAutorelease transfers ownership of a Core Foundation
+// object (one derived from CFTypeRef) to the Foundation memory management
+// system. In a traditional managed-memory environment, cf_object is
+// autoreleased and returned as an NSObject. In a garbage-collected
+// environment, cf_object is marked as eligible for garbage collection.
+//
+// This function should only be used to convert a concrete CFTypeRef type to
+// its equivalent "toll-free bridged" NSObject subclass, for example,
+// converting a CFStringRef to NSString.
+//
+// By calling this function, callers relinquish any ownership claim to
+// cf_object. In a managed-memory environment, the object's ownership will be
+// managed by the innermost NSAutoreleasePool, so after this function returns,
+// callers should not assume that cf_object is valid any longer than the
+// returned NSObject.
+//
+// Returns an id, typed here for C++'s sake as a void*.
+BASE_EXPORT void* CFTypeRefToNSObjectAutorelease(CFTypeRef cf_object);
+
+// Returns the base bundle ID, which can be set by SetBaseBundleID but
+// defaults to a reasonable string. This never returns NULL. BaseBundleID
+// returns a pointer to static storage that must not be freed.
+BASE_EXPORT const char* BaseBundleID();
+
+// Sets the base bundle ID to override the default. The implementation will
+// make its own copy of new_base_bundle_id.
+BASE_EXPORT void SetBaseBundleID(const char* new_base_bundle_id);
+
+} // namespace mac
+} // namespace base
+
+#if !defined(__OBJC__)
+#define OBJC_CPP_CLASS_DECL(x) class x;
+#else // __OBJC__
+#define OBJC_CPP_CLASS_DECL(x)
+#endif // __OBJC__
+
+// Convert toll-free bridged CFTypes to NSTypes and vice-versa. This does not
+// autorelease |cf_val|. This is useful for the case where there is a CFType in
+// a call that expects an NSType and the compiler is complaining about const
+// casting problems.
+// The calls are used like this:
+// NSString *foo = CFToNSCast(CFSTR("Hello"));
+// CFStringRef foo2 = NSToCFCast(@"Hello");
+// The macro magic below is to enforce safe casting. It could possibly have
+// been done using template function specialization, but template function
+// specialization doesn't always work intuitively,
+// (http://www.gotw.ca/publications/mill17.htm) so the trusty combination
+// of macros and function overloading is used instead.
+
+#define CF_TO_NS_CAST_DECL(TypeCF, TypeNS) \
+OBJC_CPP_CLASS_DECL(TypeNS) \
+\
+namespace base { \
+namespace mac { \
+BASE_EXPORT TypeNS* CFToNSCast(TypeCF##Ref cf_val); \
+BASE_EXPORT TypeCF##Ref NSToCFCast(TypeNS* ns_val); \
+} \
+}
+
+#define CF_TO_NS_MUTABLE_CAST_DECL(name) \
+CF_TO_NS_CAST_DECL(CF##name, NS##name) \
+OBJC_CPP_CLASS_DECL(NSMutable##name) \
+\
+namespace base { \
+namespace mac { \
+BASE_EXPORT NSMutable##name* CFToNSCast(CFMutable##name##Ref cf_val); \
+BASE_EXPORT CFMutable##name##Ref NSToCFCast(NSMutable##name* ns_val); \
+} \
+}
+
+// List of toll-free bridged types taken from:
+// http://www.cocoadev.com/index.pl?TollFreeBridged
+
+CF_TO_NS_MUTABLE_CAST_DECL(Array);
+CF_TO_NS_MUTABLE_CAST_DECL(AttributedString);
+CF_TO_NS_CAST_DECL(CFCalendar, NSCalendar);
+CF_TO_NS_MUTABLE_CAST_DECL(CharacterSet);
+CF_TO_NS_MUTABLE_CAST_DECL(Data);
+CF_TO_NS_CAST_DECL(CFDate, NSDate);
+CF_TO_NS_MUTABLE_CAST_DECL(Dictionary);
+CF_TO_NS_CAST_DECL(CFError, NSError);
+CF_TO_NS_CAST_DECL(CFLocale, NSLocale);
+CF_TO_NS_CAST_DECL(CFNumber, NSNumber);
+CF_TO_NS_CAST_DECL(CFRunLoopTimer, NSTimer);
+CF_TO_NS_CAST_DECL(CFTimeZone, NSTimeZone);
+CF_TO_NS_MUTABLE_CAST_DECL(Set);
+CF_TO_NS_CAST_DECL(CFReadStream, NSInputStream);
+CF_TO_NS_CAST_DECL(CFWriteStream, NSOutputStream);
+CF_TO_NS_MUTABLE_CAST_DECL(String);
+CF_TO_NS_CAST_DECL(CFURL, NSURL);
+
+#undef CF_TO_NS_CAST_DECL
+#undef CF_TO_NS_MUTABLE_CAST_DECL
+#undef OBJC_CPP_CLASS_DECL
+
+namespace base {
+namespace mac {
+
+// CFCast<>() and CFCastStrict<>() cast a basic CFTypeRef to a more
+// specific CoreFoundation type. The compatibility of the passed
+// object is found by comparing its opaque type against the
+// requested type identifier. If the supplied object is not
+// compatible with the requested return type, CFCast<>() returns
+// NULL and CFCastStrict<>() will DCHECK. Providing a NULL pointer
+// to either variant results in NULL being returned without
+// triggering any DCHECK.
+//
+// Example usage:
+// CFNumberRef some_number = base::mac::CFCast<CFNumberRef>(
+// CFArrayGetValueAtIndex(array, index));
+//
+// CFTypeRef hello = CFSTR("hello world");
+// CFStringRef some_string = base::mac::CFCastStrict<CFStringRef>(hello);
+
+template<typename T>
+T CFCast(const CFTypeRef& cf_val);
+
+template<typename T>
+T CFCastStrict(const CFTypeRef& cf_val);
+
+#define CF_CAST_DECL(TypeCF) \
+template<> BASE_EXPORT TypeCF##Ref \
+CFCast<TypeCF##Ref>(const CFTypeRef& cf_val);\
+\
+template<> BASE_EXPORT TypeCF##Ref \
+CFCastStrict<TypeCF##Ref>(const CFTypeRef& cf_val);
+
+CF_CAST_DECL(CFArray);
+CF_CAST_DECL(CFBag);
+CF_CAST_DECL(CFBoolean);
+CF_CAST_DECL(CFData);
+CF_CAST_DECL(CFDate);
+CF_CAST_DECL(CFDictionary);
+CF_CAST_DECL(CFNull);
+CF_CAST_DECL(CFNumber);
+CF_CAST_DECL(CFSet);
+CF_CAST_DECL(CFString);
+CF_CAST_DECL(CFURL);
+CF_CAST_DECL(CFUUID);
+
+CF_CAST_DECL(CGColor);
+
+CF_CAST_DECL(CTFont);
+CF_CAST_DECL(CTRun);
+
+CF_CAST_DECL(SecACL);
+CF_CAST_DECL(SecTrustedApplication);
+
+#undef CF_CAST_DECL
+
+#if defined(__OBJC__)
+
+// ObjCCast<>() and ObjCCastStrict<>() cast a basic id to a more
+// specific (NSObject-derived) type. The compatibility of the passed
+// object is found by checking if it's a kind of the requested type
+// identifier. If the supplied object is not compatible with the
+// requested return type, ObjCCast<>() returns nil and
+// ObjCCastStrict<>() will DCHECK. Providing a nil pointer to either
+// variant results in nil being returned without triggering any DCHECK.
+//
+// The strict variant is useful when retrieving a value from a
+// collection which only has values of a specific type, e.g. an
+// NSArray of NSStrings. The non-strict variant is useful when
+// retrieving values from data that you can't fully control. For
+// example, a plist read from disk may be beyond your exclusive
+// control, so you'd only want to check that the values you retrieve
+// from it are of the expected types, but not crash if they're not.
+//
+// Example usage:
+// NSString* version = base::mac::ObjCCast<NSString>(
+// [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]);
+//
+// NSString* str = base::mac::ObjCCastStrict<NSString>(
+// [ns_arr_of_ns_strs objectAtIndex:0]);
+template<typename T>
+T* ObjCCast(id objc_val) {
+ if ([objc_val isKindOfClass:[T class]]) {
+ return reinterpret_cast<T*>(objc_val);
+ }
+ return nil;
+}
+
+template<typename T>
+T* ObjCCastStrict(id objc_val) {
+ T* rv = ObjCCast<T>(objc_val);
+ DCHECK(objc_val == nil || rv);
+ return rv;
+}
+
+#endif // defined(__OBJC__)
+
+// Helper function for GetValueFromDictionary to create the error message
+// that appears when a type mismatch is encountered.
+BASE_EXPORT std::string GetValueFromDictionaryErrorMessage(
+ CFStringRef key, const std::string& expected_type, CFTypeRef value);
+
+// Utility function to pull out a value from a dictionary, check its type, and
+// return it. Returns NULL if the key is not present or of the wrong type.
+template<typename T>
+T GetValueFromDictionary(CFDictionaryRef dict, CFStringRef key) {
+ CFTypeRef value = CFDictionaryGetValue(dict, key);
+ T value_specific = CFCast<T>(value);
+
+ if (value && !value_specific) {
+ std::string expected_type = TypeNameForCFType(value_specific);
+ DLOG(WARNING) << GetValueFromDictionaryErrorMessage(key,
+ expected_type,
+ value);
+ }
+
+ return value_specific;
+}
+
+// Converts |path| to an autoreleased NSString. Returns nil if |path| is empty.
+BASE_EXPORT NSString* FilePathToNSString(const FilePath& path);
+
+// Converts |str| to a FilePath. Returns an empty path if |str| is nil.
+BASE_EXPORT FilePath NSStringToFilePath(NSString* str);
+
+} // namespace mac
+} // namespace base
+
+// Stream operations for CFTypes. They can be used with NSTypes as well
+// by using the NSToCFCast methods above.
+// e.g. LOG(INFO) << base::mac::NSToCFCast(@"foo");
+// Operator << can not be overloaded for ObjectiveC types as the compiler
+// can not distinguish between overloads for id with overloads for void*.
+BASE_EXPORT extern std::ostream& operator<<(std::ostream& o,
+ const CFErrorRef err);
+BASE_EXPORT extern std::ostream& operator<<(std::ostream& o,
+ const CFStringRef str);
+
+#endif // BASE_MAC_FOUNDATION_UTIL_H_
diff --git a/src/base/mac/foundation_util.mm b/src/base/mac/foundation_util.mm
new file mode 100644
index 0000000..3fd9a57
--- /dev/null
+++ b/src/base/mac/foundation_util.mm
@@ -0,0 +1,401 @@
+// Copyright (c) 2012 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/mac/foundation_util.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/mac/bundle_locations.h"
+#include "base/mac/mac_logging.h"
+#include "base/sys_string_conversions.h"
+
+#if !defined(OS_IOS)
+extern "C" {
+CFTypeID SecACLGetTypeID();
+CFTypeID SecTrustedApplicationGetTypeID();
+} // extern "C"
+#endif
+
+namespace base {
+namespace mac {
+
+static bool g_override_am_i_bundled = false;
+static bool g_override_am_i_bundled_value = false;
+
+// Adapted from http://developer.apple.com/carbon/tipsandtricks.html#AmIBundled
+static bool UncachedAmIBundled() {
+#if defined(OS_IOS)
+ // All apps are bundled on iOS
+ return true;
+#else
+ if (g_override_am_i_bundled)
+ return g_override_am_i_bundled_value;
+
+ ProcessSerialNumber psn = {0, kCurrentProcess};
+
+ FSRef fsref;
+ OSStatus pbErr;
+ if ((pbErr = GetProcessBundleLocation(&psn, &fsref)) != noErr) {
+ OSSTATUS_DLOG(ERROR, pbErr) << "GetProcessBundleLocation failed";
+ return false;
+ }
+
+ FSCatalogInfo info;
+ OSErr fsErr;
+ if ((fsErr = FSGetCatalogInfo(&fsref, kFSCatInfoNodeFlags, &info,
+ NULL, NULL, NULL)) != noErr) {
+ OSSTATUS_DLOG(ERROR, fsErr) << "FSGetCatalogInfo failed";
+ return false;
+ }
+
+ return info.nodeFlags & kFSNodeIsDirectoryMask;
+#endif
+}
+
+bool AmIBundled() {
+ // If the return value is not cached, this function will return different
+ // values depending on when it's called. This confuses some client code, see
+ // http://crbug.com/63183 .
+ static bool result = UncachedAmIBundled();
+ DCHECK_EQ(result, UncachedAmIBundled())
+ << "The return value of AmIBundled() changed. This will confuse tests. "
+ << "Call SetAmIBundled() override manually if your test binary "
+ << "delay-loads the framework.";
+ return result;
+}
+
+void SetOverrideAmIBundled(bool value) {
+#if defined(OS_IOS)
+ // It doesn't make sense not to be bundled on iOS.
+ if (!value)
+ NOTREACHED();
+#endif
+ g_override_am_i_bundled = true;
+ g_override_am_i_bundled_value = value;
+}
+
+bool IsBackgroundOnlyProcess() {
+ // This function really does want to examine NSBundle's idea of the main
+ // bundle dictionary. It needs to look at the actual running .app's
+ // Info.plist to access its LSUIElement property.
+ NSDictionary* info_dictionary = [base::mac::MainBundle() infoDictionary];
+ return [[info_dictionary objectForKey:@"LSUIElement"] boolValue] != NO;
+}
+
+FilePath PathForFrameworkBundleResource(CFStringRef resourceName) {
+ NSBundle* bundle = base::mac::FrameworkBundle();
+ NSString* resourcePath = [bundle pathForResource:(NSString*)resourceName
+ ofType:nil];
+ return NSStringToFilePath(resourcePath);
+}
+
+OSType CreatorCodeForCFBundleRef(CFBundleRef bundle) {
+ OSType creator = kUnknownType;
+ CFBundleGetPackageInfo(bundle, NULL, &creator);
+ return creator;
+}
+
+OSType CreatorCodeForApplication() {
+ CFBundleRef bundle = CFBundleGetMainBundle();
+ if (!bundle)
+ return kUnknownType;
+
+ return CreatorCodeForCFBundleRef(bundle);
+}
+
+bool GetSearchPathDirectory(NSSearchPathDirectory directory,
+ NSSearchPathDomainMask domain_mask,
+ FilePath* result) {
+ DCHECK(result);
+ NSArray* dirs =
+ NSSearchPathForDirectoriesInDomains(directory, domain_mask, YES);
+ if ([dirs count] < 1) {
+ return false;
+ }
+ *result = NSStringToFilePath([dirs objectAtIndex:0]);
+ return true;
+}
+
+bool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result) {
+ return GetSearchPathDirectory(directory, NSLocalDomainMask, result);
+}
+
+bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result) {
+ return GetSearchPathDirectory(directory, NSUserDomainMask, result);
+}
+
+FilePath GetUserLibraryPath() {
+ FilePath user_library_path;
+ if (!GetUserDirectory(NSLibraryDirectory, &user_library_path)) {
+ DLOG(WARNING) << "Could not get user library path";
+ }
+ return user_library_path;
+}
+
+// Takes a path to an (executable) binary and tries to provide the path to an
+// application bundle containing it. It takes the outermost bundle that it can
+// find (so for "/Foo/Bar.app/.../Baz.app/..." it produces "/Foo/Bar.app").
+// |exec_name| - path to the binary
+// returns - path to the application bundle, or empty on error
+FilePath GetAppBundlePath(const FilePath& exec_name) {
+ const char kExt[] = ".app";
+ const size_t kExtLength = arraysize(kExt) - 1;
+
+ // Split the path into components.
+ std::vector<std::string> components;
+ exec_name.GetComponents(&components);
+
+ // It's an error if we don't get any components.
+ if (!components.size())
+ return FilePath();
+
+ // Don't prepend '/' to the first component.
+ std::vector<std::string>::const_iterator it = components.begin();
+ std::string bundle_name = *it;
+ DCHECK_GT(it->length(), 0U);
+ // If the first component ends in ".app", we're already done.
+ if (it->length() > kExtLength &&
+ !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength))
+ return FilePath(bundle_name);
+
+ // The first component may be "/" or "//", etc. Only append '/' if it doesn't
+ // already end in '/'.
+ if (bundle_name[bundle_name.length() - 1] != '/')
+ bundle_name += '/';
+
+ // Go through the remaining components.
+ for (++it; it != components.end(); ++it) {
+ DCHECK_GT(it->length(), 0U);
+
+ bundle_name += *it;
+
+ // If the current component ends in ".app", we're done.
+ if (it->length() > kExtLength &&
+ !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength))
+ return FilePath(bundle_name);
+
+ // Separate this component from the next one.
+ bundle_name += '/';
+ }
+
+ return FilePath();
+}
+
+#define TYPE_NAME_FOR_CF_TYPE_DEFN(TypeCF) \
+std::string TypeNameForCFType(TypeCF##Ref) { \
+ return #TypeCF; \
+}
+
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFArray);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFBag);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFBoolean);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFData);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFDate);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFDictionary);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFNull);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFNumber);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFSet);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFString);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFURL);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CFUUID);
+
+TYPE_NAME_FOR_CF_TYPE_DEFN(CGColor);
+
+TYPE_NAME_FOR_CF_TYPE_DEFN(CTFont);
+TYPE_NAME_FOR_CF_TYPE_DEFN(CTRun);
+
+#undef TYPE_NAME_FOR_CF_TYPE_DEFN
+
+void NSObjectRetain(void* obj) {
+ id<NSObject> nsobj = static_cast<id<NSObject> >(obj);
+ [nsobj retain];
+}
+
+void NSObjectRelease(void* obj) {
+ id<NSObject> nsobj = static_cast<id<NSObject> >(obj);
+ [nsobj release];
+}
+
+void* CFTypeRefToNSObjectAutorelease(CFTypeRef cf_object) {
+ // When GC is on, NSMakeCollectable marks cf_object for GC and autorelease
+ // is a no-op.
+ //
+ // In the traditional GC-less environment, NSMakeCollectable is a no-op,
+ // and cf_object is autoreleased, balancing out the caller's ownership claim.
+ //
+ // NSMakeCollectable returns nil when used on a NULL object.
+ return [NSMakeCollectable(cf_object) autorelease];
+}
+
+static const char* base_bundle_id;
+
+const char* BaseBundleID() {
+ if (base_bundle_id) {
+ return base_bundle_id;
+ }
+
+#if defined(GOOGLE_CHROME_BUILD)
+ return "com.google.Chrome";
+#else
+ return "org.chromium.Chromium";
+#endif
+}
+
+void SetBaseBundleID(const char* new_base_bundle_id) {
+ if (new_base_bundle_id != base_bundle_id) {
+ free((void*)base_bundle_id);
+ base_bundle_id = new_base_bundle_id ? strdup(new_base_bundle_id) : NULL;
+ }
+}
+
+// Definitions for the corresponding CF_TO_NS_CAST_DECL macros in
+// foundation_util.h.
+#define CF_TO_NS_CAST_DEFN(TypeCF, TypeNS) \
+\
+TypeNS* CFToNSCast(TypeCF##Ref cf_val) { \
+ DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \
+ TypeNS* ns_val = \
+ const_cast<TypeNS*>(reinterpret_cast<const TypeNS*>(cf_val)); \
+ return ns_val; \
+} \
+\
+TypeCF##Ref NSToCFCast(TypeNS* ns_val) { \
+ TypeCF##Ref cf_val = reinterpret_cast<TypeCF##Ref>(ns_val); \
+ DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \
+ return cf_val; \
+}
+
+#define CF_TO_NS_MUTABLE_CAST_DEFN(name) \
+CF_TO_NS_CAST_DEFN(CF##name, NS##name) \
+\
+NSMutable##name* CFToNSCast(CFMutable##name##Ref cf_val) { \
+ DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \
+ NSMutable##name* ns_val = reinterpret_cast<NSMutable##name*>(cf_val); \
+ return ns_val; \
+} \
+\
+CFMutable##name##Ref NSToCFCast(NSMutable##name* ns_val) { \
+ CFMutable##name##Ref cf_val = \
+ reinterpret_cast<CFMutable##name##Ref>(ns_val); \
+ DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \
+ return cf_val; \
+}
+
+CF_TO_NS_MUTABLE_CAST_DEFN(Array);
+CF_TO_NS_MUTABLE_CAST_DEFN(AttributedString);
+CF_TO_NS_CAST_DEFN(CFCalendar, NSCalendar);
+CF_TO_NS_MUTABLE_CAST_DEFN(CharacterSet);
+CF_TO_NS_MUTABLE_CAST_DEFN(Data);
+CF_TO_NS_CAST_DEFN(CFDate, NSDate);
+CF_TO_NS_MUTABLE_CAST_DEFN(Dictionary);
+CF_TO_NS_CAST_DEFN(CFError, NSError);
+CF_TO_NS_CAST_DEFN(CFLocale, NSLocale);
+CF_TO_NS_CAST_DEFN(CFNumber, NSNumber);
+CF_TO_NS_CAST_DEFN(CFRunLoopTimer, NSTimer);
+CF_TO_NS_CAST_DEFN(CFTimeZone, NSTimeZone);
+CF_TO_NS_MUTABLE_CAST_DEFN(Set);
+CF_TO_NS_CAST_DEFN(CFReadStream, NSInputStream);
+CF_TO_NS_CAST_DEFN(CFWriteStream, NSOutputStream);
+CF_TO_NS_MUTABLE_CAST_DEFN(String);
+CF_TO_NS_CAST_DEFN(CFURL, NSURL);
+
+#undef CF_TO_NS_CAST_DEFN
+#undef CF_TO_NS_MUTABLE_CAST_DEFN
+
+#define CF_CAST_DEFN(TypeCF) \
+template<> TypeCF##Ref \
+CFCast<TypeCF##Ref>(const CFTypeRef& cf_val) { \
+ if (cf_val == NULL) { \
+ return NULL; \
+ } \
+ if (CFGetTypeID(cf_val) == TypeCF##GetTypeID()) { \
+ return (TypeCF##Ref)(cf_val); \
+ } \
+ return NULL; \
+} \
+\
+template<> TypeCF##Ref \
+CFCastStrict<TypeCF##Ref>(const CFTypeRef& cf_val) { \
+ TypeCF##Ref rv = CFCast<TypeCF##Ref>(cf_val); \
+ DCHECK(cf_val == NULL || rv); \
+ return rv; \
+}
+
+CF_CAST_DEFN(CFArray);
+CF_CAST_DEFN(CFBag);
+CF_CAST_DEFN(CFBoolean);
+CF_CAST_DEFN(CFData);
+CF_CAST_DEFN(CFDate);
+CF_CAST_DEFN(CFDictionary);
+CF_CAST_DEFN(CFNull);
+CF_CAST_DEFN(CFNumber);
+CF_CAST_DEFN(CFSet);
+CF_CAST_DEFN(CFString);
+CF_CAST_DEFN(CFURL);
+CF_CAST_DEFN(CFUUID);
+
+CF_CAST_DEFN(CGColor);
+
+CF_CAST_DEFN(CTFont);
+CF_CAST_DEFN(CTRun);
+
+#if !defined(OS_IOS)
+CF_CAST_DEFN(SecACL);
+CF_CAST_DEFN(SecTrustedApplication);
+#endif
+
+#undef CF_CAST_DEFN
+
+std::string GetValueFromDictionaryErrorMessage(
+ CFStringRef key, const std::string& expected_type, CFTypeRef value) {
+ ScopedCFTypeRef<CFStringRef> actual_type_ref(
+ CFCopyTypeIDDescription(CFGetTypeID(value)));
+ return "Expected value for key " +
+ base::SysCFStringRefToUTF8(key) +
+ " to be " +
+ expected_type +
+ " but it was " +
+ base::SysCFStringRefToUTF8(actual_type_ref) +
+ " instead";
+}
+
+NSString* FilePathToNSString(const FilePath& path) {
+ if (path.empty())
+ return nil;
+ return [NSString stringWithUTF8String:path.value().c_str()];
+}
+
+FilePath NSStringToFilePath(NSString* str) {
+ if (![str length])
+ return FilePath();
+ return FilePath([str fileSystemRepresentation]);
+}
+
+} // namespace mac
+} // namespace base
+
+std::ostream& operator<<(std::ostream& o, const CFStringRef string) {
+ return o << base::SysCFStringRefToUTF8(string);
+}
+
+std::ostream& operator<<(std::ostream& o, const CFErrorRef err) {
+ base::mac::ScopedCFTypeRef<CFStringRef> desc(CFErrorCopyDescription(err));
+ base::mac::ScopedCFTypeRef<CFDictionaryRef> user_info(
+ CFErrorCopyUserInfo(err));
+ CFStringRef errorDesc = NULL;
+ if (user_info.get()) {
+ errorDesc = reinterpret_cast<CFStringRef>(
+ CFDictionaryGetValue(user_info.get(), kCFErrorDescriptionKey));
+ }
+ o << "Code: " << CFErrorGetCode(err)
+ << " Domain: " << CFErrorGetDomain(err)
+ << " Desc: " << desc.get();
+ if(errorDesc) {
+ o << "(" << errorDesc << ")";
+ }
+ return o;
+}
diff --git a/src/base/mac/foundation_util_unittest.mm b/src/base/mac/foundation_util_unittest.mm
new file mode 100644
index 0000000..4653006
--- /dev/null
+++ b/src/base/mac/foundation_util_unittest.mm
@@ -0,0 +1,334 @@
+// Copyright (c) 2012 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/mac/foundation_util.h"
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/mac/scoped_nsautorelease_pool.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#import "testing/gtest_mac.h"
+
+TEST(FoundationUtilTest, CFCast) {
+ // Build out the CF types to be tested as empty containers.
+ base::mac::ScopedCFTypeRef<CFTypeRef> test_array(
+ CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks));
+ base::mac::ScopedCFTypeRef<CFTypeRef> test_array_mutable(
+ CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks));
+ base::mac::ScopedCFTypeRef<CFTypeRef> test_bag(
+ CFBagCreate(NULL, NULL, 0, &kCFTypeBagCallBacks));
+ base::mac::ScopedCFTypeRef<CFTypeRef> test_bag_mutable(
+ CFBagCreateMutable(NULL, 0, &kCFTypeBagCallBacks));
+ CFTypeRef test_bool = kCFBooleanTrue;
+ base::mac::ScopedCFTypeRef<CFTypeRef> test_data(
+ CFDataCreate(NULL, NULL, 0));
+ base::mac::ScopedCFTypeRef<CFTypeRef> test_data_mutable(
+ CFDataCreateMutable(NULL, 0));
+ base::mac::ScopedCFTypeRef<CFTypeRef> test_date(
+ CFDateCreate(NULL, 0));
+ base::mac::ScopedCFTypeRef<CFTypeRef> test_dict(
+ CFDictionaryCreate(NULL, NULL, NULL, 0,
+ &kCFCopyStringDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+ base::mac::ScopedCFTypeRef<CFTypeRef> test_dict_mutable(
+ CFDictionaryCreateMutable(NULL, 0,
+ &kCFCopyStringDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+ int int_val = 256;
+ base::mac::ScopedCFTypeRef<CFTypeRef> test_number(
+ CFNumberCreate(NULL, kCFNumberIntType, &int_val));
+ CFTypeRef test_null = kCFNull;
+ base::mac::ScopedCFTypeRef<CFTypeRef> test_set(
+ CFSetCreate(NULL, NULL, 0, &kCFTypeSetCallBacks));
+ base::mac::ScopedCFTypeRef<CFTypeRef> test_set_mutable(
+ CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks));
+ base::mac::ScopedCFTypeRef<CFTypeRef> test_str(
+ CFStringCreateWithBytes(NULL, NULL, 0, kCFStringEncodingASCII,
+ false));
+ CFTypeRef test_str_const = CFSTR("hello");
+ base::mac::ScopedCFTypeRef<CFTypeRef> test_str_mutable(
+ CFStringCreateMutable(NULL, 0));
+
+ // Make sure the allocations of CF types are good.
+ EXPECT_TRUE(test_array);
+ EXPECT_TRUE(test_array_mutable);
+ EXPECT_TRUE(test_bag);
+ EXPECT_TRUE(test_bag_mutable);
+ EXPECT_TRUE(test_bool);
+ EXPECT_TRUE(test_data);
+ EXPECT_TRUE(test_data_mutable);
+ EXPECT_TRUE(test_date);
+ EXPECT_TRUE(test_dict);
+ EXPECT_TRUE(test_dict_mutable);
+ EXPECT_TRUE(test_number);
+ EXPECT_TRUE(test_null);
+ EXPECT_TRUE(test_set);
+ EXPECT_TRUE(test_set_mutable);
+ EXPECT_TRUE(test_str);
+ EXPECT_TRUE(test_str_const);
+ EXPECT_TRUE(test_str_mutable);
+
+ // Casting the CFTypeRef objects correctly provides the same pointer.
+ EXPECT_EQ(test_array, base::mac::CFCast<CFArrayRef>(test_array));
+ EXPECT_EQ(test_array_mutable,
+ base::mac::CFCast<CFArrayRef>(test_array_mutable));
+ EXPECT_EQ(test_bag, base::mac::CFCast<CFBagRef>(test_bag));
+ EXPECT_EQ(test_bag_mutable,
+ base::mac::CFCast<CFBagRef>(test_bag_mutable));
+ EXPECT_EQ(test_bool, base::mac::CFCast<CFBooleanRef>(test_bool));
+ EXPECT_EQ(test_data, base::mac::CFCast<CFDataRef>(test_data));
+ EXPECT_EQ(test_data_mutable,
+ base::mac::CFCast<CFDataRef>(test_data_mutable));
+ EXPECT_EQ(test_date, base::mac::CFCast<CFDateRef>(test_date));
+ EXPECT_EQ(test_dict, base::mac::CFCast<CFDictionaryRef>(test_dict));
+ EXPECT_EQ(test_dict_mutable,
+ base::mac::CFCast<CFDictionaryRef>(test_dict_mutable));
+ EXPECT_EQ(test_number, base::mac::CFCast<CFNumberRef>(test_number));
+ EXPECT_EQ(test_null, base::mac::CFCast<CFNullRef>(test_null));
+ EXPECT_EQ(test_set, base::mac::CFCast<CFSetRef>(test_set));
+ EXPECT_EQ(test_set_mutable, base::mac::CFCast<CFSetRef>(test_set_mutable));
+ EXPECT_EQ(test_str, base::mac::CFCast<CFStringRef>(test_str));
+ EXPECT_EQ(test_str_const, base::mac::CFCast<CFStringRef>(test_str_const));
+ EXPECT_EQ(test_str_mutable,
+ base::mac::CFCast<CFStringRef>(test_str_mutable));
+
+ // When given an incorrect CF cast, provide NULL.
+ EXPECT_FALSE(base::mac::CFCast<CFStringRef>(test_array));
+ EXPECT_FALSE(base::mac::CFCast<CFStringRef>(test_array_mutable));
+ EXPECT_FALSE(base::mac::CFCast<CFStringRef>(test_bag));
+ EXPECT_FALSE(base::mac::CFCast<CFSetRef>(test_bag_mutable));
+ EXPECT_FALSE(base::mac::CFCast<CFSetRef>(test_bool));
+ EXPECT_FALSE(base::mac::CFCast<CFNullRef>(test_data));
+ EXPECT_FALSE(base::mac::CFCast<CFDictionaryRef>(test_data_mutable));
+ EXPECT_FALSE(base::mac::CFCast<CFDictionaryRef>(test_date));
+ EXPECT_FALSE(base::mac::CFCast<CFNumberRef>(test_dict));
+ EXPECT_FALSE(base::mac::CFCast<CFDateRef>(test_dict_mutable));
+ EXPECT_FALSE(base::mac::CFCast<CFDataRef>(test_number));
+ EXPECT_FALSE(base::mac::CFCast<CFDataRef>(test_null));
+ EXPECT_FALSE(base::mac::CFCast<CFBooleanRef>(test_set));
+ EXPECT_FALSE(base::mac::CFCast<CFBagRef>(test_set_mutable));
+ EXPECT_FALSE(base::mac::CFCast<CFBagRef>(test_str));
+ EXPECT_FALSE(base::mac::CFCast<CFArrayRef>(test_str_const));
+ EXPECT_FALSE(base::mac::CFCast<CFArrayRef>(test_str_mutable));
+
+ // Giving a NULL provides a NULL.
+ EXPECT_FALSE(base::mac::CFCast<CFArrayRef>(NULL));
+ EXPECT_FALSE(base::mac::CFCast<CFBagRef>(NULL));
+ EXPECT_FALSE(base::mac::CFCast<CFBooleanRef>(NULL));
+ EXPECT_FALSE(base::mac::CFCast<CFDataRef>(NULL));
+ EXPECT_FALSE(base::mac::CFCast<CFDateRef>(NULL));
+ EXPECT_FALSE(base::mac::CFCast<CFDictionaryRef>(NULL));
+ EXPECT_FALSE(base::mac::CFCast<CFNullRef>(NULL));
+ EXPECT_FALSE(base::mac::CFCast<CFNumberRef>(NULL));
+ EXPECT_FALSE(base::mac::CFCast<CFSetRef>(NULL));
+ EXPECT_FALSE(base::mac::CFCast<CFStringRef>(NULL));
+
+ // CFCastStrict: correct cast results in correct pointer being returned.
+ EXPECT_EQ(test_array, base::mac::CFCastStrict<CFArrayRef>(test_array));
+ EXPECT_EQ(test_array_mutable,
+ base::mac::CFCastStrict<CFArrayRef>(test_array_mutable));
+ EXPECT_EQ(test_bag, base::mac::CFCastStrict<CFBagRef>(test_bag));
+ EXPECT_EQ(test_bag_mutable,
+ base::mac::CFCastStrict<CFBagRef>(test_bag_mutable));
+ EXPECT_EQ(test_bool, base::mac::CFCastStrict<CFBooleanRef>(test_bool));
+ EXPECT_EQ(test_data, base::mac::CFCastStrict<CFDataRef>(test_data));
+ EXPECT_EQ(test_data_mutable,
+ base::mac::CFCastStrict<CFDataRef>(test_data_mutable));
+ EXPECT_EQ(test_date, base::mac::CFCastStrict<CFDateRef>(test_date));
+ EXPECT_EQ(test_dict, base::mac::CFCastStrict<CFDictionaryRef>(test_dict));
+ EXPECT_EQ(test_dict_mutable,
+ base::mac::CFCastStrict<CFDictionaryRef>(test_dict_mutable));
+ EXPECT_EQ(test_number, base::mac::CFCastStrict<CFNumberRef>(test_number));
+ EXPECT_EQ(test_null, base::mac::CFCastStrict<CFNullRef>(test_null));
+ EXPECT_EQ(test_set, base::mac::CFCastStrict<CFSetRef>(test_set));
+ EXPECT_EQ(test_set_mutable,
+ base::mac::CFCastStrict<CFSetRef>(test_set_mutable));
+ EXPECT_EQ(test_str, base::mac::CFCastStrict<CFStringRef>(test_str));
+ EXPECT_EQ(test_str_const,
+ base::mac::CFCastStrict<CFStringRef>(test_str_const));
+ EXPECT_EQ(test_str_mutable,
+ base::mac::CFCastStrict<CFStringRef>(test_str_mutable));
+
+ // CFCastStrict: Giving a NULL provides a NULL.
+ EXPECT_FALSE(base::mac::CFCastStrict<CFArrayRef>(NULL));
+ EXPECT_FALSE(base::mac::CFCastStrict<CFBagRef>(NULL));
+ EXPECT_FALSE(base::mac::CFCastStrict<CFBooleanRef>(NULL));
+ EXPECT_FALSE(base::mac::CFCastStrict<CFDataRef>(NULL));
+ EXPECT_FALSE(base::mac::CFCastStrict<CFDateRef>(NULL));
+ EXPECT_FALSE(base::mac::CFCastStrict<CFDictionaryRef>(NULL));
+ EXPECT_FALSE(base::mac::CFCastStrict<CFNullRef>(NULL));
+ EXPECT_FALSE(base::mac::CFCastStrict<CFNumberRef>(NULL));
+ EXPECT_FALSE(base::mac::CFCastStrict<CFSetRef>(NULL));
+ EXPECT_FALSE(base::mac::CFCastStrict<CFStringRef>(NULL));
+}
+
+TEST(FoundationUtilTest, ObjCCast) {
+ base::mac::ScopedNSAutoreleasePool pool;
+
+ id test_array = [NSArray array];
+ id test_array_mutable = [NSMutableArray array];
+ id test_data = [NSData data];
+ id test_data_mutable = [NSMutableData dataWithCapacity:10];
+ id test_date = [NSDate date];
+ id test_dict =
+ [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:42]
+ forKey:@"meaning"];
+ id test_dict_mutable = [NSMutableDictionary dictionaryWithCapacity:10];
+ id test_number = [NSNumber numberWithInt:42];
+ id test_null = [NSNull null];
+ id test_set = [NSSet setWithObject:@"string object"];
+ id test_set_mutable = [NSMutableSet setWithCapacity:10];
+ id test_str = [NSString string];
+ id test_str_const = @"bonjour";
+ id test_str_mutable = [NSMutableString stringWithCapacity:10];
+
+ // Make sure the allocations of NS types are good.
+ EXPECT_TRUE(test_array);
+ EXPECT_TRUE(test_array_mutable);
+ EXPECT_TRUE(test_data);
+ EXPECT_TRUE(test_data_mutable);
+ EXPECT_TRUE(test_date);
+ EXPECT_TRUE(test_dict);
+ EXPECT_TRUE(test_dict_mutable);
+ EXPECT_TRUE(test_number);
+ EXPECT_TRUE(test_null);
+ EXPECT_TRUE(test_set);
+ EXPECT_TRUE(test_set_mutable);
+ EXPECT_TRUE(test_str);
+ EXPECT_TRUE(test_str_const);
+ EXPECT_TRUE(test_str_mutable);
+
+ // Casting the id correctly provides the same pointer.
+ EXPECT_EQ(test_array, base::mac::ObjCCast<NSArray>(test_array));
+ EXPECT_EQ(test_array_mutable,
+ base::mac::ObjCCast<NSArray>(test_array_mutable));
+ EXPECT_EQ(test_data, base::mac::ObjCCast<NSData>(test_data));
+ EXPECT_EQ(test_data_mutable,
+ base::mac::ObjCCast<NSData>(test_data_mutable));
+ EXPECT_EQ(test_date, base::mac::ObjCCast<NSDate>(test_date));
+ EXPECT_EQ(test_dict, base::mac::ObjCCast<NSDictionary>(test_dict));
+ EXPECT_EQ(test_dict_mutable,
+ base::mac::ObjCCast<NSDictionary>(test_dict_mutable));
+ EXPECT_EQ(test_number, base::mac::ObjCCast<NSNumber>(test_number));
+ EXPECT_EQ(test_null, base::mac::ObjCCast<NSNull>(test_null));
+ EXPECT_EQ(test_set, base::mac::ObjCCast<NSSet>(test_set));
+ EXPECT_EQ(test_set_mutable, base::mac::ObjCCast<NSSet>(test_set_mutable));
+ EXPECT_EQ(test_str, base::mac::ObjCCast<NSString>(test_str));
+ EXPECT_EQ(test_str_const, base::mac::ObjCCast<NSString>(test_str_const));
+ EXPECT_EQ(test_str_mutable,
+ base::mac::ObjCCast<NSString>(test_str_mutable));
+
+ // When given an incorrect ObjC cast, provide nil.
+ EXPECT_FALSE(base::mac::ObjCCast<NSString>(test_array));
+ EXPECT_FALSE(base::mac::ObjCCast<NSString>(test_array_mutable));
+ EXPECT_FALSE(base::mac::ObjCCast<NSString>(test_data));
+ EXPECT_FALSE(base::mac::ObjCCast<NSString>(test_data_mutable));
+ EXPECT_FALSE(base::mac::ObjCCast<NSSet>(test_date));
+ EXPECT_FALSE(base::mac::ObjCCast<NSSet>(test_dict));
+ EXPECT_FALSE(base::mac::ObjCCast<NSNumber>(test_dict_mutable));
+ EXPECT_FALSE(base::mac::ObjCCast<NSNull>(test_number));
+ EXPECT_FALSE(base::mac::ObjCCast<NSDictionary>(test_null));
+ EXPECT_FALSE(base::mac::ObjCCast<NSDictionary>(test_set));
+ EXPECT_FALSE(base::mac::ObjCCast<NSDate>(test_set_mutable));
+ EXPECT_FALSE(base::mac::ObjCCast<NSData>(test_str));
+ EXPECT_FALSE(base::mac::ObjCCast<NSData>(test_str_const));
+ EXPECT_FALSE(base::mac::ObjCCast<NSArray>(test_str_mutable));
+
+ // Giving a nil provides a nil.
+ EXPECT_FALSE(base::mac::ObjCCast<NSArray>(nil));
+ EXPECT_FALSE(base::mac::ObjCCast<NSData>(nil));
+ EXPECT_FALSE(base::mac::ObjCCast<NSDate>(nil));
+ EXPECT_FALSE(base::mac::ObjCCast<NSDictionary>(nil));
+ EXPECT_FALSE(base::mac::ObjCCast<NSNull>(nil));
+ EXPECT_FALSE(base::mac::ObjCCast<NSNumber>(nil));
+ EXPECT_FALSE(base::mac::ObjCCast<NSSet>(nil));
+ EXPECT_FALSE(base::mac::ObjCCast<NSString>(nil));
+
+ // ObjCCastStrict: correct cast results in correct pointer being returned.
+ EXPECT_EQ(test_array, base::mac::ObjCCastStrict<NSArray>(test_array));
+ EXPECT_EQ(test_array_mutable,
+ base::mac::ObjCCastStrict<NSArray>(test_array_mutable));
+ EXPECT_EQ(test_data, base::mac::ObjCCastStrict<NSData>(test_data));
+ EXPECT_EQ(test_data_mutable,
+ base::mac::ObjCCastStrict<NSData>(test_data_mutable));
+ EXPECT_EQ(test_date, base::mac::ObjCCastStrict<NSDate>(test_date));
+ EXPECT_EQ(test_dict, base::mac::ObjCCastStrict<NSDictionary>(test_dict));
+ EXPECT_EQ(test_dict_mutable,
+ base::mac::ObjCCastStrict<NSDictionary>(test_dict_mutable));
+ EXPECT_EQ(test_number, base::mac::ObjCCastStrict<NSNumber>(test_number));
+ EXPECT_EQ(test_null, base::mac::ObjCCastStrict<NSNull>(test_null));
+ EXPECT_EQ(test_set, base::mac::ObjCCastStrict<NSSet>(test_set));
+ EXPECT_EQ(test_set_mutable,
+ base::mac::ObjCCastStrict<NSSet>(test_set_mutable));
+ EXPECT_EQ(test_str, base::mac::ObjCCastStrict<NSString>(test_str));
+ EXPECT_EQ(test_str_const,
+ base::mac::ObjCCastStrict<NSString>(test_str_const));
+ EXPECT_EQ(test_str_mutable,
+ base::mac::ObjCCastStrict<NSString>(test_str_mutable));
+
+ // ObjCCastStrict: Giving a nil provides a nil.
+ EXPECT_FALSE(base::mac::ObjCCastStrict<NSArray>(nil));
+ EXPECT_FALSE(base::mac::ObjCCastStrict<NSData>(nil));
+ EXPECT_FALSE(base::mac::ObjCCastStrict<NSDate>(nil));
+ EXPECT_FALSE(base::mac::ObjCCastStrict<NSDictionary>(nil));
+ EXPECT_FALSE(base::mac::ObjCCastStrict<NSNull>(nil));
+ EXPECT_FALSE(base::mac::ObjCCastStrict<NSNumber>(nil));
+ EXPECT_FALSE(base::mac::ObjCCastStrict<NSSet>(nil));
+ EXPECT_FALSE(base::mac::ObjCCastStrict<NSString>(nil));
+}
+
+TEST(FoundationUtilTest, GetValueFromDictionary) {
+ int one = 1, two = 2, three = 3;
+
+ base::mac::ScopedCFTypeRef<CFNumberRef> cf_one(
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &one));
+ base::mac::ScopedCFTypeRef<CFNumberRef> cf_two(
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &two));
+ base::mac::ScopedCFTypeRef<CFNumberRef> cf_three(
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &three));
+
+ CFStringRef keys[] = { CFSTR("one"), CFSTR("two"), CFSTR("three") };
+ CFNumberRef values[] = { cf_one, cf_two, cf_three };
+
+ COMPILE_ASSERT(arraysize(keys) == arraysize(values),
+ keys_and_values_arraysizes_are_different);
+
+ base::mac::ScopedCFTypeRef<CFDictionaryRef> test_dict(
+ CFDictionaryCreate(kCFAllocatorDefault,
+ reinterpret_cast<const void**>(keys),
+ reinterpret_cast<const void**>(values),
+ arraysize(values),
+ &kCFCopyStringDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+
+ // base::mac::GetValueFromDictionary<>(_, _) should produce the correct
+ // expected output.
+ EXPECT_EQ(values[0],
+ base::mac::GetValueFromDictionary<CFNumberRef>(test_dict,
+ CFSTR("one")));
+ EXPECT_EQ(values[1],
+ base::mac::GetValueFromDictionary<CFNumberRef>(test_dict,
+ CFSTR("two")));
+ EXPECT_EQ(values[2],
+ base::mac::GetValueFromDictionary<CFNumberRef>(test_dict,
+ CFSTR("three")));
+
+ // Bad input should produce bad output.
+ EXPECT_FALSE(base::mac::GetValueFromDictionary<CFNumberRef>(test_dict,
+ CFSTR("four")));
+ EXPECT_FALSE(base::mac::GetValueFromDictionary<CFStringRef>(test_dict,
+ CFSTR("one")));
+}
+
+TEST(FoundationUtilTest, FilePathToNSString) {
+ EXPECT_NSEQ(nil, base::mac::FilePathToNSString(FilePath()));
+ EXPECT_NSEQ(@"/a/b", base::mac::FilePathToNSString(FilePath("/a/b")));
+}
+
+TEST(FoundationUtilTest, NSStringToFilePath) {
+ EXPECT_EQ(FilePath(), base::mac::NSStringToFilePath(nil));
+ EXPECT_EQ(FilePath(), base::mac::NSStringToFilePath(@""));
+ EXPECT_EQ(FilePath("/a/b"), base::mac::NSStringToFilePath(@"/a/b"));
+}
diff --git a/src/base/mac/launchd.cc b/src/base/mac/launchd.cc
new file mode 100644
index 0000000..1d384c9
--- /dev/null
+++ b/src/base/mac/launchd.cc
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 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/mac/launchd.h"
+
+#include "base/logging.h"
+#include "base/mac/scoped_launch_data.h"
+
+namespace base {
+namespace mac {
+
+// MessageForJob sends a single message to launchd with a simple dictionary
+// mapping |operation| to |job_label|, and returns the result of calling
+// launch_msg to send that message. On failure, returns NULL. The caller
+// assumes ownership of the returned launch_data_t object.
+launch_data_t MessageForJob(const std::string& job_label,
+ const char* operation) {
+ // launch_data_alloc returns something that needs to be freed.
+ ScopedLaunchData message(launch_data_alloc(LAUNCH_DATA_DICTIONARY));
+ if (!message) {
+ LOG(ERROR) << "launch_data_alloc";
+ return NULL;
+ }
+
+ // launch_data_new_string returns something that needs to be freed, but
+ // the dictionary will assume ownership when launch_data_dict_insert is
+ // called, so put it in a scoper and .release() it when given to the
+ // dictionary.
+ ScopedLaunchData job_label_launchd(launch_data_new_string(job_label.c_str()));
+ if (!job_label_launchd) {
+ LOG(ERROR) << "launch_data_new_string";
+ return NULL;
+ }
+
+ if (!launch_data_dict_insert(message,
+ job_label_launchd.release(),
+ operation)) {
+ return NULL;
+ }
+
+ return launch_msg(message);
+}
+
+pid_t PIDForJob(const std::string& job_label) {
+ ScopedLaunchData response(MessageForJob(job_label, LAUNCH_KEY_GETJOB));
+ if (!response) {
+ return -1;
+ }
+
+ launch_data_type_t response_type = launch_data_get_type(response);
+ if (response_type != LAUNCH_DATA_DICTIONARY) {
+ if (response_type == LAUNCH_DATA_ERRNO) {
+ LOG(ERROR) << "PIDForJob: error " << launch_data_get_errno(response);
+ } else {
+ LOG(ERROR) << "PIDForJob: expected dictionary, got " << response_type;
+ }
+ return -1;
+ }
+
+ launch_data_t pid_data = launch_data_dict_lookup(response,
+ LAUNCH_JOBKEY_PID);
+ if (!pid_data)
+ return 0;
+
+ if (launch_data_get_type(pid_data) != LAUNCH_DATA_INTEGER) {
+ LOG(ERROR) << "PIDForJob: expected integer";
+ return -1;
+ }
+
+ return launch_data_get_integer(pid_data);
+}
+
+} // namespace mac
+} // namespace base
diff --git a/src/base/mac/launchd.h b/src/base/mac/launchd.h
new file mode 100644
index 0000000..9e4514e
--- /dev/null
+++ b/src/base/mac/launchd.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 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_MAC_LAUNCHD_H_
+#define BASE_MAC_LAUNCHD_H_
+
+#include <launch.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "base/base_export.h"
+
+namespace base {
+namespace mac {
+
+// MessageForJob sends a single message to launchd with a simple dictionary
+// mapping |operation| to |job_label|, and returns the result of calling
+// launch_msg to send that message. On failure, returns NULL. The caller
+// assumes ownership of the returned launch_data_t object.
+BASE_EXPORT
+launch_data_t MessageForJob(const std::string& job_label,
+ const char* operation);
+
+// Returns the process ID for |job_label| if the job is running, 0 if the job
+// is loaded but not running, or -1 on error.
+BASE_EXPORT
+pid_t PIDForJob(const std::string& job_label);
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_LAUNCHD_H_
diff --git a/src/base/mac/libdispatch_task_runner.cc b/src/base/mac/libdispatch_task_runner.cc
new file mode 100644
index 0000000..4b5abaf
--- /dev/null
+++ b/src/base/mac/libdispatch_task_runner.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2012 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/mac/libdispatch_task_runner.h"
+
+#include "base/callback.h"
+
+namespace base {
+namespace mac {
+
+LibDispatchTaskRunner::LibDispatchTaskRunner(const char* name)
+ : queue_(dispatch_queue_create(name, NULL)),
+ queue_finalized_(false, false) {
+ dispatch_set_context(queue_, this);
+ dispatch_set_finalizer_f(queue_, &LibDispatchTaskRunner::Finalizer);
+}
+
+bool LibDispatchTaskRunner::PostDelayedTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ base::TimeDelta delay) {
+ if (!queue_)
+ return false;
+
+ // The block runtime would implicitly copy the reference, not the object
+ // it's referencing. Copy the closure into block storage so it's available
+ // to run.
+ __block const Closure task_copy = task;
+ void(^run_task)(void) = ^{
+ task_copy.Run();
+ };
+
+ int64 delay_nano =
+ delay.InMicroseconds() * base::Time::kNanosecondsPerMicrosecond;
+ if (delay_nano > 0) {
+ dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, delay_nano);
+ dispatch_after(time, queue_, run_task);
+ } else {
+ dispatch_async(queue_, run_task);
+ }
+ return true;
+}
+
+bool LibDispatchTaskRunner::RunsTasksOnCurrentThread() const {
+ return queue_ == dispatch_get_current_queue();
+}
+
+bool LibDispatchTaskRunner::PostNonNestableDelayedTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ base::TimeDelta delay) {
+ return PostDelayedTask(from_here, task, delay);
+}
+
+void LibDispatchTaskRunner::Shutdown() {
+ dispatch_release(queue_);
+ queue_ = NULL;
+ queue_finalized_.Wait();
+}
+
+dispatch_queue_t LibDispatchTaskRunner::GetDispatchQueue() const {
+ return queue_;
+}
+
+LibDispatchTaskRunner::~LibDispatchTaskRunner() {
+ if (queue_) {
+ dispatch_set_context(queue_, NULL);
+ dispatch_set_finalizer_f(queue_, NULL);
+ dispatch_release(queue_);
+ }
+}
+
+void LibDispatchTaskRunner::Finalizer(void* context) {
+ LibDispatchTaskRunner* self = static_cast<LibDispatchTaskRunner*>(context);
+ self->queue_finalized_.Signal();
+}
+
+} // namespace mac
+} // namespace base
diff --git a/src/base/mac/libdispatch_task_runner.h b/src/base/mac/libdispatch_task_runner.h
new file mode 100644
index 0000000..b1d90e2
--- /dev/null
+++ b/src/base/mac/libdispatch_task_runner.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2012 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_MAC_LIBDISPATCH_SEQUENCED_TASK_RUNNER_H_
+#define BASE_MAC_LIBDISPATCH_SEQUENCED_TASK_RUNNER_H_
+
+#include <dispatch/dispatch.h>
+
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+
+namespace base {
+namespace mac {
+
+// This is an implementation of the TaskRunner interface that runs closures on
+// a thread managed by Apple's libdispatch. This has the benefit of being able
+// to PostTask() and friends to a dispatch queue, while being reusable as a
+// dispatch_queue_t.
+//
+// One would use this class if an object lives exclusively on one thread but
+// needs a dispatch_queue_t for use in a system API. This ensures all dispatch
+// callbacks happen on the same thread as Closure tasks.
+//
+// A LibDispatchTaskRunner will continue to run until all references to the
+// underlying dispatch queue are released.
+//
+// Important Notes:
+// - There is no MessageLoop running on this thread, and ::current() returns
+// NULL.
+// - No nested loops can be run, and all tasks are run non-nested.
+// - Work scheduled via libdispatch runs at the same priority as and is
+// interleaved with posted tasks, though FIFO order is guaranteed.
+//
+class BASE_EXPORT LibDispatchTaskRunner : public base::SingleThreadTaskRunner {
+ public:
+ // Starts a new serial dispatch queue with a given name.
+ explicit LibDispatchTaskRunner(const char* name);
+
+ // base::TaskRunner:
+ virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
+ const Closure& task,
+ base::TimeDelta delay) OVERRIDE;
+ virtual bool RunsTasksOnCurrentThread() const OVERRIDE;
+
+ // base::SequencedTaskRunner:
+ virtual bool PostNonNestableDelayedTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ base::TimeDelta delay) OVERRIDE;
+
+ // This blocks the calling thread until all work on the dispatch queue has
+ // been run and the queue has been destroyed. Destroying a queue requires
+ // ALL retained references to it to be released. Any new tasks posted to
+ // this thread after shutdown are dropped.
+ void Shutdown();
+
+ // Returns the dispatch queue associated with this task runner, for use with
+ // system APIs that take dispatch queues. The caller is responsible for
+ // retaining the result.
+ //
+ // All properties (context, finalizer, etc.) are managed by this class, and
+ // clients should only use the result of this for dispatch_async().
+ dispatch_queue_t GetDispatchQueue() const;
+
+ protected:
+ virtual ~LibDispatchTaskRunner();
+
+ private:
+ static void Finalizer(void* context);
+
+ dispatch_queue_t queue_;
+
+ // The event on which Shutdown waits until Finalizer runs.
+ base::WaitableEvent queue_finalized_;
+};
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_LIBDISPATCH_SEQUENCED_TASK_RUNNER_H_
diff --git a/src/base/mac/libdispatch_task_runner_unittest.cc b/src/base/mac/libdispatch_task_runner_unittest.cc
new file mode 100644
index 0000000..c3488d2
--- /dev/null
+++ b/src/base/mac/libdispatch_task_runner_unittest.cc
@@ -0,0 +1,221 @@
+// Copyright (c) 2012 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/mac/libdispatch_task_runner.h"
+
+#include "base/bind.h"
+#include "base/mac/bind_objc_block.h"
+#include "base/message_loop.h"
+#include "base/stringprintf.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class LibDispatchTaskRunnerTest : public testing::Test {
+ public:
+ virtual void SetUp() OVERRIDE {
+ task_runner_ = new base::mac::LibDispatchTaskRunner(
+ "org.chromium.LibDispatchTaskRunnerTest");
+ }
+
+ // DispatchLastTask is used to run the main test thread's MessageLoop until
+ // all non-delayed tasks are run on the LibDispatchTaskRunner.
+ void DispatchLastTask() {
+ dispatch_async(task_runner_->GetDispatchQueue(), ^{
+ (&message_loop_)->PostTask(FROM_HERE, MessageLoop::QuitClosure());
+ });
+ message_loop_.Run();
+ task_runner_->Shutdown();
+ }
+
+ // VerifyTaskOrder takes the expectations from TaskOrderMarkers and compares
+ // them against the recorded values.
+ void VerifyTaskOrder(const char* const expectations[],
+ size_t num_expectations) {
+ size_t actual_size = task_order_.size();
+
+ for (size_t i = 0; i < num_expectations; ++i) {
+ if (i >= actual_size) {
+ EXPECT_LE(i, actual_size) << "Expected " << expectations[i];
+ continue;
+ }
+
+ EXPECT_EQ(expectations[i], task_order_[i]);
+ }
+
+ if (actual_size > num_expectations) {
+ EXPECT_LE(actual_size, num_expectations) << "Extra tasks were run:";
+ for (size_t i = num_expectations; i < actual_size; ++i) {
+ EXPECT_EQ("<none>", task_order_[i]) << " (i=" << i << ")";
+ }
+ }
+ }
+
+ // The message loop for the test main thread.
+ MessageLoop message_loop_;
+
+ // The task runner under test.
+ scoped_refptr<base::mac::LibDispatchTaskRunner> task_runner_;
+
+ // Vector that records data from TaskOrderMarker.
+ std::vector<std::string> task_order_;
+};
+
+// Scoper that records the beginning and end of a running task.
+class TaskOrderMarker {
+ public:
+ TaskOrderMarker(LibDispatchTaskRunnerTest* test, const std::string& name)
+ : test_(test),
+ name_(name) {
+ test->task_order_.push_back(std::string("BEGIN ") + name);
+ }
+ ~TaskOrderMarker() {
+ test_->task_order_.push_back(std::string("END ") + name_);
+ }
+
+ private:
+ LibDispatchTaskRunnerTest* test_;
+ std::string name_;
+};
+
+void RecordTaskOrder(LibDispatchTaskRunnerTest* test, const std::string& name) {
+ TaskOrderMarker marker(test, name);
+}
+
+// Returns a closure that records the task order.
+base::Closure BoundRecordTaskOrder(LibDispatchTaskRunnerTest* test,
+ const std::string& name) {
+ return base::Bind(&RecordTaskOrder, base::Unretained(test), name);
+}
+
+TEST_F(LibDispatchTaskRunnerTest, PostTask) {
+ task_runner_->PostTask(FROM_HERE, BoundRecordTaskOrder(this, "Basic Task"));
+ DispatchLastTask();
+ const char* const expectations[] = {
+ "BEGIN Basic Task",
+ "END Basic Task"
+ };
+ VerifyTaskOrder(expectations, arraysize(expectations));
+}
+
+TEST_F(LibDispatchTaskRunnerTest, PostTaskWithinTask) {
+ task_runner_->PostTask(FROM_HERE, base::BindBlock(^{
+ TaskOrderMarker marker(this, "Outer");
+ task_runner_->PostTask(FROM_HERE, BoundRecordTaskOrder(this, "Inner"));
+ }));
+ DispatchLastTask();
+
+ const char* const expectations[] = {
+ "BEGIN Outer",
+ "END Outer",
+ "BEGIN Inner",
+ "END Inner"
+ };
+ VerifyTaskOrder(expectations, arraysize(expectations));
+}
+
+TEST_F(LibDispatchTaskRunnerTest, NoMessageLoop) {
+ task_runner_->PostTask(FROM_HERE, base::BindBlock(^{
+ TaskOrderMarker marker(this,
+ base::StringPrintf("MessageLoop = %p", MessageLoop::current()));
+ }));
+ DispatchLastTask();
+
+ const char* const expectations[] = {
+ "BEGIN MessageLoop = 0x0",
+ "END MessageLoop = 0x0"
+ };
+ VerifyTaskOrder(expectations, arraysize(expectations));
+}
+
+TEST_F(LibDispatchTaskRunnerTest, DispatchAndPostTasks) {
+ dispatch_async(task_runner_->GetDispatchQueue(), ^{
+ TaskOrderMarker marker(this, "First Block");
+ });
+ task_runner_->PostTask(FROM_HERE, BoundRecordTaskOrder(this, "First Task"));
+ dispatch_async(task_runner_->GetDispatchQueue(), ^{
+ TaskOrderMarker marker(this, "Second Block");
+ });
+ task_runner_->PostTask(FROM_HERE, BoundRecordTaskOrder(this, "Second Task"));
+ DispatchLastTask();
+
+ const char* const expectations[] = {
+ "BEGIN First Block",
+ "END First Block",
+ "BEGIN First Task",
+ "END First Task",
+ "BEGIN Second Block",
+ "END Second Block",
+ "BEGIN Second Task",
+ "END Second Task",
+ };
+ VerifyTaskOrder(expectations, arraysize(expectations));
+}
+
+TEST_F(LibDispatchTaskRunnerTest, NonNestable) {
+ task_runner_->PostTask(FROM_HERE, base::BindBlock(^{
+ TaskOrderMarker marker(this, "First");
+ task_runner_->PostNonNestableTask(FROM_HERE, base::BindBlock(^{
+ TaskOrderMarker marker(this, "Second NonNestable");
+ (&message_loop_)->PostTask(FROM_HERE, MessageLoop::QuitClosure());
+ }));
+ }));
+ message_loop_.Run();
+ task_runner_->Shutdown();
+
+ const char* const expectations[] = {
+ "BEGIN First",
+ "END First",
+ "BEGIN Second NonNestable",
+ "END Second NonNestable"
+ };
+ VerifyTaskOrder(expectations, arraysize(expectations));
+}
+
+TEST_F(LibDispatchTaskRunnerTest, PostDelayed) {
+ base::TimeTicks post_time;
+ __block base::TimeTicks run_time;
+ const base::TimeDelta delta = base::TimeDelta::FromMilliseconds(50);
+
+ task_runner_->PostTask(FROM_HERE, BoundRecordTaskOrder(this, "First"));
+ post_time = base::TimeTicks::Now();
+ task_runner_->PostDelayedTask(FROM_HERE, base::BindBlock(^{
+ TaskOrderMarker marker(this, "Timed");
+ run_time = base::TimeTicks::Now();
+ (&message_loop_)->PostTask(FROM_HERE, MessageLoop::QuitClosure());
+ }), delta);
+ task_runner_->PostTask(FROM_HERE, BoundRecordTaskOrder(this, "Second"));
+ message_loop_.Run();
+ task_runner_->Shutdown();
+
+ const char* const expectations[] = {
+ "BEGIN First",
+ "END First",
+ "BEGIN Second",
+ "END Second",
+ "BEGIN Timed",
+ "END Timed",
+ };
+ VerifyTaskOrder(expectations, arraysize(expectations));
+
+ EXPECT_GE(run_time, post_time + delta);
+}
+
+TEST_F(LibDispatchTaskRunnerTest, PostAfterShutdown) {
+ EXPECT_TRUE(task_runner_->PostTask(FROM_HERE,
+ BoundRecordTaskOrder(this, "First")));
+ EXPECT_TRUE(task_runner_->PostTask(FROM_HERE,
+ BoundRecordTaskOrder(this, "Second")));
+ task_runner_->Shutdown();
+ EXPECT_FALSE(task_runner_->PostTask(FROM_HERE, base::BindBlock(^{
+ TaskOrderMarker marker(this, "Not Run");
+ ADD_FAILURE() << "Should not run a task after Shutdown";
+ })));
+
+ const char* const expectations[] = {
+ "BEGIN First",
+ "END First",
+ "BEGIN Second",
+ "END Second"
+ };
+ VerifyTaskOrder(expectations, arraysize(expectations));
+}
diff --git a/src/base/mac/mac_logging.cc b/src/base/mac/mac_logging.cc
new file mode 100644
index 0000000..d58220f
--- /dev/null
+++ b/src/base/mac/mac_logging.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 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/mac/mac_logging.h"
+
+#include <iomanip>
+
+#if !defined(OS_IOS)
+#include <CoreServices/CoreServices.h>
+#endif
+
+namespace logging {
+
+OSStatusLogMessage::OSStatusLogMessage(const char* file_path,
+ int line,
+ LogSeverity severity,
+ OSStatus status)
+ : LogMessage(file_path, line, severity),
+ status_(status) {
+}
+
+OSStatusLogMessage::~OSStatusLogMessage() {
+#if defined(OS_IOS)
+ // TODO(ios): Consider using NSError with NSOSStatusErrorDomain to try to
+ // get a description of the failure.
+ stream() << ": " << status_;
+#else
+ stream() << ": "
+ << GetMacOSStatusErrorString(status_)
+ << " ("
+ << status_
+ << ")";
+#endif
+}
+
+} // namespace logging
diff --git a/src/base/mac/mac_logging.h b/src/base/mac/mac_logging.h
new file mode 100644
index 0000000..9a0003e
--- /dev/null
+++ b/src/base/mac/mac_logging.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2012 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_MAC_MAC_LOGGING_H_
+#define BASE_MAC_MAC_LOGGING_H_
+
+#include "base/logging.h"
+#include "build/build_config.h"
+
+#if defined(OS_IOS)
+#include <MacTypes.h>
+#else
+#include <libkern/OSTypes.h>
+#endif
+
+// Use the OSSTATUS_LOG family to log messages related to errors in Mac OS X
+// system routines that report status via an OSStatus or OSErr value. It is
+// similar to the PLOG family which operates on errno, but because there is no
+// global (or thread-local) OSStatus or OSErr value, the specific error must
+// be supplied as an argument to the OSSTATUS_LOG macro. The message logged
+// will contain the symbolic constant name corresponding to the status value,
+// along with the value itself.
+//
+// OSErr is just an older 16-bit form of the newer 32-bit OSStatus. Despite
+// the name, OSSTATUS_LOG can be used equally well for OSStatus and OSErr.
+
+namespace logging {
+
+class BASE_EXPORT OSStatusLogMessage : public logging::LogMessage {
+ public:
+ OSStatusLogMessage(const char* file_path,
+ int line,
+ LogSeverity severity,
+ OSStatus status);
+ ~OSStatusLogMessage();
+
+ private:
+ OSStatus status_;
+
+ DISALLOW_COPY_AND_ASSIGN(OSStatusLogMessage);
+};
+
+} // namespace logging
+
+#define OSSTATUS_LOG_STREAM(severity, status) \
+ COMPACT_GOOGLE_LOG_EX_ ## severity(OSStatusLogMessage, status).stream()
+#define OSSTATUS_VLOG_STREAM(verbose_level, status) \
+ logging::OSStatusLogMessage(__FILE__, __LINE__, \
+ -verbose_level, status).stream()
+
+#define OSSTATUS_LOG(severity, status) \
+ LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), LOG_IS_ON(severity))
+#define OSSTATUS_LOG_IF(severity, condition, status) \
+ LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), \
+ LOG_IS_ON(severity) && (condition))
+
+#define OSSTATUS_VLOG(verbose_level, status) \
+ LAZY_STREAM(OSSTATUS_VLOG_STREAM(verbose_level, status), \
+ VLOG_IS_ON(verbose_level))
+#define OSSTATUS_VLOG_IF(verbose_level, condition, status) \
+ LAZY_STREAM(OSSTATUS_VLOG_STREAM(verbose_level, status), \
+ VLOG_IS_ON(verbose_level) && (condition))
+
+#define OSSTATUS_CHECK(condition, status) \
+ LAZY_STREAM(OSSTATUS_LOG_STREAM(FATAL, status), !(condition)) \
+ << "Check failed: " # condition << ". "
+
+#define OSSTATUS_DLOG(severity, status) \
+ LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), DLOG_IS_ON(severity))
+#define OSSTATUS_DLOG_IF(severity, condition, status) \
+ LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), \
+ DLOG_IS_ON(severity) && (condition))
+
+#define OSSTATUS_DVLOG(verbose_level, status) \
+ LAZY_STREAM(OSSTATUS_VPLOG_STREAM(verbose_level, status), \
+ DVLOG_IS_ON(verbose_level))
+#define OSSTATUS_DVLOG_IF(verbose_level, condition, status) \
+ LAZY_STREAM(OSSTATUS_VPLOG_STREAM(verbose_level, status) \
+ DVLOG_IS_ON(verbose_level) && (condition))
+
+#define OSSTATUS_DCHECK(condition, status) \
+ LAZY_STREAM(OSSTATUS_LOG_STREAM(FATAL, status), \
+ DCHECK_IS_ON() && !(condition)) \
+ << "Check failed: " # condition << ". "
+
+#endif // BASE_MAC_MAC_LOGGING_H_
diff --git a/src/base/mac/mac_util.h b/src/base/mac/mac_util.h
new file mode 100644
index 0000000..ff8b2b2
--- /dev/null
+++ b/src/base/mac/mac_util.h
@@ -0,0 +1,195 @@
+// Copyright (c) 2012 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_MAC_MAC_UTIL_H_
+#define BASE_MAC_MAC_UTIL_H_
+
+#include <AvailabilityMacros.h>
+#include <Carbon/Carbon.h>
+#include <string>
+
+#include "base/base_export.h"
+#include "base/logging.h"
+
+// TODO(rohitrao): Clean up sites that include mac_util.h and remove this line.
+#include "base/mac/foundation_util.h"
+
+#if defined(__OBJC__)
+#import <Foundation/Foundation.h>
+#else // __OBJC__
+class NSImage;
+#endif // __OBJC__
+
+class FilePath;
+
+namespace base {
+namespace mac {
+
+// Full screen modes, in increasing order of priority. More permissive modes
+// take predecence.
+enum FullScreenMode {
+ kFullScreenModeHideAll = 0,
+ kFullScreenModeHideDock = 1,
+ kFullScreenModeAutoHideAll = 2,
+ kNumFullScreenModes = 3,
+
+ // kFullScreenModeNormal is not a valid FullScreenMode, but it is useful to
+ // other classes, so we include it here.
+ kFullScreenModeNormal = 10,
+};
+
+BASE_EXPORT std::string PathFromFSRef(const FSRef& ref);
+BASE_EXPORT bool FSRefFromPath(const std::string& path, FSRef* ref);
+
+// Returns an sRGB color space. The return value is a static value; do not
+// release it!
+BASE_EXPORT CGColorSpaceRef GetSRGBColorSpace();
+
+// Returns the color space being used by the main display. The return value
+// is a static value; do not release it!
+BASE_EXPORT CGColorSpaceRef GetSystemColorSpace();
+
+// Add a full screen request for the given |mode|. Must be paired with a
+// ReleaseFullScreen() call for the same |mode|. This does not by itself create
+// a fullscreen window; rather, it manages per-application state related to
+// hiding the dock and menubar. Must be called on the main thread.
+BASE_EXPORT void RequestFullScreen(FullScreenMode mode);
+
+// Release a request for full screen mode. Must be matched with a
+// RequestFullScreen() call for the same |mode|. As with RequestFullScreen(),
+// this does not affect windows directly, but rather manages per-application
+// state. For example, if there are no other outstanding
+// |kFullScreenModeAutoHideAll| requests, this will reshow the menu bar. Must
+// be called on main thread.
+BASE_EXPORT void ReleaseFullScreen(FullScreenMode mode);
+
+// Convenience method to switch the current fullscreen mode. This has the same
+// net effect as a ReleaseFullScreen(from_mode) call followed immediately by a
+// RequestFullScreen(to_mode). Must be called on the main thread.
+BASE_EXPORT void SwitchFullScreenModes(FullScreenMode from_mode,
+ FullScreenMode to_mode);
+
+// Set the visibility of the cursor.
+BASE_EXPORT void SetCursorVisibility(bool visible);
+
+// Should windows miniaturize on a double-click (on the title bar)?
+BASE_EXPORT bool ShouldWindowsMiniaturizeOnDoubleClick();
+
+// Activates the process with the given PID.
+BASE_EXPORT void ActivateProcess(pid_t pid);
+
+// Returns true if this process is in the foreground, meaning that it's the
+// frontmost process, the one whose menu bar is shown at the top of the main
+// display.
+BASE_EXPORT bool AmIForeground();
+
+// Excludes the file given by |file_path| from being backed up by Time Machine.
+BASE_EXPORT bool SetFileBackupExclusion(const FilePath& file_path);
+
+// Sets the process name as displayed in Activity Monitor to process_name.
+BASE_EXPORT void SetProcessName(CFStringRef process_name);
+
+// Converts a NSImage to a CGImageRef. Normally, the system frameworks can do
+// this fine, especially on 10.6. On 10.5, however, CGImage cannot handle
+// converting a PDF-backed NSImage into a CGImageRef. This function will
+// rasterize the PDF into a bitmap CGImage. The caller is responsible for
+// releasing the return value.
+BASE_EXPORT CGImageRef CopyNSImageToCGImage(NSImage* image);
+
+// Checks if the current application is set as a Login Item, so it will launch
+// on Login. If a non-NULL pointer to is_hidden is passed, the Login Item also
+// is queried for the 'hide on launch' flag.
+BASE_EXPORT bool CheckLoginItemStatus(bool* is_hidden);
+
+// Adds current application to the set of Login Items with specified "hide"
+// flag. This has the same effect as adding/removing the application in
+// SystemPreferences->Accounts->LoginItems or marking Application in the Dock
+// as "Options->Open on Login".
+// Does nothing if the application is already set up as Login Item with
+// specified hide flag.
+BASE_EXPORT void AddToLoginItems(bool hide_on_startup);
+
+// Removes the current application from the list Of Login Items.
+BASE_EXPORT void RemoveFromLoginItems();
+
+// Returns true if the current process was automatically launched as a
+// 'Login Item' or via Lion's Resume. Used to suppress opening windows.
+BASE_EXPORT bool WasLaunchedAsLoginOrResumeItem();
+
+// Returns true if the current process was automatically launched as a
+// 'Login Item' with 'hide on startup' flag. Used to suppress opening windows.
+BASE_EXPORT bool WasLaunchedAsHiddenLoginItem();
+
+// Run-time OS version checks. Use these instead of
+// base::SysInfo::OperatingSystemVersionNumbers. Prefer the "OrEarlier" and
+// "OrLater" variants to those that check for a specific version, unless you
+// know for sure that you need to check for a specific version.
+
+// Snow Leopard is Mac OS X 10.6, Darwin 10.
+BASE_EXPORT bool IsOSSnowLeopard();
+
+// Lion is Mac OS X 10.7, Darwin 11.
+BASE_EXPORT bool IsOSLion();
+BASE_EXPORT bool IsOSLionOrEarlier();
+BASE_EXPORT bool IsOSLionOrLater();
+
+// Mountain Lion is Mac OS X 10.8, Darwin 12.
+BASE_EXPORT bool IsOSMountainLion();
+BASE_EXPORT bool IsOSMountainLionOrLater();
+
+// This should be infrequently used. It only makes sense to use this to avoid
+// codepaths that are very likely to break on future (unreleased, untested,
+// unborn) OS releases, or to log when the OS is newer than any known version.
+BASE_EXPORT bool IsOSLaterThanMountainLion_DontCallThis();
+
+// When the deployment target is set, the code produced cannot run on earlier
+// OS releases. That enables some of the IsOS* family to be implemented as
+// constant-value inline functions. The MAC_OS_X_VERSION_MIN_REQUIRED macro
+// contains the value of the deployment target.
+
+#if defined(MAC_OS_X_VERSION_10_7) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7
+#define BASE_MAC_MAC_UTIL_H_INLINED_GE_10_7
+inline bool IsOSSnowLeopard() { return false; }
+inline bool IsOSLionOrLater() { return true; }
+#endif
+
+#if defined(MAC_OS_X_VERSION_10_7) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_7
+#define BASE_MAC_MAC_UTIL_H_INLINED_GT_10_7
+inline bool IsOSLion() { return false; }
+inline bool IsOSLionOrEarlier() { return false; }
+#endif
+
+#if defined(MAC_OS_X_VERSION_10_8) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8
+#define BASE_MAC_MAC_UTIL_H_INLINED_GE_10_8
+inline bool IsOSMountainLionOrLater() { return true; }
+#endif
+
+#if defined(MAC_OS_X_VERSION_10_8) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_8
+#define BASE_MAC_MAC_UTIL_H_INLINED_GT_10_8
+inline bool IsOSMountainLion() { return false; }
+inline bool IsOSLaterThanMountainLion_DontCallThis() {
+ return true;
+}
+#endif
+
+// Retrieve the system's model identifier string from the IOKit registry:
+// for example, "MacPro4,1", "MacBookPro6,1". Returns empty string upon
+// failure.
+BASE_EXPORT std::string GetModelIdentifier();
+
+// Parse a model identifier string; for example, into ("MacBookPro", 6, 1).
+// If any error occurs, none of the input pointers are touched.
+BASE_EXPORT bool ParseModelIdentifier(const std::string& ident,
+ std::string* type,
+ int32* major,
+ int32* minor);
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_MAC_UTIL_H_
diff --git a/src/base/mac/mac_util.mm b/src/base/mac/mac_util.mm
new file mode 100644
index 0000000..4fb1a42
--- /dev/null
+++ b/src/base/mac/mac_util.mm
@@ -0,0 +1,666 @@
+// Copyright (c) 2012 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/mac/mac_util.h"
+
+#import <Cocoa/Cocoa.h>
+#import <IOKit/IOKitLib.h>
+#include <string.h>
+#include <sys/utsname.h>
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/mac/bundle_locations.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/mac_logging.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/memory/scoped_generic_obj.h"
+#include "base/memory/scoped_nsobject.h"
+#include "base/string_number_conversions.h"
+#include "base/string_piece.h"
+#include "base/sys_string_conversions.h"
+
+namespace base {
+namespace mac {
+
+namespace {
+
+// The current count of outstanding requests for full screen mode from browser
+// windows, plugins, etc.
+int g_full_screen_requests[kNumFullScreenModes] = { 0, 0, 0};
+
+// Sets the appropriate SystemUIMode based on the current full screen requests.
+// Since only one SystemUIMode can be active at a given time, full screen
+// requests are ordered by priority. If there are no outstanding full screen
+// requests, reverts to normal mode. If the correct SystemUIMode is already
+// set, does nothing.
+void SetUIMode() {
+ // Get the current UI mode.
+ SystemUIMode current_mode;
+ GetSystemUIMode(¤t_mode, NULL);
+
+ // Determine which mode should be active, based on which requests are
+ // currently outstanding. More permissive requests take precedence. For
+ // example, plugins request |kFullScreenModeAutoHideAll|, while browser
+ // windows request |kFullScreenModeHideDock| when the fullscreen overlay is
+ // down. Precedence goes to plugins in this case, so AutoHideAll wins over
+ // HideDock.
+ SystemUIMode desired_mode = kUIModeNormal;
+ SystemUIOptions desired_options = 0;
+ if (g_full_screen_requests[kFullScreenModeAutoHideAll] > 0) {
+ desired_mode = kUIModeAllHidden;
+ desired_options = kUIOptionAutoShowMenuBar;
+ } else if (g_full_screen_requests[kFullScreenModeHideDock] > 0) {
+ desired_mode = kUIModeContentHidden;
+ } else if (g_full_screen_requests[kFullScreenModeHideAll] > 0) {
+ desired_mode = kUIModeAllHidden;
+ }
+
+ if (current_mode != desired_mode)
+ SetSystemUIMode(desired_mode, desired_options);
+}
+
+// Looks into Shared File Lists corresponding to Login Items for the item
+// representing the current application. If such an item is found, returns a
+// retained reference to it. Caller is responsible for releasing the reference.
+LSSharedFileListItemRef GetLoginItemForApp() {
+ ScopedCFTypeRef<LSSharedFileListRef> login_items(LSSharedFileListCreate(
+ NULL, kLSSharedFileListSessionLoginItems, NULL));
+
+ if (!login_items.get()) {
+ DLOG(ERROR) << "Couldn't get a Login Items list.";
+ return NULL;
+ }
+
+ scoped_nsobject<NSArray> login_items_array(
+ CFToNSCast(LSSharedFileListCopySnapshot(login_items, NULL)));
+
+ NSURL* url = [NSURL fileURLWithPath:[base::mac::MainBundle() bundlePath]];
+
+ for(NSUInteger i = 0; i < [login_items_array count]; ++i) {
+ LSSharedFileListItemRef item = reinterpret_cast<LSSharedFileListItemRef>(
+ [login_items_array objectAtIndex:i]);
+ CFURLRef item_url_ref = NULL;
+
+ if (LSSharedFileListItemResolve(item, 0, &item_url_ref, NULL) == noErr) {
+ ScopedCFTypeRef<CFURLRef> item_url(item_url_ref);
+ if (CFEqual(item_url, url)) {
+ CFRetain(item);
+ return item;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+bool IsHiddenLoginItem(LSSharedFileListItemRef item) {
+ ScopedCFTypeRef<CFBooleanRef> hidden(reinterpret_cast<CFBooleanRef>(
+ LSSharedFileListItemCopyProperty(item,
+ reinterpret_cast<CFStringRef>(kLSSharedFileListLoginItemHidden))));
+
+ return hidden && hidden == kCFBooleanTrue;
+}
+
+} // namespace
+
+std::string PathFromFSRef(const FSRef& ref) {
+ ScopedCFTypeRef<CFURLRef> url(
+ CFURLCreateFromFSRef(kCFAllocatorDefault, &ref));
+ NSString *path_string = [(NSURL *)url.get() path];
+ return [path_string fileSystemRepresentation];
+}
+
+bool FSRefFromPath(const std::string& path, FSRef* ref) {
+ OSStatus status = FSPathMakeRef((const UInt8*)path.c_str(),
+ ref, nil);
+ return status == noErr;
+}
+
+CGColorSpaceRef GetSRGBColorSpace() {
+ // Leaked. That's OK, it's scoped to the lifetime of the application.
+ static CGColorSpaceRef g_color_space_sRGB =
+ CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
+ DLOG_IF(ERROR, !g_color_space_sRGB) << "Couldn't get the sRGB color space";
+ return g_color_space_sRGB;
+}
+
+CGColorSpaceRef GetSystemColorSpace() {
+ // Leaked. That's OK, it's scoped to the lifetime of the application.
+ // Try to get the main display's color space.
+ static CGColorSpaceRef g_system_color_space =
+ CGDisplayCopyColorSpace(CGMainDisplayID());
+
+ if (!g_system_color_space) {
+ // Use a generic RGB color space. This is better than nothing.
+ g_system_color_space = CGColorSpaceCreateDeviceRGB();
+
+ if (g_system_color_space) {
+ DLOG(WARNING) <<
+ "Couldn't get the main display's color space, using generic";
+ } else {
+ DLOG(ERROR) << "Couldn't get any color space";
+ }
+ }
+
+ return g_system_color_space;
+}
+
+// Add a request for full screen mode. Must be called on the main thread.
+void RequestFullScreen(FullScreenMode mode) {
+ DCHECK_LT(mode, kNumFullScreenModes);
+ if (mode >= kNumFullScreenModes)
+ return;
+
+ DCHECK_GE(g_full_screen_requests[mode], 0);
+ g_full_screen_requests[mode] = std::max(g_full_screen_requests[mode] + 1, 1);
+ SetUIMode();
+}
+
+// Release a request for full screen mode. Must be called on the main thread.
+void ReleaseFullScreen(FullScreenMode mode) {
+ DCHECK_LT(mode, kNumFullScreenModes);
+ if (mode >= kNumFullScreenModes)
+ return;
+
+ DCHECK_GT(g_full_screen_requests[mode], 0);
+ g_full_screen_requests[mode] = std::max(g_full_screen_requests[mode] - 1, 0);
+ SetUIMode();
+}
+
+// Switches full screen modes. Releases a request for |from_mode| and adds a
+// new request for |to_mode|. Must be called on the main thread.
+void SwitchFullScreenModes(FullScreenMode from_mode, FullScreenMode to_mode) {
+ DCHECK_LT(from_mode, kNumFullScreenModes);
+ DCHECK_LT(to_mode, kNumFullScreenModes);
+ if (from_mode >= kNumFullScreenModes || to_mode >= kNumFullScreenModes)
+ return;
+
+ DCHECK_GT(g_full_screen_requests[from_mode], 0);
+ DCHECK_GE(g_full_screen_requests[to_mode], 0);
+ g_full_screen_requests[from_mode] =
+ std::max(g_full_screen_requests[from_mode] - 1, 0);
+ g_full_screen_requests[to_mode] =
+ std::max(g_full_screen_requests[to_mode] + 1, 1);
+ SetUIMode();
+}
+
+void SetCursorVisibility(bool visible) {
+ if (visible)
+ [NSCursor unhide];
+ else
+ [NSCursor hide];
+}
+
+bool ShouldWindowsMiniaturizeOnDoubleClick() {
+ // We use an undocumented method in Cocoa; if it doesn't exist, default to
+ // |true|. If it ever goes away, we can do (using an undocumented pref key):
+ // NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+ // return ![defaults objectForKey:@"AppleMiniaturizeOnDoubleClick"] ||
+ // [defaults boolForKey:@"AppleMiniaturizeOnDoubleClick"];
+ BOOL methodImplemented =
+ [NSWindow respondsToSelector:@selector(_shouldMiniaturizeOnDoubleClick)];
+ DCHECK(methodImplemented);
+ return !methodImplemented ||
+ [NSWindow performSelector:@selector(_shouldMiniaturizeOnDoubleClick)];
+}
+
+void ActivateProcess(pid_t pid) {
+ ProcessSerialNumber process;
+ OSStatus status = GetProcessForPID(pid, &process);
+ if (status == noErr) {
+ SetFrontProcess(&process);
+ } else {
+ OSSTATUS_DLOG(WARNING, status) << "Unable to get process for pid " << pid;
+ }
+}
+
+bool AmIForeground() {
+ ProcessSerialNumber foreground_psn = { 0 };
+ OSErr err = GetFrontProcess(&foreground_psn);
+ if (err != noErr) {
+ OSSTATUS_DLOG(WARNING, err) << "GetFrontProcess";
+ return false;
+ }
+
+ ProcessSerialNumber my_psn = { 0, kCurrentProcess };
+
+ Boolean result = FALSE;
+ err = SameProcess(&foreground_psn, &my_psn, &result);
+ if (err != noErr) {
+ OSSTATUS_DLOG(WARNING, err) << "SameProcess";
+ return false;
+ }
+
+ return result;
+}
+
+bool SetFileBackupExclusion(const FilePath& file_path) {
+ NSString* file_path_ns =
+ [NSString stringWithUTF8String:file_path.value().c_str()];
+ NSURL* file_url = [NSURL fileURLWithPath:file_path_ns];
+
+ // When excludeByPath is true the application must be running with root
+ // privileges (admin for 10.6 and earlier) but the URL does not have to
+ // already exist. When excludeByPath is false the URL must already exist but
+ // can be used in non-root (or admin as above) mode. We use false so that
+ // non-root (or admin) users don't get their TimeMachine drive filled up with
+ // unnecessary backups.
+ OSStatus os_err =
+ CSBackupSetItemExcluded(base::mac::NSToCFCast(file_url), TRUE, FALSE);
+ if (os_err != noErr) {
+ OSSTATUS_DLOG(WARNING, os_err)
+ << "Failed to set backup exclusion for file '"
+ << file_path.value().c_str() << "'";
+ }
+ return os_err == noErr;
+}
+
+void SetProcessName(CFStringRef process_name) {
+ if (!process_name || CFStringGetLength(process_name) == 0) {
+ NOTREACHED() << "SetProcessName given bad name.";
+ return;
+ }
+
+ if (![NSThread isMainThread]) {
+ NOTREACHED() << "Should only set process name from main thread.";
+ return;
+ }
+
+ // Warning: here be dragons! This is SPI reverse-engineered from WebKit's
+ // plugin host, and could break at any time (although realistically it's only
+ // likely to break in a new major release).
+ // When 10.7 is available, check that this still works, and update this
+ // comment for 10.8.
+
+ // Private CFType used in these LaunchServices calls.
+ typedef CFTypeRef PrivateLSASN;
+ typedef PrivateLSASN (*LSGetCurrentApplicationASNType)();
+ typedef OSStatus (*LSSetApplicationInformationItemType)(int, PrivateLSASN,
+ CFStringRef,
+ CFStringRef,
+ CFDictionaryRef*);
+
+ static LSGetCurrentApplicationASNType ls_get_current_application_asn_func =
+ NULL;
+ static LSSetApplicationInformationItemType
+ ls_set_application_information_item_func = NULL;
+ static CFStringRef ls_display_name_key = NULL;
+
+ static bool did_symbol_lookup = false;
+ if (!did_symbol_lookup) {
+ did_symbol_lookup = true;
+ CFBundleRef launch_services_bundle =
+ CFBundleGetBundleWithIdentifier(CFSTR("com.apple.LaunchServices"));
+ if (!launch_services_bundle) {
+ DLOG(ERROR) << "Failed to look up LaunchServices bundle";
+ return;
+ }
+
+ ls_get_current_application_asn_func =
+ reinterpret_cast<LSGetCurrentApplicationASNType>(
+ CFBundleGetFunctionPointerForName(
+ launch_services_bundle, CFSTR("_LSGetCurrentApplicationASN")));
+ if (!ls_get_current_application_asn_func)
+ DLOG(ERROR) << "Could not find _LSGetCurrentApplicationASN";
+
+ ls_set_application_information_item_func =
+ reinterpret_cast<LSSetApplicationInformationItemType>(
+ CFBundleGetFunctionPointerForName(
+ launch_services_bundle,
+ CFSTR("_LSSetApplicationInformationItem")));
+ if (!ls_set_application_information_item_func)
+ DLOG(ERROR) << "Could not find _LSSetApplicationInformationItem";
+
+ CFStringRef* key_pointer = reinterpret_cast<CFStringRef*>(
+ CFBundleGetDataPointerForName(launch_services_bundle,
+ CFSTR("_kLSDisplayNameKey")));
+ ls_display_name_key = key_pointer ? *key_pointer : NULL;
+ if (!ls_display_name_key)
+ DLOG(ERROR) << "Could not find _kLSDisplayNameKey";
+
+ // Internally, this call relies on the Mach ports that are started up by the
+ // Carbon Process Manager. In debug builds this usually happens due to how
+ // the logging layers are started up; but in release, it isn't started in as
+ // much of a defined order. So if the symbols had to be loaded, go ahead
+ // and force a call to make sure the manager has been initialized and hence
+ // the ports are opened.
+ ProcessSerialNumber psn;
+ GetCurrentProcess(&psn);
+ }
+ if (!ls_get_current_application_asn_func ||
+ !ls_set_application_information_item_func ||
+ !ls_display_name_key) {
+ return;
+ }
+
+ PrivateLSASN asn = ls_get_current_application_asn_func();
+ // Constant used by WebKit; what exactly it means is unknown.
+ const int magic_session_constant = -2;
+ OSErr err =
+ ls_set_application_information_item_func(magic_session_constant, asn,
+ ls_display_name_key,
+ process_name,
+ NULL /* optional out param */);
+ OSSTATUS_DLOG_IF(ERROR, err != noErr, err)
+ << "Call to set process name failed";
+}
+
+// Converts a NSImage to a CGImageRef. Normally, the system frameworks can do
+// this fine, especially on 10.6. On 10.5, however, CGImage cannot handle
+// converting a PDF-backed NSImage into a CGImageRef. This function will
+// rasterize the PDF into a bitmap CGImage. The caller is responsible for
+// releasing the return value.
+CGImageRef CopyNSImageToCGImage(NSImage* image) {
+ // This is based loosely on http://www.cocoadev.com/index.pl?CGImageRef .
+ NSSize size = [image size];
+ ScopedCFTypeRef<CGContextRef> context(
+ CGBitmapContextCreate(NULL, // Allow CG to allocate memory.
+ size.width,
+ size.height,
+ 8, // bitsPerComponent
+ 0, // bytesPerRow - CG will calculate by default.
+ [[NSColorSpace genericRGBColorSpace] CGColorSpace],
+ kCGBitmapByteOrder32Host |
+ kCGImageAlphaPremultipliedFirst));
+ if (!context.get())
+ return NULL;
+
+ [NSGraphicsContext saveGraphicsState];
+ [NSGraphicsContext setCurrentContext:
+ [NSGraphicsContext graphicsContextWithGraphicsPort:context.get()
+ flipped:NO]];
+ [image drawInRect:NSMakeRect(0,0, size.width, size.height)
+ fromRect:NSZeroRect
+ operation:NSCompositeCopy
+ fraction:1.0];
+ [NSGraphicsContext restoreGraphicsState];
+
+ return CGBitmapContextCreateImage(context);
+}
+
+bool CheckLoginItemStatus(bool* is_hidden) {
+ ScopedCFTypeRef<LSSharedFileListItemRef> item(GetLoginItemForApp());
+ if (!item.get())
+ return false;
+
+ if (is_hidden)
+ *is_hidden = IsHiddenLoginItem(item);
+
+ return true;
+}
+
+void AddToLoginItems(bool hide_on_startup) {
+ ScopedCFTypeRef<LSSharedFileListItemRef> item(GetLoginItemForApp());
+ if (item.get() && (IsHiddenLoginItem(item) == hide_on_startup)) {
+ return; // Already is a login item with required hide flag.
+ }
+
+ ScopedCFTypeRef<LSSharedFileListRef> login_items(LSSharedFileListCreate(
+ NULL, kLSSharedFileListSessionLoginItems, NULL));
+
+ if (!login_items.get()) {
+ DLOG(ERROR) << "Couldn't get a Login Items list.";
+ return;
+ }
+
+ // Remove the old item, it has wrong hide flag, we'll create a new one.
+ if (item.get()) {
+ LSSharedFileListItemRemove(login_items, item);
+ }
+
+ NSURL* url = [NSURL fileURLWithPath:[base::mac::MainBundle() bundlePath]];
+
+ BOOL hide = hide_on_startup ? YES : NO;
+ NSDictionary* properties =
+ [NSDictionary
+ dictionaryWithObject:[NSNumber numberWithBool:hide]
+ forKey:(NSString*)kLSSharedFileListLoginItemHidden];
+
+ ScopedCFTypeRef<LSSharedFileListItemRef> new_item;
+ new_item.reset(LSSharedFileListInsertItemURL(
+ login_items, kLSSharedFileListItemLast, NULL, NULL,
+ reinterpret_cast<CFURLRef>(url),
+ reinterpret_cast<CFDictionaryRef>(properties), NULL));
+
+ if (!new_item.get()) {
+ DLOG(ERROR) << "Couldn't insert current app into Login Items list.";
+ }
+}
+
+void RemoveFromLoginItems() {
+ ScopedCFTypeRef<LSSharedFileListItemRef> item(GetLoginItemForApp());
+ if (!item.get())
+ return;
+
+ ScopedCFTypeRef<LSSharedFileListRef> login_items(LSSharedFileListCreate(
+ NULL, kLSSharedFileListSessionLoginItems, NULL));
+
+ if (!login_items.get()) {
+ DLOG(ERROR) << "Couldn't get a Login Items list.";
+ return;
+ }
+
+ LSSharedFileListItemRemove(login_items, item);
+}
+
+bool WasLaunchedAsLoginOrResumeItem() {
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+
+ scoped_nsobject<NSDictionary> process_info(
+ CFToNSCast(ProcessInformationCopyDictionary(&psn,
+ kProcessDictionaryIncludeAllInformationMask)));
+
+ long long temp = [[process_info objectForKey:@"ParentPSN"] longLongValue];
+ ProcessSerialNumber parent_psn =
+ { (temp >> 32) & 0x00000000FFFFFFFFLL, temp & 0x00000000FFFFFFFFLL };
+
+ scoped_nsobject<NSDictionary> parent_info(
+ CFToNSCast(ProcessInformationCopyDictionary(&parent_psn,
+ kProcessDictionaryIncludeAllInformationMask)));
+
+ // Check that creator process code is that of loginwindow.
+ BOOL result =
+ [[parent_info objectForKey:@"FileCreator"] isEqualToString:@"lgnw"];
+
+ return result == YES;
+}
+
+bool WasLaunchedAsHiddenLoginItem() {
+ if (!WasLaunchedAsLoginOrResumeItem())
+ return false;
+
+ ScopedCFTypeRef<LSSharedFileListItemRef> item(GetLoginItemForApp());
+ if (!item.get()) {
+ // Lion can launch items for the resume feature. So log an error only for
+ // Snow Leopard or earlier.
+ if (IsOSSnowLeopard())
+ DLOG(ERROR) <<
+ "Process launched at Login but can't access Login Item List.";
+
+ return false;
+ }
+ return IsHiddenLoginItem(item);
+}
+
+namespace {
+
+// Returns the running system's Darwin major version. Don't call this, it's
+// an implementation detail and its result is meant to be cached by
+// MacOSXMinorVersion.
+int DarwinMajorVersionInternal() {
+ // base::OperatingSystemVersionNumbers calls Gestalt, which is a
+ // higher-level operation than is needed. It might perform unnecessary
+ // operations. On 10.6, it was observed to be able to spawn threads (see
+ // http://crbug.com/53200). It might also read files or perform other
+ // blocking operations. Actually, nobody really knows for sure just what
+ // Gestalt might do, or what it might be taught to do in the future.
+ //
+ // uname, on the other hand, is implemented as a simple series of sysctl
+ // system calls to obtain the relevant data from the kernel. The data is
+ // compiled right into the kernel, so no threads or blocking or other
+ // funny business is necessary.
+
+ struct utsname uname_info;
+ if (uname(&uname_info) != 0) {
+ DPLOG(ERROR) << "uname";
+ return 0;
+ }
+
+ if (strcmp(uname_info.sysname, "Darwin") != 0) {
+ DLOG(ERROR) << "unexpected uname sysname " << uname_info.sysname;
+ return 0;
+ }
+
+ int darwin_major_version = 0;
+ char* dot = strchr(uname_info.release, '.');
+ if (dot) {
+ if (!base::StringToInt(base::StringPiece(uname_info.release,
+ dot - uname_info.release),
+ &darwin_major_version)) {
+ dot = NULL;
+ }
+ }
+
+ if (!dot) {
+ DLOG(ERROR) << "could not parse uname release " << uname_info.release;
+ return 0;
+ }
+
+ return darwin_major_version;
+}
+
+// Returns the running system's Mac OS X minor version. This is the |y| value
+// in 10.y or 10.y.z. Don't call this, it's an implementation detail and the
+// result is meant to be cached by MacOSXMinorVersion.
+int MacOSXMinorVersionInternal() {
+ int darwin_major_version = DarwinMajorVersionInternal();
+
+ // The Darwin major version is always 4 greater than the Mac OS X minor
+ // version for Darwin versions beginning with 6, corresponding to Mac OS X
+ // 10.2. Since this correspondence may change in the future, warn when
+ // encountering a version higher than anything seen before. Older Darwin
+ // versions, or versions that can't be determined, result in
+ // immediate death.
+ CHECK(darwin_major_version >= 6);
+ int mac_os_x_minor_version = darwin_major_version - 4;
+ DLOG_IF(WARNING, darwin_major_version > 12) << "Assuming Darwin "
+ << base::IntToString(darwin_major_version) << " is Mac OS X 10."
+ << base::IntToString(mac_os_x_minor_version);
+
+ return mac_os_x_minor_version;
+}
+
+// Returns the running system's Mac OS X minor version. This is the |y| value
+// in 10.y or 10.y.z.
+int MacOSXMinorVersion() {
+ static int mac_os_x_minor_version = MacOSXMinorVersionInternal();
+ return mac_os_x_minor_version;
+}
+
+enum {
+ SNOW_LEOPARD_MINOR_VERSION = 6,
+ LION_MINOR_VERSION = 7,
+ MOUNTAIN_LION_MINOR_VERSION = 8,
+};
+
+} // namespace
+
+#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GE_10_7)
+bool IsOSSnowLeopard() {
+ return MacOSXMinorVersion() == SNOW_LEOPARD_MINOR_VERSION;
+}
+#endif
+
+#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GT_10_7)
+bool IsOSLion() {
+ return MacOSXMinorVersion() == LION_MINOR_VERSION;
+}
+#endif
+
+#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GT_10_7)
+bool IsOSLionOrEarlier() {
+ return MacOSXMinorVersion() <= LION_MINOR_VERSION;
+}
+#endif
+
+#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GE_10_7)
+bool IsOSLionOrLater() {
+ return MacOSXMinorVersion() >= LION_MINOR_VERSION;
+}
+#endif
+
+#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GT_10_8)
+bool IsOSMountainLion() {
+ return MacOSXMinorVersion() == MOUNTAIN_LION_MINOR_VERSION;
+}
+#endif
+
+#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GE_10_8)
+bool IsOSMountainLionOrLater() {
+ return MacOSXMinorVersion() >= MOUNTAIN_LION_MINOR_VERSION;
+}
+#endif
+
+#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GT_10_8)
+bool IsOSLaterThanMountainLion_DontCallThis() {
+ return MacOSXMinorVersion() > MOUNTAIN_LION_MINOR_VERSION;
+}
+#endif
+
+namespace {
+
+// ScopedGenericObj functor for IOObjectRelease().
+class ScopedReleaseIOObject {
+ public:
+ void operator()(io_object_t x) const {
+ IOObjectRelease(x);
+ }
+};
+
+} // namespace
+
+std::string GetModelIdentifier() {
+ ScopedGenericObj<io_service_t, ScopedReleaseIOObject>
+ platform_expert(IOServiceGetMatchingService(
+ kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice")));
+ if (!platform_expert)
+ return "";
+ ScopedCFTypeRef<CFDataRef> model_data(
+ static_cast<CFDataRef>(IORegistryEntryCreateCFProperty(
+ platform_expert,
+ CFSTR("model"),
+ kCFAllocatorDefault,
+ 0)));
+ if (!model_data)
+ return "";
+ return reinterpret_cast<const char*>(
+ CFDataGetBytePtr(model_data));
+}
+
+bool ParseModelIdentifier(const std::string& ident,
+ std::string* type,
+ int32* major,
+ int32* minor) {
+ size_t number_loc = ident.find_first_of("0123456789");
+ if (number_loc == std::string::npos)
+ return false;
+ size_t comma_loc = ident.find(',', number_loc);
+ if (comma_loc == std::string::npos)
+ return false;
+ int32 major_tmp, minor_tmp;
+ std::string::const_iterator begin = ident.begin();
+ if (!StringToInt(
+ StringPiece(begin + number_loc, begin + comma_loc), &major_tmp) ||
+ !StringToInt(
+ StringPiece(begin + comma_loc + 1, ident.end()), &minor_tmp))
+ return false;
+ *type = ident.substr(0, number_loc);
+ *major = major_tmp;
+ *minor = minor_tmp;
+ return true;
+}
+
+} // namespace mac
+} // namespace base
diff --git a/src/base/mac/mac_util_unittest.mm b/src/base/mac/mac_util_unittest.mm
new file mode 100644
index 0000000..d69e077
--- /dev/null
+++ b/src/base/mac/mac_util_unittest.mm
@@ -0,0 +1,212 @@
+// Copyright (c) 2012 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.
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/mac/mac_util.h"
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/memory/scoped_nsobject.h"
+#include "base/sys_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace base {
+namespace mac {
+
+namespace {
+
+typedef PlatformTest MacUtilTest;
+
+TEST_F(MacUtilTest, TestFSRef) {
+ FSRef ref;
+ std::string path("/System/Library");
+
+ ASSERT_TRUE(FSRefFromPath(path, &ref));
+ EXPECT_EQ(path, PathFromFSRef(ref));
+}
+
+TEST_F(MacUtilTest, GetUserDirectoryTest) {
+ // Try a few keys, make sure they come back with non-empty paths.
+ FilePath caches_dir;
+ EXPECT_TRUE(GetUserDirectory(NSCachesDirectory, &caches_dir));
+ EXPECT_FALSE(caches_dir.empty());
+
+ FilePath application_support_dir;
+ EXPECT_TRUE(GetUserDirectory(NSApplicationSupportDirectory,
+ &application_support_dir));
+ EXPECT_FALSE(application_support_dir.empty());
+
+ FilePath library_dir;
+ EXPECT_TRUE(GetUserDirectory(NSLibraryDirectory, &library_dir));
+ EXPECT_FALSE(library_dir.empty());
+}
+
+TEST_F(MacUtilTest, TestLibraryPath) {
+ FilePath library_dir = GetUserLibraryPath();
+ // Make sure the string isn't empty.
+ EXPECT_FALSE(library_dir.value().empty());
+}
+
+TEST_F(MacUtilTest, TestGetAppBundlePath) {
+ FilePath out;
+
+ // Make sure it doesn't crash.
+ out = GetAppBundlePath(FilePath());
+ EXPECT_TRUE(out.empty());
+
+ // Some more invalid inputs.
+ const char* invalid_inputs[] = {
+ "/", "/foo", "foo", "/foo/bar.", "foo/bar.", "/foo/bar./bazquux",
+ "foo/bar./bazquux", "foo/.app", "//foo",
+ };
+ for (size_t i = 0; i < arraysize(invalid_inputs); i++) {
+ out = GetAppBundlePath(FilePath(invalid_inputs[i]));
+ EXPECT_TRUE(out.empty()) << "loop: " << i;
+ }
+
+ // Some valid inputs; this and |expected_outputs| should be in sync.
+ struct {
+ const char *in;
+ const char *expected_out;
+ } valid_inputs[] = {
+ { "FooBar.app/", "FooBar.app" },
+ { "/FooBar.app", "/FooBar.app" },
+ { "/FooBar.app/", "/FooBar.app" },
+ { "//FooBar.app", "//FooBar.app" },
+ { "/Foo/Bar.app", "/Foo/Bar.app" },
+ { "/Foo/Bar.app/", "/Foo/Bar.app" },
+ { "/F/B.app", "/F/B.app" },
+ { "/F/B.app/", "/F/B.app" },
+ { "/Foo/Bar.app/baz", "/Foo/Bar.app" },
+ { "/Foo/Bar.app/baz/", "/Foo/Bar.app" },
+ { "/Foo/Bar.app/baz/quux.app/quuux", "/Foo/Bar.app" },
+ { "/Applications/Google Foo.app/bar/Foo Helper.app/quux/Foo Helper",
+ "/Applications/Google Foo.app" },
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(valid_inputs); i++) {
+ out = GetAppBundlePath(FilePath(valid_inputs[i].in));
+ EXPECT_FALSE(out.empty()) << "loop: " << i;
+ EXPECT_STREQ(valid_inputs[i].expected_out,
+ out.value().c_str()) << "loop: " << i;
+ }
+}
+
+TEST_F(MacUtilTest, TestExcludeFileFromBackups) {
+ // The file must already exist in order to set its exclusion property.
+ ScopedTempDir temp_dir_;
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ FilePath dummy_file_path = temp_dir_.path().Append("DummyFile");
+ const char dummy_data[] = "All your base are belong to us!";
+ // Dump something real into the file.
+ ASSERT_EQ(static_cast<int>(arraysize(dummy_data)),
+ file_util::WriteFile(dummy_file_path, dummy_data, arraysize(dummy_data)));
+ NSString* fileURLString =
+ [NSString stringWithUTF8String:dummy_file_path.value().c_str()];
+ NSURL* fileURL = [NSURL URLWithString:fileURLString];
+ // Initial state should be non-excluded.
+ EXPECT_FALSE(CSBackupIsItemExcluded(base::mac::NSToCFCast(fileURL), NULL));
+ // Exclude the file.
+ EXPECT_TRUE(SetFileBackupExclusion(dummy_file_path));
+ // SetFileBackupExclusion never excludes by path.
+ Boolean excluded_by_path = FALSE;
+ Boolean excluded =
+ CSBackupIsItemExcluded(base::mac::NSToCFCast(fileURL), &excluded_by_path);
+ EXPECT_TRUE(excluded);
+ EXPECT_FALSE(excluded_by_path);
+}
+
+TEST_F(MacUtilTest, CopyNSImageToCGImage) {
+ scoped_nsobject<NSImage> nsImage(
+ [[NSImage alloc] initWithSize:NSMakeSize(20, 20)]);
+ [nsImage lockFocus];
+ [[NSColor redColor] set];
+ NSRect rect = NSZeroRect;
+ rect.size = [nsImage size];
+ NSRectFill(rect);
+ [nsImage unlockFocus];
+
+ ScopedCFTypeRef<CGImageRef> cgImage(CopyNSImageToCGImage(nsImage.get()));
+ EXPECT_TRUE(cgImage.get());
+}
+
+TEST_F(MacUtilTest, NSObjectRetainRelease) {
+ scoped_nsobject<NSArray> array([[NSArray alloc] initWithObjects:@"foo", nil]);
+ EXPECT_EQ(1U, [array retainCount]);
+
+ NSObjectRetain(array);
+ EXPECT_EQ(2U, [array retainCount]);
+
+ NSObjectRelease(array);
+ EXPECT_EQ(1U, [array retainCount]);
+}
+
+TEST_F(MacUtilTest, IsOSEllipsis) {
+ int32 major, minor, bugfix;
+ base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix);
+
+ if (major == 10) {
+ if (minor == 6) {
+ EXPECT_TRUE(IsOSSnowLeopard());
+ EXPECT_FALSE(IsOSLion());
+ EXPECT_TRUE(IsOSLionOrEarlier());
+ EXPECT_FALSE(IsOSLionOrLater());
+ EXPECT_FALSE(IsOSMountainLion());
+ EXPECT_FALSE(IsOSMountainLionOrLater());
+ EXPECT_FALSE(IsOSLaterThanMountainLion_DontCallThis());
+ } else if (minor == 7) {
+ EXPECT_FALSE(IsOSSnowLeopard());
+ EXPECT_TRUE(IsOSLion());
+ EXPECT_TRUE(IsOSLionOrEarlier());
+ EXPECT_TRUE(IsOSLionOrLater());
+ EXPECT_FALSE(IsOSMountainLion());
+ EXPECT_FALSE(IsOSMountainLionOrLater());
+ EXPECT_FALSE(IsOSLaterThanMountainLion_DontCallThis());
+ } else if (minor == 8) {
+ EXPECT_FALSE(IsOSSnowLeopard());
+ EXPECT_FALSE(IsOSLion());
+ EXPECT_FALSE(IsOSLionOrEarlier());
+ EXPECT_TRUE(IsOSLionOrLater());
+ EXPECT_TRUE(IsOSMountainLion());
+ EXPECT_TRUE(IsOSMountainLionOrLater());
+ EXPECT_FALSE(IsOSLaterThanMountainLion_DontCallThis());
+ } else {
+ // Not five, six, seven, or eight. Ah, ah, ah.
+ EXPECT_TRUE(false);
+ }
+ } else {
+ // Not ten. What you gonna do?
+ EXPECT_FALSE(true);
+ }
+}
+
+TEST_F(MacUtilTest, ParseModelIdentifier) {
+ std::string model;
+ int32 major = 1, minor = 2;
+
+ EXPECT_FALSE(ParseModelIdentifier("", &model, &major, &minor));
+ EXPECT_EQ(0U, model.length());
+ EXPECT_EQ(1, major);
+ EXPECT_EQ(2, minor);
+ EXPECT_FALSE(ParseModelIdentifier("FooBar", &model, &major, &minor));
+
+ EXPECT_TRUE(ParseModelIdentifier("MacPro4,1", &model, &major, &minor));
+ EXPECT_EQ(model, "MacPro");
+ EXPECT_EQ(4, major);
+ EXPECT_EQ(1, minor);
+
+ EXPECT_TRUE(ParseModelIdentifier("MacBookPro6,2", &model, &major, &minor));
+ EXPECT_EQ(model, "MacBookPro");
+ EXPECT_EQ(6, major);
+ EXPECT_EQ(2, minor);
+}
+
+} // namespace
+
+} // namespace mac
+} // namespace base
diff --git a/src/base/mac/objc_property_releaser.h b/src/base/mac/objc_property_releaser.h
new file mode 100644
index 0000000..973d793
--- /dev/null
+++ b/src/base/mac/objc_property_releaser.h
@@ -0,0 +1,127 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MAC_OBJC_PROPERTY_RELEASER_H_
+#define BASE_MAC_OBJC_PROPERTY_RELEASER_H_
+
+#import <Foundation/Foundation.h>
+
+#include "base/base_export.h"
+
+namespace base {
+namespace mac {
+
+// ObjCPropertyReleaser is a C++ class that can automatically release
+// synthesized Objective-C properties marked "retain" or "copy". The expected
+// use is to place an ObjCPropertyReleaser object within an Objective-C class
+// definition. When built with the -fobjc-call-cxx-cdtors compiler option,
+// the ObjCPropertyReleaser's destructor will be called when the Objective-C
+// object that owns it is deallocated, and it will send a -release message to
+// the instance variables backing the appropriate properties. If
+// -fobjc-call-cxx-cdtors is not in use, ObjCPropertyReleaser's
+// ReleaseProperties method can be called from -dealloc to achieve the same
+// effect.
+//
+// Example usage:
+//
+// @interface AllaysIBF : NSObject {
+// @private
+// NSString* string_;
+// NSMutableDictionary* dictionary_;
+// NSString* notAProperty_;
+// IBFDelegate* delegate_; // weak
+//
+// // It's recommended to put the class name into the property releaser's
+// // instance variable name to gracefully handle subclassing, where
+// // multiple classes in a hierarchy might want their own property
+// // releasers.
+// base::mac::ObjCPropertyReleaser propertyReleaser_AllaysIBF_;
+// }
+//
+// @property(retain, nonatomic) NSString* string;
+// @property(copy, nonatomic) NSMutableDictionary* dictionary;
+// @property(assign, nonatomic) IBFDelegate* delegate;
+// @property(retain, nonatomic) NSString* autoProp;
+//
+// @end // @interface AllaysIBF
+//
+// @implementation AllaysIBF
+//
+// @synthesize string = string_;
+// @synthesize dictionary = dictionary_;
+// @synthesize delegate = delegate_;
+// @synthesize autoProp;
+//
+// - (id)init {
+// if ((self = [super init])) {
+// // Initialize with [AllaysIBF class]. Never use [self class] because
+// // in the case of subclassing, it will return the most specific class
+// // for |self|, which may not be the same as [AllaysIBF class]. This
+// // would cause AllaysIBF's -.cxx_destruct or -dealloc to release
+// // instance variables that only exist in subclasses, likely causing
+// // mass disaster.
+// propertyReleaser_AllaysIBF_.Init(self, [AllaysIBF class]);
+// }
+// return self;
+// }
+//
+// @end // @implementation AllaysIBF
+//
+// When an instance of AllaysIBF is deallocated, the ObjCPropertyReleaser will
+// send a -release message to string_, dictionary_, and the compiler-created
+// autoProp instance variables. No -release will be sent to delegate_ as it
+// is marked "assign" and not "retain" or "copy". No -release will be sent to
+// notAProperty_ because it doesn't correspond to any declared @property.
+//
+// Another way of doing this would be to provide a base class that others can
+// inherit from, and to have the base class' -dealloc walk the property lists
+// of all subclasses in an object to send the -release messages. Since this
+// involves a base reaching into its subclasses, it's deemed scary, so don't
+// do it. ObjCPropertyReleaser's design ensures that the property releaser
+// will only operate on instance variables in the immediate object in which
+// the property releaser is placed.
+
+class BASE_EXPORT ObjCPropertyReleaser {
+ public:
+ // ObjCPropertyReleaser can only be owned by an Objective-C object, so its
+ // memory is always guaranteed to be 0-initialized. Not defining the default
+ // constructor can prevent an otherwise no-op -.cxx_construct method from
+ // showing up in Objective-C classes that contain a ObjCPropertyReleaser.
+
+ // Upon destruction (expected to occur from an Objective-C object's
+ // -.cxx_destruct method), release all properties.
+ ~ObjCPropertyReleaser() {
+ ReleaseProperties();
+ }
+
+ // Initialize this object so that it's armed to release the properties of
+ // object |object|, which must be of type |classy|. The class argument must
+ // be supplied separately and cannot be gleaned from the object's own type
+ // because an object will allays identify itself as the most-specific type
+ // that describes it, but the ObjCPropertyReleaser needs to know which class
+ // type in the class hierarchy it's responsible for releasing properties
+ // for. For the same reason, Init must be called with a |classy| argument
+ // initialized using a +class (class) method such as [MyClass class], and
+ // never a -class (instance) method such as [self class].
+ //
+ // -.cxx_construct can only call the default constructor, but
+ // ObjCPropertyReleaser needs to know about the Objective-C object that owns
+ // it, so this can't be handled in a constructor, it needs to be a distinct
+ // Init method.
+ void Init(id object, Class classy);
+
+ // Release all of the properties in object_ defined in class_ as either
+ // "retain" or "copy" and with an identifiable backing instance variable.
+ // Properties must be synthesized to have identifiable instance variables.
+ void ReleaseProperties();
+
+ private:
+ id object_;
+ Class class_;
+};
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_OBJC_PROPERTY_RELEASER_H_
diff --git a/src/base/mac/objc_property_releaser.mm b/src/base/mac/objc_property_releaser.mm
new file mode 100644
index 0000000..f7ee88f
--- /dev/null
+++ b/src/base/mac/objc_property_releaser.mm
@@ -0,0 +1,131 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "base/mac/objc_property_releaser.h"
+
+#import <objc/runtime.h>
+#include <stdlib.h>
+
+#include <string>
+
+#include "base/logging.h"
+
+namespace base {
+namespace mac {
+
+namespace {
+
+// Returns the name of the instance variable backing the property, if known,
+// if the property is marked "retain" or "copy". If the instance variable name
+// is not known (perhaps because it was not automatically associated with the
+// property by @synthesize) or if the property is not "retain" or "copy",
+// returns an empty string.
+std::string ReleasableInstanceName(objc_property_t property) {
+ // TODO(mark): Starting in newer system releases, the Objective-C runtime
+ // provides a function to break the property attribute string into
+ // individual attributes (property_copyAttributeList), as well as a function
+ // to look up the value of a specific attribute
+ // (property_copyAttributeValue). When the SDK defining that interface is
+ // final, this function should be adapted to walk the attribute list as
+ // returned by property_copyAttributeList when that function is available in
+ // preference to scanning through the attribute list manually.
+
+ // The format of the string returned by property_getAttributes is documented
+ // at
+ // http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW6
+ const char* property_attributes = property_getAttributes(property);
+
+ std::string instance_name;
+ bool releasable = false;
+ while (*property_attributes) {
+ char name = *property_attributes;
+
+ const char* value = ++property_attributes;
+ while (*property_attributes && *property_attributes != ',') {
+ ++property_attributes;
+ }
+
+ switch (name) {
+ // It might seem intelligent to check the type ('T') attribute to verify
+ // that it identifies an NSObject-derived type (the attribute value
+ // begins with '@'.) This is a bad idea beacuse it fails to identify
+ // CFTypeRef-based properties declared as __attribute__((NSObject)),
+ // which just show up as pointers to their underlying CFType structs.
+ //
+ // Quoting
+ // http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html#//apple_ref/doc/uid/TP30001163-CH17-SW27
+ //
+ // > In Mac OS X v10.6 and later, you can use the __attribute__ keyword
+ // > to specify that a Core Foundation property should be treated like
+ // > an Objective-C object for memory management:
+ // > @property(retain) __attribute__((NSObject)) CFDictionaryRef
+ // > myDictionary;
+ case 'C': // copy
+ case '&': // retain
+ releasable = true;
+ break;
+ case 'V': // instance variable name
+ // 'V' is specified as the last attribute to occur in the
+ // documentation, but empirically, it's not always the last. In
+ // GC-supported or GC-required code, the 'P' (GC-eligible) attribute
+ // occurs after 'V'.
+ instance_name.assign(value, property_attributes - value);
+ break;
+ }
+
+ if (*property_attributes) {
+ ++property_attributes;
+ }
+ }
+
+ if (releasable) {
+ return instance_name;
+ }
+
+ return std::string();
+}
+
+} // namespace
+
+void ObjCPropertyReleaser::Init(id object, Class classy) {
+ DCHECK(!object_);
+ DCHECK(!class_);
+ CHECK([object isKindOfClass:classy]);
+
+ object_ = object;
+ class_ = classy;
+}
+
+void ObjCPropertyReleaser::ReleaseProperties() {
+ DCHECK(object_);
+ DCHECK(class_);
+
+ unsigned int property_count = 0;
+ objc_property_t* properties = class_copyPropertyList(class_, &property_count);
+
+ for (unsigned int property_index = 0;
+ property_index < property_count;
+ ++property_index) {
+ objc_property_t property = properties[property_index];
+ std::string instance_name = ReleasableInstanceName(property);
+ if (!instance_name.empty()) {
+ id instance_value = nil;
+ Ivar instance_variable =
+ object_getInstanceVariable(object_, instance_name.c_str(),
+ (void**)&instance_value);
+ DCHECK(instance_variable);
+ [instance_value release];
+ }
+ }
+
+ free(properties);
+
+ // Clear object_ and class_ in case this ObjCPropertyReleaser will live on.
+ // It's only expected to release the properties it supervises once per Init.
+ object_ = nil;
+ class_ = nil;
+}
+
+} // namespace mac
+} // namespace base
diff --git a/src/base/mac/objc_property_releaser_unittest.mm b/src/base/mac/objc_property_releaser_unittest.mm
new file mode 100644
index 0000000..50f81a8
--- /dev/null
+++ b/src/base/mac/objc_property_releaser_unittest.mm
@@ -0,0 +1,350 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Foundation/Foundation.h>
+
+#import "base/mac/objc_property_releaser.h"
+#import "base/mac/scoped_nsautorelease_pool.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// "When I'm alone, I count myself."
+// --Count von Count, http://www.youtube.com/watch?v=FKzszqa9WA4
+
+namespace {
+
+// The number of CountVonCounts outstanding.
+int ah_ah_ah;
+
+// NumberHolder exists to exercise the property attribute string parser by
+// providing a named struct and an anonymous union.
+struct NumberHolder {
+ union {
+ long long sixty_four;
+ int thirty_two;
+ short sixteen;
+ char eight;
+ } what;
+ enum {
+ SIXTY_FOUR,
+ THIRTY_TWO,
+ SIXTEEN,
+ EIGHT
+ } how;
+};
+
+} // namespace
+
+@interface CountVonCount : NSObject<NSCopying>
+
++ (CountVonCount*)countVonCount;
+
+@end // @interface CountVonCount
+
+@implementation CountVonCount
+
++ (CountVonCount*)countVonCount {
+ return [[[CountVonCount alloc] init] autorelease];
+}
+
+- (id)init {
+ ++ah_ah_ah;
+ return [super init];
+}
+
+- (void)dealloc {
+ --ah_ah_ah;
+ [super dealloc];
+}
+
+- (id)copyWithZone:(NSZone*)zone {
+ return [[CountVonCount allocWithZone:zone] init];
+}
+
+@end // @implementation CountVonCount
+
+@interface ObjCPropertyTestBase : NSObject {
+ @private
+ CountVonCount* baseCvcRetain_;
+ CountVonCount* baseCvcCopy_;
+ CountVonCount* baseCvcAssign_;
+ CountVonCount* baseCvcNotProperty_;
+ CountVonCount* baseCvcNil_;
+ CountVonCount* baseCvcCustom_;
+ int baseInt_;
+ double baseDouble_;
+ void* basePointer_;
+ NumberHolder baseStruct_;
+
+ base::mac::ObjCPropertyReleaser propertyReleaser_ObjCPropertyTestBase_;
+}
+
+@property(retain, nonatomic) CountVonCount* baseCvcRetain;
+@property(copy, nonatomic) CountVonCount* baseCvcCopy;
+@property(assign, nonatomic) CountVonCount* baseCvcAssign;
+@property(retain, nonatomic) CountVonCount* baseCvcNil;
+@property(retain, nonatomic, getter=baseCustom, setter=setBaseCustom:)
+ CountVonCount* baseCvcCustom;
+@property(retain, nonatomic) CountVonCount* baseCvcDynamic;
+@property(assign, nonatomic) int baseInt;
+@property(assign, nonatomic) double baseDouble;
+@property(assign, nonatomic) void* basePointer;
+@property(assign, nonatomic) NumberHolder baseStruct;
+
+- (void)setBaseCvcNotProperty:(CountVonCount*)cvc;
+
+@end // @interface ObjCPropertyTestBase
+
+@implementation ObjCPropertyTestBase
+
+@synthesize baseCvcRetain = baseCvcRetain_;
+@synthesize baseCvcCopy = baseCvcCopy_;
+@synthesize baseCvcAssign = baseCvcAssign_;
+@synthesize baseCvcNil = baseCvcNil_;
+@synthesize baseCvcCustom = baseCvcCustom_;
+@dynamic baseCvcDynamic;
+@synthesize baseInt = baseInt_;
+@synthesize baseDouble = baseDouble_;
+@synthesize basePointer = basePointer_;
+@synthesize baseStruct = baseStruct_;
+
+- (id)init {
+ if ((self = [super init])) {
+ propertyReleaser_ObjCPropertyTestBase_.Init(
+ self, [ObjCPropertyTestBase class]);
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [baseCvcNotProperty_ release];
+ [super dealloc];
+}
+
+- (void)setBaseCvcNotProperty:(CountVonCount*)cvc {
+ if (cvc != baseCvcNotProperty_) {
+ [baseCvcNotProperty_ release];
+ baseCvcNotProperty_ = [cvc retain];
+ }
+}
+
+@end // @implementation ObjCPropertyTestBase
+
+@protocol ObjCPropertyTestProtocol
+
+@property(retain, nonatomic) CountVonCount* protoCvcRetain;
+@property(copy, nonatomic) CountVonCount* protoCvcCopy;
+@property(assign, nonatomic) CountVonCount* protoCvcAssign;
+@property(retain, nonatomic) CountVonCount* protoCvcNil;
+@property(retain, nonatomic, getter=protoCustom, setter=setProtoCustom:)
+ CountVonCount* protoCvcCustom;
+@property(retain, nonatomic) CountVonCount* protoCvcDynamic;
+@property(assign, nonatomic) int protoInt;
+@property(assign, nonatomic) double protoDouble;
+@property(assign, nonatomic) void* protoPointer;
+@property(assign, nonatomic) NumberHolder protoStruct;
+
+@end // @protocol ObjCPropertyTestProtocol
+
+@interface ObjCPropertyTestDerived
+ : ObjCPropertyTestBase<ObjCPropertyTestProtocol> {
+ @private
+ CountVonCount* derivedCvcRetain_;
+ CountVonCount* derivedCvcCopy_;
+ CountVonCount* derivedCvcAssign_;
+ CountVonCount* derivedCvcNotProperty_;
+ CountVonCount* derivedCvcNil_;
+ CountVonCount* derivedCvcCustom_;
+ int derivedInt_;
+ double derivedDouble_;
+ void* derivedPointer_;
+ NumberHolder derivedStruct_;
+
+ CountVonCount* protoCvcRetain_;
+ CountVonCount* protoCvcCopy_;
+ CountVonCount* protoCvcAssign_;
+ CountVonCount* protoCvcNil_;
+ CountVonCount* protoCvcCustom_;
+ int protoInt_;
+ double protoDouble_;
+ void* protoPointer_;
+ NumberHolder protoStruct_;
+
+ base::mac::ObjCPropertyReleaser propertyReleaser_ObjCPropertyTestDerived_;
+}
+
+@property(retain, nonatomic) CountVonCount* derivedCvcRetain;
+@property(copy, nonatomic) CountVonCount* derivedCvcCopy;
+@property(assign, nonatomic) CountVonCount* derivedCvcAssign;
+@property(retain, nonatomic) CountVonCount* derivedCvcNil;
+@property(retain, nonatomic, getter=derivedCustom, setter=setDerivedCustom:)
+ CountVonCount* derivedCvcCustom;
+@property(retain, nonatomic) CountVonCount* derivedCvcDynamic;
+@property(assign, nonatomic) int derivedInt;
+@property(assign, nonatomic) double derivedDouble;
+@property(assign, nonatomic) void* derivedPointer;
+@property(assign, nonatomic) NumberHolder derivedStruct;
+
+- (void)setDerivedCvcNotProperty:(CountVonCount*)cvc;
+
+@end // @interface ObjCPropertyTestDerived
+
+@implementation ObjCPropertyTestDerived
+
+@synthesize derivedCvcRetain = derivedCvcRetain_;
+@synthesize derivedCvcCopy = derivedCvcCopy_;
+@synthesize derivedCvcAssign = derivedCvcAssign_;
+@synthesize derivedCvcNil = derivedCvcNil_;
+@synthesize derivedCvcCustom = derivedCvcCustom_;
+@dynamic derivedCvcDynamic;
+@synthesize derivedInt = derivedInt_;
+@synthesize derivedDouble = derivedDouble_;
+@synthesize derivedPointer = derivedPointer_;
+@synthesize derivedStruct = derivedStruct_;
+
+@synthesize protoCvcRetain = protoCvcRetain_;
+@synthesize protoCvcCopy = protoCvcCopy_;
+@synthesize protoCvcAssign = protoCvcAssign_;
+@synthesize protoCvcNil = protoCvcNil_;
+@synthesize protoCvcCustom = protoCvcCustom_;
+@dynamic protoCvcDynamic;
+@synthesize protoInt = protoInt_;
+@synthesize protoDouble = protoDouble_;
+@synthesize protoPointer = protoPointer_;
+@synthesize protoStruct = protoStruct_;
+
+- (id)init {
+ if ((self = [super init])) {
+ propertyReleaser_ObjCPropertyTestDerived_.Init(
+ self, [ObjCPropertyTestDerived class]);
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [derivedCvcNotProperty_ release];
+ [super dealloc];
+}
+
+- (void)setDerivedCvcNotProperty:(CountVonCount*)cvc {
+ if (cvc != derivedCvcNotProperty_) {
+ [derivedCvcNotProperty_ release];
+ derivedCvcNotProperty_ = [cvc retain];
+ }
+}
+
+@end // @implementation ObjCPropertyTestDerived
+
+namespace {
+
+TEST(ObjCPropertyReleaserTest, SesameStreet) {
+ ObjCPropertyTestDerived* test_object = [[ObjCPropertyTestDerived alloc] init];
+
+ // Assure a clean slate.
+ EXPECT_EQ(0, ah_ah_ah);
+ EXPECT_EQ(1U, [test_object retainCount]);
+
+ CountVonCount* baseAssign = [[CountVonCount alloc] init];
+ CountVonCount* derivedAssign = [[CountVonCount alloc] init];
+ CountVonCount* protoAssign = [[CountVonCount alloc] init];
+
+ // Make sure that worked before things get more involved.
+ EXPECT_EQ(3, ah_ah_ah);
+
+ {
+ base::mac::ScopedNSAutoreleasePool pool;
+
+ test_object.baseCvcRetain = [CountVonCount countVonCount];
+ test_object.baseCvcCopy = [CountVonCount countVonCount];
+ test_object.baseCvcAssign = baseAssign;
+ test_object.baseCvcCustom = [CountVonCount countVonCount];
+ [test_object setBaseCvcNotProperty:[CountVonCount countVonCount]];
+
+ // That added 4 objects, plus 1 more that was copied.
+ EXPECT_EQ(8, ah_ah_ah);
+
+ test_object.derivedCvcRetain = [CountVonCount countVonCount];
+ test_object.derivedCvcCopy = [CountVonCount countVonCount];
+ test_object.derivedCvcAssign = derivedAssign;
+ test_object.derivedCvcCustom = [CountVonCount countVonCount];
+ [test_object setDerivedCvcNotProperty:[CountVonCount countVonCount]];
+
+ // That added 4 objects, plus 1 more that was copied.
+ EXPECT_EQ(13, ah_ah_ah);
+
+ test_object.protoCvcRetain = [CountVonCount countVonCount];
+ test_object.protoCvcCopy = [CountVonCount countVonCount];
+ test_object.protoCvcAssign = protoAssign;
+ test_object.protoCvcCustom = [CountVonCount countVonCount];
+
+ // That added 3 objects, plus 1 more that was copied.
+ EXPECT_EQ(17, ah_ah_ah);
+ }
+
+ // Now that the autorelease pool has been popped, the 3 objects that were
+ // copied when placed into the test object will have been deallocated.
+ EXPECT_EQ(14, ah_ah_ah);
+
+ // Make sure that the setters work and have the expected semantics.
+ test_object.baseCvcRetain = nil;
+ test_object.baseCvcCopy = nil;
+ test_object.baseCvcAssign = nil;
+ test_object.baseCvcCustom = nil;
+ test_object.derivedCvcRetain = nil;
+ test_object.derivedCvcCopy = nil;
+ test_object.derivedCvcAssign = nil;
+ test_object.derivedCvcCustom = nil;
+ test_object.protoCvcRetain = nil;
+ test_object.protoCvcCopy = nil;
+ test_object.protoCvcAssign = nil;
+ test_object.protoCvcCustom = nil;
+
+ // The CountVonCounts marked "retain" and "copy" should have been
+ // deallocated. Those marked assign should not have been. The only ones that
+ // should exist now are the ones marked "assign" and the ones held in
+ // non-property instance variables.
+ EXPECT_EQ(5, ah_ah_ah);
+
+ {
+ base::mac::ScopedNSAutoreleasePool pool;
+
+ // Put things back to how they were.
+ test_object.baseCvcRetain = [CountVonCount countVonCount];
+ test_object.baseCvcCopy = [CountVonCount countVonCount];
+ test_object.baseCvcAssign = baseAssign;
+ test_object.baseCvcCustom = [CountVonCount countVonCount];
+ test_object.derivedCvcRetain = [CountVonCount countVonCount];
+ test_object.derivedCvcCopy = [CountVonCount countVonCount];
+ test_object.derivedCvcAssign = derivedAssign;
+ test_object.derivedCvcCustom = [CountVonCount countVonCount];
+ test_object.protoCvcRetain = [CountVonCount countVonCount];
+ test_object.protoCvcCopy = [CountVonCount countVonCount];
+ test_object.protoCvcAssign = protoAssign;
+ test_object.protoCvcCustom = [CountVonCount countVonCount];
+
+ // 9 more CountVonCounts, 3 of which were copied.
+ EXPECT_EQ(17, ah_ah_ah);
+ }
+
+ // Now that the autorelease pool has been popped, the 3 copies are gone.
+ EXPECT_EQ(14, ah_ah_ah);
+
+ // Releasing the test object should get rid of everything that it owns.
+ [test_object release];
+
+ // The property releaser should have released all of the CountVonCounts
+ // associated with properties marked "retain" or "copy". The -dealloc
+ // methods in each should have released the single non-property objects in
+ // each. Only the CountVonCounts assigned to the properties marked "assign"
+ // should remain.
+ EXPECT_EQ(3, ah_ah_ah);
+
+ [baseAssign release];
+ [derivedAssign release];
+ [protoAssign release];
+
+ // Zero! Zero counts! Ah, ah, ah.
+ EXPECT_EQ(0, ah_ah_ah);
+}
+
+} // namespace
diff --git a/src/base/mac/os_crash_dumps.cc b/src/base/mac/os_crash_dumps.cc
new file mode 100644
index 0000000..e6b0996
--- /dev/null
+++ b/src/base/mac/os_crash_dumps.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/mac/os_crash_dumps.h"
+
+#include <signal.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace base {
+namespace mac {
+
+namespace {
+
+void ExitSignalHandler(int sig) {
+ // A call to exit() can call atexit() handlers. If we SIGSEGV due
+ // to a corrupt heap, and if we have an atexit handler that
+ // allocates or frees memory, we are in trouble if we do not _exit.
+ _exit(128 + sig);
+}
+
+} // namespace
+
+void DisableOSCrashDumps() {
+ // These are the POSIX signals corresponding to the Mach exceptions that
+ // Apple Crash Reporter handles. See ux_exception() in xnu's
+ // bsd/uxkern/ux_exception.c and machine_exception() in xnu's
+ // bsd/dev/*/unix_signal.c.
+ const int signals_to_intercept[] = {
+ SIGILL, // EXC_BAD_INSTRUCTION
+ SIGTRAP, // EXC_BREAKPOINT
+ SIGFPE, // EXC_ARITHMETIC
+ SIGBUS, // EXC_BAD_ACCESS
+ SIGSEGV // EXC_BAD_ACCESS
+ };
+
+ // For all these signals, just wire things up so we exit immediately.
+ for (size_t i = 0; i < arraysize(signals_to_intercept); ++i) {
+ struct sigaction act = {};
+ act.sa_handler = ExitSignalHandler;
+
+ // It is better to allow the signal handler to run on the stack
+ // registered with sigaltstack(), if one is present.
+ act.sa_flags = SA_ONSTACK;
+
+ if (sigemptyset(&act.sa_mask) != 0)
+ DLOG_ERRNO(FATAL) << "sigemptyset() failed";
+ if (sigaction(signals_to_intercept[i], &act, NULL) != 0)
+ DLOG_ERRNO(FATAL) << "sigaction() failed";
+ }
+}
+
+} // namespace mac
+} // namespace base
diff --git a/src/base/mac/os_crash_dumps.h b/src/base/mac/os_crash_dumps.h
new file mode 100644
index 0000000..31d90fb
--- /dev/null
+++ b/src/base/mac/os_crash_dumps.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MAC_OS_CRASH_DUMPS_H_
+#define BASE_MAC_OS_CRASH_DUMPS_H_
+
+#include "base/base_export.h"
+
+namespace base {
+namespace mac {
+
+// On Mac OS X, it can take a really long time for the OS crash handler to
+// process a Chrome crash when debugging symbols are available. This
+// translates into a long wait until the process actually dies. This call
+// disables Apple Crash Reporter entirely.
+BASE_EXPORT void DisableOSCrashDumps();
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_OS_CRASH_DUMPS_H_
diff --git a/src/base/mac/scoped_aedesc.h b/src/base/mac/scoped_aedesc.h
new file mode 100644
index 0000000..a1323c0
--- /dev/null
+++ b/src/base/mac/scoped_aedesc.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MAC_SCOPED_AEDESC_H_
+#define BASE_MAC_SCOPED_AEDESC_H_
+
+#import <CoreServices/CoreServices.h>
+
+#include "base/basictypes.h"
+
+namespace base {
+namespace mac {
+
+// The ScopedAEDesc is used to scope AppleEvent descriptors. On creation,
+// it will store a NULL descriptor. On destruction, it will dispose of the
+// descriptor.
+//
+// This class is parameterized for additional type safety checks. You can use
+// the generic AEDesc type by not providing a template parameter:
+// ScopedAEDesc<> desc;
+template <typename AEDescType = AEDesc>
+class ScopedAEDesc {
+ public:
+ ScopedAEDesc() {
+ AECreateDesc(typeNull, NULL, 0, &desc_);
+ }
+
+ ~ScopedAEDesc() {
+ AEDisposeDesc(&desc_);
+ }
+
+ // Used for in parameters.
+ operator const AEDescType*() {
+ return &desc_;
+ }
+
+ // Used for out parameters.
+ AEDescType* OutPointer() {
+ return &desc_;
+ }
+
+ private:
+ AEDescType desc_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAEDesc);
+};
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_SCOPED_AEDESC_H_
diff --git a/src/base/mac/scoped_authorizationref.h b/src/base/mac/scoped_authorizationref.h
new file mode 100644
index 0000000..6413f2e
--- /dev/null
+++ b/src/base/mac/scoped_authorizationref.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2012 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_MAC_SCOPED_AUTHORIZATIONREF_H_
+#define BASE_MAC_SCOPED_AUTHORIZATIONREF_H_
+
+#include <Security/Authorization.h>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+
+// ScopedAuthorizationRef maintains ownership of an AuthorizationRef. It is
+// patterned after the scoped_ptr interface.
+
+namespace base {
+namespace mac {
+
+class ScopedAuthorizationRef {
+ public:
+ explicit ScopedAuthorizationRef(AuthorizationRef authorization = NULL)
+ : authorization_(authorization) {
+ }
+
+ ~ScopedAuthorizationRef() {
+ if (authorization_) {
+ AuthorizationFree(authorization_, kAuthorizationFlagDestroyRights);
+ }
+ }
+
+ void reset(AuthorizationRef authorization = NULL) {
+ if (authorization_ != authorization) {
+ if (authorization_) {
+ AuthorizationFree(authorization_, kAuthorizationFlagDestroyRights);
+ }
+ authorization_ = authorization;
+ }
+ }
+
+ bool operator==(AuthorizationRef that) const {
+ return authorization_ == that;
+ }
+
+ bool operator!=(AuthorizationRef that) const {
+ return authorization_ != that;
+ }
+
+ operator AuthorizationRef() const {
+ return authorization_;
+ }
+
+ AuthorizationRef* operator&() {
+ return &authorization_;
+ }
+
+ AuthorizationRef get() const {
+ return authorization_;
+ }
+
+ void swap(ScopedAuthorizationRef& that) {
+ AuthorizationRef temp = that.authorization_;
+ that.authorization_ = authorization_;
+ authorization_ = temp;
+ }
+
+ // ScopedAuthorizationRef::release() is like scoped_ptr<>::release. It is
+ // NOT a wrapper for AuthorizationFree(). To force a
+ // ScopedAuthorizationRef object to call AuthorizationFree(), use
+ // ScopedAuthorizationRef::reset().
+ AuthorizationRef release() WARN_UNUSED_RESULT {
+ AuthorizationRef temp = authorization_;
+ authorization_ = NULL;
+ return temp;
+ }
+
+ private:
+ AuthorizationRef authorization_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAuthorizationRef);
+};
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_SCOPED_AUTHORIZATIONREF_H_
diff --git a/src/base/mac/scoped_cffiledescriptorref.h b/src/base/mac/scoped_cffiledescriptorref.h
new file mode 100644
index 0000000..07196aa
--- /dev/null
+++ b/src/base/mac/scoped_cffiledescriptorref.h
@@ -0,0 +1,75 @@
+// Copyright 2012 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_MAC_SCOPED_CFFILEDESCRIPTORREF_H_
+#define BASE_MAC_SCOPED_CFFILEDESCRIPTORREF_H_
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+
+namespace base {
+namespace mac {
+
+// ScopedCFFileDescriptorRef is designed after ScopedCFTypeRef<>. On
+// destruction, it will invalidate the file descriptor.
+// ScopedCFFileDescriptorRef (unlike ScopedCFTypeRef<>) does not support RETAIN
+// semantics, copying, or assignment, as doing so would increase the chances
+// that a file descriptor is invalidated while still in use.
+class ScopedCFFileDescriptorRef {
+ public:
+ explicit ScopedCFFileDescriptorRef(CFFileDescriptorRef fdref = NULL)
+ : fdref_(fdref) {
+ }
+
+ ~ScopedCFFileDescriptorRef() {
+ if (fdref_) {
+ CFFileDescriptorInvalidate(fdref_);
+ CFRelease(fdref_);
+ }
+ }
+
+ void reset(CFFileDescriptorRef fdref = NULL) {
+ if (fdref_ == fdref)
+ return;
+ if (fdref_) {
+ CFFileDescriptorInvalidate(fdref_);
+ CFRelease(fdref_);
+ }
+ fdref_ = fdref;
+ }
+
+ bool operator==(CFFileDescriptorRef that) const {
+ return fdref_ == that;
+ }
+
+ bool operator!=(CFFileDescriptorRef that) const {
+ return fdref_ != that;
+ }
+
+ operator CFFileDescriptorRef() const {
+ return fdref_;
+ }
+
+ CFFileDescriptorRef get() const {
+ return fdref_;
+ }
+
+ CFFileDescriptorRef release() WARN_UNUSED_RESULT {
+ CFFileDescriptorRef temp = fdref_;
+ fdref_ = NULL;
+ return temp;
+ }
+
+ private:
+ CFFileDescriptorRef fdref_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedCFFileDescriptorRef);
+};
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_SCOPED_CFFILEDESCRIPTORREF_H_
diff --git a/src/base/mac/scoped_cftyperef.h b/src/base/mac/scoped_cftyperef.h
new file mode 100644
index 0000000..c6ca46a
--- /dev/null
+++ b/src/base/mac/scoped_cftyperef.h
@@ -0,0 +1,108 @@
+// Copyright (c) 2012 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_MAC_SCOPED_CFTYPEREF_H_
+#define BASE_MAC_SCOPED_CFTYPEREF_H_
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_policy.h"
+
+namespace base {
+namespace mac {
+
+// ScopedCFTypeRef<> is patterned after scoped_ptr<>, but maintains ownership
+// of a CoreFoundation object: any object that can be represented as a
+// CFTypeRef. Style deviations here are solely for compatibility with
+// scoped_ptr<>'s interface, with which everyone is already familiar.
+//
+// By default, ScopedCFTypeRef<> takes ownership of an object (in the
+// constructor or in reset()) by taking over the caller's existing ownership
+// claim. The caller must own the object it gives to ScopedCFTypeRef<>, and
+// relinquishes an ownership claim to that object. ScopedCFTypeRef<> does not
+// call CFRetain(). This behavior is parameterized by the |OwnershipPolicy|
+// enum. If the value |RETAIN| is passed (in the constructor or in reset()),
+// then ScopedCFTypeRef<> will call CFRetain() on the object, and the initial
+// ownership is not changed.
+
+template<typename CFT>
+class ScopedCFTypeRef {
+ public:
+ typedef CFT element_type;
+
+ explicit ScopedCFTypeRef(
+ CFT object = NULL,
+ base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
+ : object_(object) {
+ if (object_ && policy == base::scoped_policy::RETAIN)
+ CFRetain(object_);
+ }
+
+ ScopedCFTypeRef(const ScopedCFTypeRef<CFT>& that)
+ : object_(that.object_) {
+ if (object_)
+ CFRetain(object_);
+ }
+
+ ~ScopedCFTypeRef() {
+ if (object_)
+ CFRelease(object_);
+ }
+
+ ScopedCFTypeRef& operator=(const ScopedCFTypeRef<CFT>& that) {
+ reset(that.get(), base::scoped_policy::RETAIN);
+ return *this;
+ }
+
+ void reset(CFT object = NULL,
+ base::scoped_policy::OwnershipPolicy policy =
+ base::scoped_policy::ASSUME) {
+ if (object && policy == base::scoped_policy::RETAIN)
+ CFRetain(object);
+ if (object_)
+ CFRelease(object_);
+ object_ = object;
+ }
+
+ bool operator==(CFT that) const {
+ return object_ == that;
+ }
+
+ bool operator!=(CFT that) const {
+ return object_ != that;
+ }
+
+ operator CFT() const {
+ return object_;
+ }
+
+ CFT get() const {
+ return object_;
+ }
+
+ void swap(ScopedCFTypeRef& that) {
+ CFT temp = that.object_;
+ that.object_ = object_;
+ object_ = temp;
+ }
+
+ // ScopedCFTypeRef<>::release() is like scoped_ptr<>::release. It is NOT
+ // a wrapper for CFRelease(). To force a ScopedCFTypeRef<> object to call
+ // CFRelease(), use ScopedCFTypeRef<>::reset().
+ CFT release() WARN_UNUSED_RESULT {
+ CFT temp = object_;
+ object_ = NULL;
+ return temp;
+ }
+
+ private:
+ CFT object_;
+};
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_SCOPED_CFTYPEREF_H_
diff --git a/src/base/mac/scoped_ioobject.h b/src/base/mac/scoped_ioobject.h
new file mode 100644
index 0000000..854039b
--- /dev/null
+++ b/src/base/mac/scoped_ioobject.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2012 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_MAC_SCOPED_IOOBJECT_H_
+#define BASE_MAC_SCOPED_IOOBJECT_H_
+
+#include <IOKit/IOKitLib.h>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+
+namespace base {
+namespace mac {
+
+// Just like ScopedCFTypeRef but for io_object_t and subclasses.
+template<typename IOT>
+class ScopedIOObject {
+ public:
+ typedef IOT element_type;
+
+ explicit ScopedIOObject(IOT object = IO_OBJECT_NULL)
+ : object_(object) {
+ }
+
+ ~ScopedIOObject() {
+ if (object_)
+ IOObjectRelease(object_);
+ }
+
+ void reset(IOT object = IO_OBJECT_NULL) {
+ if (object_)
+ IOObjectRelease(object_);
+ object_ = object;
+ }
+
+ bool operator==(IOT that) const {
+ return object_ == that;
+ }
+
+ bool operator!=(IOT that) const {
+ return object_ != that;
+ }
+
+ operator IOT() const {
+ return object_;
+ }
+
+ IOT get() const {
+ return object_;
+ }
+
+ void swap(ScopedIOObject& that) {
+ IOT temp = that.object_;
+ that.object_ = object_;
+ object_ = temp;
+ }
+
+ IOT release() WARN_UNUSED_RESULT {
+ IOT temp = object_;
+ object_ = IO_OBJECT_NULL;
+ return temp;
+ }
+
+ private:
+ IOT object_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedIOObject);
+};
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_SCOPED_IOOBJECT_H_
diff --git a/src/base/mac/scoped_launch_data.h b/src/base/mac/scoped_launch_data.h
new file mode 100644
index 0000000..e4343b8
--- /dev/null
+++ b/src/base/mac/scoped_launch_data.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 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_MAC_SCOPED_LAUNCH_DATA_H_
+#define BASE_MAC_SCOPED_LAUNCH_DATA_H_
+
+#include <launch.h>
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+
+namespace base {
+namespace mac {
+
+// Just like scoped_ptr<> but for launch_data_t.
+class ScopedLaunchData {
+ public:
+ typedef launch_data_t element_type;
+
+ explicit ScopedLaunchData(launch_data_t object = NULL)
+ : object_(object) {
+ }
+
+ ~ScopedLaunchData() {
+ if (object_)
+ launch_data_free(object_);
+ }
+
+ void reset(launch_data_t object = NULL) {
+ if (object != object_) {
+ if (object_)
+ launch_data_free(object_);
+ object_ = object;
+ }
+ }
+
+ bool operator==(launch_data_t that) const {
+ return object_ == that;
+ }
+
+ bool operator!=(launch_data_t that) const {
+ return object_ != that;
+ }
+
+ operator launch_data_t() const {
+ return object_;
+ }
+
+ launch_data_t get() const {
+ return object_;
+ }
+
+ void swap(ScopedLaunchData& that) {
+ std::swap(object_, that.object_);
+ }
+
+ launch_data_t release() WARN_UNUSED_RESULT {
+ launch_data_t temp = object_;
+ object_ = NULL;
+ return temp;
+ }
+
+ private:
+ launch_data_t object_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedLaunchData);
+};
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_SCOPED_LAUNCH_DATA_H_
diff --git a/src/base/mac/scoped_mach_port.cc b/src/base/mac/scoped_mach_port.cc
new file mode 100644
index 0000000..652e3f4
--- /dev/null
+++ b/src/base/mac/scoped_mach_port.cc
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 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/mac/scoped_mach_port.h"
+
+namespace base {
+namespace mac {
+
+ScopedMachPort::ScopedMachPort(mach_port_t port) : port_(port) {
+}
+
+ScopedMachPort::~ScopedMachPort() {
+ if (port_ != MACH_PORT_NULL) {
+ mach_port_deallocate(mach_task_self(), port_);
+ }
+}
+
+} // namespace mac
+} // namespace base
diff --git a/src/base/mac/scoped_mach_port.h b/src/base/mac/scoped_mach_port.h
new file mode 100644
index 0000000..d2aa2f7
--- /dev/null
+++ b/src/base/mac/scoped_mach_port.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2012 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_MAC_SCOPED_MACH_PORT_H_
+#define BASE_MAC_SCOPED_MACH_PORT_H_
+
+#include <mach/mach.h>
+
+#include "base/basictypes.h"
+#include "base/base_export.h"
+
+namespace base {
+namespace mac {
+
+// A class for managing the life of a Mach port, releasing via
+// mach_port_deallocate either its send and/or receive rights.
+class BASE_EXPORT ScopedMachPort {
+ public:
+ // Creates a scoper by taking ownership of the port.
+ explicit ScopedMachPort(mach_port_t port);
+
+ ~ScopedMachPort();
+
+ operator mach_port_t() const {
+ return port_;
+ }
+
+ mach_port_t get() const {
+ return port_;
+ }
+
+ private:
+ mach_port_t port_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedMachPort);
+};
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_SCOPED_MACH_PORT_H_
diff --git a/src/base/mac/scoped_nsautorelease_pool.h b/src/base/mac/scoped_nsautorelease_pool.h
new file mode 100644
index 0000000..60af71a
--- /dev/null
+++ b/src/base/mac/scoped_nsautorelease_pool.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MAC_SCOPED_NSAUTORELEASE_POOL_H_
+#define BASE_MAC_SCOPED_NSAUTORELEASE_POOL_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+#if defined(__OBJC__)
+@class NSAutoreleasePool;
+#else // __OBJC__
+class NSAutoreleasePool;
+#endif // __OBJC__
+
+namespace base {
+namespace mac {
+
+// ScopedNSAutoreleasePool allocates an NSAutoreleasePool when instantiated and
+// sends it a -drain message when destroyed. This allows an autorelease pool to
+// be maintained in ordinary C++ code without bringing in any direct Objective-C
+// dependency.
+
+class BASE_EXPORT ScopedNSAutoreleasePool {
+ public:
+ ScopedNSAutoreleasePool();
+ ~ScopedNSAutoreleasePool();
+
+ // Clear out the pool in case its position on the stack causes it to be
+ // alive for long periods of time (such as the entire length of the app).
+ // Only use then when you're certain the items currently in the pool are
+ // no longer needed.
+ void Recycle();
+ private:
+ NSAutoreleasePool* autorelease_pool_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedNSAutoreleasePool);
+};
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_SCOPED_NSAUTORELEASE_POOL_H_
diff --git a/src/base/mac/scoped_nsautorelease_pool.mm b/src/base/mac/scoped_nsautorelease_pool.mm
new file mode 100644
index 0000000..e542ca8
--- /dev/null
+++ b/src/base/mac/scoped_nsautorelease_pool.mm
@@ -0,0 +1,32 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/mac/scoped_nsautorelease_pool.h"
+
+#import <Foundation/Foundation.h>
+
+#include "base/logging.h"
+
+namespace base {
+namespace mac {
+
+ScopedNSAutoreleasePool::ScopedNSAutoreleasePool()
+ : autorelease_pool_([[NSAutoreleasePool alloc] init]) {
+ DCHECK(autorelease_pool_);
+}
+
+ScopedNSAutoreleasePool::~ScopedNSAutoreleasePool() {
+ [autorelease_pool_ drain];
+}
+
+// Cycle the internal pool, allowing everything there to get cleaned up and
+// start anew.
+void ScopedNSAutoreleasePool::Recycle() {
+ [autorelease_pool_ drain];
+ autorelease_pool_ = [[NSAutoreleasePool alloc] init];
+ DCHECK(autorelease_pool_);
+}
+
+} // namespace mac
+} // namespace base
diff --git a/src/base/mac/scoped_nsexception_enabler.h b/src/base/mac/scoped_nsexception_enabler.h
new file mode 100644
index 0000000..e1d0b3c
--- /dev/null
+++ b/src/base/mac/scoped_nsexception_enabler.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MAC_SCOPED_NSEXCEPTION_ENABLER_H_
+#define BASE_MAC_SCOPED_NSEXCEPTION_ENABLER_H_
+
+#import <Foundation/Foundation.h>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+namespace mac {
+
+// BrowserCrApplication attempts to restrict throwing of NSExceptions
+// because they interact badly with C++ scoping rules. Unfortunately,
+// there are some cases where exceptions must be supported, such as
+// when third-party printer drivers are used. These helpers can be
+// used to enable exceptions for narrow windows.
+
+// Make it easy to safely allow NSException to be thrown in a limited
+// scope. Note that if an exception is thrown, then this object will
+// not be appropriately destructed! If the exception ends up in the
+// top-level event loop, things are cleared in -reportException:. If
+// the exception is caught at a lower level, a higher level scoper
+// should eventually reset things.
+class BASE_EXPORT ScopedNSExceptionEnabler {
+ public:
+ ScopedNSExceptionEnabler();
+ ~ScopedNSExceptionEnabler();
+
+ private:
+ bool was_enabled_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedNSExceptionEnabler);
+};
+
+// Access the exception setting for the current thread. This is for
+// the support code in BrowserCrApplication, other code should use
+// the scoper.
+BASE_EXPORT bool GetNSExceptionsAllowed();
+BASE_EXPORT void SetNSExceptionsAllowed(bool allowed);
+
+// Executes [target performSelector:sel] with fatal-exceptions turned
+// off, and returns the result. If an exception is thrown during the
+// perform, nil is returned.
+BASE_EXPORT id PerformSelectorIgnoringExceptions(NSObject* target, SEL sel);
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_SCOPED_NSEXCEPTION_ENABLER_H_
diff --git a/src/base/mac/scoped_nsexception_enabler.mm b/src/base/mac/scoped_nsexception_enabler.mm
new file mode 100644
index 0000000..9898789
--- /dev/null
+++ b/src/base/mac/scoped_nsexception_enabler.mm
@@ -0,0 +1,63 @@
+// Copyright (c) 2012 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.
+
+#import "base/mac/scoped_nsexception_enabler.h"
+
+#import "base/lazy_instance.h"
+#import "base/threading/thread_local.h"
+
+// To make the |g_exceptionsAllowed| declaration readable.
+using base::LazyInstance;
+using base::ThreadLocalBoolean;
+
+// When C++ exceptions are disabled, the C++ library defines |try| and
+// |catch| so as to allow exception-expecting C++ code to build properly when
+// language support for exceptions is not present. These macros interfere
+// with the use of |@try| and |@catch| in Objective-C files such as this one.
+// Undefine these macros here, after everything has been #included, since
+// there will be no C++ uses and only Objective-C uses from this point on.
+#undef try
+#undef catch
+
+namespace {
+
+// Whether to allow NSExceptions to be raised on the current thread.
+LazyInstance<ThreadLocalBoolean>::Leaky
+ g_exceptionsAllowed = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+namespace base {
+namespace mac {
+
+bool GetNSExceptionsAllowed() {
+ return g_exceptionsAllowed.Get().Get();
+}
+
+void SetNSExceptionsAllowed(bool allowed) {
+ return g_exceptionsAllowed.Get().Set(allowed);
+}
+
+id PerformSelectorIgnoringExceptions(NSObject* target, SEL sel) {
+ id ret = nil;
+ @try {
+ base::mac::ScopedNSExceptionEnabler enable;
+ ret = [target performSelector:sel];
+ }
+ @catch(id exception) {
+ }
+ return ret;
+}
+
+ScopedNSExceptionEnabler::ScopedNSExceptionEnabler() {
+ was_enabled_ = GetNSExceptionsAllowed();
+ SetNSExceptionsAllowed(true);
+}
+
+ScopedNSExceptionEnabler::~ScopedNSExceptionEnabler() {
+ SetNSExceptionsAllowed(was_enabled_);
+}
+
+} // namespace mac
+} // namespace base
diff --git a/src/base/mac/scoped_sending_event.h b/src/base/mac/scoped_sending_event.h
new file mode 100644
index 0000000..f7637bb
--- /dev/null
+++ b/src/base/mac/scoped_sending_event.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 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_MAC_SCOPED_SENDING_EVENT_H_
+#define BASE_MAC_SCOPED_SENDING_EVENT_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/memory/scoped_nsobject.h"
+#include "base/message_pump_mac.h"
+
+// Nested event loops can pump IPC messages, including
+// script-initiated tab closes, which could release objects that the
+// nested event loop might message. CrAppProtocol defines how to ask
+// the embedding NSApplication subclass if an event is currently being
+// handled, in which case such closes are deferred to the top-level
+// event loop.
+//
+// ScopedSendingEvent allows script-initiated event loops to work like
+// a nested event loop, as such events do not arrive via -sendEvent:.
+// CrAppControlProtocol lets ScopedSendingEvent tell the embedding
+// NSApplication what to return from -handlingSendEvent.
+
+@protocol CrAppControlProtocol<CrAppProtocol>
+- (void)setHandlingSendEvent:(BOOL)handlingSendEvent;
+@end
+
+namespace base {
+namespace mac {
+
+class BASE_EXPORT ScopedSendingEvent {
+ public:
+ ScopedSendingEvent();
+ ~ScopedSendingEvent();
+
+ private:
+ // The NSApp in control at the time the constructor was run, to be
+ // sure the |handling_| setting is restored appropriately.
+ NSObject<CrAppControlProtocol>* app_;
+ BOOL handling_; // Value of -[app_ handlingSendEvent] at construction.
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedSendingEvent);
+};
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_SCOPED_SENDING_EVENT_H_
diff --git a/src/base/mac/scoped_sending_event.mm b/src/base/mac/scoped_sending_event.mm
new file mode 100644
index 0000000..c3813d8
--- /dev/null
+++ b/src/base/mac/scoped_sending_event.mm
@@ -0,0 +1,24 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "base/mac/scoped_sending_event.h"
+
+#include "base/logging.h"
+
+namespace base {
+namespace mac {
+
+ScopedSendingEvent::ScopedSendingEvent()
+ : app_(static_cast<NSObject<CrAppControlProtocol>*>(NSApp)) {
+ DCHECK([app_ conformsToProtocol:@protocol(CrAppControlProtocol)]);
+ handling_ = [app_ isHandlingSendEvent];
+ [app_ setHandlingSendEvent:YES];
+}
+
+ScopedSendingEvent::~ScopedSendingEvent() {
+ [app_ setHandlingSendEvent:handling_];
+}
+
+} // namespace mac
+} // namespace base
diff --git a/src/base/mac/scoped_sending_event_unittest.mm b/src/base/mac/scoped_sending_event_unittest.mm
new file mode 100644
index 0000000..9ae9985
--- /dev/null
+++ b/src/base/mac/scoped_sending_event_unittest.mm
@@ -0,0 +1,38 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "base/mac/scoped_sending_event.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Sets the flag within scope, resets when leaving scope.
+TEST(ScopedSendingEventTest, SetHandlingSendEvent) {
+ id<CrAppProtocol> app = NSApp;
+ EXPECT_FALSE([app isHandlingSendEvent]);
+ {
+ base::mac::ScopedSendingEvent is_handling_send_event;
+ EXPECT_TRUE([app isHandlingSendEvent]);
+ }
+ EXPECT_FALSE([app isHandlingSendEvent]);
+}
+
+// Nested call restores previous value rather than resetting flag.
+TEST(ScopedSendingEventTest, NestedSetHandlingSendEvent) {
+ id<CrAppProtocol> app = NSApp;
+ EXPECT_FALSE([app isHandlingSendEvent]);
+ {
+ base::mac::ScopedSendingEvent is_handling_send_event;
+ EXPECT_TRUE([app isHandlingSendEvent]);
+ {
+ base::mac::ScopedSendingEvent nested_is_handling_send_event;
+ EXPECT_TRUE([app isHandlingSendEvent]);
+ }
+ EXPECT_TRUE([app isHandlingSendEvent]);
+ }
+ EXPECT_FALSE([app isHandlingSendEvent]);
+}
+
+} // namespace
diff --git a/src/base/mach_ipc_mac.h b/src/base/mach_ipc_mac.h
new file mode 100644
index 0000000..1730d37
--- /dev/null
+++ b/src/base/mach_ipc_mac.h
@@ -0,0 +1,326 @@
+// Copyright (c) 2012 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_MACH_IPC_MAC_H_
+#define BASE_MACH_IPC_MAC_H_
+
+#include <mach/mach.h>
+#include <mach/message.h>
+#include <servers/bootstrap.h>
+#include <sys/types.h>
+
+#include <CoreServices/CoreServices.h>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+//==============================================================================
+// DISCUSSION:
+//
+// The three main classes of interest are
+//
+// MachMessage: a wrapper for a Mach message of the following form
+// mach_msg_header_t
+// mach_msg_body_t
+// optional descriptors
+// optional extra message data
+//
+// MachReceiveMessage and MachSendMessage subclass MachMessage
+// and are used instead of MachMessage which is an abstract base class
+//
+// ReceivePort:
+// Represents a Mach port for which we have receive rights
+//
+// MachPortSender:
+// Represents a Mach port for which we have send rights
+//
+// Here's an example to receive a message on a server port:
+//
+// // This creates our named server port
+// ReceivePort receivePort("com.Google.MyService");
+//
+// MachReceiveMessage message;
+// kern_return_t result = receivePort.WaitForMessage(&message, 0);
+//
+// if (result == KERN_SUCCESS && message.GetMessageID() == 57) {
+// mach_port_t task = message.GetTranslatedPort(0);
+// mach_port_t thread = message.GetTranslatedPort(1);
+//
+// char *messageString = message.GetData();
+//
+// printf("message string = %s\n", messageString);
+// }
+//
+// Here is an example of using these classes to send a message to this port:
+//
+// // send to already named port
+// MachPortSender sender("com.Google.MyService");
+// MachSendMessage message(57); // our message ID is 57
+//
+// // add some ports to be translated for us
+// message.AddDescriptor(mach_task_self()); // our task
+// message.AddDescriptor(mach_thread_self()); // this thread
+//
+// char messageString[] = "Hello server!\n";
+// message.SetData(messageString, strlen(messageString)+1);
+// // timeout 1000ms
+// kern_return_t result = sender.SendMessage(message, 1000);
+//
+
+#define PRINT_MACH_RESULT(result_, message_) \
+ printf(message_" %s (%d)\n", mach_error_string(result_), result_ );
+
+namespace base {
+
+//==============================================================================
+// A wrapper class for mach_msg_port_descriptor_t (with same memory layout)
+// with convenient constructors and accessors
+class MachMsgPortDescriptor : public mach_msg_port_descriptor_t {
+ public:
+ // General-purpose constructor
+ MachMsgPortDescriptor(mach_port_t in_name,
+ mach_msg_type_name_t in_disposition) {
+ name = in_name;
+ pad1 = 0;
+ pad2 = 0;
+ disposition = in_disposition;
+ type = MACH_MSG_PORT_DESCRIPTOR;
+ }
+
+ // For passing send rights to a port
+ MachMsgPortDescriptor(mach_port_t in_name) {
+ name = in_name;
+ pad1 = 0;
+ pad2 = 0;
+ disposition = MACH_MSG_TYPE_PORT_SEND;
+ type = MACH_MSG_PORT_DESCRIPTOR;
+ }
+
+ // Copy constructor
+ MachMsgPortDescriptor(const MachMsgPortDescriptor& desc) {
+ name = desc.name;
+ pad1 = desc.pad1;
+ pad2 = desc.pad2;
+ disposition = desc.disposition;
+ type = desc.type;
+ }
+
+ mach_port_t GetMachPort() const {
+ return name;
+ }
+
+ mach_msg_type_name_t GetDisposition() const {
+ return disposition;
+ }
+
+ // For convenience
+ operator mach_port_t() const {
+ return GetMachPort();
+ }
+};
+
+//==============================================================================
+// MachMessage: a wrapper for a Mach message
+// (mach_msg_header_t, mach_msg_body_t, extra data)
+//
+// This considerably simplifies the construction of a message for sending
+// and the getting at relevant data and descriptors for the receiver.
+//
+// This class can be initialized using external storage of an arbitrary size
+// or it can manage storage internally.
+// 1. If storage is allocated internally, the combined size of the descriptors
+// plus data must be less than 1024. But as a benefit no memory allocation is
+// necessary.
+// 2. For external storage, a buffer of at least EmptyMessageSize() must be
+// provided.
+//
+// A MachMessage object is used by ReceivePort::WaitForMessage
+// and MachPortSender::SendMessage
+//
+class BASE_EXPORT MachMessage {
+ public:
+ static const size_t kEmptyMessageSize;
+
+ virtual ~MachMessage();
+
+ // The receiver of the message can retrieve the raw data this way
+ u_int8_t *GetData() {
+ return GetDataLength() > 0 ? GetDataPacket()->data : NULL;
+ }
+
+ u_int32_t GetDataLength() {
+ return EndianU32_LtoN(GetDataPacket()->data_length);
+ }
+
+ // The message ID may be used as a code identifying the type of message
+ void SetMessageID(int32_t message_id) {
+ GetDataPacket()->id = EndianU32_NtoL(message_id);
+ }
+
+ int32_t GetMessageID() { return EndianU32_LtoN(GetDataPacket()->id); }
+
+ // Adds a descriptor (typically a Mach port) to be translated
+ // returns true if successful, otherwise not enough space
+ bool AddDescriptor(const MachMsgPortDescriptor &desc);
+
+ int GetDescriptorCount() const {
+ return storage_->body.msgh_descriptor_count;
+ }
+
+ MachMsgPortDescriptor *GetDescriptor(int n);
+
+ // Convenience method which gets the Mach port described by the descriptor
+ mach_port_t GetTranslatedPort(int n);
+
+ // A simple message is one with no descriptors
+ bool IsSimpleMessage() const { return GetDescriptorCount() == 0; }
+
+ // Sets raw data for the message (returns false if not enough space)
+ bool SetData(const void* data, int32_t data_length);
+
+ protected:
+ // Consider this an abstract base class - must create an actual instance
+ // of MachReceiveMessage or MachSendMessage
+ MachMessage();
+
+ // Constructor for use with preallocate storage.
+ // storage_length must be >= EmptyMessageSize()
+ MachMessage(void *storage, size_t storage_length);
+
+ friend class ReceivePort;
+ friend class MachPortSender;
+
+ // Represents raw data in our message
+ struct MessageDataPacket {
+ int32_t id; // little-endian
+ int32_t data_length; // little-endian
+ u_int8_t data[1]; // actual size limited by storage_length_bytes_
+ };
+
+ MessageDataPacket* GetDataPacket();
+
+ void SetDescriptorCount(int n);
+ void SetDescriptor(int n, const MachMsgPortDescriptor &desc);
+
+ // Returns total message size setting msgh_size in the header to this value
+ int CalculateSize();
+
+ // Returns total storage size that this object can grow to, this is inclusive
+ // of the Mach header.
+ size_t MaxSize() const { return storage_length_bytes_; }
+
+ mach_msg_header_t *Head() { return &(storage_->head); }
+
+ private:
+ struct MachMessageData {
+ mach_msg_header_t head;
+ mach_msg_body_t body;
+ // descriptors and data may be embedded here.
+ u_int8_t padding[1024];
+ };
+
+ MachMessageData *storage_;
+ size_t storage_length_bytes_;
+ bool own_storage_; // Is storage owned by this object?
+};
+
+//==============================================================================
+// MachReceiveMessage and MachSendMessage are useful to separate the idea
+// of a Mach message being sent and being received, and adds increased type
+// safety:
+// ReceivePort::WaitForMessage() only accepts a MachReceiveMessage
+// MachPortSender::SendMessage() only accepts a MachSendMessage
+
+//==============================================================================
+class MachReceiveMessage : public MachMessage {
+ public:
+ MachReceiveMessage() : MachMessage() {}
+ MachReceiveMessage(void *storage, size_t storage_length)
+ : MachMessage(storage, storage_length) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MachReceiveMessage);
+};
+
+//==============================================================================
+class BASE_EXPORT MachSendMessage : public MachMessage {
+ public:
+ explicit MachSendMessage(int32_t message_id);
+ MachSendMessage(void *storage, size_t storage_length, int32_t message_id);
+
+ private:
+ void Initialize(int32_t message_id);
+
+ DISALLOW_COPY_AND_ASSIGN(MachSendMessage);
+};
+
+//==============================================================================
+// Represents a Mach port for which we have receive rights
+class BASE_EXPORT ReceivePort {
+ public:
+ // Creates a new Mach port for receiving messages and registers a name for it
+ explicit ReceivePort(const char *receive_port_name);
+
+ // Given an already existing Mach port, use it. We take ownership of the
+ // port and deallocate it in our destructor.
+ explicit ReceivePort(mach_port_t receive_port);
+
+ // Create a new Mach port for receiving messages
+ ReceivePort();
+
+ ~ReceivePort();
+
+ // Waits on the Mach port until message received or timeout. If |timeout| is
+ // MACH_MSG_TIMEOUT_NONE, this method waits forever.
+ kern_return_t WaitForMessage(MachReceiveMessage *out_message,
+ mach_msg_timeout_t timeout);
+
+ // The underlying Mach port that we wrap
+ mach_port_t GetPort() const { return port_; }
+
+ private:
+ mach_port_t port_;
+ kern_return_t init_result_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReceivePort);
+};
+
+//==============================================================================
+// Represents a Mach port for which we have send rights
+class BASE_EXPORT MachPortSender {
+ public:
+ // get a port with send rights corresponding to a named registered service
+ explicit MachPortSender(const char *receive_port_name);
+
+
+ // Given an already existing Mach port, use it. Does not take ownership of
+ // |send_port|.
+ explicit MachPortSender(mach_port_t send_port);
+
+ kern_return_t SendMessage(MachSendMessage &message,
+ mach_msg_timeout_t timeout);
+
+ private:
+ mach_port_t send_port_;
+ kern_return_t init_result_;
+
+ DISALLOW_COPY_AND_ASSIGN(MachPortSender);
+};
+
+//==============================================================================
+// Static utility functions.
+
+namespace mac {
+
+// Returns the number of Mach ports to which the given task has a right.
+// Note that unless the calling task has send rights to the passed task port,
+// this will fail unless the calling task is running as root.
+kern_return_t BASE_EXPORT GetNumberOfMachPorts(mach_port_t task_port,
+ int* port_count);
+
+} // namespace mac
+
+} // namespace base
+
+#endif // BASE_MACH_IPC_MAC_H_
diff --git a/src/base/mach_ipc_mac.mm b/src/base/mach_ipc_mac.mm
new file mode 100644
index 0000000..3d45dd7
--- /dev/null
+++ b/src/base/mach_ipc_mac.mm
@@ -0,0 +1,358 @@
+// Copyright (c) 2012 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/mach_ipc_mac.h"
+
+#import <Foundation/Foundation.h>
+#include <mach/vm_map.h>
+
+#include <stdio.h>
+#include "base/logging.h"
+
+namespace base {
+
+// static
+const size_t MachMessage::kEmptyMessageSize = sizeof(mach_msg_header_t) +
+ sizeof(mach_msg_body_t) + sizeof(MessageDataPacket);
+
+//==============================================================================
+MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() {
+ Initialize(message_id);
+}
+
+MachSendMessage::MachSendMessage(void *storage, size_t storage_length,
+ int32_t message_id)
+ : MachMessage(storage, storage_length) {
+ Initialize(message_id);
+}
+
+void MachSendMessage::Initialize(int32_t message_id) {
+ Head()->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
+
+ // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage()
+ Head()->msgh_local_port = MACH_PORT_NULL;
+ Head()->msgh_reserved = 0;
+ Head()->msgh_id = 0;
+
+ SetDescriptorCount(0); // start out with no descriptors
+
+ SetMessageID(message_id);
+ SetData(NULL, 0); // client may add data later
+}
+
+//==============================================================================
+MachMessage::MachMessage()
+ : storage_(new MachMessageData), // Allocate storage_ ourselves
+ storage_length_bytes_(sizeof(MachMessageData)),
+ own_storage_(true) {
+ memset(storage_, 0, storage_length_bytes_);
+}
+
+//==============================================================================
+MachMessage::MachMessage(void *storage, size_t storage_length)
+ : storage_(static_cast<MachMessageData*>(storage)),
+ storage_length_bytes_(storage_length),
+ own_storage_(false) {
+ DCHECK(storage);
+ DCHECK_GE(storage_length, kEmptyMessageSize);
+}
+
+//==============================================================================
+MachMessage::~MachMessage() {
+ if (own_storage_) {
+ delete storage_;
+ storage_ = NULL;
+ }
+}
+
+//==============================================================================
+// returns true if successful
+bool MachMessage::SetData(const void* data,
+ int32_t data_length) {
+ // Enforce the fact that it's only safe to call this method once on a
+ // message.
+ DCHECK(GetDataPacket()->data_length == 0);
+
+ // first check to make sure we have enough space
+ int size = CalculateSize();
+ int new_size = size + data_length;
+
+ if ((unsigned)new_size > storage_length_bytes_) {
+ return false; // not enough space
+ }
+
+ GetDataPacket()->data_length = EndianU32_NtoL(data_length);
+ if (data) memcpy(GetDataPacket()->data, data, data_length);
+
+ // Update the Mach header with the new aligned size of the message.
+ CalculateSize();
+
+ return true;
+}
+
+//==============================================================================
+// calculates and returns the total size of the message
+// Currently, the entire message MUST fit inside of the MachMessage
+// messsage size <= EmptyMessageSize()
+int MachMessage::CalculateSize() {
+ int size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t);
+
+ // add space for MessageDataPacket
+ int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3;
+ size += 2*sizeof(int32_t) + alignedDataLength;
+
+ // add space for descriptors
+ size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor);
+
+ Head()->msgh_size = size;
+
+ return size;
+}
+
+//==============================================================================
+MachMessage::MessageDataPacket *MachMessage::GetDataPacket() {
+ int desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount();
+ MessageDataPacket *packet =
+ reinterpret_cast<MessageDataPacket*>(storage_->padding + desc_size);
+
+ return packet;
+}
+
+//==============================================================================
+void MachMessage::SetDescriptor(int n,
+ const MachMsgPortDescriptor &desc) {
+ MachMsgPortDescriptor *desc_array =
+ reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding);
+ desc_array[n] = desc;
+}
+
+//==============================================================================
+// returns true if successful otherwise there was not enough space
+bool MachMessage::AddDescriptor(const MachMsgPortDescriptor &desc) {
+ // first check to make sure we have enough space
+ int size = CalculateSize();
+ int new_size = size + sizeof(MachMsgPortDescriptor);
+
+ if ((unsigned)new_size > storage_length_bytes_) {
+ return false; // not enough space
+ }
+
+ // unfortunately, we need to move the data to allow space for the
+ // new descriptor
+ u_int8_t *p = reinterpret_cast<u_int8_t*>(GetDataPacket());
+ bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t));
+
+ SetDescriptor(GetDescriptorCount(), desc);
+ SetDescriptorCount(GetDescriptorCount() + 1);
+
+ CalculateSize();
+
+ return true;
+}
+
+//==============================================================================
+void MachMessage::SetDescriptorCount(int n) {
+ storage_->body.msgh_descriptor_count = n;
+
+ if (n > 0) {
+ Head()->msgh_bits |= MACH_MSGH_BITS_COMPLEX;
+ } else {
+ Head()->msgh_bits &= ~MACH_MSGH_BITS_COMPLEX;
+ }
+}
+
+//==============================================================================
+MachMsgPortDescriptor *MachMessage::GetDescriptor(int n) {
+ if (n < GetDescriptorCount()) {
+ MachMsgPortDescriptor *desc =
+ reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding);
+ return desc + n;
+ }
+
+ return nil;
+}
+
+//==============================================================================
+mach_port_t MachMessage::GetTranslatedPort(int n) {
+ if (n < GetDescriptorCount()) {
+ return GetDescriptor(n)->GetMachPort();
+ }
+ return MACH_PORT_NULL;
+}
+
+#pragma mark -
+
+//==============================================================================
+// create a new mach port for receiving messages and register a name for it
+ReceivePort::ReceivePort(const char *receive_port_name) {
+ mach_port_t current_task = mach_task_self();
+
+ init_result_ = mach_port_allocate(current_task,
+ MACH_PORT_RIGHT_RECEIVE,
+ &port_);
+
+ if (init_result_ != KERN_SUCCESS)
+ return;
+
+ init_result_ = mach_port_insert_right(current_task,
+ port_,
+ port_,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ if (init_result_ != KERN_SUCCESS)
+ return;
+
+ // Without |NSMachPortDeallocateNone|, the NSMachPort seems to deallocate
+ // receive rights on port when it is eventually released. It is not necessary
+ // to deallocate any rights here as |port_| is fully deallocated in the
+ // ReceivePort destructor.
+ NSPort *ns_port = [NSMachPort portWithMachPort:port_
+ options:NSMachPortDeallocateNone];
+ NSString *port_name = [NSString stringWithUTF8String:receive_port_name];
+ [[NSMachBootstrapServer sharedInstance] registerPort:ns_port name:port_name];
+}
+
+//==============================================================================
+// create a new mach port for receiving messages
+ReceivePort::ReceivePort() {
+ mach_port_t current_task = mach_task_self();
+
+ init_result_ = mach_port_allocate(current_task,
+ MACH_PORT_RIGHT_RECEIVE,
+ &port_);
+
+ if (init_result_ != KERN_SUCCESS)
+ return;
+
+ init_result_ = mach_port_insert_right(current_task,
+ port_,
+ port_,
+ MACH_MSG_TYPE_MAKE_SEND);
+}
+
+//==============================================================================
+// Given an already existing mach port, use it. We take ownership of the
+// port and deallocate it in our destructor.
+ReceivePort::ReceivePort(mach_port_t receive_port)
+ : port_(receive_port),
+ init_result_(KERN_SUCCESS) {
+}
+
+//==============================================================================
+ReceivePort::~ReceivePort() {
+ if (init_result_ == KERN_SUCCESS)
+ mach_port_deallocate(mach_task_self(), port_);
+}
+
+//==============================================================================
+kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message,
+ mach_msg_timeout_t timeout) {
+ if (!out_message) {
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ // return any error condition encountered in constructor
+ if (init_result_ != KERN_SUCCESS)
+ return init_result_;
+
+ out_message->Head()->msgh_bits = 0;
+ out_message->Head()->msgh_local_port = port_;
+ out_message->Head()->msgh_remote_port = MACH_PORT_NULL;
+ out_message->Head()->msgh_reserved = 0;
+ out_message->Head()->msgh_id = 0;
+
+ mach_msg_option_t rcv_options = MACH_RCV_MSG;
+ if (timeout != MACH_MSG_TIMEOUT_NONE)
+ rcv_options |= MACH_RCV_TIMEOUT;
+
+ kern_return_t result = mach_msg(out_message->Head(),
+ rcv_options,
+ 0,
+ out_message->MaxSize(),
+ port_,
+ timeout, // timeout in ms
+ MACH_PORT_NULL);
+
+ return result;
+}
+
+#pragma mark -
+
+//==============================================================================
+// get a port with send rights corresponding to a named registered service
+MachPortSender::MachPortSender(const char *receive_port_name) {
+ mach_port_t bootstrap_port = 0;
+ init_result_ = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
+
+ if (init_result_ != KERN_SUCCESS)
+ return;
+
+ init_result_ = bootstrap_look_up(bootstrap_port,
+ const_cast<char*>(receive_port_name),
+ &send_port_);
+}
+
+//==============================================================================
+MachPortSender::MachPortSender(mach_port_t send_port)
+ : send_port_(send_port),
+ init_result_(KERN_SUCCESS) {
+}
+
+//==============================================================================
+kern_return_t MachPortSender::SendMessage(MachSendMessage &message,
+ mach_msg_timeout_t timeout) {
+ if (message.Head()->msgh_size == 0) {
+ NOTREACHED();
+ return KERN_INVALID_VALUE; // just for safety -- never should occur
+ };
+
+ if (init_result_ != KERN_SUCCESS)
+ return init_result_;
+
+ message.Head()->msgh_remote_port = send_port_;
+
+ kern_return_t result = mach_msg(message.Head(),
+ MACH_SEND_MSG | MACH_SEND_TIMEOUT,
+ message.Head()->msgh_size,
+ 0,
+ MACH_PORT_NULL,
+ timeout, // timeout in ms
+ MACH_PORT_NULL);
+
+ return result;
+}
+
+//==============================================================================
+
+namespace mac {
+
+kern_return_t GetNumberOfMachPorts(mach_port_t task_port, int* num_ports) {
+ mach_port_name_array_t names;
+ mach_msg_type_number_t names_count;
+ mach_port_type_array_t types;
+ mach_msg_type_number_t types_count;
+
+ // A friendlier interface would allow NULL buffers to only get the counts.
+ kern_return_t kr = mach_port_names(task_port, &names, &names_count,
+ &types, &types_count);
+ if (kr != KERN_SUCCESS)
+ return kr;
+
+ // The documentation states this is an invariant.
+ DCHECK_EQ(names_count, types_count);
+ *num_ports = names_count;
+
+ kr = vm_deallocate(mach_task_self(),
+ reinterpret_cast<vm_address_t>(names),
+ names_count * sizeof(mach_port_name_array_t));
+ kr = vm_deallocate(mach_task_self(),
+ reinterpret_cast<vm_address_t>(types),
+ types_count * sizeof(mach_port_type_array_t));
+
+ return kr;
+}
+
+} // namespace mac
+
+} // namespace base
diff --git a/src/base/md5.cc b/src/base/md5.cc
new file mode 100644
index 0000000..754994c
--- /dev/null
+++ b/src/base/md5.cc
@@ -0,0 +1,292 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The original file was copied from sqlite, and was in the public domain.
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#include "base/md5.h"
+
+#include "base/basictypes.h"
+
+namespace {
+
+struct Context {
+ uint32 buf[4];
+ uint32 bits[2];
+ unsigned char in[64];
+};
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs) {
+ uint32 t;
+ do {
+ t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
+ ((unsigned)buf[1]<<8 | buf[0]);
+ *(uint32 *)buf = t;
+ buf += 4;
+ } while (--longs);
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(uint32 buf[4], const uint32 in[16]) {
+ register uint32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+} // namespace
+
+namespace base {
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(MD5Context* context) {
+ struct Context *ctx = (struct Context *)context;
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(MD5Context* context, const StringPiece& data) {
+ const unsigned char* inbuf = (const unsigned char*)data.data();
+ size_t len = data.size();
+ struct Context *ctx = (struct Context *)context;
+ const unsigned char* buf = (const unsigned char*)inbuf;
+ uint32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32)len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += static_cast<uint32>(len >> 29);
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *)ctx->in + t;
+
+ t = 64-t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+ buf += t;
+ len -= t;
+ }
+
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(MD5Digest* digest, MD5Context* context) {
+ struct Context *ctx = (struct Context *)context;
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count-8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0];
+ ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+ byteReverse((unsigned char *)ctx->buf, 4);
+ memcpy(digest->a, ctx->buf, 16);
+ memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
+}
+
+std::string MD5DigestToBase16(const MD5Digest& digest) {
+ static char const zEncode[] = "0123456789abcdef";
+
+ std::string ret;
+ ret.resize(32);
+
+ int j = 0;
+ for (int i = 0; i < 16; i ++) {
+ int a = digest.a[i];
+ ret[j++] = zEncode[(a>>4)&0xf];
+ ret[j++] = zEncode[a & 0xf];
+ }
+ return ret;
+}
+
+void MD5Sum(const void* data, size_t length, MD5Digest* digest) {
+ MD5Context ctx;
+ MD5Init(&ctx);
+ MD5Update(&ctx,
+ StringPiece(reinterpret_cast<const char*>(data), length));
+ MD5Final(digest, &ctx);
+}
+
+std::string MD5String(const StringPiece& str) {
+ MD5Digest digest;
+ MD5Sum(str.data(), str.length(), &digest);
+ return MD5DigestToBase16(digest);
+}
+
+} // namespace base
diff --git a/src/base/md5.h b/src/base/md5.h
new file mode 100644
index 0000000..4a29a2d
--- /dev/null
+++ b/src/base/md5.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MD5_H_
+#define BASE_MD5_H_
+
+#include "base/base_export.h"
+#include "base/string_piece.h"
+
+namespace base {
+
+// MD5 stands for Message Digest algorithm 5.
+// MD5 is a robust hash function, designed for cyptography, but often used
+// for file checksums. The code is complex and slow, but has few
+// collisions.
+// See Also:
+// http://en.wikipedia.org/wiki/MD5
+
+// These functions perform MD5 operations. The simplest call is MD5Sum() to
+// generate the MD5 sum of the given data.
+//
+// You can also compute the MD5 sum of data incrementally by making multiple
+// calls to MD5Update():
+// MD5Context ctx; // intermediate MD5 data: do not use
+// MD5Init(&ctx);
+// MD5Update(&ctx, data1, length1);
+// MD5Update(&ctx, data2, length2);
+// ...
+//
+// MD5Digest digest; // the result of the computation
+// MD5Final(&digest, &ctx);
+//
+// You can call MD5DigestToBase16() to generate a string of the digest.
+
+// The output of an MD5 operation.
+struct MD5Digest {
+ unsigned char a[16];
+};
+
+// Used for storing intermediate data during an MD5 computation. Callers
+// should not access the data.
+typedef char MD5Context[88];
+
+// Computes the MD5 sum of the given data buffer with the given length.
+// The given 'digest' structure will be filled with the result data.
+BASE_EXPORT void MD5Sum(const void* data, size_t length, MD5Digest* digest);
+
+// Initializes the given MD5 context structure for subsequent calls to
+// MD5Update().
+BASE_EXPORT void MD5Init(MD5Context* context);
+
+// For the given buffer of |data| as a StringPiece, updates the given MD5
+// context with the sum of the data. You can call this any number of times
+// during the computation, except that MD5Init() must have been called first.
+BASE_EXPORT void MD5Update(MD5Context* context, const StringPiece& data);
+
+// Finalizes the MD5 operation and fills the buffer with the digest.
+BASE_EXPORT void MD5Final(MD5Digest* digest, MD5Context* context);
+
+// Converts a digest into human-readable hexadecimal.
+BASE_EXPORT std::string MD5DigestToBase16(const MD5Digest& digest);
+
+// Returns the MD5 (in hexadecimal) of a string.
+BASE_EXPORT std::string MD5String(const StringPiece& str);
+
+} // namespace base
+
+#endif // BASE_MD5_H_
diff --git a/src/base/md5_unittest.cc b/src/base/md5_unittest.cc
new file mode 100644
index 0000000..a20d819
--- /dev/null
+++ b/src/base/md5_unittest.cc
@@ -0,0 +1,207 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string.h>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/md5.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(MD5, DigestToBase16) {
+ MD5Digest digest;
+
+ int data[] = {
+ 0xd4, 0x1d, 0x8c, 0xd9,
+ 0x8f, 0x00, 0xb2, 0x04,
+ 0xe9, 0x80, 0x09, 0x98,
+ 0xec, 0xf8, 0x42, 0x7e
+ };
+
+ for (int i = 0; i < 16; ++i)
+ digest.a[i] = data[i] & 0xff;
+
+ std::string actual = MD5DigestToBase16(digest);
+ std::string expected = "d41d8cd98f00b204e9800998ecf8427e";
+
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(MD5, MD5SumEmtpyData) {
+ MD5Digest digest;
+ const char* data = "";
+
+ MD5Sum(data, strlen(data), &digest);
+
+ int expected[] = {
+ 0xd4, 0x1d, 0x8c, 0xd9,
+ 0x8f, 0x00, 0xb2, 0x04,
+ 0xe9, 0x80, 0x09, 0x98,
+ 0xec, 0xf8, 0x42, 0x7e
+ };
+
+ for (int i = 0; i < 16; ++i)
+ EXPECT_EQ(expected[i], digest.a[i] & 0xFF);
+}
+
+TEST(MD5, MD5SumOneByteData) {
+ MD5Digest digest;
+ const char* data = "a";
+
+ MD5Sum(data, strlen(data), &digest);
+
+ int expected[] = {
+ 0x0c, 0xc1, 0x75, 0xb9,
+ 0xc0, 0xf1, 0xb6, 0xa8,
+ 0x31, 0xc3, 0x99, 0xe2,
+ 0x69, 0x77, 0x26, 0x61
+ };
+
+ for (int i = 0; i < 16; ++i)
+ EXPECT_EQ(expected[i], digest.a[i] & 0xFF);
+}
+
+TEST(MD5, MD5SumLongData) {
+ const int length = 10 * 1024 * 1024 + 1;
+ scoped_array<char> data(new char[length]);
+
+ for (int i = 0; i < length; ++i)
+ data[i] = i & 0xFF;
+
+ MD5Digest digest;
+ MD5Sum(data.get(), length, &digest);
+
+ int expected[] = {
+ 0x90, 0xbd, 0x6a, 0xd9,
+ 0x0a, 0xce, 0xf5, 0xad,
+ 0xaa, 0x92, 0x20, 0x3e,
+ 0x21, 0xc7, 0xa1, 0x3e
+ };
+
+ for (int i = 0; i < 16; ++i)
+ EXPECT_EQ(expected[i], digest.a[i] & 0xFF);
+}
+
+TEST(MD5, ContextWithEmptyData) {
+ MD5Context ctx;
+ MD5Init(&ctx);
+
+ MD5Digest digest;
+ MD5Final(&digest, &ctx);
+
+ int expected[] = {
+ 0xd4, 0x1d, 0x8c, 0xd9,
+ 0x8f, 0x00, 0xb2, 0x04,
+ 0xe9, 0x80, 0x09, 0x98,
+ 0xec, 0xf8, 0x42, 0x7e
+ };
+
+ for (int i = 0; i < 16; ++i)
+ EXPECT_EQ(expected[i], digest.a[i] & 0xFF);
+}
+
+TEST(MD5, ContextWithLongData) {
+ MD5Context ctx;
+ MD5Init(&ctx);
+
+ const int length = 10 * 1024 * 1024 + 1;
+ scoped_array<char> data(new char[length]);
+
+ for (int i = 0; i < length; ++i)
+ data[i] = i & 0xFF;
+
+ int total = 0;
+ while (total < length) {
+ int len = 4097; // intentionally not 2^k.
+ if (len > length - total)
+ len = length - total;
+
+ MD5Update(&ctx,
+ StringPiece(reinterpret_cast<char*>(data.get() + total), len));
+ total += len;
+ }
+
+ EXPECT_EQ(length, total);
+
+ MD5Digest digest;
+ MD5Final(&digest, &ctx);
+
+ int expected[] = {
+ 0x90, 0xbd, 0x6a, 0xd9,
+ 0x0a, 0xce, 0xf5, 0xad,
+ 0xaa, 0x92, 0x20, 0x3e,
+ 0x21, 0xc7, 0xa1, 0x3e
+ };
+
+ for (int i = 0; i < 16; ++i)
+ EXPECT_EQ(expected[i], digest.a[i] & 0xFF);
+}
+
+// Example data from http://www.ietf.org/rfc/rfc1321.txt A.5 Test Suite
+TEST(MD5, MD5StringTestSuite1) {
+ std::string actual = MD5String("");
+ std::string expected = "d41d8cd98f00b204e9800998ecf8427e";
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(MD5, MD5StringTestSuite2) {
+ std::string actual = MD5String("a");
+ std::string expected = "0cc175b9c0f1b6a831c399e269772661";
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(MD5, MD5StringTestSuite3) {
+ std::string actual = MD5String("abc");
+ std::string expected = "900150983cd24fb0d6963f7d28e17f72";
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(MD5, MD5StringTestSuite4) {
+ std::string actual = MD5String("message digest");
+ std::string expected = "f96b697d7cb7938d525a2f31aaf161d0";
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(MD5, MD5StringTestSuite5) {
+ std::string actual = MD5String("abcdefghijklmnopqrstuvwxyz");
+ std::string expected = "c3fcd3d76192e4007dfb496cca67e13b";
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(MD5, MD5StringTestSuite6) {
+ std::string actual = MD5String("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789");
+ std::string expected = "d174ab98d277d9f5a5611c2c9f419d9f";
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(MD5, MD5StringTestSuite7) {
+ std::string actual = MD5String("12345678901234567890"
+ "12345678901234567890"
+ "12345678901234567890"
+ "12345678901234567890");
+ std::string expected = "57edf4a22be3c955ac49da2e2107b67a";
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(MD5, ContextWithStringData) {
+ MD5Context ctx;
+ MD5Init(&ctx);
+
+ MD5Update(&ctx, "abc");
+
+ MD5Digest digest;
+ MD5Final(&digest, &ctx);
+
+ std::string actual = MD5DigestToBase16(digest);
+ std::string expected = "900150983cd24fb0d6963f7d28e17f72";
+
+ EXPECT_EQ(expected, actual);
+}
+
+} // namespace base
diff --git a/src/base/memory/aligned_memory.cc b/src/base/memory/aligned_memory.cc
new file mode 100644
index 0000000..6b0ed98
--- /dev/null
+++ b/src/base/memory/aligned_memory.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 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/memory/aligned_memory.h"
+
+#include "base/logging.h"
+
+#if defined(OS_ANDROID) || defined(OS_NACL) || defined(__LB_SHELL__)
+#include <malloc.h>
+#endif
+
+namespace base {
+
+void* AlignedAlloc(size_t size, size_t alignment) {
+ DCHECK_GT(size, 0U);
+ DCHECK_EQ(alignment & (alignment - 1), 0U);
+ DCHECK_EQ(alignment % sizeof(void*), 0U);
+ void* ptr = NULL;
+#if defined(OS_STARBOARD)
+ ptr = SbMemoryAllocateAligned(alignment, size);
+#elif defined(COMPILER_MSVC)
+ ptr = _aligned_malloc(size, alignment);
+// Both Android and NaCl technically support posix_memalign(), but do not expose
+// it in the current version of the library headers used by Chrome. Luckily,
+// memalign() on both platforms returns pointers which can safely be used with
+// free(), so we can use it instead. Issues filed with each project for docs:
+// http://code.google.com/p/android/issues/detail?id=35391
+// http://code.google.com/p/chromium/issues/detail?id=138579
+#elif defined(OS_ANDROID) || defined(OS_NACL) || defined(__LB_SHELL__)
+ ptr = memalign(alignment, size);
+#else
+ if (posix_memalign(&ptr, alignment, size))
+ ptr = NULL;
+#endif
+ // Since aligned allocations may fail for non-memory related reasons, force a
+ // crash if we encounter a failed allocation; maintaining consistent behavior
+ // with a normal allocation failure in Chrome.
+ if (!ptr) {
+ DLOG(ERROR) << "If you crashed here, your aligned allocation is incorrect: "
+ << "size=" << size << ", alignment=" << alignment;
+ CHECK(false);
+ }
+ // Sanity check alignment just to be safe.
+ DCHECK_EQ(reinterpret_cast<uintptr_t>(ptr) & (alignment - 1), 0U);
+ return ptr;
+}
+
+} // namespace base
diff --git a/src/base/memory/aligned_memory.h b/src/base/memory/aligned_memory.h
new file mode 100644
index 0000000..040b56d
--- /dev/null
+++ b/src/base/memory/aligned_memory.h
@@ -0,0 +1,118 @@
+// Copyright (c) 2012 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.
+
+// AlignedMemory is a POD type that gives you a portable way to specify static
+// or local stack data of a given alignment and size. For example, if you need
+// static storage for a class, but you want manual control over when the object
+// is constructed and destructed (you don't want static initialization and
+// destruction), use AlignedMemory:
+//
+// static AlignedMemory<sizeof(MyClass), ALIGNOF(MyClass)> my_class;
+//
+// // ... at runtime:
+// new(my_class.void_data()) MyClass();
+//
+// // ... use it:
+// MyClass* mc = my_class.data_as<MyClass>();
+//
+// // ... later, to destruct my_class:
+// my_class.data_as<MyClass>()->MyClass::~MyClass();
+//
+// Alternatively, a runtime sized aligned allocation can be created:
+//
+// float* my_array = static_cast<float*>(AlignedAlloc(size, alignment));
+//
+// // ... later, to release the memory:
+// AlignedFree(my_array);
+//
+// Or using scoped_ptr_malloc:
+//
+// scoped_ptr_malloc<float, ScopedPtrAlignedFree> my_array(
+// static_cast<float*>(AlignedAlloc(size, alignment)));
+
+#ifndef BASE_MEMORY_ALIGNED_MEMORY_H_
+#define BASE_MEMORY_ALIGNED_MEMORY_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+
+#if defined(COMPILER_MSVC)
+#include <malloc.h>
+#elif defined(OS_STARBOARD)
+#include "starboard/memory.h"
+#else
+#include <stdlib.h>
+#endif
+
+namespace base {
+
+// AlignedMemory is specialized for all supported alignments.
+// Make sure we get a compiler error if someone uses an unsupported alignment.
+template <size_t Size, size_t ByteAlignment>
+struct AlignedMemory {};
+
+#define BASE_DECL_ALIGNED_MEMORY(byte_alignment) \
+ template <size_t Size> \
+ class AlignedMemory<Size, byte_alignment> { \
+ public: \
+ ALIGNAS(byte_alignment) uint8 data_[Size]; \
+ void* void_data() { return static_cast<void*>(data_); } \
+ const void* void_data() const { \
+ return static_cast<const void*>(data_); \
+ } \
+ template<typename Type> \
+ Type* data_as() { return static_cast<Type*>(void_data()); } \
+ template<typename Type> \
+ const Type* data_as() const { \
+ return static_cast<const Type*>(void_data()); \
+ } \
+ private: \
+ void* operator new(size_t); \
+ void operator delete(void*); \
+ }
+
+// Specialization for all alignments is required because MSVC (as of VS 2008)
+// does not understand ALIGNAS(ALIGNOF(Type)) or ALIGNAS(template_param).
+// Greater than 4096 alignment is not supported by some compilers, so 4096 is
+// the maximum specified here.
+BASE_DECL_ALIGNED_MEMORY(1);
+BASE_DECL_ALIGNED_MEMORY(2);
+BASE_DECL_ALIGNED_MEMORY(4);
+BASE_DECL_ALIGNED_MEMORY(8);
+BASE_DECL_ALIGNED_MEMORY(16);
+BASE_DECL_ALIGNED_MEMORY(32);
+BASE_DECL_ALIGNED_MEMORY(64);
+BASE_DECL_ALIGNED_MEMORY(128);
+BASE_DECL_ALIGNED_MEMORY(256);
+BASE_DECL_ALIGNED_MEMORY(512);
+BASE_DECL_ALIGNED_MEMORY(1024);
+BASE_DECL_ALIGNED_MEMORY(2048);
+BASE_DECL_ALIGNED_MEMORY(4096);
+
+#undef BASE_DECL_ALIGNED_MEMORY
+
+BASE_EXPORT void* AlignedAlloc(size_t size, size_t alignment);
+
+inline void AlignedFree(void* ptr) {
+#if defined(COMPILER_MSVC)
+ _aligned_free(ptr);
+#elif defined(OS_STARBOARD)
+ SbMemoryFreeAligned(ptr);
+#else
+ free(ptr);
+#endif
+}
+
+// Helper class for use with scoped_ptr_malloc.
+class BASE_EXPORT ScopedPtrAlignedFree {
+ public:
+ inline void operator()(void* ptr) const {
+ AlignedFree(ptr);
+ }
+};
+
+} // namespace base
+
+#endif // BASE_MEMORY_ALIGNED_MEMORY_H_
diff --git a/src/base/memory/aligned_memory_unittest.cc b/src/base/memory/aligned_memory_unittest.cc
new file mode 100644
index 0000000..ba82618
--- /dev/null
+++ b/src/base/memory/aligned_memory_unittest.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2012 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/memory/aligned_memory.h"
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#define EXPECT_ALIGNED(ptr, align) \
+ EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(ptr) & (align - 1))
+
+namespace {
+
+using base::AlignedMemory;
+
+TEST(AlignedMemoryTest, StaticAlignment) {
+ static AlignedMemory<8, 8> raw8;
+ static AlignedMemory<8, 16> raw16;
+ static AlignedMemory<8, 256> raw256;
+ static AlignedMemory<8, 4096> raw4096;
+
+ EXPECT_EQ(8u, ALIGNOF(raw8));
+ EXPECT_EQ(16u, ALIGNOF(raw16));
+ EXPECT_EQ(256u, ALIGNOF(raw256));
+ EXPECT_EQ(4096u, ALIGNOF(raw4096));
+
+ EXPECT_ALIGNED(raw8.void_data(), 8);
+ EXPECT_ALIGNED(raw16.void_data(), 16);
+ EXPECT_ALIGNED(raw256.void_data(), 256);
+ EXPECT_ALIGNED(raw4096.void_data(), 4096);
+}
+
+TEST(AlignedMemoryTest, StackAlignment) {
+ AlignedMemory<8, 8> raw8;
+ AlignedMemory<8, 16> raw16;
+ AlignedMemory<8, 256> raw256;
+
+ EXPECT_EQ(8u, ALIGNOF(raw8));
+ EXPECT_EQ(16u, ALIGNOF(raw16));
+ EXPECT_EQ(256u, ALIGNOF(raw256));
+
+ EXPECT_ALIGNED(raw8.void_data(), 8);
+ EXPECT_ALIGNED(raw16.void_data(), 16);
+ EXPECT_ALIGNED(raw256.void_data(), 256);
+
+ // TODO: This test hits an armv7 bug in clang. crbug.com/138066
+#if !defined(ARCH_CPU_ARM_FAMILY)
+ AlignedMemory<8, 4096> raw4096;
+ EXPECT_EQ(4096u, ALIGNOF(raw4096));
+ EXPECT_ALIGNED(raw4096.void_data(), 4096);
+#endif // !(defined(OS_IOS) && defined(ARCH_CPU_ARM_FAMILY))
+}
+
+TEST(AlignedMemoryTest, DynamicAllocation) {
+ void* p = base::AlignedAlloc(8, 8);
+ EXPECT_TRUE(p);
+ EXPECT_ALIGNED(p, 8);
+ base::AlignedFree(p);
+
+ p = base::AlignedAlloc(8, 16);
+ EXPECT_TRUE(p);
+ EXPECT_ALIGNED(p, 16);
+ base::AlignedFree(p);
+
+ p = base::AlignedAlloc(8, 256);
+ EXPECT_TRUE(p);
+ EXPECT_ALIGNED(p, 256);
+ base::AlignedFree(p);
+
+ p = base::AlignedAlloc(8, 4096);
+ EXPECT_TRUE(p);
+ EXPECT_ALIGNED(p, 4096);
+ base::AlignedFree(p);
+}
+
+TEST(AlignedMemoryTest, ScopedDynamicAllocation) {
+ scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> p(
+ static_cast<float*>(base::AlignedAlloc(8, 8)));
+ EXPECT_TRUE(p.get());
+ EXPECT_ALIGNED(p.get(), 8);
+}
+
+} // namespace
diff --git a/src/base/memory/linked_ptr.h b/src/base/memory/linked_ptr.h
new file mode 100644
index 0000000..80044ad
--- /dev/null
+++ b/src/base/memory/linked_ptr.h
@@ -0,0 +1,181 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A "smart" pointer type with reference tracking. Every pointer to a
+// particular object is kept on a circular linked list. When the last pointer
+// to an object is destroyed or reassigned, the object is deleted.
+//
+// Used properly, this deletes the object when the last reference goes away.
+// There are several caveats:
+// - Like all reference counting schemes, cycles lead to leaks.
+// - Each smart pointer is actually two pointers (8 bytes instead of 4).
+// - Every time a pointer is released, the entire list of pointers to that
+// object is traversed. This class is therefore NOT SUITABLE when there
+// will often be more than two or three pointers to a particular object.
+// - References are only tracked as long as linked_ptr<> objects are copied.
+// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS
+// will happen (double deletion).
+//
+// A good use of this class is storing object references in STL containers.
+// You can safely put linked_ptr<> in a vector<>.
+// Other uses may not be as good.
+//
+// Note: If you use an incomplete type with linked_ptr<>, the class
+// *containing* linked_ptr<> must have a constructor and destructor (even
+// if they do nothing!).
+//
+// Thread Safety:
+// A linked_ptr is NOT thread safe. Copying a linked_ptr object is
+// effectively a read-write operation.
+//
+// Alternative: to linked_ptr is shared_ptr, which
+// - is also two pointers in size (8 bytes for 32 bit addresses)
+// - is thread safe for copying and deletion
+// - supports weak_ptrs
+
+#ifndef BASE_MEMORY_LINKED_PTR_H_
+#define BASE_MEMORY_LINKED_PTR_H_
+
+#include "base/logging.h" // for CHECK macros
+
+// This is used internally by all instances of linked_ptr<>. It needs to be
+// a non-template class because different types of linked_ptr<> can refer to
+// the same object (linked_ptr<Superclass>(obj) vs linked_ptr<Subclass>(obj)).
+// So, it needs to be possible for different types of linked_ptr to participate
+// in the same circular linked list, so we need a single class type here.
+//
+// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr<T>.
+class linked_ptr_internal {
+ public:
+ // Create a new circle that includes only this instance.
+ void join_new() {
+ next_ = this;
+ }
+
+ // Join an existing circle.
+ void join(linked_ptr_internal const* ptr) {
+ next_ = ptr->next_;
+ ptr->next_ = this;
+ }
+
+ // Leave whatever circle we're part of. Returns true iff we were the
+ // last member of the circle. Once this is done, you can join() another.
+ bool depart() {
+ if (next_ == this) return true;
+ linked_ptr_internal const* p = next_;
+ while (p->next_ != this) p = p->next_;
+ p->next_ = next_;
+ return false;
+ }
+
+ private:
+ mutable linked_ptr_internal const* next_;
+};
+
+template <typename T>
+class linked_ptr {
+ public:
+ typedef T element_type;
+
+ // Take over ownership of a raw pointer. This should happen as soon as
+ // possible after the object is created.
+ explicit linked_ptr(T* ptr = NULL) { capture(ptr); }
+ ~linked_ptr() { depart(); }
+
+ // Copy an existing linked_ptr<>, adding ourselves to the list of references.
+ template <typename U> linked_ptr(linked_ptr<U> const& ptr) { copy(&ptr); }
+
+ linked_ptr(linked_ptr const& ptr) {
+ DCHECK_NE(&ptr, this);
+ copy(&ptr);
+ }
+
+ // Assignment releases the old value and acquires the new.
+ template <typename U> linked_ptr& operator=(linked_ptr<U> const& ptr) {
+ depart();
+ copy(&ptr);
+ return *this;
+ }
+
+ linked_ptr& operator=(linked_ptr const& ptr) {
+ if (&ptr != this) {
+ depart();
+ copy(&ptr);
+ }
+ return *this;
+ }
+
+ // Smart pointer members.
+ void reset(T* ptr = NULL) {
+ depart();
+ capture(ptr);
+ }
+ T* get() const { return value_; }
+ T* operator->() const { return value_; }
+ T& operator*() const { return *value_; }
+ // Release ownership of the pointed object and returns it.
+ // Sole ownership by this linked_ptr object is required.
+ T* release() {
+ bool last = link_.depart();
+ CHECK(last);
+ T* v = value_;
+ value_ = NULL;
+ return v;
+ }
+
+ bool operator==(const T* p) const { return value_ == p; }
+ bool operator!=(const T* p) const { return value_ != p; }
+ template <typename U>
+ bool operator==(linked_ptr<U> const& ptr) const {
+ return value_ == ptr.get();
+ }
+ template <typename U>
+ bool operator!=(linked_ptr<U> const& ptr) const {
+ return value_ != ptr.get();
+ }
+
+ private:
+ template <typename U>
+ friend class linked_ptr;
+
+ T* value_;
+ linked_ptr_internal link_;
+
+ void depart() {
+ if (link_.depart()) delete value_;
+ }
+
+ void capture(T* ptr) {
+ value_ = ptr;
+ link_.join_new();
+ }
+
+ template <typename U> void copy(linked_ptr<U> const* ptr) {
+ value_ = ptr->get();
+ if (value_)
+ link_.join(&ptr->link_);
+ else
+ link_.join_new();
+ }
+};
+
+template<typename T> inline
+bool operator==(T* ptr, const linked_ptr<T>& x) {
+ return ptr == x.get();
+}
+
+template<typename T> inline
+bool operator!=(T* ptr, const linked_ptr<T>& x) {
+ return ptr != x.get();
+}
+
+// A function to convert T* into linked_ptr<T>
+// Doing e.g. make_linked_ptr(new FooBarBaz<type>(arg)) is a shorter notation
+// for linked_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg))
+template <typename T>
+linked_ptr<T> make_linked_ptr(T* ptr) {
+ return linked_ptr<T>(ptr);
+}
+
+#endif // BASE_MEMORY_LINKED_PTR_H_
diff --git a/src/base/memory/linked_ptr_unittest.cc b/src/base/memory/linked_ptr_unittest.cc
new file mode 100644
index 0000000..4550350
--- /dev/null
+++ b/src/base/memory/linked_ptr_unittest.cc
@@ -0,0 +1,110 @@
+// Copyright (c) 2012 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/memory/linked_ptr.h"
+#include "base/stringprintf.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+int num = 0;
+
+std::string history;
+
+// Class which tracks allocation/deallocation
+struct A {
+ A(): mynum(num++) { history += base::StringPrintf("A%d ctor\n", mynum); }
+ virtual ~A() { history += base::StringPrintf("A%d dtor\n", mynum); }
+ virtual void Use() { history += base::StringPrintf("A%d use\n", mynum); }
+ int mynum;
+};
+
+// Subclass
+struct B: public A {
+ B() { history += base::StringPrintf("B%d ctor\n", mynum); }
+ virtual ~B() { history += base::StringPrintf("B%d dtor\n", mynum); }
+ virtual void Use() OVERRIDE {
+ history += base::StringPrintf("B%d use\n", mynum);
+ }
+};
+
+} // namespace
+
+TEST(LinkedPtrTest, Test) {
+ {
+ linked_ptr<A> a0, a1, a2;
+ a0 = a0;
+ a1 = a2;
+ ASSERT_EQ(a0.get(), static_cast<A*>(NULL));
+ ASSERT_EQ(a1.get(), static_cast<A*>(NULL));
+ ASSERT_EQ(a2.get(), static_cast<A*>(NULL));
+ ASSERT_TRUE(a0 == NULL);
+ ASSERT_TRUE(a1 == NULL);
+ ASSERT_TRUE(a2 == NULL);
+
+ {
+ linked_ptr<A> a3(new A);
+ a0 = a3;
+ ASSERT_TRUE(a0 == a3);
+ ASSERT_TRUE(a0 != NULL);
+ ASSERT_TRUE(a0.get() == a3);
+ ASSERT_TRUE(a0 == a3.get());
+ linked_ptr<A> a4(a0);
+ a1 = a4;
+ linked_ptr<A> a5(new A);
+ ASSERT_TRUE(a5.get() != a3);
+ ASSERT_TRUE(a5 != a3.get());
+ a2 = a5;
+ linked_ptr<B> b0(new B);
+ linked_ptr<A> a6(b0);
+ ASSERT_TRUE(b0 == a6);
+ ASSERT_TRUE(a6 == b0);
+ ASSERT_TRUE(b0 != NULL);
+ a5 = b0;
+ a5 = b0;
+ a3->Use();
+ a4->Use();
+ a5->Use();
+ a6->Use();
+ b0->Use();
+ (*b0).Use();
+ b0.get()->Use();
+ }
+
+ a0->Use();
+ a1->Use();
+ a2->Use();
+
+ a1 = a2;
+ a2.reset(new A);
+ a0.reset();
+
+ linked_ptr<A> a7;
+ }
+
+ ASSERT_EQ(history,
+ "A0 ctor\n"
+ "A1 ctor\n"
+ "A2 ctor\n"
+ "B2 ctor\n"
+ "A0 use\n"
+ "A0 use\n"
+ "B2 use\n"
+ "B2 use\n"
+ "B2 use\n"
+ "B2 use\n"
+ "B2 use\n"
+ "B2 dtor\n"
+ "A2 dtor\n"
+ "A0 use\n"
+ "A0 use\n"
+ "A1 use\n"
+ "A3 ctor\n"
+ "A0 dtor\n"
+ "A3 dtor\n"
+ "A1 dtor\n"
+ );
+}
diff --git a/src/base/memory/manual_constructor.h b/src/base/memory/manual_constructor.h
new file mode 100644
index 0000000..9275f73
--- /dev/null
+++ b/src/base/memory/manual_constructor.h
@@ -0,0 +1,125 @@
+// Copyright (c) 2012 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.
+
+// ManualConstructor statically-allocates space in which to store some
+// object, but does not initialize it. You can then call the constructor
+// and destructor for the object yourself as you see fit. This is useful
+// for memory management optimizations, where you want to initialize and
+// destroy an object multiple times but only allocate it once.
+//
+// (When I say ManualConstructor statically allocates space, I mean that
+// the ManualConstructor object itself is forced to be the right size.)
+//
+// For example usage, check out base/containers/small_map.h.
+
+#ifndef BASE_MEMORY_MANUAL_CONSTRUCTOR_H_
+#define BASE_MEMORY_MANUAL_CONSTRUCTOR_H_
+
+#include <stddef.h>
+
+#include "base/memory/aligned_memory.h"
+
+namespace base {
+
+template <typename Type>
+class ManualConstructor {
+ public:
+ // No constructor or destructor because one of the most useful uses of
+ // this class is as part of a union, and members of a union cannot have
+ // constructors or destructors. And, anyway, the whole point of this
+ // class is to bypass these.
+
+ // Support users creating arrays of ManualConstructor<>s. This ensures that
+ // the array itself has the correct alignment.
+ static void* operator new[](size_t size) {
+#if defined(COMPILER_MSVC)
+ return AlignedAlloc(size, __alignof(Type));
+#else
+ return AlignedAlloc(size, __alignof__(Type));
+#endif
+ }
+ static void operator delete[](void* mem) {
+ AlignedFree(mem);
+ }
+
+ inline Type* get() {
+ return space_.template data_as<Type>();
+ }
+ inline const Type* get() const {
+ return space_.template data_as<Type>();
+ }
+
+ inline Type* operator->() { return get(); }
+ inline const Type* operator->() const { return get(); }
+
+ inline Type& operator*() { return *get(); }
+ inline const Type& operator*() const { return *get(); }
+
+ // You can pass up to eight constructor arguments as arguments of Init().
+ inline void Init() {
+ new(space_.void_data()) Type;
+ }
+
+ template <typename T1>
+ inline void Init(const T1& p1) {
+ new(space_.void_data()) Type(p1);
+ }
+
+ template <typename T1, typename T2>
+ inline void Init(const T1& p1, const T2& p2) {
+ new(space_.void_data()) Type(p1, p2);
+ }
+
+ template <typename T1, typename T2, typename T3>
+ inline void Init(const T1& p1, const T2& p2, const T3& p3) {
+ new(space_.void_data()) Type(p1, p2, p3);
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4>
+ inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4) {
+ new(space_.void_data()) Type(p1, p2, p3, p4);
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5>
+ inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4,
+ const T5& p5) {
+ new(space_.void_data()) Type(p1, p2, p3, p4, p5);
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+ inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4,
+ const T5& p5, const T6& p6) {
+ new(space_.void_data()) Type(p1, p2, p3, p4, p5, p6);
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+ inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4,
+ const T5& p5, const T6& p6, const T7& p7) {
+ new(space_.void_data()) Type(p1, p2, p3, p4, p5, p6, p7);
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+ inline void Init(const T1& p1, const T2& p2, const T3& p3, const T4& p4,
+ const T5& p5, const T6& p6, const T7& p7, const T8& p8) {
+ new(space_.void_data()) Type(p1, p2, p3, p4, p5, p6, p7, p8);
+ }
+
+ inline void Destroy() {
+ get()->~Type();
+ }
+
+ private:
+#if defined(COMPILER_MSVC)
+ AlignedMemory<sizeof(Type), __alignof(Type)> space_;
+#else
+ AlignedMemory<sizeof(Type), __alignof__(Type)> space_;
+#endif
+};
+
+} // namespace base
+
+#endif // BASE_MEMORY_MANUAL_CONSTRUCTOR_H_
diff --git a/src/base/memory/raw_scoped_refptr_mismatch_checker.h b/src/base/memory/raw_scoped_refptr_mismatch_checker.h
new file mode 100644
index 0000000..7974f30
--- /dev/null
+++ b/src/base/memory/raw_scoped_refptr_mismatch_checker.h
@@ -0,0 +1,129 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_
+#define BASE_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/template_util.h"
+#include "base/tuple.h"
+#include "build/build_config.h"
+
+// It is dangerous to post a task with a T* argument where T is a subtype of
+// RefCounted(Base|ThreadSafeBase), since by the time the parameter is used, the
+// object may already have been deleted since it was not held with a
+// scoped_refptr. Example: http://crbug.com/27191
+// The following set of traits are designed to generate a compile error
+// whenever this antipattern is attempted.
+
+namespace base {
+
+// This is a base internal implementation file used by task.h and callback.h.
+// Not for public consumption, so we wrap it in namespace internal.
+namespace internal {
+
+template <typename T>
+struct NeedsScopedRefptrButGetsRawPtr {
+#if defined(OS_WIN)
+ enum {
+ value = base::false_type::value
+ };
+#else
+ enum {
+ // Human readable translation: you needed to be a scoped_refptr if you are a
+ // raw pointer type and are convertible to a RefCounted(Base|ThreadSafeBase)
+ // type.
+ value = (is_pointer<T>::value &&
+ (is_convertible<T, subtle::RefCountedBase*>::value ||
+ is_convertible<T, subtle::RefCountedThreadSafeBase*>::value))
+ };
+#endif
+};
+
+template <typename Params>
+struct ParamsUseScopedRefptrCorrectly {
+ enum { value = 0 };
+};
+
+template <>
+struct ParamsUseScopedRefptrCorrectly<Tuple0> {
+ enum { value = 1 };
+};
+
+template <typename A>
+struct ParamsUseScopedRefptrCorrectly<Tuple1<A> > {
+ enum { value = !NeedsScopedRefptrButGetsRawPtr<A>::value };
+};
+
+template <typename A, typename B>
+struct ParamsUseScopedRefptrCorrectly<Tuple2<A, B> > {
+ enum { value = !(NeedsScopedRefptrButGetsRawPtr<A>::value ||
+ NeedsScopedRefptrButGetsRawPtr<B>::value) };
+};
+
+template <typename A, typename B, typename C>
+struct ParamsUseScopedRefptrCorrectly<Tuple3<A, B, C> > {
+ enum { value = !(NeedsScopedRefptrButGetsRawPtr<A>::value ||
+ NeedsScopedRefptrButGetsRawPtr<B>::value ||
+ NeedsScopedRefptrButGetsRawPtr<C>::value) };
+};
+
+template <typename A, typename B, typename C, typename D>
+struct ParamsUseScopedRefptrCorrectly<Tuple4<A, B, C, D> > {
+ enum { value = !(NeedsScopedRefptrButGetsRawPtr<A>::value ||
+ NeedsScopedRefptrButGetsRawPtr<B>::value ||
+ NeedsScopedRefptrButGetsRawPtr<C>::value ||
+ NeedsScopedRefptrButGetsRawPtr<D>::value) };
+};
+
+template <typename A, typename B, typename C, typename D, typename E>
+struct ParamsUseScopedRefptrCorrectly<Tuple5<A, B, C, D, E> > {
+ enum { value = !(NeedsScopedRefptrButGetsRawPtr<A>::value ||
+ NeedsScopedRefptrButGetsRawPtr<B>::value ||
+ NeedsScopedRefptrButGetsRawPtr<C>::value ||
+ NeedsScopedRefptrButGetsRawPtr<D>::value ||
+ NeedsScopedRefptrButGetsRawPtr<E>::value) };
+};
+
+template <typename A, typename B, typename C, typename D, typename E,
+ typename F>
+struct ParamsUseScopedRefptrCorrectly<Tuple6<A, B, C, D, E, F> > {
+ enum { value = !(NeedsScopedRefptrButGetsRawPtr<A>::value ||
+ NeedsScopedRefptrButGetsRawPtr<B>::value ||
+ NeedsScopedRefptrButGetsRawPtr<C>::value ||
+ NeedsScopedRefptrButGetsRawPtr<D>::value ||
+ NeedsScopedRefptrButGetsRawPtr<E>::value ||
+ NeedsScopedRefptrButGetsRawPtr<F>::value) };
+};
+
+template <typename A, typename B, typename C, typename D, typename E,
+ typename F, typename G>
+struct ParamsUseScopedRefptrCorrectly<Tuple7<A, B, C, D, E, F, G> > {
+ enum { value = !(NeedsScopedRefptrButGetsRawPtr<A>::value ||
+ NeedsScopedRefptrButGetsRawPtr<B>::value ||
+ NeedsScopedRefptrButGetsRawPtr<C>::value ||
+ NeedsScopedRefptrButGetsRawPtr<D>::value ||
+ NeedsScopedRefptrButGetsRawPtr<E>::value ||
+ NeedsScopedRefptrButGetsRawPtr<F>::value ||
+ NeedsScopedRefptrButGetsRawPtr<G>::value) };
+};
+
+template <typename A, typename B, typename C, typename D, typename E,
+ typename F, typename G, typename H>
+struct ParamsUseScopedRefptrCorrectly<Tuple8<A, B, C, D, E, F, G, H> > {
+ enum { value = !(NeedsScopedRefptrButGetsRawPtr<A>::value ||
+ NeedsScopedRefptrButGetsRawPtr<B>::value ||
+ NeedsScopedRefptrButGetsRawPtr<C>::value ||
+ NeedsScopedRefptrButGetsRawPtr<D>::value ||
+ NeedsScopedRefptrButGetsRawPtr<E>::value ||
+ NeedsScopedRefptrButGetsRawPtr<F>::value ||
+ NeedsScopedRefptrButGetsRawPtr<G>::value ||
+ NeedsScopedRefptrButGetsRawPtr<H>::value) };
+};
+
+} // namespace internal
+
+} // namespace base
+
+#endif // BASE_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_
diff --git a/src/base/memory/ref_counted.cc b/src/base/memory/ref_counted.cc
new file mode 100644
index 0000000..31ad509
--- /dev/null
+++ b/src/base/memory/ref_counted.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/ref_counted.h"
+
+#include "base/logging.h"
+#include "base/threading/thread_collision_warner.h"
+
+namespace base {
+
+namespace subtle {
+
+RefCountedBase::RefCountedBase()
+ : ref_count_(0)
+#ifndef NDEBUG
+ , in_dtor_(false)
+#endif
+ {
+}
+
+RefCountedBase::~RefCountedBase() {
+#ifndef NDEBUG
+ DCHECK(in_dtor_) << "RefCounted object deleted without calling Release()";
+#endif
+}
+
+void RefCountedBase::AddRef() const {
+ // TODO(maruel): Add back once it doesn't assert 500 times/sec.
+ // Current thread books the critical section "AddRelease" without release it.
+ // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_);
+#ifndef NDEBUG
+ DCHECK(!in_dtor_);
+#endif
+ ++ref_count_;
+}
+
+bool RefCountedBase::Release() const {
+ // TODO(maruel): Add back once it doesn't assert 500 times/sec.
+ // Current thread books the critical section "AddRelease" without release it.
+ // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_);
+#ifndef NDEBUG
+ DCHECK(!in_dtor_);
+#endif
+ if (--ref_count_ == 0) {
+#ifndef NDEBUG
+ in_dtor_ = true;
+#endif
+ return true;
+ }
+ return false;
+}
+
+bool RefCountedThreadSafeBase::HasOneRef() const {
+ return AtomicRefCountIsOne(
+ &const_cast<RefCountedThreadSafeBase*>(this)->ref_count_);
+}
+
+RefCountedThreadSafeBase::RefCountedThreadSafeBase() : ref_count_(0) {
+#ifndef NDEBUG
+ in_dtor_ = false;
+#endif
+}
+
+RefCountedThreadSafeBase::~RefCountedThreadSafeBase() {
+#ifndef NDEBUG
+ DCHECK(in_dtor_) << "RefCountedThreadSafe object deleted without "
+ "calling Release()";
+#endif
+}
+
+void RefCountedThreadSafeBase::AddRef() const {
+#ifndef NDEBUG
+ DCHECK(!in_dtor_);
+#endif
+ AtomicRefCountInc(&ref_count_);
+}
+
+bool RefCountedThreadSafeBase::Release() const {
+#ifndef NDEBUG
+ DCHECK(!in_dtor_);
+ DCHECK(!AtomicRefCountIsZero(&ref_count_));
+#endif
+ if (!AtomicRefCountDec(&ref_count_)) {
+#ifndef NDEBUG
+ in_dtor_ = true;
+#endif
+ return true;
+ }
+ return false;
+}
+
+} // namespace subtle
+
+} // namespace base
diff --git a/src/base/memory/ref_counted.h b/src/base/memory/ref_counted.h
new file mode 100644
index 0000000..f5941e5
--- /dev/null
+++ b/src/base/memory/ref_counted.h
@@ -0,0 +1,378 @@
+// Copyright (c) 2012 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_MEMORY_REF_COUNTED_H_
+#define BASE_MEMORY_REF_COUNTED_H_
+
+#include "base/atomic_ref_count.h"
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/threading/thread_collision_warner.h"
+
+namespace base {
+
+namespace subtle {
+
+class BASE_EXPORT RefCountedBase {
+ public:
+ bool HasOneRef() const { return ref_count_ == 1; }
+
+ protected:
+ RefCountedBase();
+ ~RefCountedBase();
+
+ void AddRef() const;
+
+ // Returns true if the object should self-delete.
+ bool Release() const;
+
+ private:
+ mutable int ref_count_;
+#ifndef NDEBUG
+ mutable bool in_dtor_;
+#endif
+
+ DFAKE_MUTEX(add_release_);
+
+ DISALLOW_COPY_AND_ASSIGN(RefCountedBase);
+};
+
+class BASE_EXPORT RefCountedThreadSafeBase {
+ public:
+ bool HasOneRef() const;
+
+ protected:
+ RefCountedThreadSafeBase();
+ ~RefCountedThreadSafeBase();
+
+ void AddRef() const;
+
+ // Returns true if the object should self-delete.
+ bool Release() const;
+
+ private:
+ mutable AtomicRefCount ref_count_;
+#ifndef NDEBUG
+ mutable bool in_dtor_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafeBase);
+};
+
+} // namespace subtle
+
+//
+// A base class for reference counted classes. Otherwise, known as a cheap
+// knock-off of WebKit's RefCounted<T> class. To use this guy just extend your
+// class from it like so:
+//
+// class MyFoo : public base::RefCounted<MyFoo> {
+// ...
+// private:
+// friend class base::RefCounted<MyFoo>;
+// ~MyFoo();
+// };
+//
+// You should always make your destructor private, to avoid any code deleting
+// the object accidently while there are references to it.
+template <class T>
+class RefCounted : public subtle::RefCountedBase {
+ public:
+ RefCounted() {}
+
+ void AddRef() const {
+ subtle::RefCountedBase::AddRef();
+ }
+
+ void Release() const {
+ if (subtle::RefCountedBase::Release()) {
+ delete static_cast<const T*>(this);
+ }
+ }
+
+ protected:
+ ~RefCounted() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RefCounted<T>);
+};
+
+// Forward declaration.
+template <class T, typename Traits> class RefCountedThreadSafe;
+
+// Default traits for RefCountedThreadSafe<T>. Deletes the object when its ref
+// count reaches 0. Overload to delete it on a different thread etc.
+template<typename T>
+struct DefaultRefCountedThreadSafeTraits {
+ static void Destruct(const T* x) {
+ // Delete through RefCountedThreadSafe to make child classes only need to be
+ // friend with RefCountedThreadSafe instead of this struct, which is an
+ // implementation detail.
+ RefCountedThreadSafe<T,
+ DefaultRefCountedThreadSafeTraits>::DeleteInternal(x);
+ }
+};
+
+//
+// A thread-safe variant of RefCounted<T>
+//
+// class MyFoo : public base::RefCountedThreadSafe<MyFoo> {
+// ...
+// };
+//
+// If you're using the default trait, then you should add compile time
+// asserts that no one else is deleting your object. i.e.
+// private:
+// friend class base::RefCountedThreadSafe<MyFoo>;
+// ~MyFoo();
+template <class T, typename Traits = DefaultRefCountedThreadSafeTraits<T> >
+class RefCountedThreadSafe : public subtle::RefCountedThreadSafeBase {
+ public:
+ RefCountedThreadSafe() {}
+
+ void AddRef() const {
+ subtle::RefCountedThreadSafeBase::AddRef();
+ }
+
+ void Release() const {
+ if (subtle::RefCountedThreadSafeBase::Release()) {
+ Traits::Destruct(static_cast<const T*>(this));
+ }
+ }
+
+ protected:
+ ~RefCountedThreadSafe() {}
+
+ private:
+ friend struct DefaultRefCountedThreadSafeTraits<T>;
+ static void DeleteInternal(const T* x) { delete x; }
+
+ DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafe);
+};
+
+//
+// A thread-safe wrapper for some piece of data so we can place other
+// things in scoped_refptrs<>.
+//
+template<typename T>
+class RefCountedData
+ : public base::RefCountedThreadSafe< base::RefCountedData<T> > {
+ public:
+ RefCountedData() : data() {}
+ RefCountedData(const T& in_value) : data(in_value) {}
+
+ T data;
+
+ private:
+ friend class base::RefCountedThreadSafe<base::RefCountedData<T> >;
+ ~RefCountedData() {}
+};
+
+} // namespace base
+
+//
+// A smart pointer class for reference counted objects. Use this class instead
+// of calling AddRef and Release manually on a reference counted object to
+// avoid common memory leaks caused by forgetting to Release an object
+// reference. Sample usage:
+//
+// class MyFoo : public RefCounted<MyFoo> {
+// ...
+// };
+//
+// void some_function() {
+// scoped_refptr<MyFoo> foo = new MyFoo();
+// foo->Method(param);
+// // |foo| is released when this function returns
+// }
+//
+// void some_other_function() {
+// scoped_refptr<MyFoo> foo = new MyFoo();
+// ...
+// foo = NULL; // explicitly releases |foo|
+// ...
+// if (foo)
+// foo->Method(param);
+// }
+//
+// The above examples show how scoped_refptr<T> acts like a pointer to T.
+// Given two scoped_refptr<T> classes, it is also possible to exchange
+// references between the two objects, like so:
+//
+// {
+// scoped_refptr<MyFoo> a = new MyFoo();
+// scoped_refptr<MyFoo> b;
+//
+// b.swap(a);
+// // now, |b| references the MyFoo object, and |a| references NULL.
+// }
+//
+// To make both |a| and |b| in the above example reference the same MyFoo
+// object, simply use the assignment operator:
+//
+// {
+// scoped_refptr<MyFoo> a = new MyFoo();
+// scoped_refptr<MyFoo> b;
+//
+// b = a;
+// // now, |a| and |b| each own a reference to the same MyFoo object.
+// }
+//
+template <class T>
+class scoped_refptr {
+ public:
+ typedef T element_type;
+
+ scoped_refptr() : ptr_(NULL) {
+ }
+
+ scoped_refptr(T* p) : ptr_(p) {
+ if (ptr_)
+ ptr_->AddRef();
+ }
+
+ scoped_refptr(const scoped_refptr<T>& r) : ptr_(r.ptr_) {
+ if (ptr_)
+ ptr_->AddRef();
+ }
+
+ template <typename U>
+ scoped_refptr(const scoped_refptr<U>& r) : ptr_(r.get()) {
+ if (ptr_)
+ ptr_->AddRef();
+ }
+
+ ~scoped_refptr() {
+ if (ptr_)
+ ptr_->Release();
+ }
+
+ T* get() const { return ptr_; }
+ operator T*() const { return ptr_; }
+ T* operator->() const {
+ DCHECK(ptr_);
+ return ptr_;
+ }
+ // The compiler requires an explicit * operator here.
+#if defined(__LB_PS3__)
+ T& operator*() const {
+ DCHECK(ptr_);
+ return *ptr_;
+ }
+#endif
+
+ scoped_refptr<T>& operator=(T* p) {
+ // AddRef first so that self assignment should work
+ if (p)
+ p->AddRef();
+ T* old_ptr = ptr_;
+ ptr_ = p;
+ if (old_ptr)
+ old_ptr->Release();
+ return *this;
+ }
+
+ scoped_refptr<T>& operator=(const scoped_refptr<T>& r) {
+ return *this = r.ptr_;
+ }
+
+ template <typename U>
+ scoped_refptr<T>& operator=(const scoped_refptr<U>& r) {
+ return *this = r.get();
+ }
+
+ void swap(T** pp) {
+ T* p = ptr_;
+ ptr_ = *pp;
+ *pp = p;
+ }
+
+ void swap(scoped_refptr<T>& r) {
+ swap(&r.ptr_);
+ }
+
+ protected:
+ T* ptr_;
+};
+
+// Handy utility for creating a scoped_refptr<T> out of a T* explicitly without
+// having to retype all the template arguments
+template <typename T>
+scoped_refptr<T> make_scoped_refptr(T* t) {
+ return scoped_refptr<T>(t);
+}
+
+// Make scoped_reftr usable as key in base::hash_map.
+
+//
+// GCC-flavored hash functor.
+//
+#if defined(COMPILER_GCC) && defined(__LB_LINUX__)
+
+namespace __gnu_cxx {
+
+// Forward declaration in case <hash_fun.h> is not #include'd.
+template <typename Key>
+struct hash;
+
+template <typename T>
+struct hash<scoped_refptr<T> > {
+ size_t operator()(const scoped_refptr<T>& key) const {
+ return base_hash(key.get());
+ }
+
+ hash<T*> base_hash;
+};
+
+} // namespace __gnu_cxx
+
+//
+// Dinkumware-flavored hash functor.
+//
+#else
+
+#if defined(COMPILER_MSVC)
+namespace stdext {
+#else
+namespace std {
+#endif
+
+// Forward declaration in case <xhash> is not #include'd.
+template <typename Key, typename Predicate>
+class hash_compare;
+
+template <typename T, typename Predicate>
+class hash_compare<scoped_refptr<T>, Predicate> {
+ public:
+ typedef hash_compare<T*, Predicate> BaseHashCompare;
+
+ enum {
+ bucket_size = BaseHashCompare::bucket_size,
+#if !defined(COMPILER_MSVC)
+ min_buckets = BaseHashCompare::min_buckets,
+#endif
+ };
+
+ hash_compare() {}
+ hash_compare(Predicate predicate) : base_hash_compare_(predicate) {}
+
+ size_t operator()(const scoped_refptr<T>& key) const {
+ return base_hash_compare_(key.get());
+ }
+
+ bool operator()(const scoped_refptr<T>& lhs,
+ const scoped_refptr<T>& rhs) const {
+ return base_hash_compare_(lhs.get(), rhs.get());
+ }
+
+ private:
+ BaseHashCompare base_hash_compare_;
+};
+
+} // namespace std[ext]
+
+#endif
+
+#endif // BASE_MEMORY_REF_COUNTED_H_
diff --git a/src/base/memory/ref_counted_memory.cc b/src/base/memory/ref_counted_memory.cc
new file mode 100644
index 0000000..b048a6e
--- /dev/null
+++ b/src/base/memory/ref_counted_memory.cc
@@ -0,0 +1,77 @@
+// Copyright (c) 2012 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/memory/ref_counted_memory.h"
+
+#include "base/logging.h"
+
+namespace base {
+
+bool RefCountedMemory::Equals(
+ const scoped_refptr<RefCountedMemory>& other) const {
+ return other.get() &&
+ size() == other->size() &&
+ (memcmp(front(), other->front(), size()) == 0);
+}
+
+RefCountedMemory::RefCountedMemory() {}
+
+RefCountedMemory::~RefCountedMemory() {}
+
+const unsigned char* RefCountedStaticMemory::front() const {
+ return data_;
+}
+
+size_t RefCountedStaticMemory::size() const {
+ return length_;
+}
+
+RefCountedStaticMemory::~RefCountedStaticMemory() {}
+
+RefCountedBytes::RefCountedBytes() {}
+
+RefCountedBytes::RefCountedBytes(const std::vector<unsigned char>& initializer)
+ : data_(initializer) {
+}
+
+RefCountedBytes* RefCountedBytes::TakeVector(
+ std::vector<unsigned char>* to_destroy) {
+ RefCountedBytes* bytes = new RefCountedBytes;
+ bytes->data_.swap(*to_destroy);
+ return bytes;
+}
+
+const unsigned char* RefCountedBytes::front() const {
+ // STL will assert if we do front() on an empty vector, but calling code
+ // expects a NULL.
+ return size() ? &data_.front() : NULL;
+}
+
+size_t RefCountedBytes::size() const {
+ return data_.size();
+}
+
+RefCountedBytes::~RefCountedBytes() {}
+
+RefCountedString::RefCountedString() {}
+
+RefCountedString::~RefCountedString() {}
+
+// static
+RefCountedString* RefCountedString::TakeString(std::string* to_destroy) {
+ RefCountedString* self = new RefCountedString;
+ to_destroy->swap(self->data_);
+ return self;
+}
+
+const unsigned char* RefCountedString::front() const {
+ return data_.empty() ? NULL :
+ reinterpret_cast<const unsigned char*>(data_.data());
+}
+
+size_t RefCountedString::size() const {
+ return data_.size();
+}
+
+} // namespace base
diff --git a/src/base/memory/ref_counted_memory.h b/src/base/memory/ref_counted_memory.h
new file mode 100644
index 0000000..b99871b
--- /dev/null
+++ b/src/base/memory/ref_counted_memory.h
@@ -0,0 +1,118 @@
+// Copyright (c) 2012 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_MEMORY_REF_COUNTED_MEMORY_H_
+#define BASE_MEMORY_REF_COUNTED_MEMORY_H_
+
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+
+namespace base {
+
+// A generic interface to memory. This object is reference counted because one
+// of its two subclasses own the data they carry, and we need to have
+// heterogeneous containers of these two types of memory.
+class BASE_EXPORT RefCountedMemory
+ : public base::RefCountedThreadSafe<RefCountedMemory> {
+ public:
+ // Retrieves a pointer to the beginning of the data we point to. If the data
+ // is empty, this will return NULL.
+ virtual const unsigned char* front() const = 0;
+
+ // Size of the memory pointed to.
+ virtual size_t size() const = 0;
+
+ // Returns true if |other| is byte for byte equal.
+ bool Equals(const scoped_refptr<RefCountedMemory>& other) const;
+
+ protected:
+ friend class base::RefCountedThreadSafe<RefCountedMemory>;
+ RefCountedMemory();
+ virtual ~RefCountedMemory();
+};
+
+// An implementation of RefCountedMemory, where the ref counting does not
+// matter.
+class BASE_EXPORT RefCountedStaticMemory : public RefCountedMemory {
+ public:
+ RefCountedStaticMemory()
+ : data_(NULL), length_(0) {}
+ RefCountedStaticMemory(const unsigned char* data, size_t length)
+ : data_(length ? data : NULL), length_(length) {}
+
+ // Overridden from RefCountedMemory:
+ virtual const unsigned char* front() const OVERRIDE;
+ virtual size_t size() const OVERRIDE;
+
+ private:
+ virtual ~RefCountedStaticMemory();
+
+ const unsigned char* data_;
+ size_t length_;
+
+ DISALLOW_COPY_AND_ASSIGN(RefCountedStaticMemory);
+};
+
+// An implementation of RefCountedMemory, where we own our the data in a
+// vector.
+class BASE_EXPORT RefCountedBytes : public RefCountedMemory {
+ public:
+ RefCountedBytes();
+
+ // Constructs a RefCountedBytes object by _copying_ from |initializer|.
+ RefCountedBytes(const std::vector<unsigned char>& initializer);
+
+ // Constructs a RefCountedBytes object by performing a swap. (To non
+ // destructively build a RefCountedBytes, use the constructor that takes a
+ // vector.)
+ static RefCountedBytes* TakeVector(std::vector<unsigned char>* to_destroy);
+
+ // Overridden from RefCountedMemory:
+ virtual const unsigned char* front() const OVERRIDE;
+ virtual size_t size() const OVERRIDE;
+
+ const std::vector<unsigned char>& data() const { return data_; }
+ std::vector<unsigned char>& data() { return data_; }
+
+ private:
+ virtual ~RefCountedBytes();
+
+ std::vector<unsigned char> data_;
+
+ DISALLOW_COPY_AND_ASSIGN(RefCountedBytes);
+};
+
+// An implementation of RefCountedMemory, where the bytes are stored in an STL
+// string. Use this if your data naturally arrives in that format.
+class BASE_EXPORT RefCountedString : public RefCountedMemory {
+ public:
+ RefCountedString();
+
+ // Constructs a RefCountedString object by performing a swap. (To non
+ // destructively build a RefCountedString, use the default constructor and
+ // copy into object->data()).
+ static RefCountedString* TakeString(std::string* to_destroy);
+
+ // Overridden from RefCountedMemory:
+ virtual const unsigned char* front() const OVERRIDE;
+ virtual size_t size() const OVERRIDE;
+
+ const std::string& data() const { return data_; }
+ std::string& data() { return data_; }
+
+ private:
+ virtual ~RefCountedString();
+
+ std::string data_;
+
+ DISALLOW_COPY_AND_ASSIGN(RefCountedString);
+};
+
+} // namespace base
+
+#endif // BASE_MEMORY_REF_COUNTED_MEMORY_H_
diff --git a/src/base/memory/ref_counted_memory_unittest.cc b/src/base/memory/ref_counted_memory_unittest.cc
new file mode 100644
index 0000000..c6f2b9c
--- /dev/null
+++ b/src/base/memory/ref_counted_memory_unittest.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/ref_counted_memory.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(RefCountedMemoryUnitTest, RefCountedStaticMemory) {
+ scoped_refptr<RefCountedMemory> mem = new RefCountedStaticMemory(
+ reinterpret_cast<const uint8*>("static mem00"), 10);
+
+ EXPECT_EQ(10U, mem->size());
+ EXPECT_EQ("static mem",
+ std::string(reinterpret_cast<const char*>(mem->front()),
+ mem->size()));
+}
+
+TEST(RefCountedMemoryUnitTest, RefCountedBytes) {
+ std::vector<uint8> data;
+ data.push_back(45);
+ data.push_back(99);
+ scoped_refptr<RefCountedMemory> mem = RefCountedBytes::TakeVector(&data);
+
+ EXPECT_EQ(0U, data.size());
+
+ EXPECT_EQ(2U, mem->size());
+ EXPECT_EQ(45U, mem->front()[0]);
+ EXPECT_EQ(99U, mem->front()[1]);
+}
+
+TEST(RefCountedMemoryUnitTest, RefCountedString) {
+ std::string s("destroy me");
+ scoped_refptr<RefCountedMemory> mem = RefCountedString::TakeString(&s);
+
+ EXPECT_EQ(0U, s.size());
+
+ EXPECT_EQ(10U, mem->size());
+ EXPECT_EQ('d', mem->front()[0]);
+ EXPECT_EQ('e', mem->front()[1]);
+}
+
+TEST(RefCountedMemoryUnitTest, Equals) {
+ std::string s1("same");
+ scoped_refptr<RefCountedMemory> mem1 = RefCountedString::TakeString(&s1);
+
+ std::vector<unsigned char> d2;
+ d2.push_back('s');
+ d2.push_back('a');
+ d2.push_back('m');
+ d2.push_back('e');
+ scoped_refptr<RefCountedMemory> mem2 = RefCountedBytes::TakeVector(&d2);
+
+ EXPECT_TRUE(mem1->Equals(mem2));
+
+ std::string s3("diff");
+ scoped_refptr<RefCountedMemory> mem3 = RefCountedString::TakeString(&s3);
+
+ EXPECT_FALSE(mem1->Equals(mem3));
+ EXPECT_FALSE(mem2->Equals(mem3));
+}
+
+TEST(RefCountedMemoryUnitTest, EqualsNull) {
+ std::string s("str");
+ scoped_refptr<RefCountedMemory> mem = RefCountedString::TakeString(&s);
+ EXPECT_FALSE(mem->Equals(NULL));
+}
+
+} // namespace base
diff --git a/src/base/memory/ref_counted_unittest.cc b/src/base/memory/ref_counted_unittest.cc
new file mode 100644
index 0000000..8ddd5be
--- /dev/null
+++ b/src/base/memory/ref_counted_unittest.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2012 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/memory/ref_counted.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class SelfAssign : public base::RefCounted<SelfAssign> {
+ friend class base::RefCounted<SelfAssign>;
+
+ ~SelfAssign() {}
+};
+
+class CheckDerivedMemberAccess : public scoped_refptr<SelfAssign> {
+ public:
+ CheckDerivedMemberAccess() {
+ // This shouldn't compile if we don't have access to the member variable.
+ SelfAssign** pptr = &ptr_;
+ EXPECT_EQ(*pptr, ptr_);
+ }
+};
+
+class ScopedRefPtrToSelf : public base::RefCounted<ScopedRefPtrToSelf> {
+ public:
+ ScopedRefPtrToSelf()
+ : ALLOW_THIS_IN_INITIALIZER_LIST(self_ptr_(this)) {
+ }
+
+ static bool was_destroyed() { return was_destroyed_; }
+
+ void SelfDestruct() { self_ptr_ = NULL; }
+
+ private:
+ friend class base::RefCounted<ScopedRefPtrToSelf>;
+ ~ScopedRefPtrToSelf() { was_destroyed_ = true; }
+
+ static bool was_destroyed_;
+
+ scoped_refptr<ScopedRefPtrToSelf> self_ptr_;
+};
+
+bool ScopedRefPtrToSelf::was_destroyed_ = false;
+
+} // end namespace
+
+TEST(RefCountedUnitTest, TestSelfAssignment) {
+ SelfAssign* p = new SelfAssign;
+ scoped_refptr<SelfAssign> var(p);
+ var = var;
+ EXPECT_EQ(var.get(), p);
+}
+
+TEST(RefCountedUnitTest, ScopedRefPtrMemberAccess) {
+ CheckDerivedMemberAccess check;
+}
+
+TEST(RefCountedUnitTest, ScopedRefPtrToSelf) {
+ ScopedRefPtrToSelf* check = new ScopedRefPtrToSelf();
+ EXPECT_FALSE(ScopedRefPtrToSelf::was_destroyed());
+ check->SelfDestruct();
+ EXPECT_TRUE(ScopedRefPtrToSelf::was_destroyed());
+}
diff --git a/src/base/memory/scoped_generic_obj.h b/src/base/memory/scoped_generic_obj.h
new file mode 100644
index 0000000..4b3cb86
--- /dev/null
+++ b/src/base/memory/scoped_generic_obj.h
@@ -0,0 +1,129 @@
+// Copyright (c) 2012 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_MEMORY_SCOPED_GENERIC_OBJ_H_
+#define BASE_MEMORY_SCOPED_GENERIC_OBJ_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+
+// ScopedGenericObj<> is patterned after scoped_ptr_malloc<>, except
+// that it assumes the template argument is typedef'ed to a pointer
+// type. It does not support retain/release semantics. It takes as its
+// second template argument a functor which frees the object.
+//
+// Example (Mac-specific):
+//
+// class ScopedDestroyRendererInfo {
+// public:
+// void operator()(CGLRendererInfoObj x) const {
+// CGLDestroyRendererInfo(x);
+// }
+// };
+//
+// ...
+//
+// CGLRendererInfoObj renderer_info = NULL;
+// ...
+// ScopedGenericObj<CGLRendererInfoObj, ScopedDestroyRendererInfo>
+// scoper(renderer_info);
+
+template<class C, class FreeProc>
+class ScopedGenericObj {
+ public:
+
+ // The element type
+ typedef C element_type;
+
+ // Constructor. Defaults to initializing with NULL.
+ // There is no way to create an uninitialized ScopedGenericObj.
+ // The input parameter must be allocated with an allocator that matches the
+ // Free functor.
+ explicit ScopedGenericObj(C p = C()): obj_(p) {}
+
+ // Destructor. If there is a C object, call the Free functor.
+ ~ScopedGenericObj() {
+ reset();
+ }
+
+ // Reset. Calls the Free functor on the current owned object, if any.
+ // Then takes ownership of a new object, if given.
+ // this->reset(this->get()) works.
+ void reset(C p = C()) {
+ if (obj_ != p) {
+ FreeProc free_proc;
+ free_proc(obj_);
+ obj_ = p;
+ }
+ }
+
+ operator C() const {
+ return obj_;
+ }
+
+ C get() const {
+ return obj_;
+ }
+
+ // Comparison operators.
+ // These return whether a ScopedGenericObj and a plain pointer refer
+ // to the same object, not just to two different but equal objects.
+ // For compatibility with the boost-derived implementation, these
+ // take non-const arguments.
+ bool operator==(C p) const {
+ return obj_ == p;
+ }
+
+ bool operator!=(C p) const {
+ return obj_ != p;
+ }
+
+ // Swap two ScopedGenericObjs.
+ void swap(ScopedGenericObj& b) {
+ C tmp = b.obj_;
+ b.obj_ = obj_;
+ obj_ = tmp;
+ }
+
+ // Release a pointer.
+ // The return value is the current pointer held by this object.
+ // If this object holds a NULL pointer, the return value is NULL.
+ // After this operation, this object will hold a NULL pointer,
+ // and will not own the object any more.
+ C release() WARN_UNUSED_RESULT {
+ C tmp = obj_;
+ obj_ = NULL;
+ return tmp;
+ }
+
+ private:
+ C obj_;
+
+ // no reason to use these: each ScopedGenericObj should have its own object.
+ template <class C2, class GP>
+ bool operator==(ScopedGenericObj<C2, GP> const& p) const;
+ template <class C2, class GP>
+ bool operator!=(ScopedGenericObj<C2, GP> const& p) const;
+
+ // Disallow evil constructors.
+ ScopedGenericObj(const ScopedGenericObj&);
+ void operator=(const ScopedGenericObj&);
+};
+
+template<class C, class FP> inline
+void swap(ScopedGenericObj<C, FP>& a, ScopedGenericObj<C, FP>& b) {
+ a.swap(b);
+}
+
+template<class C, class FP> inline
+bool operator==(C* p, const ScopedGenericObj<C, FP>& b) {
+ return p == b.get();
+}
+
+template<class C, class FP> inline
+bool operator!=(C* p, const ScopedGenericObj<C, FP>& b) {
+ return p != b.get();
+}
+
+#endif // BASE_MEMORY_SCOPED_GENERIC_OBJ_H_
diff --git a/src/base/memory/scoped_handle.h b/src/base/memory/scoped_handle.h
new file mode 100644
index 0000000..b95559d
--- /dev/null
+++ b/src/base/memory/scoped_handle.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_SCOPED_HANDLE_H_
+#define BASE_MEMORY_SCOPED_HANDLE_H_
+
+#include <stdio.h>
+
+#include "base/basictypes.h"
+
+class ScopedStdioHandle {
+ public:
+ ScopedStdioHandle()
+ : handle_(NULL) { }
+
+ explicit ScopedStdioHandle(FILE* handle)
+ : handle_(handle) { }
+
+ ~ScopedStdioHandle() {
+ Close();
+ }
+
+ void Close() {
+ if (handle_) {
+ fclose(handle_);
+ handle_ = NULL;
+ }
+ }
+
+ FILE* get() const { return handle_; }
+
+ FILE* Take() {
+ FILE* temp = handle_;
+ handle_ = NULL;
+ return temp;
+ }
+
+ void Set(FILE* newhandle) {
+ Close();
+ handle_ = newhandle;
+ }
+
+ private:
+ FILE* handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedStdioHandle);
+};
+
+#endif // BASE_MEMORY_SCOPED_HANDLE_H_
diff --git a/src/base/memory/scoped_nsobject.h b/src/base/memory/scoped_nsobject.h
new file mode 100644
index 0000000..5d98e3f
--- /dev/null
+++ b/src/base/memory/scoped_nsobject.h
@@ -0,0 +1,174 @@
+// Copyright (c) 2012 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_MEMORY_SCOPED_NSOBJECT_H_
+#define BASE_MEMORY_SCOPED_NSOBJECT_H_
+
+#import <Foundation/Foundation.h>
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_policy.h"
+
+// scoped_nsobject<> is patterned after scoped_ptr<>, but maintains ownership
+// of an NSObject subclass object. Style deviations here are solely for
+// compatibility with scoped_ptr<>'s interface, with which everyone is already
+// familiar.
+//
+// By default, scoped_nsobject<> takes ownership of an object (in the
+// constructor or in reset()) by taking over the caller's existing ownership
+// claim. The caller must own the object it gives to scoped_nsobject<>, and
+// relinquishes an ownership claim to that object. scoped_nsobject<> does not
+// call -retain. This behavior is parametrized by the |OwnershipPolicy| enum.
+// If the value |RETAIN| is passed (in the constructor or in reset()), then
+// scoped_nsobject<> will call -retain on the object, and the initial
+// ownership is not changed.
+//
+// scoped_nsprotocol<> has the same behavior as scoped_nsobject, but can be used
+// with protocols.
+//
+// scoped_nsobject<> is not to be used for NSAutoreleasePools. For
+// NSAutoreleasePools use ScopedNSAutoreleasePool from
+// scoped_nsautorelease_pool.h instead.
+// We check for bad uses of scoped_nsobject and NSAutoreleasePool at compile
+// time with a template specialization (see below).
+
+template<typename NST>
+class scoped_nsprotocol {
+ public:
+ explicit scoped_nsprotocol(
+ NST object = nil,
+ base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
+ : object_(object) {
+ if (policy == base::scoped_policy::RETAIN)
+ [object retain];
+ }
+
+ scoped_nsprotocol(const scoped_nsprotocol<NST>& that)
+ : object_([that.object_ retain]) {
+ }
+
+ ~scoped_nsprotocol() {
+ [object_ release];
+ }
+
+ scoped_nsprotocol& operator=(const scoped_nsprotocol<NST>& that) {
+ reset(that.get(), base::scoped_policy::RETAIN);
+ return *this;
+ }
+
+ void reset(NST object = nil,
+ base::scoped_policy::OwnershipPolicy policy =
+ base::scoped_policy::ASSUME) {
+ if (policy == base::scoped_policy::RETAIN)
+ [object retain];
+ // We intentionally do not check that object != object_ as the caller must
+ // either already have an ownership claim over whatever it passes to this
+ // method, or call it with the |RETAIN| policy which will have ensured that
+ // the object is retained once more when reaching this point.
+ [object_ release];
+ object_ = object;
+ }
+
+ bool operator==(NST that) const { return object_ == that; }
+ bool operator!=(NST that) const { return object_ != that; }
+
+ operator NST() const {
+ return object_;
+ }
+
+ NST get() const {
+ return object_;
+ }
+
+ void swap(scoped_nsprotocol& that) {
+ NST temp = that.object_;
+ that.object_ = object_;
+ object_ = temp;
+ }
+
+ // scoped_nsprotocol<>::release() is like scoped_ptr<>::release. It is NOT a
+ // wrapper for [object_ release]. To force a scoped_nsprotocol<> to call
+ // [object_ release], use scoped_nsprotocol<>::reset().
+ NST release() WARN_UNUSED_RESULT {
+ NST temp = object_;
+ object_ = nil;
+ return temp;
+ }
+
+ // Shift reference to the autorelease pool to be released later.
+ NST autorelease() {
+ return [release() autorelease];
+ }
+
+ private:
+ NST object_;
+};
+
+// Free functions
+template <class C>
+void swap(scoped_nsprotocol<C>& p1, scoped_nsprotocol<C>& p2) {
+ p1.swap(p2);
+}
+
+template <class C>
+bool operator==(C p1, const scoped_nsprotocol<C>& p2) {
+ return p1 == p2.get();
+}
+
+template <class C>
+bool operator!=(C p1, const scoped_nsprotocol<C>& p2) {
+ return p1 != p2.get();
+}
+
+template<typename NST>
+class scoped_nsobject : public scoped_nsprotocol<NST*> {
+ public:
+ explicit scoped_nsobject(
+ NST* object = nil,
+ base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
+ : scoped_nsprotocol<NST*>(object, policy) {
+ }
+
+ scoped_nsobject(const scoped_nsobject<NST>& that)
+ : scoped_nsprotocol<NST*>(that) {
+ }
+
+ scoped_nsobject& operator=(const scoped_nsobject<NST>& that) {
+ scoped_nsprotocol<NST*>::operator=(that);
+ return *this;
+ }
+};
+
+// Specialization to make scoped_nsobject<id> work.
+template<>
+class scoped_nsobject<id> : public scoped_nsprotocol<id> {
+ public:
+ explicit scoped_nsobject(
+ id object = nil,
+ base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
+ : scoped_nsprotocol<id>(object, policy) {
+ }
+
+ scoped_nsobject(const scoped_nsobject<id>& that)
+ : scoped_nsprotocol<id>(that) {
+ }
+
+ scoped_nsobject& operator=(const scoped_nsobject<id>& that) {
+ scoped_nsprotocol<id>::operator=(that);
+ return *this;
+ }
+};
+
+// Do not use scoped_nsobject for NSAutoreleasePools, use
+// ScopedNSAutoreleasePool instead. This is a compile time check. See details
+// at top of header.
+template<>
+class scoped_nsobject<NSAutoreleasePool> {
+ private:
+ explicit scoped_nsobject(NSAutoreleasePool* object = nil,
+ base::scoped_policy::OwnershipPolicy policy =
+ base::scoped_policy::ASSUME);
+ DISALLOW_COPY_AND_ASSIGN(scoped_nsobject);
+};
+#endif // BASE_MEMORY_SCOPED_NSOBJECT_H_
diff --git a/src/base/memory/scoped_nsobject_unittest.mm b/src/base/memory/scoped_nsobject_unittest.mm
new file mode 100644
index 0000000..377a3de
--- /dev/null
+++ b/src/base/memory/scoped_nsobject_unittest.mm
@@ -0,0 +1,86 @@
+// Copyright (c) 2012 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 <vector>
+
+#include "base/basictypes.h"
+#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/memory/scoped_nsobject.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+TEST(ScopedNSObjectTest, ScopedNSObject) {
+ scoped_nsobject<NSObject> p1([[NSObject alloc] init]);
+ ASSERT_TRUE(p1.get());
+ ASSERT_EQ(1u, [p1 retainCount]);
+ scoped_nsobject<NSObject> p2(p1);
+ ASSERT_EQ(p1.get(), p2.get());
+ ASSERT_EQ(2u, [p1 retainCount]);
+ p2.reset();
+ ASSERT_EQ(nil, p2.get());
+ ASSERT_EQ(1u, [p1 retainCount]);
+ {
+ scoped_nsobject<NSObject> p3 = p1;
+ ASSERT_EQ(p1.get(), p3.get());
+ ASSERT_EQ(2u, [p1 retainCount]);
+ p3 = p1;
+ ASSERT_EQ(p1.get(), p3.get());
+ ASSERT_EQ(2u, [p1 retainCount]);
+ }
+ ASSERT_EQ(1u, [p1 retainCount]);
+ scoped_nsobject<NSObject> p4(p1.get(), base::scoped_policy::RETAIN);
+ ASSERT_EQ(2u, [p1 retainCount]);
+ ASSERT_TRUE(p1 == p1.get());
+ ASSERT_TRUE(p1 == p1);
+ ASSERT_FALSE(p1 != p1);
+ ASSERT_FALSE(p1 != p1.get());
+ scoped_nsobject<NSObject> p5([[NSObject alloc] init]);
+ ASSERT_TRUE(p1 != p5);
+ ASSERT_TRUE(p1 != p5.get());
+ ASSERT_FALSE(p1 == p5);
+ ASSERT_FALSE(p1 == p5.get());
+
+ scoped_nsobject<NSObject> p6 = p1;
+ ASSERT_EQ(3u, [p6 retainCount]);
+ {
+ base::mac::ScopedNSAutoreleasePool pool;
+ p6.autorelease();
+ ASSERT_EQ(nil, p6.get());
+ ASSERT_EQ(3u, [p1 retainCount]);
+ }
+ ASSERT_EQ(2u, [p1 retainCount]);
+}
+
+TEST(ScopedNSObjectTest, ScopedNSObjectInContainer) {
+ scoped_nsobject<id> p([[NSObject alloc] init]);
+ ASSERT_TRUE(p.get());
+ ASSERT_EQ(1u, [p retainCount]);
+ {
+ std::vector<scoped_nsobject<id> > objects;
+ objects.push_back(p);
+ ASSERT_EQ(2u, [p retainCount]);
+ ASSERT_EQ(p.get(), objects[0].get());
+ objects.push_back(scoped_nsobject<id>([[NSObject alloc] init]));
+ ASSERT_TRUE(objects[1].get());
+ ASSERT_EQ(1u, [objects[1] retainCount]);
+ }
+ ASSERT_EQ(1u, [p retainCount]);
+}
+
+TEST(ScopedNSObjectTest, ScopedNSObjectFreeFunctions) {
+ scoped_nsobject<id> p1([[NSObject alloc] init]);
+ id o1 = p1.get();
+ ASSERT_TRUE(o1 == p1);
+ ASSERT_FALSE(o1 != p1);
+ scoped_nsobject<id> p2([[NSObject alloc] init]);
+ ASSERT_TRUE(o1 != p2);
+ ASSERT_FALSE(o1 == p2);
+ id o2 = p2.get();
+ swap(p1, p2);
+ ASSERT_EQ(o2, p1.get());
+ ASSERT_EQ(o1, p2.get());
+}
+
+} // namespace
diff --git a/src/base/memory/scoped_open_process.h b/src/base/memory/scoped_open_process.h
new file mode 100644
index 0000000..93ba387
--- /dev/null
+++ b/src/base/memory/scoped_open_process.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_SCOPED_OPEN_PROCESS_H_
+#define BASE_MEMORY_SCOPED_OPEN_PROCESS_H_
+
+#include "base/process.h"
+#include "base/process_util.h"
+
+namespace base {
+
+// A class that opens a process from its process id and closes it when the
+// instance goes out of scope.
+class ScopedOpenProcess {
+ public:
+ ScopedOpenProcess() : handle_(kNullProcessHandle) {
+ }
+
+ // Automatically close the process.
+ ~ScopedOpenProcess() {
+ Close();
+ }
+
+ // Open a new process by pid. Closes any previously opened process (even if
+ // opening the new one fails).
+ bool Open(ProcessId pid) {
+ Close();
+ return OpenProcessHandle(pid, &handle_);
+ }
+
+ // Close the previously opened process.
+ void Close() {
+ if (handle_ == kNullProcessHandle)
+ return;
+
+ CloseProcessHandle(handle_);
+ handle_ = kNullProcessHandle;
+ }
+
+ ProcessHandle handle() const { return handle_; }
+
+ private:
+ ProcessHandle handle_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedOpenProcess);
+};
+} // namespace base
+
+#endif // BASE_MEMORY_SCOPED_OPEN_PROCESS_H_
diff --git a/src/base/memory/scoped_policy.h b/src/base/memory/scoped_policy.h
new file mode 100644
index 0000000..5dbf204
--- /dev/null
+++ b/src/base/memory/scoped_policy.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2012 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_MEMORY_SCOPED_POLICY_H_
+#define BASE_MEMORY_SCOPED_POLICY_H_
+
+namespace base {
+namespace scoped_policy {
+
+// Defines the ownership policy for a scoped object.
+enum OwnershipPolicy {
+ // The scoped object takes ownership of an object by taking over an existing
+ // ownership claim.
+ ASSUME,
+
+ // The scoped object will retain the the object and any initial ownership is
+ // not changed.
+ RETAIN
+};
+
+} // namespace scoped_policy
+} // namespace base
+
+#endif // BASE_MEMORY_SCOPED_POLICY_H_
diff --git a/src/base/memory/scoped_ptr.h b/src/base/memory/scoped_ptr.h
new file mode 100644
index 0000000..9ee780e
--- /dev/null
+++ b/src/base/memory/scoped_ptr.h
@@ -0,0 +1,537 @@
+// Copyright (c) 2012 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.
+
+// Scopers help you manage ownership of a pointer, helping you easily manage the
+// a pointer within a scope, and automatically destroying the pointer at the
+// end of a scope. There are two main classes you will use, which correspond
+// to the operators new/delete and new[]/delete[].
+//
+// Example usage (scoped_ptr):
+// {
+// scoped_ptr<Foo> foo(new Foo("wee"));
+// } // foo goes out of scope, releasing the pointer with it.
+//
+// {
+// scoped_ptr<Foo> foo; // No pointer managed.
+// foo.reset(new Foo("wee")); // Now a pointer is managed.
+// foo.reset(new Foo("wee2")); // Foo("wee") was destroyed.
+// foo.reset(new Foo("wee3")); // Foo("wee2") was destroyed.
+// foo->Method(); // Foo::Method() called.
+// foo.get()->Method(); // Foo::Method() called.
+// SomeFunc(foo.release()); // SomeFunc takes ownership, foo no longer
+// // manages a pointer.
+// foo.reset(new Foo("wee4")); // foo manages a pointer again.
+// foo.reset(); // Foo("wee4") destroyed, foo no longer
+// // manages a pointer.
+// } // foo wasn't managing a pointer, so nothing was destroyed.
+//
+// Example usage (scoped_array):
+// {
+// scoped_array<Foo> foo(new Foo[100]);
+// foo.get()->Method(); // Foo::Method on the 0th element.
+// foo[10].Method(); // Foo::Method on the 10th element.
+// }
+//
+// These scopers also implement part of the functionality of C++11 unique_ptr
+// in that they are "movable but not copyable." You can use the scopers in
+// the parameter and return types of functions to signify ownership transfer
+// in to and out of a function. When calling a function that has a scoper
+// as the argument type, it must be called with the result of an analogous
+// scoper's Pass() function or another function that generates a temporary;
+// passing by copy will NOT work. Here is an example using scoped_ptr:
+//
+// void TakesOwnership(scoped_ptr<Foo> arg) {
+// // Do something with arg
+// }
+// scoped_ptr<Foo> CreateFoo() {
+// // No need for calling Pass() because we are constructing a temporary
+// // for the return value.
+// return scoped_ptr<Foo>(new Foo("new"));
+// }
+// scoped_ptr<Foo> PassThru(scoped_ptr<Foo> arg) {
+// return arg.Pass();
+// }
+//
+// {
+// scoped_ptr<Foo> ptr(new Foo("yay")); // ptr manages Foo("yay").
+// TakesOwnership(ptr.Pass()); // ptr no longer owns Foo("yay").
+// scoped_ptr<Foo> ptr2 = CreateFoo(); // ptr2 owns the return Foo.
+// scoped_ptr<Foo> ptr3 = // ptr3 now owns what was in ptr2.
+// PassThru(ptr2.Pass()); // ptr2 is correspondingly NULL.
+// }
+//
+// Notice that if you do not call Pass() when returning from PassThru(), or
+// when invoking TakesOwnership(), the code will not compile because scopers
+// are not copyable; they only implement move semantics which require calling
+// the Pass() function to signify a destructive transfer of state. CreateFoo()
+// is different though because we are constructing a temporary on the return
+// line and thus can avoid needing to call Pass().
+//
+// Pass() properly handles upcast in assignment, i.e. you can assign
+// scoped_ptr<Child> to scoped_ptr<Parent>:
+//
+// scoped_ptr<Foo> foo(new Foo());
+// scoped_ptr<FooParent> parent = foo.Pass();
+//
+// PassAs<>() should be used to upcast return value in return statement:
+//
+// scoped_ptr<Foo> CreateFoo() {
+// scoped_ptr<FooChild> result(new FooChild());
+// return result.PassAs<Foo>();
+// }
+//
+// Note that PassAs<>() is implemented only for scoped_ptr, but not for
+// scoped_array. This is because casting array pointers may not be safe.
+
+#ifndef BASE_MEMORY_SCOPED_PTR_H_
+#define BASE_MEMORY_SCOPED_PTR_H_
+
+// This is an implementation designed to match the anticipated future TR2
+// implementation of the scoped_ptr class, and its closely-related brethren,
+// scoped_array, scoped_ptr_malloc.
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/move.h"
+#include "base/template_util.h"
+#include "build/build_config.h"
+
+#if defined(OS_STARBOARD)
+#include "starboard/memory.h"
+#endif
+
+namespace base {
+
+namespace subtle {
+class RefCountedBase;
+class RefCountedThreadSafeBase;
+} // namespace subtle
+
+namespace internal {
+
+template <typename T> struct IsNotRefCounted {
+ enum {
+ value = !base::is_convertible<T*, base::subtle::RefCountedBase*>::value &&
+ !base::is_convertible<T*, base::subtle::RefCountedThreadSafeBase*>::
+ value
+ };
+};
+
+} // namespace internal
+} // namespace base
+
+// A scoped_ptr<T> is like a T*, except that the destructor of scoped_ptr<T>
+// automatically deletes the pointer it holds (if any).
+// That is, scoped_ptr<T> owns the T object that it points to.
+// Like a T*, a scoped_ptr<T> may hold either NULL or a pointer to a T object.
+// Also like T*, scoped_ptr<T> is thread-compatible, and once you
+// dereference it, you get the thread safety guarantees of T.
+//
+// The size of a scoped_ptr is small:
+// sizeof(scoped_ptr<C>) == sizeof(C*)
+template <class C>
+class scoped_ptr {
+ MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue)
+
+ COMPILE_ASSERT(base::internal::IsNotRefCounted<C>::value,
+ C_is_refcounted_type_and_needs_scoped_refptr);
+
+ public:
+
+ // The element type
+ typedef C element_type;
+
+ // Constructor. Defaults to initializing with NULL.
+ // There is no way to create an uninitialized scoped_ptr.
+ // The input parameter must be allocated with new.
+ explicit scoped_ptr(C* p = NULL) : ptr_(p) { }
+
+ // The GHS compiler always chooses this copy constructor over the next one,
+ // so disable this to promote the more important and frequently used constr.
+#if !defined(COMPILER_GHS)
+ // Constructor. Allows construction from a scoped_ptr rvalue for a
+ // convertible type.
+ template <typename U>
+ scoped_ptr(scoped_ptr<U> other) : ptr_(other.release()) { }
+#endif
+
+ // Constructor. Move constructor for C++03 move emulation of this type.
+ scoped_ptr(RValue rvalue)
+ : ptr_(rvalue.object->release()) {
+ }
+
+ // Destructor. If there is a C object, delete it.
+ // We don't need to test ptr_ == NULL because C++ does that for us.
+ ~scoped_ptr() {
+ enum { type_must_be_complete = sizeof(C) };
+ delete ptr_;
+ }
+
+ // operator=. Allows assignment from a scoped_ptr rvalue for a convertible
+ // type.
+ template <typename U>
+ scoped_ptr& operator=(scoped_ptr<U> rhs) {
+ reset(rhs.release());
+ return *this;
+ }
+
+ // operator=. Move operator= for C++03 move emulation of this type.
+ scoped_ptr& operator=(RValue rhs) {
+ swap(*rhs->object);
+ return *this;
+ }
+
+ // Reset. Deletes the current owned object, if any.
+ // Then takes ownership of a new object, if given.
+ // this->reset(this->get()) works.
+ void reset(C* p = NULL) {
+ if (p != ptr_) {
+ enum { type_must_be_complete = sizeof(C) };
+ delete ptr_;
+ ptr_ = p;
+ }
+ }
+
+ // Accessors to get the owned object.
+ // operator* and operator-> will assert() if there is no current object.
+ C& operator*() const {
+ assert(ptr_ != NULL);
+ return *ptr_;
+ }
+ C* operator->() const {
+ assert(ptr_ != NULL);
+ return ptr_;
+ }
+ C* get() const { return ptr_; }
+
+ // Allow scoped_ptr<C> to be used in boolean expressions, but not
+ // implicitly convertible to a real bool (which is dangerous).
+ typedef C* scoped_ptr::*Testable;
+ operator Testable() const { return ptr_ ? &scoped_ptr::ptr_ : NULL; }
+
+ // Comparison operators.
+ // These return whether two scoped_ptr refer to the same object, not just to
+ // two different but equal objects.
+ bool operator==(C* p) const { return ptr_ == p; }
+ bool operator!=(C* p) const { return ptr_ != p; }
+
+ // Swap two scoped pointers.
+ void swap(scoped_ptr& p2) {
+ C* tmp = ptr_;
+ ptr_ = p2.ptr_;
+ p2.ptr_ = tmp;
+ }
+
+ // Release a pointer.
+ // The return value is the current pointer held by this object.
+ // If this object holds a NULL pointer, the return value is NULL.
+ // After this operation, this object will hold a NULL pointer,
+ // and will not own the object any more.
+ C* release() WARN_UNUSED_RESULT {
+ C* retVal = ptr_;
+ ptr_ = NULL;
+ return retVal;
+ }
+
+ template <typename PassAsType>
+ scoped_ptr<PassAsType> PassAs() {
+ return scoped_ptr<PassAsType>(release());
+ }
+
+ private:
+ C* ptr_;
+
+ // Forbid comparison of scoped_ptr types. If C2 != C, it totally doesn't
+ // make sense, and if C2 == C, it still doesn't make sense because you should
+ // never have the same object owned by two different scoped_ptrs.
+ template <class C2> bool operator==(scoped_ptr<C2> const& p2) const;
+ template <class C2> bool operator!=(scoped_ptr<C2> const& p2) const;
+
+};
+
+// Free functions
+template <class C>
+void swap(scoped_ptr<C>& p1, scoped_ptr<C>& p2) {
+ p1.swap(p2);
+}
+
+template <class C>
+bool operator==(C* p1, const scoped_ptr<C>& p2) {
+ return p1 == p2.get();
+}
+
+template <class C>
+bool operator!=(C* p1, const scoped_ptr<C>& p2) {
+ return p1 != p2.get();
+}
+
+// scoped_array<C> is like scoped_ptr<C>, except that the caller must allocate
+// with new [] and the destructor deletes objects with delete [].
+//
+// As with scoped_ptr<C>, a scoped_array<C> either points to an object
+// or is NULL. A scoped_array<C> owns the object that it points to.
+// scoped_array<T> is thread-compatible, and once you index into it,
+// the returned objects have only the thread safety guarantees of T.
+//
+// Size: sizeof(scoped_array<C>) == sizeof(C*)
+template <class C>
+class scoped_array {
+ MOVE_ONLY_TYPE_FOR_CPP_03(scoped_array, RValue)
+
+ public:
+
+ // The element type
+ typedef C element_type;
+
+ // Constructor. Defaults to initializing with NULL.
+ // There is no way to create an uninitialized scoped_array.
+ // The input parameter must be allocated with new [].
+ explicit scoped_array(C* p = NULL) : array_(p) { }
+
+ // Constructor. Move constructor for C++03 move emulation of this type.
+ scoped_array(RValue rvalue)
+ : array_(rvalue.object->release()) {
+ }
+
+ // Destructor. If there is a C object, delete it.
+ // We don't need to test ptr_ == NULL because C++ does that for us.
+ ~scoped_array() {
+ enum { type_must_be_complete = sizeof(C) };
+ delete[] array_;
+ }
+
+ // operator=. Move operator= for C++03 move emulation of this type.
+ scoped_array& operator=(RValue rhs) {
+ swap(*rhs.object);
+ return *this;
+ }
+
+ // Reset. Deletes the current owned object, if any.
+ // Then takes ownership of a new object, if given.
+ // this->reset(this->get()) works.
+ void reset(C* p = NULL) {
+ if (p != array_) {
+ enum { type_must_be_complete = sizeof(C) };
+ delete[] array_;
+ array_ = p;
+ }
+ }
+
+ // Get one element of the current object.
+ // Will assert() if there is no current object, or index i is negative.
+ C& operator[](ptrdiff_t i) const {
+ assert(i >= 0);
+ assert(array_ != NULL);
+ return array_[i];
+ }
+
+ // Get a pointer to the zeroth element of the current object.
+ // If there is no current object, return NULL.
+ C* get() const {
+ return array_;
+ }
+
+ // Allow scoped_array<C> to be used in boolean expressions, but not
+ // implicitly convertible to a real bool (which is dangerous).
+ typedef C* scoped_array::*Testable;
+ operator Testable() const { return array_ ? &scoped_array::array_ : NULL; }
+
+ // Comparison operators.
+ // These return whether two scoped_array refer to the same object, not just to
+ // two different but equal objects.
+ bool operator==(C* p) const { return array_ == p; }
+ bool operator!=(C* p) const { return array_ != p; }
+
+ // Swap two scoped arrays.
+ void swap(scoped_array& p2) {
+ C* tmp = array_;
+ array_ = p2.array_;
+ p2.array_ = tmp;
+ }
+
+ // Release an array.
+ // The return value is the current pointer held by this object.
+ // If this object holds a NULL pointer, the return value is NULL.
+ // After this operation, this object will hold a NULL pointer,
+ // and will not own the object any more.
+ C* release() WARN_UNUSED_RESULT {
+ C* retVal = array_;
+ array_ = NULL;
+ return retVal;
+ }
+
+ private:
+ C* array_;
+
+ // Forbid comparison of different scoped_array types.
+ template <class C2> bool operator==(scoped_array<C2> const& p2) const;
+ template <class C2> bool operator!=(scoped_array<C2> const& p2) const;
+};
+
+// Free functions
+template <class C>
+void swap(scoped_array<C>& p1, scoped_array<C>& p2) {
+ p1.swap(p2);
+}
+
+template <class C>
+bool operator==(C* p1, const scoped_array<C>& p2) {
+ return p1 == p2.get();
+}
+
+template <class C>
+bool operator!=(C* p1, const scoped_array<C>& p2) {
+ return p1 != p2.get();
+}
+
+// This class wraps the c library function free() in a class that can be
+// passed as a template argument to scoped_ptr_malloc below.
+class ScopedPtrMallocFree {
+ public:
+ inline void operator()(void* x) const {
+#if defined(OS_STARBOARD)
+ SbMemoryFree(x);
+#else
+ free(x);
+#endif
+ }
+};
+
+// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a
+// second template argument, the functor used to free the object.
+
+template<class C, class FreeProc = ScopedPtrMallocFree>
+class scoped_ptr_malloc {
+ MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr_malloc, RValue)
+
+ public:
+
+ // The element type
+ typedef C element_type;
+
+ // Constructor. Defaults to initializing with NULL.
+ // There is no way to create an uninitialized scoped_ptr.
+ // The input parameter must be allocated with an allocator that matches the
+ // Free functor. For the default Free functor, this is malloc, calloc, or
+ // realloc.
+ explicit scoped_ptr_malloc(C* p = NULL): ptr_(p) {}
+
+ // Constructor. Move constructor for C++03 move emulation of this type.
+ scoped_ptr_malloc(RValue rvalue)
+ : ptr_(rvalue.object->release()) {
+ }
+
+ // Destructor. If there is a C object, call the Free functor.
+ ~scoped_ptr_malloc() {
+ reset();
+ }
+
+ // operator=. Move operator= for C++03 move emulation of this type.
+ scoped_ptr_malloc& operator=(RValue rhs) {
+ swap(*rhs.object);
+ return *this;
+ }
+
+ // Reset. Calls the Free functor on the current owned object, if any.
+ // Then takes ownership of a new object, if given.
+ // this->reset(this->get()) works.
+ void reset(C* p = NULL) {
+ if (ptr_ != p) {
+ FreeProc free_proc;
+ free_proc(ptr_);
+ ptr_ = p;
+ }
+ }
+
+ // Get the current object.
+ // operator* and operator-> will cause an assert() failure if there is
+ // no current object.
+ C& operator*() const {
+ assert(ptr_ != NULL);
+ return *ptr_;
+ }
+
+ C* operator->() const {
+ assert(ptr_ != NULL);
+ return ptr_;
+ }
+
+ C* get() const {
+ return ptr_;
+ }
+
+ // Allow scoped_ptr_malloc<C> to be used in boolean expressions, but not
+ // implicitly convertible to a real bool (which is dangerous).
+ typedef C* scoped_ptr_malloc::*Testable;
+ operator Testable() const { return ptr_ ? &scoped_ptr_malloc::ptr_ : NULL; }
+
+ // Comparison operators.
+ // These return whether a scoped_ptr_malloc and a plain pointer refer
+ // to the same object, not just to two different but equal objects.
+ // For compatibility with the boost-derived implementation, these
+ // take non-const arguments.
+ bool operator==(C* p) const {
+ return ptr_ == p;
+ }
+
+ bool operator!=(C* p) const {
+ return ptr_ != p;
+ }
+
+ // Swap two scoped pointers.
+ void swap(scoped_ptr_malloc & b) {
+ C* tmp = b.ptr_;
+ b.ptr_ = ptr_;
+ ptr_ = tmp;
+ }
+
+ // Release a pointer.
+ // The return value is the current pointer held by this object.
+ // If this object holds a NULL pointer, the return value is NULL.
+ // After this operation, this object will hold a NULL pointer,
+ // and will not own the object any more.
+ C* release() WARN_UNUSED_RESULT {
+ C* tmp = ptr_;
+ ptr_ = NULL;
+ return tmp;
+ }
+
+ private:
+ C* ptr_;
+
+ // no reason to use these: each scoped_ptr_malloc should have its own object
+ template <class C2, class GP>
+ bool operator==(scoped_ptr_malloc<C2, GP> const& p) const;
+ template <class C2, class GP>
+ bool operator!=(scoped_ptr_malloc<C2, GP> const& p) const;
+};
+
+template<class C, class FP> inline
+void swap(scoped_ptr_malloc<C, FP>& a, scoped_ptr_malloc<C, FP>& b) {
+ a.swap(b);
+}
+
+template<class C, class FP> inline
+bool operator==(C* p, const scoped_ptr_malloc<C, FP>& b) {
+ return p == b.get();
+}
+
+template<class C, class FP> inline
+bool operator!=(C* p, const scoped_ptr_malloc<C, FP>& b) {
+ return p != b.get();
+}
+
+// A function to convert T* into scoped_ptr<T>
+// Doing e.g. make_scoped_ptr(new FooBarBaz<type>(arg)) is a shorter notation
+// for scoped_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg))
+template <typename T>
+scoped_ptr<T> make_scoped_ptr(T* ptr) {
+ return scoped_ptr<T>(ptr);
+}
+
+#endif // BASE_MEMORY_SCOPED_PTR_H_
diff --git a/src/base/memory/scoped_ptr_unittest.cc b/src/base/memory/scoped_ptr_unittest.cc
new file mode 100644
index 0000000..2a1b455
--- /dev/null
+++ b/src/base/memory/scoped_ptr_unittest.cc
@@ -0,0 +1,366 @@
+// Copyright (c) 2012 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/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Used to test depth subtyping.
+class ConDecLoggerParent {
+ public:
+ virtual ~ConDecLoggerParent() {}
+
+ virtual void SetPtr(int* ptr) = 0;
+
+ virtual int SomeMeth(int x) const = 0;
+};
+
+class ConDecLogger : public ConDecLoggerParent {
+ public:
+ ConDecLogger() : ptr_(NULL) { }
+ explicit ConDecLogger(int* ptr) { SetPtr(ptr); }
+ virtual ~ConDecLogger() { --*ptr_; }
+
+ virtual void SetPtr(int* ptr) OVERRIDE { ptr_ = ptr; ++*ptr_; }
+
+ virtual int SomeMeth(int x) const OVERRIDE { return x; }
+
+ private:
+ int* ptr_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConDecLogger);
+};
+
+scoped_ptr<ConDecLogger> PassThru(scoped_ptr<ConDecLogger> logger) {
+ return logger.Pass();
+}
+
+void GrabAndDrop(scoped_ptr<ConDecLogger> logger) {
+}
+
+// Do not delete this function! It's existence is to test that you can
+// return a temporarily constructed version of the scoper.
+scoped_ptr<ConDecLogger> TestReturnOfType(int* constructed) {
+ return scoped_ptr<ConDecLogger>(new ConDecLogger(constructed));
+}
+
+scoped_ptr<ConDecLoggerParent> UpcastUsingPassAs(
+ scoped_ptr<ConDecLogger> object) {
+ return object.PassAs<ConDecLoggerParent>();
+}
+
+} // namespace
+
+TEST(ScopedPtrTest, ScopedPtr) {
+ int constructed = 0;
+
+ {
+ scoped_ptr<ConDecLogger> scoper(new ConDecLogger(&constructed));
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper.get());
+
+ EXPECT_EQ(10, scoper->SomeMeth(10));
+ EXPECT_EQ(10, scoper.get()->SomeMeth(10));
+ EXPECT_EQ(10, (*scoper).SomeMeth(10));
+ }
+ EXPECT_EQ(0, constructed);
+
+ // Test reset() and release()
+ {
+ scoped_ptr<ConDecLogger> scoper(new ConDecLogger(&constructed));
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper.get());
+
+ scoper.reset(new ConDecLogger(&constructed));
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper.get());
+
+ scoper.reset();
+ EXPECT_EQ(0, constructed);
+ EXPECT_FALSE(scoper.get());
+
+ scoper.reset(new ConDecLogger(&constructed));
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper.get());
+
+ ConDecLogger* take = scoper.release();
+ EXPECT_EQ(1, constructed);
+ EXPECT_FALSE(scoper.get());
+ delete take;
+ EXPECT_EQ(0, constructed);
+
+ scoper.reset(new ConDecLogger(&constructed));
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper.get());
+ }
+ EXPECT_EQ(0, constructed);
+
+ // Test swap(), == and !=
+ {
+ scoped_ptr<ConDecLogger> scoper1;
+ scoped_ptr<ConDecLogger> scoper2;
+ EXPECT_TRUE(scoper1 == scoper2.get());
+ EXPECT_FALSE(scoper1 != scoper2.get());
+
+ ConDecLogger* logger = new ConDecLogger(&constructed);
+ scoper1.reset(logger);
+ EXPECT_EQ(logger, scoper1.get());
+ EXPECT_FALSE(scoper2.get());
+ EXPECT_FALSE(scoper1 == scoper2.get());
+ EXPECT_TRUE(scoper1 != scoper2.get());
+
+ scoper2.swap(scoper1);
+ EXPECT_EQ(logger, scoper2.get());
+ EXPECT_FALSE(scoper1.get());
+ EXPECT_FALSE(scoper1 == scoper2.get());
+ EXPECT_TRUE(scoper1 != scoper2.get());
+ }
+ EXPECT_EQ(0, constructed);
+}
+
+TEST(ScopedPtrTest, ScopedPtrDepthSubtyping) {
+ int constructed = 0;
+
+#if !defined(COMPILER_GHS)
+ // Test construction from a scoped_ptr to a derived class.
+ {
+ scoped_ptr<ConDecLogger> scoper(new ConDecLogger(&constructed));
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper.get());
+
+ scoped_ptr<ConDecLoggerParent> scoper_parent(scoper.Pass());
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper_parent.get());
+ EXPECT_FALSE(scoper.get());
+
+ EXPECT_EQ(10, scoper_parent->SomeMeth(10));
+ EXPECT_EQ(10, scoper_parent.get()->SomeMeth(10));
+ EXPECT_EQ(10, (*scoper_parent).SomeMeth(10));
+ }
+ EXPECT_EQ(0, constructed);
+#endif
+
+ // Test assignment from a scoped_ptr to a derived class.
+ {
+ scoped_ptr<ConDecLogger> scoper(new ConDecLogger(&constructed));
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper.get());
+
+ scoped_ptr<ConDecLoggerParent> scoper_parent;
+ scoper_parent = scoper.Pass();
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper_parent.get());
+ EXPECT_FALSE(scoper.get());
+ }
+ EXPECT_EQ(0, constructed);
+
+#if !defined(COMPILER_GHS)
+ // For the GHS compiler we have had to disable depth subtyping constructor,
+ // since that interferes with the move constructor. Luckily, we don't use
+ // the subtyping constructor anywhere in chromium, yet.
+ // Test construction of a scoped_ptr with an additional const annotation.
+ {
+ scoped_ptr<ConDecLogger> scoper(new ConDecLogger(&constructed));
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper.get());
+
+ scoped_ptr<const ConDecLogger> scoper_const(scoper.Pass());
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper_const.get());
+ EXPECT_FALSE(scoper.get());
+
+ EXPECT_EQ(10, scoper_const->SomeMeth(10));
+ EXPECT_EQ(10, scoper_const.get()->SomeMeth(10));
+ EXPECT_EQ(10, (*scoper_const).SomeMeth(10));
+ }
+ EXPECT_EQ(0, constructed);
+#endif
+
+ // Test assignment to a scoped_ptr with an additional const annotation.
+ {
+ scoped_ptr<ConDecLogger> scoper(new ConDecLogger(&constructed));
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper.get());
+
+ scoped_ptr<const ConDecLogger> scoper_const;
+ scoper_const = scoper.Pass();
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper_const.get());
+ EXPECT_FALSE(scoper.get());
+ }
+ EXPECT_EQ(0, constructed);
+}
+
+TEST(ScopedPtrTest, ScopedArray) {
+ static const int kNumLoggers = 12;
+
+ int constructed = 0;
+
+ {
+ scoped_array<ConDecLogger> scoper(new ConDecLogger[kNumLoggers]);
+ EXPECT_TRUE(scoper.get());
+ EXPECT_EQ(&scoper[0], scoper.get());
+ for (int i = 0; i < kNumLoggers; ++i) {
+ scoper[i].SetPtr(&constructed);
+ }
+ EXPECT_EQ(12, constructed);
+
+ EXPECT_EQ(10, scoper.get()->SomeMeth(10));
+ EXPECT_EQ(10, scoper[2].SomeMeth(10));
+ }
+ EXPECT_EQ(0, constructed);
+
+ // Test reset() and release()
+ {
+ scoped_array<ConDecLogger> scoper;
+ EXPECT_FALSE(scoper.get());
+ EXPECT_FALSE(scoper.release());
+ EXPECT_FALSE(scoper.get());
+ scoper.reset();
+ EXPECT_FALSE(scoper.get());
+
+ scoper.reset(new ConDecLogger[kNumLoggers]);
+ for (int i = 0; i < kNumLoggers; ++i) {
+ scoper[i].SetPtr(&constructed);
+ }
+ EXPECT_EQ(12, constructed);
+ scoper.reset();
+ EXPECT_EQ(0, constructed);
+
+ scoper.reset(new ConDecLogger[kNumLoggers]);
+ for (int i = 0; i < kNumLoggers; ++i) {
+ scoper[i].SetPtr(&constructed);
+ }
+ EXPECT_EQ(12, constructed);
+ ConDecLogger* ptr = scoper.release();
+ EXPECT_EQ(12, constructed);
+ delete[] ptr;
+ EXPECT_EQ(0, constructed);
+ }
+ EXPECT_EQ(0, constructed);
+
+ // Test swap(), == and !=
+ {
+ scoped_array<ConDecLogger> scoper1;
+ scoped_array<ConDecLogger> scoper2;
+ EXPECT_TRUE(scoper1 == scoper2.get());
+ EXPECT_FALSE(scoper1 != scoper2.get());
+
+ ConDecLogger* loggers = new ConDecLogger[kNumLoggers];
+ for (int i = 0; i < kNumLoggers; ++i) {
+ loggers[i].SetPtr(&constructed);
+ }
+ scoper1.reset(loggers);
+ EXPECT_EQ(loggers, scoper1.get());
+ EXPECT_FALSE(scoper2.get());
+ EXPECT_FALSE(scoper1 == scoper2.get());
+ EXPECT_TRUE(scoper1 != scoper2.get());
+
+ scoper2.swap(scoper1);
+ EXPECT_EQ(loggers, scoper2.get());
+ EXPECT_FALSE(scoper1.get());
+ EXPECT_FALSE(scoper1 == scoper2.get());
+ EXPECT_TRUE(scoper1 != scoper2.get());
+ }
+ EXPECT_EQ(0, constructed);
+}
+
+TEST(ScopedPtrTest, PassBehavior) {
+ int constructed = 0;
+ {
+ ConDecLogger* logger = new ConDecLogger(&constructed);
+ scoped_ptr<ConDecLogger> scoper(logger);
+ EXPECT_EQ(1, constructed);
+
+ // Test Pass() with constructor;
+ scoped_ptr<ConDecLogger> scoper2(scoper.Pass());
+ EXPECT_EQ(1, constructed);
+
+ // Test Pass() with assignment;
+ scoped_ptr<ConDecLogger> scoper3;
+ scoper3 = scoper2.Pass();
+ EXPECT_EQ(1, constructed);
+ EXPECT_FALSE(scoper.get());
+ EXPECT_FALSE(scoper2.get());
+ EXPECT_TRUE(scoper3.get());
+ }
+
+ // Test uncaught Pass() does not leak.
+ {
+ ConDecLogger* logger = new ConDecLogger(&constructed);
+ scoped_ptr<ConDecLogger> scoper(logger);
+ EXPECT_EQ(1, constructed);
+
+ // Should auto-destruct logger by end of scope.
+ scoper.Pass();
+ EXPECT_FALSE(scoper.get());
+ }
+ EXPECT_EQ(0, constructed);
+
+ // Test that passing to function which does nothing does not leak.
+ {
+ ConDecLogger* logger = new ConDecLogger(&constructed);
+ scoped_ptr<ConDecLogger> scoper(logger);
+ EXPECT_EQ(1, constructed);
+
+ // Should auto-destruct logger by end of scope.
+ GrabAndDrop(scoper.Pass());
+ EXPECT_FALSE(scoper.get());
+ }
+ EXPECT_EQ(0, constructed);
+}
+
+TEST(ScopedPtrTest, ReturnTypeBehavior) {
+ int constructed = 0;
+
+ // Test that we can return a scoped_ptr.
+ {
+ ConDecLogger* logger = new ConDecLogger(&constructed);
+ scoped_ptr<ConDecLogger> scoper(logger);
+ EXPECT_EQ(1, constructed);
+
+ PassThru(scoper.Pass());
+ EXPECT_FALSE(scoper.get());
+ }
+ EXPECT_EQ(0, constructed);
+
+ // Test uncaught return type not leak.
+ {
+ ConDecLogger* logger = new ConDecLogger(&constructed);
+ scoped_ptr<ConDecLogger> scoper(logger);
+ EXPECT_EQ(1, constructed);
+
+ // Should auto-destruct logger by end of scope.
+ PassThru(scoper.Pass());
+ EXPECT_FALSE(scoper.get());
+ }
+ EXPECT_EQ(0, constructed);
+
+ // Call TestReturnOfType() so the compiler doesn't warn for an unused
+ // function.
+ {
+ TestReturnOfType(&constructed);
+ }
+ EXPECT_EQ(0, constructed);
+}
+
+TEST(ScopedPtrTest, PassAs) {
+ int constructed = 0;
+ {
+ scoped_ptr<ConDecLogger> scoper(new ConDecLogger(&constructed));
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper.get());
+
+ scoped_ptr<ConDecLoggerParent> scoper_parent;
+ scoper_parent = UpcastUsingPassAs(scoper.Pass());
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper_parent.get());
+ EXPECT_FALSE(scoper.get());
+ }
+ EXPECT_EQ(0, constructed);
+}
+
+// TODO scoped_ptr_malloc
diff --git a/src/base/memory/scoped_ptr_unittest.nc b/src/base/memory/scoped_ptr_unittest.nc
new file mode 100644
index 0000000..30a332e
--- /dev/null
+++ b/src/base/memory/scoped_ptr_unittest.nc
@@ -0,0 +1,35 @@
+// Copyright (c) 2012 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/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/ref_counted.h"
+
+namespace {
+
+class Parent {
+};
+
+class Child : public Parent {
+};
+
+class RefCountedClass : public base::RefCountedThreadSafe<RefCountedClass> {
+};
+
+} // namespace
+
+#if defined(NCTEST_NO_PASSAS_DOWNCAST) // [r"invalid conversion from"]
+
+scoped_ptr<Child> DowncastUsingPassAs(scoped_ptr<Parent> object) {
+ return object.PassAs<Child>();
+}
+
+#elif defined(NCTEST_NO_REF_COUNTED_SCOPED_PTR) // [r"creating array with negative size"]
+
+// scoped_ptr<> should not work for ref-counted objects.
+void WontCompile() {
+ scoped_ptr<RefCountedClass> x;
+}
+
+#endif
diff --git a/src/base/memory/scoped_vector.h b/src/base/memory/scoped_vector.h
new file mode 100644
index 0000000..90a1f7d
--- /dev/null
+++ b/src/base/memory/scoped_vector.h
@@ -0,0 +1,130 @@
+// Copyright (c) 2012 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_MEMORY_SCOPED_VECTOR_H_
+#define BASE_MEMORY_SCOPED_VECTOR_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/move.h"
+#include "base/stl_util.h"
+
+// ScopedVector wraps a vector deleting the elements from its
+// destructor.
+template <class T>
+class ScopedVector {
+ MOVE_ONLY_TYPE_FOR_CPP_03(ScopedVector, RValue)
+
+ public:
+ typedef typename std::vector<T*>::allocator_type allocator_type;
+ typedef typename std::vector<T*>::size_type size_type;
+ typedef typename std::vector<T*>::difference_type difference_type;
+ typedef typename std::vector<T*>::pointer pointer;
+ typedef typename std::vector<T*>::const_pointer const_pointer;
+ typedef typename std::vector<T*>::reference reference;
+ typedef typename std::vector<T*>::const_reference const_reference;
+ typedef typename std::vector<T*>::value_type value_type;
+ typedef typename std::vector<T*>::iterator iterator;
+ typedef typename std::vector<T*>::const_iterator const_iterator;
+ typedef typename std::vector<T*>::reverse_iterator reverse_iterator;
+ typedef typename std::vector<T*>::const_reverse_iterator
+ const_reverse_iterator;
+
+ ScopedVector() {}
+ ~ScopedVector() { clear(); }
+ ScopedVector(RValue other) { swap(*other.object); }
+
+ ScopedVector& operator=(RValue rhs) {
+ swap(*rhs.object);
+ return *this;
+ }
+
+ T*& operator[](size_t index) { return v_[index]; }
+ const T* operator[](size_t index) const { return v_[index]; }
+
+ bool empty() const { return v_.empty(); }
+ size_t size() const { return v_.size(); }
+
+ reverse_iterator rbegin() { return v_.rbegin(); }
+ const_reverse_iterator rbegin() const { return v_.rbegin(); }
+ reverse_iterator rend() { return v_.rend(); }
+ const_reverse_iterator rend() const { return v_.rend(); }
+
+ iterator begin() { return v_.begin(); }
+ const_iterator begin() const { return v_.begin(); }
+ iterator end() { return v_.end(); }
+ const_iterator end() const { return v_.end(); }
+
+ const_reference front() const { return v_.front(); }
+ reference front() { return v_.front(); }
+ const_reference back() const { return v_.back(); }
+ reference back() { return v_.back(); }
+
+ void push_back(T* elem) { v_.push_back(elem); }
+
+ std::vector<T*>& get() { return v_; }
+ const std::vector<T*>& get() const { return v_; }
+ void swap(std::vector<T*>& other) { v_.swap(other); }
+ void swap(ScopedVector<T>& other) { v_.swap(other.v_); }
+ void release(std::vector<T*>* out) {
+ out->swap(v_);
+ v_.clear();
+ }
+
+ void reserve(size_t capacity) { v_.reserve(capacity); }
+
+ // Resize, deleting elements in the disappearing range if we are shrinking.
+ void resize(size_t new_size) {
+ if (v_.size() > new_size)
+ STLDeleteContainerPointers(v_.begin() + new_size, v_.end());
+ v_.resize(new_size);
+ }
+
+ template<typename InputIterator>
+ void assign(InputIterator begin, InputIterator end) {
+ v_.assign(begin, end);
+ }
+
+ void clear() { STLDeleteElements(&v_); }
+
+ // Like |clear()|, but doesn't delete any elements.
+ void weak_clear() { v_.clear(); }
+
+ // Lets the ScopedVector take ownership of |x|.
+ iterator insert(iterator position, T* x) {
+ return v_.insert(position, x);
+ }
+
+ // Lets the ScopedVector take ownership of elements in [first,last).
+ template<typename InputIterator>
+ void insert(iterator position, InputIterator first, InputIterator last) {
+ v_.insert(position, first, last);
+ }
+
+ iterator erase(iterator position) {
+ delete *position;
+ return v_.erase(position);
+ }
+
+ iterator erase(iterator first, iterator last) {
+ STLDeleteContainerPointers(first, last);
+ return v_.erase(first, last);
+ }
+
+ // Like |erase()|, but doesn't delete the element at |position|.
+ iterator weak_erase(iterator position) {
+ return v_.erase(position);
+ }
+
+ // Like |erase()|, but doesn't delete the elements in [first, last).
+ iterator weak_erase(iterator first, iterator last) {
+ return v_.erase(first, last);
+ }
+
+ private:
+ std::vector<T*> v_;
+};
+
+#endif // BASE_MEMORY_SCOPED_VECTOR_H_
diff --git a/src/base/memory/scoped_vector_unittest.cc b/src/base/memory/scoped_vector_unittest.cc
new file mode 100644
index 0000000..a91776a
--- /dev/null
+++ b/src/base/memory/scoped_vector_unittest.cc
@@ -0,0 +1,301 @@
+// Copyright (c) 2012 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/memory/scoped_vector.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// The LifeCycleObject notifies its Observer upon construction & destruction.
+class LifeCycleObject {
+ public:
+ class Observer {
+ public:
+ virtual void OnLifeCycleConstruct(LifeCycleObject* o) = 0;
+ virtual void OnLifeCycleDestroy(LifeCycleObject* o) = 0;
+
+ protected:
+ virtual ~Observer() {}
+ };
+
+ ~LifeCycleObject() {
+ observer_->OnLifeCycleDestroy(this);
+ }
+
+ private:
+ friend class LifeCycleWatcher;
+
+ explicit LifeCycleObject(Observer* observer)
+ : observer_(observer) {
+ observer_->OnLifeCycleConstruct(this);
+ }
+
+ Observer* observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(LifeCycleObject);
+};
+
+// The life cycle states we care about for the purposes of testing ScopedVector
+// against objects.
+enum LifeCycleState {
+ LC_INITIAL,
+ LC_CONSTRUCTED,
+ LC_DESTROYED,
+};
+
+// Because we wish to watch the life cycle of an object being constructed and
+// destroyed, and further wish to test expectations against the state of that
+// object, we cannot save state in that object itself. Instead, we use this
+// pairing of the watcher, which observes the object and notifies of
+// construction & destruction. Since we also may be testing assumptions about
+// things not getting freed, this class also acts like a scoping object and
+// deletes the |constructed_life_cycle_object_|, if any when the
+// LifeCycleWatcher is destroyed. To keep this simple, the only expected state
+// changes are:
+// INITIAL -> CONSTRUCTED -> DESTROYED.
+// Anything more complicated than that should start another test.
+class LifeCycleWatcher : public LifeCycleObject::Observer {
+ public:
+ LifeCycleWatcher()
+ : life_cycle_state_(LC_INITIAL),
+ constructed_life_cycle_object_(NULL) {}
+ virtual ~LifeCycleWatcher() {}
+
+ // Assert INITIAL -> CONSTRUCTED and no LifeCycleObject associated with this
+ // LifeCycleWatcher.
+ virtual void OnLifeCycleConstruct(LifeCycleObject* object) OVERRIDE {
+ ASSERT_EQ(LC_INITIAL, life_cycle_state_);
+ ASSERT_EQ(NULL, constructed_life_cycle_object_.get());
+ life_cycle_state_ = LC_CONSTRUCTED;
+ constructed_life_cycle_object_.reset(object);
+ }
+
+ // Assert CONSTRUCTED -> DESTROYED and the |object| being destroyed is the
+ // same one we saw constructed.
+ virtual void OnLifeCycleDestroy(LifeCycleObject* object) OVERRIDE {
+ ASSERT_EQ(LC_CONSTRUCTED, life_cycle_state_);
+ LifeCycleObject* constructed_life_cycle_object =
+ constructed_life_cycle_object_.release();
+ ASSERT_EQ(constructed_life_cycle_object, object);
+ life_cycle_state_ = LC_DESTROYED;
+ }
+
+ LifeCycleState life_cycle_state() const { return life_cycle_state_; }
+
+ // Factory method for creating a new LifeCycleObject tied to this
+ // LifeCycleWatcher.
+ LifeCycleObject* NewLifeCycleObject() {
+ return new LifeCycleObject(this);
+ }
+
+ // Returns true iff |object| is the same object that this watcher is tracking.
+ bool IsWatching(LifeCycleObject* object) const {
+ return object == constructed_life_cycle_object_.get();
+ }
+
+ private:
+ LifeCycleState life_cycle_state_;
+ scoped_ptr<LifeCycleObject> constructed_life_cycle_object_;
+
+ DISALLOW_COPY_AND_ASSIGN(LifeCycleWatcher);
+};
+
+TEST(ScopedVectorTest, LifeCycleWatcher) {
+ LifeCycleWatcher watcher;
+ EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state());
+ LifeCycleObject* object = watcher.NewLifeCycleObject();
+ EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state());
+ delete object;
+ EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state());
+}
+
+TEST(ScopedVectorTest, Clear) {
+ LifeCycleWatcher watcher;
+ EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state());
+ ScopedVector<LifeCycleObject> scoped_vector;
+ scoped_vector.push_back(watcher.NewLifeCycleObject());
+ EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state());
+ EXPECT_TRUE(watcher.IsWatching(scoped_vector.back()));
+ scoped_vector.clear();
+ EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state());
+ EXPECT_TRUE(scoped_vector.empty());
+}
+
+TEST(ScopedVectorTest, WeakClear) {
+ LifeCycleWatcher watcher;
+ EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state());
+ ScopedVector<LifeCycleObject> scoped_vector;
+ scoped_vector.push_back(watcher.NewLifeCycleObject());
+ EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state());
+ EXPECT_TRUE(watcher.IsWatching(scoped_vector.back()));
+ scoped_vector.weak_clear();
+ EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state());
+ EXPECT_TRUE(scoped_vector.empty());
+}
+
+TEST(ScopedVectorTest, ResizeShrink) {
+ LifeCycleWatcher first_watcher;
+ EXPECT_EQ(LC_INITIAL, first_watcher.life_cycle_state());
+ LifeCycleWatcher second_watcher;
+ EXPECT_EQ(LC_INITIAL, second_watcher.life_cycle_state());
+ ScopedVector<LifeCycleObject> scoped_vector;
+
+ scoped_vector.push_back(first_watcher.NewLifeCycleObject());
+ EXPECT_EQ(LC_CONSTRUCTED, first_watcher.life_cycle_state());
+ EXPECT_EQ(LC_INITIAL, second_watcher.life_cycle_state());
+ EXPECT_TRUE(first_watcher.IsWatching(scoped_vector[0]));
+ EXPECT_FALSE(second_watcher.IsWatching(scoped_vector[0]));
+
+ scoped_vector.push_back(second_watcher.NewLifeCycleObject());
+ EXPECT_EQ(LC_CONSTRUCTED, first_watcher.life_cycle_state());
+ EXPECT_EQ(LC_CONSTRUCTED, second_watcher.life_cycle_state());
+ EXPECT_FALSE(first_watcher.IsWatching(scoped_vector[1]));
+ EXPECT_TRUE(second_watcher.IsWatching(scoped_vector[1]));
+
+ // Test that shrinking a vector deletes elements in the disappearing range.
+ scoped_vector.resize(1);
+ EXPECT_EQ(LC_CONSTRUCTED, first_watcher.life_cycle_state());
+ EXPECT_EQ(LC_DESTROYED, second_watcher.life_cycle_state());
+ EXPECT_EQ(1u, scoped_vector.size());
+ EXPECT_TRUE(first_watcher.IsWatching(scoped_vector[0]));
+}
+
+TEST(ScopedVectorTest, ResizeGrow) {
+ LifeCycleWatcher watcher;
+ EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state());
+ ScopedVector<LifeCycleObject> scoped_vector;
+ scoped_vector.push_back(watcher.NewLifeCycleObject());
+ EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state());
+ EXPECT_TRUE(watcher.IsWatching(scoped_vector.back()));
+
+ scoped_vector.resize(5);
+ EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state());
+ ASSERT_EQ(5u, scoped_vector.size());
+ EXPECT_TRUE(watcher.IsWatching(scoped_vector[0]));
+ EXPECT_FALSE(watcher.IsWatching(scoped_vector[1]));
+ EXPECT_FALSE(watcher.IsWatching(scoped_vector[2]));
+ EXPECT_FALSE(watcher.IsWatching(scoped_vector[3]));
+ EXPECT_FALSE(watcher.IsWatching(scoped_vector[4]));
+}
+
+TEST(ScopedVectorTest, Scope) {
+ LifeCycleWatcher watcher;
+ EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state());
+ {
+ ScopedVector<LifeCycleObject> scoped_vector;
+ scoped_vector.push_back(watcher.NewLifeCycleObject());
+ EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state());
+ EXPECT_TRUE(watcher.IsWatching(scoped_vector.back()));
+ }
+ EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state());
+}
+
+TEST(ScopedVectorTest, MoveConstruct) {
+ LifeCycleWatcher watcher;
+ EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state());
+ {
+ ScopedVector<LifeCycleObject> scoped_vector;
+ scoped_vector.push_back(watcher.NewLifeCycleObject());
+ EXPECT_FALSE(scoped_vector.empty());
+ EXPECT_TRUE(watcher.IsWatching(scoped_vector.back()));
+
+ ScopedVector<LifeCycleObject> scoped_vector_copy(scoped_vector.Pass());
+ EXPECT_TRUE(scoped_vector.empty());
+ EXPECT_FALSE(scoped_vector_copy.empty());
+ EXPECT_TRUE(watcher.IsWatching(scoped_vector_copy.back()));
+
+ EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state());
+ }
+ EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state());
+}
+
+TEST(ScopedVectorTest, MoveAssign) {
+ LifeCycleWatcher watcher;
+ EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state());
+ {
+ ScopedVector<LifeCycleObject> scoped_vector;
+ scoped_vector.push_back(watcher.NewLifeCycleObject());
+ ScopedVector<LifeCycleObject> scoped_vector_assign;
+ EXPECT_FALSE(scoped_vector.empty());
+ EXPECT_TRUE(watcher.IsWatching(scoped_vector.back()));
+
+ scoped_vector_assign = scoped_vector.Pass();
+ EXPECT_TRUE(scoped_vector.empty());
+ EXPECT_FALSE(scoped_vector_assign.empty());
+ EXPECT_TRUE(watcher.IsWatching(scoped_vector_assign.back()));
+
+ EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state());
+ }
+ EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state());
+}
+
+class DeleteCounter {
+ public:
+ explicit DeleteCounter(int* deletes)
+ : deletes_(deletes) {
+ }
+
+ ~DeleteCounter() {
+ (*deletes_)++;
+ }
+
+ void VoidMethod0() {}
+
+ private:
+ int* const deletes_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeleteCounter);
+};
+
+template <typename T>
+ScopedVector<T> PassThru(ScopedVector<T> scoper) {
+ return scoper.Pass();
+}
+
+TEST(ScopedVectorTest, Passed) {
+ int deletes = 0;
+ ScopedVector<DeleteCounter> deleter_vector;
+ deleter_vector.push_back(new DeleteCounter(&deletes));
+ EXPECT_EQ(0, deletes);
+ base::Callback<ScopedVector<DeleteCounter>(void)> callback =
+ base::Bind(&PassThru<DeleteCounter>, base::Passed(&deleter_vector));
+ EXPECT_EQ(0, deletes);
+ ScopedVector<DeleteCounter> result = callback.Run();
+ EXPECT_EQ(0, deletes);
+ result.clear();
+ EXPECT_EQ(1, deletes);
+};
+
+TEST(ScopedVectorTest, InsertRange) {
+ LifeCycleWatcher watchers[5];
+
+ std::vector<LifeCycleObject*> vec;
+ for(LifeCycleWatcher* it = watchers; it != watchers + arraysize(watchers);
+ ++it) {
+ EXPECT_EQ(LC_INITIAL, it->life_cycle_state());
+ vec.push_back(it->NewLifeCycleObject());
+ EXPECT_EQ(LC_CONSTRUCTED, it->life_cycle_state());
+ }
+ // Start scope for ScopedVector.
+ {
+ ScopedVector<LifeCycleObject> scoped_vector;
+ scoped_vector.insert(scoped_vector.end(), vec.begin() + 1, vec.begin() + 3);
+ for(LifeCycleWatcher* it = watchers; it != watchers + arraysize(watchers);
+ ++it)
+ EXPECT_EQ(LC_CONSTRUCTED, it->life_cycle_state());
+ }
+ for(LifeCycleWatcher* it = watchers; it != watchers + 1; ++it)
+ EXPECT_EQ(LC_CONSTRUCTED, it->life_cycle_state());
+ for(LifeCycleWatcher* it = watchers + 1; it != watchers + 3; ++it)
+ EXPECT_EQ(LC_DESTROYED, it->life_cycle_state());
+ for(LifeCycleWatcher* it = watchers + 3; it != watchers + arraysize(watchers);
+ ++it)
+ EXPECT_EQ(LC_CONSTRUCTED, it->life_cycle_state());
+}
+
+} // namespace
diff --git a/src/base/memory/singleton.cc b/src/base/memory/singleton.cc
new file mode 100644
index 0000000..ee5e58d
--- /dev/null
+++ b/src/base/memory/singleton.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/singleton.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+namespace internal {
+
+subtle::AtomicWord WaitForInstance(subtle::AtomicWord* instance) {
+ // Handle the race. Another thread beat us and either:
+ // - Has the object in BeingCreated state
+ // - Already has the object created...
+ // We know value != NULL. It could be kBeingCreatedMarker, or a valid ptr.
+ // Unless your constructor can be very time consuming, it is very unlikely
+ // to hit this race. When it does, we just spin and yield the thread until
+ // the object has been created.
+ subtle::AtomicWord value;
+ while (true) {
+ value = subtle::NoBarrier_Load(instance);
+ if (value != kBeingCreatedMarker)
+ break;
+ PlatformThread::YieldCurrentThread();
+ }
+ return value;
+}
+
+} // namespace internal
+} // namespace base
+
diff --git a/src/base/memory/singleton.h b/src/base/memory/singleton.h
new file mode 100644
index 0000000..0d4fc89
--- /dev/null
+++ b/src/base/memory/singleton.h
@@ -0,0 +1,285 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// PLEASE READ: Do you really need a singleton?
+//
+// Singletons make it hard to determine the lifetime of an object, which can
+// lead to buggy code and spurious crashes.
+//
+// Instead of adding another singleton into the mix, try to identify either:
+// a) An existing singleton that can manage your object's lifetime
+// b) Locations where you can deterministically create the object and pass
+// into other objects
+//
+// If you absolutely need a singleton, please keep them as trivial as possible
+// and ideally a leaf dependency. Singletons get problematic when they attempt
+// to do too much in their destructor or have circular dependencies.
+
+#ifndef BASE_MEMORY_SINGLETON_H_
+#define BASE_MEMORY_SINGLETON_H_
+
+#include "base/at_exit.h"
+#include "base/atomicops.h"
+#include "base/base_export.h"
+#include "base/memory/aligned_memory.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace base {
+namespace internal {
+
+// Our AtomicWord doubles as a spinlock, where a value of
+// kBeingCreatedMarker means the spinlock is being held for creation.
+static const subtle::AtomicWord kBeingCreatedMarker = 1;
+
+// We pull out some of the functionality into a non-templated function, so that
+// we can implement the more complicated pieces out of line in the .cc file.
+BASE_EXPORT subtle::AtomicWord WaitForInstance(subtle::AtomicWord* instance);
+
+} // namespace internal
+} // namespace base
+
+// TODO(joth): Move more of this file into namespace base
+
+// Default traits for Singleton<Type>. Calls operator new and operator delete on
+// the object. Registers automatic deletion at process exit.
+// Overload if you need arguments or another memory allocation function.
+template<typename Type>
+struct DefaultSingletonTraits {
+ // Allocates the object.
+ static Type* New() {
+ // The parenthesis is very important here; it forces POD type
+ // initialization.
+ return new Type();
+ }
+
+ // Destroys the object.
+ static void Delete(Type* x) {
+ delete x;
+ }
+
+ // Set to true to automatically register deletion of the object on process
+ // exit. See below for the required call that makes this happen.
+ static const bool kRegisterAtExit = true;
+
+ // Set to false to disallow access on a non-joinable thread. This is
+ // different from kRegisterAtExit because StaticMemorySingletonTraits allows
+ // access on non-joinable threads, and gracefully handles this.
+ static const bool kAllowedToAccessOnNonjoinableThread = false;
+};
+
+
+// Alternate traits for use with the Singleton<Type>. Identical to
+// DefaultSingletonTraits except that the Singleton will not be cleaned up
+// at exit.
+template<typename Type>
+struct LeakySingletonTraits : public DefaultSingletonTraits<Type> {
+ static const bool kRegisterAtExit = false;
+ static const bool kAllowedToAccessOnNonjoinableThread = true;
+};
+
+
+// Alternate traits for use with the Singleton<Type>. Allocates memory
+// for the singleton instance from a static buffer. The singleton will
+// be cleaned up at exit, but can't be revived after destruction unless
+// the Resurrect() method is called.
+//
+// This is useful for a certain category of things, notably logging and
+// tracing, where the singleton instance is of a type carefully constructed to
+// be safe to access post-destruction.
+// In logging and tracing you'll typically get stray calls at odd times, like
+// during static destruction, thread teardown and the like, and there's a
+// termination race on the heap-based singleton - e.g. if one thread calls
+// get(), but then another thread initiates AtExit processing, the first thread
+// may call into an object residing in unallocated memory. If the instance is
+// allocated from the data segment, then this is survivable.
+//
+// The destructor is to deallocate system resources, in this case to unregister
+// a callback the system will invoke when logging levels change. Note that
+// this is also used in e.g. Chrome Frame, where you have to allow for the
+// possibility of loading briefly into someone else's process space, and
+// so leaking is not an option, as that would sabotage the state of your host
+// process once you've unloaded.
+template <typename Type>
+struct StaticMemorySingletonTraits {
+ // WARNING: User has to deal with get() in the singleton class
+ // this is traits for returning NULL.
+ static Type* New() {
+ // Only constructs once and returns pointer; otherwise returns NULL.
+ if (base::subtle::NoBarrier_AtomicExchange(&dead_, 1))
+ return NULL;
+
+ return new(buffer_.void_data()) Type();
+ }
+
+ static void Delete(Type* p) {
+ if (p != NULL)
+ p->Type::~Type();
+ }
+
+ static const bool kRegisterAtExit = true;
+ static const bool kAllowedToAccessOnNonjoinableThread = true;
+
+ // Exposed for unittesting.
+ static void Resurrect() {
+ base::subtle::NoBarrier_Store(&dead_, 0);
+ }
+
+ private:
+ static base::AlignedMemory<sizeof(Type), ALIGNOF(Type)> buffer_;
+ // Signal the object was already deleted, so it is not revived.
+ static base::subtle::Atomic32 dead_;
+};
+
+template <typename Type> base::AlignedMemory<sizeof(Type), ALIGNOF(Type)>
+ StaticMemorySingletonTraits<Type>::buffer_;
+template <typename Type> base::subtle::Atomic32
+ StaticMemorySingletonTraits<Type>::dead_ = 0;
+
+// The Singleton<Type, Traits, DifferentiatingType> class manages a single
+// instance of Type which will be created on first use and will be destroyed at
+// normal process exit). The Trait::Delete function will not be called on
+// abnormal process exit.
+//
+// DifferentiatingType is used as a key to differentiate two different
+// singletons having the same memory allocation functions but serving a
+// different purpose. This is mainly used for Locks serving different purposes.
+//
+// Example usage:
+//
+// In your header:
+// template <typename T> struct DefaultSingletonTraits;
+// class FooClass {
+// public:
+// static FooClass* GetInstance(); <-- See comment below on this.
+// void Bar() { ... }
+// private:
+// FooClass() { ... }
+// friend struct DefaultSingletonTraits<FooClass>;
+//
+// DISALLOW_COPY_AND_ASSIGN(FooClass);
+// };
+//
+// In your source file:
+// #include "base/memory/singleton.h"
+// FooClass* FooClass::GetInstance() {
+// return Singleton<FooClass>::get();
+// }
+//
+// And to call methods on FooClass:
+// FooClass::GetInstance()->Bar();
+//
+// NOTE: The method accessing Singleton<T>::get() has to be named as GetInstance
+// and it is important that FooClass::GetInstance() is not inlined in the
+// header. This makes sure that when source files from multiple targets include
+// this header they don't end up with different copies of the inlined code
+// creating multiple copies of the singleton.
+//
+// Singleton<> has no non-static members and doesn't need to actually be
+// instantiated.
+//
+// This class is itself thread-safe. The underlying Type must of course be
+// thread-safe if you want to use it concurrently. Two parameters may be tuned
+// depending on the user's requirements.
+//
+// Glossary:
+// RAE = kRegisterAtExit
+//
+// On every platform, if Traits::RAE is true, the singleton will be destroyed at
+// process exit. More precisely it uses base::AtExitManager which requires an
+// object of this type to be instantiated. AtExitManager mimics the semantics
+// of atexit() such as LIFO order but under Windows is safer to call. For more
+// information see at_exit.h.
+//
+// If Traits::RAE is false, the singleton will not be freed at process exit,
+// thus the singleton will be leaked if it is ever accessed. Traits::RAE
+// shouldn't be false unless absolutely necessary. Remember that the heap where
+// the object is allocated may be destroyed by the CRT anyway.
+//
+// Caveats:
+// (a) Every call to get(), operator->() and operator*() incurs some overhead
+// (16ns on my P4/2.8GHz) to check whether the object has already been
+// initialized. You may wish to cache the result of get(); it will not
+// change.
+//
+// (b) Your factory function must never throw an exception. This class is not
+// exception-safe.
+//
+template <typename Type,
+ typename Traits = DefaultSingletonTraits<Type>,
+ typename DifferentiatingType = Type>
+class Singleton {
+ private:
+ // Classes using the Singleton<T> pattern should declare a GetInstance()
+ // method and call Singleton::get() from within that.
+ friend Type* Type::GetInstance();
+
+ // Allow TraceLog tests to test tracing after OnExit.
+ friend class DeleteTraceLogForTesting;
+
+ // This class is safe to be constructed and copy-constructed since it has no
+ // member.
+
+ // Return a pointer to the one true instance of the class.
+ static Type* get() {
+#ifndef NDEBUG
+ // Avoid making TLS lookup on release builds.
+ if (!Traits::kAllowedToAccessOnNonjoinableThread)
+ base::ThreadRestrictions::AssertSingletonAllowed();
+#endif
+
+ base::subtle::AtomicWord value = base::subtle::NoBarrier_Load(&instance_);
+ if (value != 0 && value != base::internal::kBeingCreatedMarker) {
+ // See the corresponding HAPPENS_BEFORE below.
+ ANNOTATE_HAPPENS_AFTER(&instance_);
+ return reinterpret_cast<Type*>(value);
+ }
+
+ // Object isn't created yet, maybe we will get to create it, let's try...
+ if (base::subtle::Acquire_CompareAndSwap(
+ &instance_, 0, base::internal::kBeingCreatedMarker) == 0) {
+ // instance_ was NULL and is now kBeingCreatedMarker. Only one thread
+ // will ever get here. Threads might be spinning on us, and they will
+ // stop right after we do this store.
+ Type* newval = Traits::New();
+
+ // This annotation helps race detectors recognize correct lock-less
+ // synchronization between different threads calling get().
+ // See the corresponding HAPPENS_AFTER below and above.
+ ANNOTATE_HAPPENS_BEFORE(&instance_);
+ base::subtle::Release_Store(
+ &instance_, reinterpret_cast<base::subtle::AtomicWord>(newval));
+
+ if (newval != NULL && Traits::kRegisterAtExit)
+ base::AtExitManager::RegisterCallback(OnExit, NULL);
+
+ return newval;
+ }
+
+ // We hit a race. Wait for the other thread to complete it.
+ value = base::internal::WaitForInstance(&instance_);
+
+ // See the corresponding HAPPENS_BEFORE above.
+ ANNOTATE_HAPPENS_AFTER(&instance_);
+ return reinterpret_cast<Type*>(value);
+ }
+
+ // Adapter function for use with AtExit(). This should be called single
+ // threaded, so don't use atomic operations.
+ // Calling OnExit while singleton is in use by other threads is a mistake.
+ static void OnExit(void* /*unused*/) {
+ // AtExit should only ever be register after the singleton instance was
+ // created. We should only ever get here with a valid instance_ pointer.
+ Traits::Delete(
+ reinterpret_cast<Type*>(base::subtle::NoBarrier_Load(&instance_)));
+ instance_ = 0;
+ }
+ static base::subtle::AtomicWord instance_;
+};
+
+template <typename Type, typename Traits, typename DifferentiatingType>
+base::subtle::AtomicWord Singleton<Type, Traits, DifferentiatingType>::
+ instance_ = 0;
+
+#endif // BASE_MEMORY_SINGLETON_H_
diff --git a/src/base/memory/singleton_objc.h b/src/base/memory/singleton_objc.h
new file mode 100644
index 0000000..6df3f77
--- /dev/null
+++ b/src/base/memory/singleton_objc.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Support for using the Singleton<T> pattern with Objective-C objects. A
+// SingletonObjC is the same as a Singleton, except the default traits are
+// appropriate for Objective-C objects. A typical Objective-C object of type
+// NSExampleType can be maintained as a singleton and accessed with:
+//
+// NSExampleType* exampleSingleton = SingletonObjC<NSExampleType>::get();
+//
+// The first time this is used, it will create exampleSingleton as the result
+// of [[NSExampleType alloc] init]. Subsequent calls will return the same
+// NSExampleType* object. The object will be released by calling
+// -[NSExampleType release] when Singleton's atexit routines run
+// (see singleton.h).
+//
+// For Objective-C objects initialized through means other than the
+// no-parameter -init selector, DefaultSingletonObjCTraits may be extended
+// as needed:
+//
+// struct FooSingletonTraits : public DefaultSingletonObjCTraits<Foo> {
+// static Foo* New() {
+// return [[Foo alloc] initWithName:@"selecty"];
+// }
+// };
+// ...
+// Foo* widgetSingleton = SingletonObjC<Foo, FooSingletonTraits>::get();
+
+#ifndef BASE_MEMORY_SINGLETON_OBJC_H_
+#define BASE_MEMORY_SINGLETON_OBJC_H_
+
+#import <Foundation/Foundation.h>
+#include "base/memory/singleton.h"
+
+// Singleton traits usable to manage traditional Objective-C objects, which
+// are instantiated by sending |alloc| and |init| messages, and are deallocated
+// in a memory-managed environment when their retain counts drop to 0 by
+// sending |release| messages.
+template<typename Type>
+struct DefaultSingletonObjCTraits : public DefaultSingletonTraits<Type> {
+ static Type* New() {
+ return [[Type alloc] init];
+ }
+
+ static void Delete(Type* object) {
+ [object release];
+ }
+};
+
+// Exactly like Singleton, but without the DefaultSingletonObjCTraits as the
+// default trait class. This makes it straightforward for Objective-C++ code
+// to hold Objective-C objects as singletons.
+template<typename Type,
+ typename Traits = DefaultSingletonObjCTraits<Type>,
+ typename DifferentiatingType = Type>
+class SingletonObjC : public Singleton<Type, Traits, DifferentiatingType> {
+};
+
+#endif // BASE_MEMORY_SINGLETON_OBJC_H_
diff --git a/src/base/memory/singleton_unittest.cc b/src/base/memory/singleton_unittest.cc
new file mode 100644
index 0000000..d9892cb
--- /dev/null
+++ b/src/base/memory/singleton_unittest.cc
@@ -0,0 +1,289 @@
+// Copyright (c) 2012 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/file_util.h"
+#include "base/memory/singleton.h"
+#include "base/path_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+COMPILE_ASSERT(DefaultSingletonTraits<int>::kRegisterAtExit == true, a);
+
+typedef void (*CallbackFunc)();
+
+class IntSingleton {
+ public:
+ static IntSingleton* GetInstance() {
+ return Singleton<IntSingleton>::get();
+ }
+
+ int value_;
+};
+
+class Init5Singleton {
+ public:
+ struct Trait;
+
+ static Init5Singleton* GetInstance() {
+ return Singleton<Init5Singleton, Trait>::get();
+ }
+
+ int value_;
+};
+
+struct Init5Singleton::Trait : public DefaultSingletonTraits<Init5Singleton> {
+ static Init5Singleton* New() {
+ Init5Singleton* instance = new Init5Singleton();
+ instance->value_ = 5;
+ return instance;
+ }
+};
+
+int* SingletonInt() {
+ return &IntSingleton::GetInstance()->value_;
+}
+
+int* SingletonInt5() {
+ return &Init5Singleton::GetInstance()->value_;
+}
+
+template <typename Type>
+struct CallbackTrait : public DefaultSingletonTraits<Type> {
+ static void Delete(Type* instance) {
+ if (instance->callback_)
+ (instance->callback_)();
+ DefaultSingletonTraits<Type>::Delete(instance);
+ }
+};
+
+class CallbackSingleton {
+ public:
+ CallbackSingleton() : callback_(NULL) { }
+ CallbackFunc callback_;
+};
+
+class CallbackSingletonWithNoLeakTrait : public CallbackSingleton {
+ public:
+ struct Trait : public CallbackTrait<CallbackSingletonWithNoLeakTrait> { };
+
+ CallbackSingletonWithNoLeakTrait() : CallbackSingleton() { }
+
+ static CallbackSingletonWithNoLeakTrait* GetInstance() {
+ return Singleton<CallbackSingletonWithNoLeakTrait, Trait>::get();
+ }
+};
+
+class CallbackSingletonWithLeakTrait : public CallbackSingleton {
+ public:
+ struct Trait : public CallbackTrait<CallbackSingletonWithLeakTrait> {
+ static const bool kRegisterAtExit = false;
+ };
+
+ CallbackSingletonWithLeakTrait() : CallbackSingleton() { }
+
+ static CallbackSingletonWithLeakTrait* GetInstance() {
+ return Singleton<CallbackSingletonWithLeakTrait, Trait>::get();
+ }
+};
+
+class CallbackSingletonWithStaticTrait : public CallbackSingleton {
+ public:
+ struct Trait;
+
+ CallbackSingletonWithStaticTrait() : CallbackSingleton() { }
+
+ static CallbackSingletonWithStaticTrait* GetInstance() {
+ return Singleton<CallbackSingletonWithStaticTrait, Trait>::get();
+ }
+};
+
+struct CallbackSingletonWithStaticTrait::Trait
+ : public StaticMemorySingletonTraits<CallbackSingletonWithStaticTrait> {
+ static void Delete(CallbackSingletonWithStaticTrait* instance) {
+ if (instance->callback_)
+ (instance->callback_)();
+ StaticMemorySingletonTraits<CallbackSingletonWithStaticTrait>::Delete(
+ instance);
+ }
+};
+
+template <class Type>
+class AlignedTestSingleton {
+ public:
+ AlignedTestSingleton() {}
+ ~AlignedTestSingleton() {}
+ static AlignedTestSingleton* GetInstance() {
+ return Singleton<AlignedTestSingleton,
+ StaticMemorySingletonTraits<AlignedTestSingleton> >::get();
+ }
+
+ Type type_;
+};
+
+
+void SingletonNoLeak(CallbackFunc CallOnQuit) {
+ CallbackSingletonWithNoLeakTrait::GetInstance()->callback_ = CallOnQuit;
+}
+
+void SingletonLeak(CallbackFunc CallOnQuit) {
+ CallbackSingletonWithLeakTrait::GetInstance()->callback_ = CallOnQuit;
+}
+
+CallbackFunc* GetLeakySingleton() {
+ return &CallbackSingletonWithLeakTrait::GetInstance()->callback_;
+}
+
+void DeleteLeakySingleton() {
+ DefaultSingletonTraits<CallbackSingletonWithLeakTrait>::Delete(
+ CallbackSingletonWithLeakTrait::GetInstance());
+}
+
+void SingletonStatic(CallbackFunc CallOnQuit) {
+ CallbackSingletonWithStaticTrait::GetInstance()->callback_ = CallOnQuit;
+}
+
+CallbackFunc* GetStaticSingleton() {
+ return &CallbackSingletonWithStaticTrait::GetInstance()->callback_;
+}
+
+} // namespace
+
+class SingletonTest : public testing::Test {
+ public:
+ SingletonTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ non_leak_called_ = false;
+ leaky_called_ = false;
+ static_called_ = false;
+ }
+
+ protected:
+ void VerifiesCallbacks() {
+ EXPECT_TRUE(non_leak_called_);
+ EXPECT_FALSE(leaky_called_);
+ EXPECT_TRUE(static_called_);
+ non_leak_called_ = false;
+ leaky_called_ = false;
+ static_called_ = false;
+ }
+
+ void VerifiesCallbacksNotCalled() {
+ EXPECT_FALSE(non_leak_called_);
+ EXPECT_FALSE(leaky_called_);
+ EXPECT_FALSE(static_called_);
+ non_leak_called_ = false;
+ leaky_called_ = false;
+ static_called_ = false;
+ }
+
+ static void CallbackNoLeak() {
+ non_leak_called_ = true;
+ }
+
+ static void CallbackLeak() {
+ leaky_called_ = true;
+ }
+
+ static void CallbackStatic() {
+ static_called_ = true;
+ }
+
+ private:
+ static bool non_leak_called_;
+ static bool leaky_called_;
+ static bool static_called_;
+};
+
+bool SingletonTest::non_leak_called_ = false;
+bool SingletonTest::leaky_called_ = false;
+bool SingletonTest::static_called_ = false;
+
+TEST_F(SingletonTest, Basic) {
+ int* singleton_int;
+ int* singleton_int_5;
+ CallbackFunc* leaky_singleton;
+ CallbackFunc* static_singleton;
+
+ {
+ base::ShadowingAtExitManager sem;
+ {
+ singleton_int = SingletonInt();
+ }
+ // Ensure POD type initialization.
+ EXPECT_EQ(*singleton_int, 0);
+ *singleton_int = 1;
+
+ EXPECT_EQ(singleton_int, SingletonInt());
+ EXPECT_EQ(*singleton_int, 1);
+
+ {
+ singleton_int_5 = SingletonInt5();
+ }
+ // Is default initialized to 5.
+ EXPECT_EQ(*singleton_int_5, 5);
+
+ SingletonNoLeak(&CallbackNoLeak);
+ SingletonLeak(&CallbackLeak);
+ SingletonStatic(&CallbackStatic);
+ static_singleton = GetStaticSingleton();
+ leaky_singleton = GetLeakySingleton();
+ EXPECT_TRUE(leaky_singleton);
+ }
+
+ // Verify that only the expected callback has been called.
+ VerifiesCallbacks();
+ // Delete the leaky singleton.
+ DeleteLeakySingleton();
+
+ // The static singleton can't be acquired post-atexit.
+ EXPECT_EQ(NULL, GetStaticSingleton());
+
+ {
+ base::ShadowingAtExitManager sem;
+ // Verifiy that the variables were reset.
+ {
+ singleton_int = SingletonInt();
+ EXPECT_EQ(*singleton_int, 0);
+ }
+ {
+ singleton_int_5 = SingletonInt5();
+ EXPECT_EQ(*singleton_int_5, 5);
+ }
+ {
+ // Resurrect the static singleton, and assert that it
+ // still points to the same (static) memory.
+ CallbackSingletonWithStaticTrait::Trait::Resurrect();
+ EXPECT_EQ(GetStaticSingleton(), static_singleton);
+ }
+ }
+ // The leaky singleton shouldn't leak since SingletonLeak has not been called.
+ VerifiesCallbacksNotCalled();
+}
+
+#define EXPECT_ALIGNED(ptr, align) \
+ EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(ptr) & (align - 1))
+
+TEST_F(SingletonTest, Alignment) {
+ using base::AlignedMemory;
+
+ // Create some static singletons with increasing sizes and alignment
+ // requirements. By ordering this way, the linker will need to do some work to
+ // ensure proper alignment of the static data.
+ AlignedTestSingleton<int32>* align4 =
+ AlignedTestSingleton<int32>::GetInstance();
+ AlignedTestSingleton<AlignedMemory<32, 32> >* align32 =
+ AlignedTestSingleton<AlignedMemory<32, 32> >::GetInstance();
+ AlignedTestSingleton<AlignedMemory<128, 128> >* align128 =
+ AlignedTestSingleton<AlignedMemory<128, 128> >::GetInstance();
+ AlignedTestSingleton<AlignedMemory<4096, 4096> >* align4096 =
+ AlignedTestSingleton<AlignedMemory<4096, 4096> >::GetInstance();
+
+ EXPECT_ALIGNED(align4, 4);
+ EXPECT_ALIGNED(align32, 32);
+ EXPECT_ALIGNED(align128, 128);
+ EXPECT_ALIGNED(align4096, 4096);
+}
diff --git a/src/base/memory/weak_ptr.cc b/src/base/memory/weak_ptr.cc
new file mode 100644
index 0000000..9dec8fd
--- /dev/null
+++ b/src/base/memory/weak_ptr.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/weak_ptr.h"
+
+namespace base {
+namespace internal {
+
+WeakReference::Flag::Flag() : is_valid_(true) {
+}
+
+void WeakReference::Flag::Invalidate() {
+ // The flag being invalidated with a single ref implies that there are no
+ // weak pointers in existence. Allow deletion on other thread in this case.
+ DCHECK(thread_checker_.CalledOnValidThread() || HasOneRef());
+ is_valid_ = false;
+}
+
+bool WeakReference::Flag::IsValid() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return is_valid_;
+}
+
+WeakReference::Flag::~Flag() {
+}
+
+WeakReference::WeakReference() {
+}
+
+WeakReference::WeakReference(const Flag* flag) : flag_(flag) {
+}
+
+WeakReference::~WeakReference() {
+}
+
+bool WeakReference::is_valid() const {
+ return flag_ && flag_->IsValid();
+}
+
+WeakReferenceOwner::WeakReferenceOwner() {
+}
+
+WeakReferenceOwner::~WeakReferenceOwner() {
+ Invalidate();
+}
+
+WeakReference WeakReferenceOwner::GetRef() const {
+ // We also want to reattach to the current thread if all previous references
+ // have gone away.
+ if (!HasRefs())
+ flag_ = new WeakReference::Flag();
+ return WeakReference(flag_);
+}
+
+void WeakReferenceOwner::Invalidate() {
+ if (flag_) {
+ flag_->Invalidate();
+ flag_ = NULL;
+ }
+}
+
+WeakPtrBase::WeakPtrBase() {
+}
+
+WeakPtrBase::~WeakPtrBase() {
+}
+
+WeakPtrBase::WeakPtrBase(const WeakReference& ref) : ref_(ref) {
+}
+
+} // namespace internal
+} // namespace base
diff --git a/src/base/memory/weak_ptr.h b/src/base/memory/weak_ptr.h
new file mode 100644
index 0000000..6dc93b7
--- /dev/null
+++ b/src/base/memory/weak_ptr.h
@@ -0,0 +1,350 @@
+// Copyright (c) 2012 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.
+
+// Weak pointers help in cases where you have many objects referring back to a
+// shared object and you wish for the lifetime of the shared object to not be
+// bound to the lifetime of the referrers. In other words, this is useful when
+// reference counting is not a good fit.
+//
+// A common alternative to weak pointers is to have the shared object hold a
+// list of all referrers, and then when the shared object is destroyed, it
+// calls a method on the referrers to tell them to drop their references. This
+// approach also requires the referrers to tell the shared object when they get
+// destroyed so that the shared object can remove the referrer from its list of
+// referrers. Such a solution works, but it is a bit complex.
+//
+// EXAMPLE:
+//
+// class Controller : public SupportsWeakPtr<Controller> {
+// public:
+// void SpawnWorker() { Worker::StartNew(AsWeakPtr()); }
+// void WorkComplete(const Result& result) { ... }
+// };
+//
+// class Worker {
+// public:
+// static void StartNew(const WeakPtr<Controller>& controller) {
+// Worker* worker = new Worker(controller);
+// // Kick off asynchronous processing...
+// }
+// private:
+// Worker(const WeakPtr<Controller>& controller)
+// : controller_(controller) {}
+// void DidCompleteAsynchronousProcessing(const Result& result) {
+// if (controller_)
+// controller_->WorkComplete(result);
+// }
+// WeakPtr<Controller> controller_;
+// };
+//
+// Given the above classes, a consumer may allocate a Controller object, call
+// SpawnWorker several times, and then destroy the Controller object before all
+// of the workers have completed. Because the Worker class only holds a weak
+// pointer to the Controller, we don't have to worry about the Worker
+// dereferencing the Controller back pointer after the Controller has been
+// destroyed.
+//
+// ------------------------ Thread-safety notes ------------------------
+// When you get a WeakPtr (from a WeakPtrFactory or SupportsWeakPtr), if it's
+// the only one pointing to the object, the object become bound to the
+// current thread, as well as this WeakPtr and all later ones get created.
+//
+// You may only dereference the WeakPtr on the thread it binds to. However, it
+// is safe to destroy the WeakPtr object on another thread. Because of this,
+// querying WeakPtrFactory's HasWeakPtrs() method can be racy.
+//
+// On the other hand, the object that supports WeakPtr (extends SupportsWeakPtr)
+// can only be deleted from the thread it binds to, until all WeakPtrs are
+// deleted.
+//
+// Calling SupportsWeakPtr::DetachFromThread() can work around the limitations
+// above and cancel the thread binding of the object and all WeakPtrs pointing
+// to it, but it's not recommended and unsafe.
+//
+// WeakPtrs may be copy-constructed or assigned on threads other than the thread
+// they are bound to. This does not change the thread binding. So these WeakPtrs
+// may only be dereferenced on the thread that the original WeakPtr was bound
+// to.
+
+#ifndef BASE_MEMORY_WEAK_PTR_H_
+#define BASE_MEMORY_WEAK_PTR_H_
+
+#include "base/basictypes.h"
+#include "base/base_export.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/template_util.h"
+#include "base/threading/thread_checker.h"
+
+namespace base {
+
+template <typename T> class SupportsWeakPtr;
+template <typename T> class WeakPtr;
+
+namespace internal {
+// These classes are part of the WeakPtr implementation.
+// DO NOT USE THESE CLASSES DIRECTLY YOURSELF.
+
+class BASE_EXPORT WeakReference {
+ public:
+ // While Flag is bound to a specific thread, it may be deleted from another
+ // via base::WeakPtr::~WeakPtr().
+ class Flag : public RefCountedThreadSafe<Flag> {
+ public:
+ Flag();
+
+ void Invalidate();
+ bool IsValid() const;
+
+ void DetachFromThread() { thread_checker_.DetachFromThread(); }
+
+ private:
+ friend class base::RefCountedThreadSafe<Flag>;
+
+ ~Flag();
+
+ ThreadChecker thread_checker_;
+ bool is_valid_;
+ };
+
+ WeakReference();
+ explicit WeakReference(const Flag* flag);
+ ~WeakReference();
+
+ bool is_valid() const;
+
+ private:
+ scoped_refptr<const Flag> flag_;
+};
+
+class BASE_EXPORT WeakReferenceOwner {
+ public:
+ WeakReferenceOwner();
+ ~WeakReferenceOwner();
+
+ WeakReference GetRef() const;
+
+ bool HasRefs() const {
+ return flag_.get() && !flag_->HasOneRef();
+ }
+
+ void Invalidate();
+
+ // Indicates that this object will be used on another thread from now on.
+ void DetachFromThread() {
+ if (flag_) flag_->DetachFromThread();
+ }
+
+ private:
+ mutable scoped_refptr<WeakReference::Flag> flag_;
+};
+
+// This class simplifies the implementation of WeakPtr's type conversion
+// constructor by avoiding the need for a public accessor for ref_. A
+// WeakPtr<T> cannot access the private members of WeakPtr<U>, so this
+// base class gives us a way to access ref_ in a protected fashion.
+class BASE_EXPORT WeakPtrBase {
+ public:
+ WeakPtrBase();
+ ~WeakPtrBase();
+
+ protected:
+ explicit WeakPtrBase(const WeakReference& ref);
+
+ WeakReference ref_;
+};
+
+// This class provides a common implementation of common functions that would
+// otherwise get instantiated separately for each distinct instantiation of
+// SupportsWeakPtr<>.
+class SupportsWeakPtrBase {
+ public:
+ // A safe static downcast of a WeakPtr<Base> to WeakPtr<Derived>. This
+ // conversion will only compile if there is exists a Base which inherits
+ // from SupportsWeakPtr<Base>. See base::AsWeakPtr() below for a helper
+ // function that makes calling this easier.
+ template<typename Derived>
+ static WeakPtr<Derived> StaticAsWeakPtr(Derived* t) {
+ typedef
+ is_convertible<Derived, internal::SupportsWeakPtrBase&> convertible;
+ COMPILE_ASSERT(convertible::value,
+ AsWeakPtr_argument_inherits_from_SupportsWeakPtr);
+ return AsWeakPtrImpl<Derived>(t, *t);
+ }
+
+ private:
+ // This template function uses type inference to find a Base of Derived
+ // which is an instance of SupportsWeakPtr<Base>. We can then safely
+ // static_cast the Base* to a Derived*.
+ template <typename Derived, typename Base>
+ static WeakPtr<Derived> AsWeakPtrImpl(
+ Derived* t, const SupportsWeakPtr<Base>&) {
+ WeakPtr<Base> ptr = t->Base::AsWeakPtr();
+ return WeakPtr<Derived>(ptr.ref_, static_cast<Derived*>(ptr.ptr_));
+ }
+};
+
+} // namespace internal
+
+template <typename T> class WeakPtrFactory;
+
+// The WeakPtr class holds a weak reference to |T*|.
+//
+// This class is designed to be used like a normal pointer. You should always
+// null-test an object of this class before using it or invoking a method that
+// may result in the underlying object being destroyed.
+//
+// EXAMPLE:
+//
+// class Foo { ... };
+// WeakPtr<Foo> foo;
+// if (foo)
+// foo->method();
+//
+template <typename T>
+class WeakPtr : public internal::WeakPtrBase {
+ public:
+ WeakPtr() : ptr_(NULL) {
+ }
+
+ // Allow conversion from U to T provided U "is a" T.
+ template <typename U>
+ WeakPtr(const WeakPtr<U>& other) : WeakPtrBase(other), ptr_(other.get()) {
+ }
+
+ T* get() const { return ref_.is_valid() ? ptr_ : NULL; }
+ operator T*() const { return get(); }
+
+ T& operator*() const {
+ DCHECK(get() != NULL);
+ return *get();
+ }
+ T* operator->() const {
+ DCHECK(get() != NULL);
+ return get();
+ }
+
+ void reset() {
+ ref_ = internal::WeakReference();
+ ptr_ = NULL;
+ }
+
+ private:
+ friend class internal::SupportsWeakPtrBase;
+ friend class SupportsWeakPtr<T>;
+ friend class WeakPtrFactory<T>;
+
+ WeakPtr(const internal::WeakReference& ref, T* ptr)
+ : WeakPtrBase(ref),
+ ptr_(ptr) {
+ }
+
+ // This pointer is only valid when ref_.is_valid() is true. Otherwise, its
+ // value is undefined (as opposed to NULL).
+ T* ptr_;
+};
+
+// A class may extend from SupportsWeakPtr to expose weak pointers to itself.
+// This is useful in cases where you want others to be able to get a weak
+// pointer to your class. It also has the property that you don't need to
+// initialize it from your constructor.
+template <class T>
+class SupportsWeakPtr : public internal::SupportsWeakPtrBase {
+ public:
+ SupportsWeakPtr() {}
+
+ WeakPtr<T> AsWeakPtr() {
+ return WeakPtr<T>(weak_reference_owner_.GetRef(), static_cast<T*>(this));
+ }
+
+ // Indicates that this object will be used on another thread from now on.
+ void DetachFromThread() {
+ weak_reference_owner_.DetachFromThread();
+ }
+
+ protected:
+ ~SupportsWeakPtr() {}
+
+ // Call this method to invalidate all existing weak pointers.
+ // This may be useful to call explicitly in a destructor of a derived class,
+ // as the SupportsWeakPtr destructor won't run until late in destruction.
+ void InvalidateWeakPtrs() {
+ weak_reference_owner_.Invalidate();
+ }
+
+ private:
+ internal::WeakReferenceOwner weak_reference_owner_;
+ DISALLOW_COPY_AND_ASSIGN(SupportsWeakPtr);
+};
+
+// Helper function that uses type deduction to safely return a WeakPtr<Derived>
+// when Derived doesn't directly extend SupportsWeakPtr<Derived>, instead it
+// extends a Base that extends SupportsWeakPtr<Base>.
+//
+// EXAMPLE:
+// class Base : public base::SupportsWeakPtr<Producer> {};
+// class Derived : public Base {};
+//
+// Derived derived;
+// base::WeakPtr<Derived> ptr = base::AsWeakPtr(&derived);
+//
+// Note that the following doesn't work (invalid type conversion) since
+// Derived::AsWeakPtr() is WeakPtr<Base> SupportsWeakPtr<Base>::AsWeakPtr(),
+// and there's no way to safely cast WeakPtr<Base> to WeakPtr<Derived> at
+// the caller.
+//
+// base::WeakPtr<Derived> ptr = derived.AsWeakPtr(); // Fails.
+
+template <typename Derived>
+WeakPtr<Derived> AsWeakPtr(Derived* t) {
+ return internal::SupportsWeakPtrBase::StaticAsWeakPtr<Derived>(t);
+}
+
+// A class may alternatively be composed of a WeakPtrFactory and thereby
+// control how it exposes weak pointers to itself. This is helpful if you only
+// need weak pointers within the implementation of a class. This class is also
+// useful when working with primitive types. For example, you could have a
+// WeakPtrFactory<bool> that is used to pass around a weak reference to a bool.
+template <class T>
+class WeakPtrFactory {
+ public:
+ explicit WeakPtrFactory(T* ptr) : ptr_(ptr) {
+ }
+
+ ~WeakPtrFactory() {
+ ptr_ = NULL;
+ }
+
+ WeakPtr<T> GetWeakPtr() {
+ DCHECK(ptr_);
+ return WeakPtr<T>(weak_reference_owner_.GetRef(), ptr_);
+ }
+
+ // Call this method to invalidate all existing weak pointers.
+ void InvalidateWeakPtrs() {
+ DCHECK(ptr_);
+ weak_reference_owner_.Invalidate();
+ }
+
+ // Call this method to determine if any weak pointers exist.
+ bool HasWeakPtrs() const {
+ DCHECK(ptr_);
+ return weak_reference_owner_.HasRefs();
+ }
+
+ // Indicates that this object will be used on another thread from now on.
+ void DetachFromThread() {
+ DCHECK(ptr_);
+ weak_reference_owner_.DetachFromThread();
+ }
+
+ private:
+ internal::WeakReferenceOwner weak_reference_owner_;
+ T* ptr_;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(WeakPtrFactory);
+};
+
+} // namespace base
+
+#endif // BASE_MEMORY_WEAK_PTR_H_
diff --git a/src/base/memory/weak_ptr_unittest.cc b/src/base/memory/weak_ptr_unittest.cc
new file mode 100644
index 0000000..d5f8057
--- /dev/null
+++ b/src/base/memory/weak_ptr_unittest.cc
@@ -0,0 +1,477 @@
+// Copyright (c) 2012 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/memory/weak_ptr.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+template <class T>
+class OffThreadObjectCreator {
+ public:
+ static T* NewObject() {
+ T* result;
+ {
+ Thread creator_thread("creator_thread");
+ creator_thread.Start();
+ creator_thread.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(OffThreadObjectCreator::CreateObject, &result));
+ }
+ DCHECK(result); // We synchronized on thread destruction above.
+ return result;
+ }
+ private:
+ static void CreateObject(T** result) {
+ *result = new T;
+ }
+};
+
+struct Base {
+ std::string member;
+};
+struct Derived : Base {};
+
+struct Target : SupportsWeakPtr<Target> {};
+struct DerivedTarget : Target {};
+struct Arrow {
+ WeakPtr<Target> target;
+};
+
+// Helper class to create and destroy weak pointer copies
+// and delete objects on a background thread.
+class BackgroundThread : public Thread {
+ public:
+ BackgroundThread() : Thread("owner_thread") {}
+
+ virtual ~BackgroundThread() {
+ Stop();
+ }
+
+ void CreateArrowFromTarget(Arrow** arrow, Target* target) {
+ WaitableEvent completion(true, false);
+ message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&BackgroundThread::DoCreateArrowFromTarget,
+ arrow, target, &completion));
+ completion.Wait();
+ }
+
+ void CreateArrowFromArrow(Arrow** arrow, const Arrow* other) {
+ WaitableEvent completion(true, false);
+ message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&BackgroundThread::DoCreateArrowFromArrow,
+ arrow, other, &completion));
+ completion.Wait();
+ }
+
+ void DeleteTarget(Target* object) {
+ WaitableEvent completion(true, false);
+ message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&BackgroundThread::DoDeleteTarget, object, &completion));
+ completion.Wait();
+ }
+
+ void DeleteArrow(Arrow* object) {
+ WaitableEvent completion(true, false);
+ message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&BackgroundThread::DoDeleteArrow, object, &completion));
+ completion.Wait();
+ }
+
+ Target* DeRef(const Arrow* arrow) {
+ WaitableEvent completion(true, false);
+ Target* result = NULL;
+ message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&BackgroundThread::DoDeRef, arrow, &result, &completion));
+ completion.Wait();
+ return result;
+ }
+
+ protected:
+ static void DoCreateArrowFromArrow(Arrow** arrow,
+ const Arrow* other,
+ WaitableEvent* completion) {
+ *arrow = new Arrow;
+ **arrow = *other;
+ completion->Signal();
+ }
+
+ static void DoCreateArrowFromTarget(Arrow** arrow,
+ Target* target,
+ WaitableEvent* completion) {
+ *arrow = new Arrow;
+ (*arrow)->target = target->AsWeakPtr();
+ completion->Signal();
+ }
+
+ static void DoDeRef(const Arrow* arrow,
+ Target** result,
+ WaitableEvent* completion) {
+ *result = arrow->target.get();
+ completion->Signal();
+ }
+
+ static void DoDeleteTarget(Target* object, WaitableEvent* completion) {
+ delete object;
+ completion->Signal();
+ }
+
+ static void DoDeleteArrow(Arrow* object, WaitableEvent* completion) {
+ delete object;
+ completion->Signal();
+ }
+};
+
+} // namespace
+
+TEST(WeakPtrFactoryTest, Basic) {
+ int data;
+ WeakPtrFactory<int> factory(&data);
+ WeakPtr<int> ptr = factory.GetWeakPtr();
+ EXPECT_EQ(&data, ptr.get());
+}
+
+TEST(WeakPtrFactoryTest, Comparison) {
+ int data;
+ WeakPtrFactory<int> factory(&data);
+ WeakPtr<int> ptr = factory.GetWeakPtr();
+ WeakPtr<int> ptr2 = ptr;
+ EXPECT_EQ(ptr, ptr2);
+}
+
+TEST(WeakPtrFactoryTest, OutOfScope) {
+ WeakPtr<int> ptr;
+ EXPECT_EQ(NULL, ptr.get());
+ {
+ int data;
+ WeakPtrFactory<int> factory(&data);
+ ptr = factory.GetWeakPtr();
+ }
+ EXPECT_EQ(NULL, ptr.get());
+}
+
+TEST(WeakPtrFactoryTest, Multiple) {
+ WeakPtr<int> a, b;
+ {
+ int data;
+ WeakPtrFactory<int> factory(&data);
+ a = factory.GetWeakPtr();
+ b = factory.GetWeakPtr();
+ EXPECT_EQ(&data, a.get());
+ EXPECT_EQ(&data, b.get());
+ }
+ EXPECT_EQ(NULL, a.get());
+ EXPECT_EQ(NULL, b.get());
+}
+
+TEST(WeakPtrFactoryTest, MultipleStaged) {
+ WeakPtr<int> a;
+ {
+ int data;
+ WeakPtrFactory<int> factory(&data);
+ a = factory.GetWeakPtr();
+ {
+ WeakPtr<int> b = factory.GetWeakPtr();
+ }
+ EXPECT_TRUE(NULL != a.get());
+ }
+ EXPECT_EQ(NULL, a.get());
+}
+
+TEST(WeakPtrFactoryTest, Dereference) {
+ Base data;
+ data.member = "123456";
+ WeakPtrFactory<Base> factory(&data);
+ WeakPtr<Base> ptr = factory.GetWeakPtr();
+ EXPECT_EQ(&data, ptr.get());
+ EXPECT_EQ(data.member, (*ptr).member);
+ EXPECT_EQ(data.member, ptr->member);
+}
+
+TEST(WeakPtrFactoryTest, UpCast) {
+ Derived data;
+ WeakPtrFactory<Derived> factory(&data);
+ WeakPtr<Base> ptr = factory.GetWeakPtr();
+ ptr = factory.GetWeakPtr();
+ EXPECT_EQ(ptr.get(), &data);
+}
+
+TEST(WeakPtrTest, SupportsWeakPtr) {
+ Target target;
+ WeakPtr<Target> ptr = target.AsWeakPtr();
+ EXPECT_EQ(&target, ptr.get());
+}
+
+TEST(WeakPtrTest, DerivedTarget) {
+ DerivedTarget target;
+ WeakPtr<DerivedTarget> ptr = AsWeakPtr(&target);
+ EXPECT_EQ(&target, ptr.get());
+}
+
+TEST(WeakPtrTest, InvalidateWeakPtrs) {
+ int data;
+ WeakPtrFactory<int> factory(&data);
+ WeakPtr<int> ptr = factory.GetWeakPtr();
+ EXPECT_EQ(&data, ptr.get());
+ EXPECT_TRUE(factory.HasWeakPtrs());
+ factory.InvalidateWeakPtrs();
+ EXPECT_EQ(NULL, ptr.get());
+ EXPECT_FALSE(factory.HasWeakPtrs());
+}
+
+TEST(WeakPtrTest, HasWeakPtrs) {
+ int data;
+ WeakPtrFactory<int> factory(&data);
+ {
+ WeakPtr<int> ptr = factory.GetWeakPtr();
+ EXPECT_TRUE(factory.HasWeakPtrs());
+ }
+ EXPECT_FALSE(factory.HasWeakPtrs());
+}
+
+TEST(WeakPtrTest, ObjectAndWeakPtrOnDifferentThreads) {
+ // Test that it is OK to create an object that supports WeakPtr on one thread,
+ // but use it on another. This tests that we do not trip runtime checks that
+ // ensure that a WeakPtr is not used by multiple threads.
+ scoped_ptr<Target> target(OffThreadObjectCreator<Target>::NewObject());
+ WeakPtr<Target> weak_ptr = target->AsWeakPtr();
+ EXPECT_EQ(target.get(), weak_ptr.get());
+}
+
+TEST(WeakPtrTest, WeakPtrInitiateAndUseOnDifferentThreads) {
+ // Test that it is OK to create an object that has a WeakPtr member on one
+ // thread, but use it on another. This tests that we do not trip runtime
+ // checks that ensure that a WeakPtr is not used by multiple threads.
+ scoped_ptr<Arrow> arrow(OffThreadObjectCreator<Arrow>::NewObject());
+ Target target;
+ arrow->target = target.AsWeakPtr();
+ EXPECT_EQ(&target, arrow->target.get());
+}
+
+TEST(WeakPtrTest, MoveOwnershipImplicitly) {
+ // Move object ownership to another thread by releasing all weak pointers
+ // on the original thread first, and then establish WeakPtr on a different
+ // thread.
+ BackgroundThread background;
+ background.Start();
+
+ Target* target = new Target();
+ {
+ WeakPtr<Target> weak_ptr = target->AsWeakPtr();
+ // Main thread deletes the WeakPtr, then the thread ownership of the
+ // object can be implicitly moved.
+ }
+ Arrow* arrow;
+
+ // Background thread creates WeakPtr(and implicitly owns the object).
+ background.CreateArrowFromTarget(&arrow, target);
+ EXPECT_EQ(background.DeRef(arrow), target);
+
+ {
+ // Main thread creates another WeakPtr, but this does not trigger implicitly
+ // thread ownership move.
+ Arrow arrow;
+ arrow.target = target->AsWeakPtr();
+
+ // The new WeakPtr is owned by background thread.
+ EXPECT_EQ(target, background.DeRef(&arrow));
+ }
+
+ // Target can only be deleted on background thread.
+ background.DeleteTarget(target);
+ background.DeleteArrow(arrow);
+}
+
+TEST(WeakPtrTest, MoveOwnershipExplicitlyObjectNotReferenced) {
+ // Case 1: The target is not bound to any thread yet. So calling
+ // DetachFromThread() is a no-op.
+ Target target;
+ target.DetachFromThread();
+
+ // Case 2: The target is bound to main thread but no WeakPtr is pointing to
+ // it. In this case, it will be re-bound to any thread trying to get a
+ // WeakPtr pointing to it. So detach function call is again no-op.
+ {
+ WeakPtr<Target> weak_ptr = target.AsWeakPtr();
+ }
+ target.DetachFromThread();
+}
+
+TEST(WeakPtrTest, MoveOwnershipExplicitly) {
+ BackgroundThread background;
+ background.Start();
+
+ Arrow* arrow;
+ {
+ Target target;
+ // Background thread creates WeakPtr(and implicitly owns the object).
+ background.CreateArrowFromTarget(&arrow, &target);
+ EXPECT_EQ(&target, background.DeRef(arrow));
+
+ // Detach from background thread.
+ target.DetachFromThread();
+
+ // Re-bind to main thread.
+ EXPECT_EQ(&target, arrow->target.get());
+
+ // Main thread can now delete the target.
+ }
+
+ // WeakPtr can be deleted on non-owner thread.
+ background.DeleteArrow(arrow);
+}
+
+TEST(WeakPtrTest, MainThreadRefOutlivesBackgroundThreadRef) {
+ // Originating thread has a WeakPtr that outlives others.
+ // - Main thread creates a WeakPtr
+ // - Background thread creates a WeakPtr copy from the one in main thread
+ // - Destruct the WeakPtr on background thread
+ // - Destruct the WeakPtr on main thread
+ BackgroundThread background;
+ background.Start();
+
+ Target target;
+ Arrow arrow;
+ arrow.target = target.AsWeakPtr();
+
+ Arrow* arrow_copy;
+ background.CreateArrowFromArrow(&arrow_copy, &arrow);
+ EXPECT_EQ(arrow_copy->target, &target);
+ background.DeleteArrow(arrow_copy);
+}
+
+TEST(WeakPtrTest, BackgroundThreadRefOutlivesMainThreadRef) {
+ // Originating thread drops all references before another thread.
+ // - Main thread creates a WeakPtr and passes copy to background thread
+ // - Destruct the pointer on main thread
+ // - Destruct the pointer on background thread
+ BackgroundThread background;
+ background.Start();
+
+ Target target;
+ Arrow* arrow_copy;
+ {
+ Arrow arrow;
+ arrow.target = target.AsWeakPtr();
+ background.CreateArrowFromArrow(&arrow_copy, &arrow);
+ }
+ EXPECT_EQ(arrow_copy->target, &target);
+ background.DeleteArrow(arrow_copy);
+}
+
+TEST(WeakPtrTest, OwnerThreadDeletesObject) {
+ // Originating thread invalidates WeakPtrs while its held by other thread.
+ // - Main thread creates WeakPtr and passes Copy to background thread
+ // - Object gets destroyed on main thread
+ // (invalidates WeakPtr on background thread)
+ // - WeakPtr gets destroyed on Thread B
+ BackgroundThread background;
+ background.Start();
+ Arrow* arrow_copy;
+ {
+ Target target;
+ Arrow arrow;
+ arrow.target = target.AsWeakPtr();
+ background.CreateArrowFromArrow(&arrow_copy, &arrow);
+ }
+ EXPECT_EQ(NULL, arrow_copy->target.get());
+ background.DeleteArrow(arrow_copy);
+}
+
+TEST(WeakPtrTest, NonOwnerThreadCanDeleteWeakPtr) {
+ // Main thread creates a Target object.
+ Target target;
+ // Main thread creates an arrow referencing the Target.
+ Arrow* arrow = new Arrow();
+ arrow->target = target.AsWeakPtr();
+
+ // Background can delete arrow (as well as the WeakPtr inside).
+ BackgroundThread background;
+ background.Start();
+ background.DeleteArrow(arrow);
+}
+
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST
+
+TEST(WeakPtrDeathTest, WeakPtrCopyDoesNotChangeThreadBinding) {
+ // The default style "fast" does not support multi-threaded tests
+ // (introduces deadlock on Linux).
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+
+ BackgroundThread background;
+ background.Start();
+
+ // Main thread creates a Target object.
+ Target target;
+ // Main thread creates an arrow referencing the Target.
+ Arrow arrow;
+ arrow.target = target.AsWeakPtr();
+
+ // Background copies the WeakPtr.
+ Arrow* arrow_copy;
+ background.CreateArrowFromArrow(&arrow_copy, &arrow);
+
+ // The copy is still bound to main thread so I can deref.
+ EXPECT_EQ(arrow.target.get(), arrow_copy->target.get());
+
+ // Although background thread created the copy, it can not deref the copied
+ // WeakPtr.
+ ASSERT_DEATH(background.DeRef(arrow_copy), "");
+
+ background.DeleteArrow(arrow_copy);
+}
+
+TEST(WeakPtrDeathTest, NonOwnerThreadDereferencesWeakPtr) {
+ // The default style "fast" does not support multi-threaded tests
+ // (introduces deadlock on Linux).
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+
+ // Main thread creates a Target object.
+ Target target;
+
+ // Main thread creates an arrow referencing the Target (so target's
+ // thread ownership can not be implicitly moved).
+ Arrow arrow;
+ arrow.target = target.AsWeakPtr();
+
+ // Background thread tries to deref target, which violates thread ownership.
+ BackgroundThread background;
+ background.Start();
+ ASSERT_DEATH(background.DeRef(&arrow), "");
+}
+
+TEST(WeakPtrDeathTest, NonOwnerThreadDeletesObject) {
+ // The default style "fast" does not support multi-threaded tests
+ // (introduces deadlock on Linux).
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+
+ scoped_ptr<Target> target(new Target());
+ // Main thread creates an arrow referencing the Target (so target's thread
+ // ownership can not be implicitly moved).
+ Arrow arrow;
+ arrow.target = target->AsWeakPtr();
+
+ // Background thread tries to delete target, which violates thread ownership.
+ BackgroundThread background;
+ background.Start();
+ ASSERT_DEATH(background.DeleteTarget(target.release()), "");
+}
+
+#endif
+
+} // namespace base
diff --git a/src/base/memory/weak_ptr_unittest.nc b/src/base/memory/weak_ptr_unittest.nc
new file mode 100644
index 0000000..afc8060
--- /dev/null
+++ b/src/base/memory/weak_ptr_unittest.nc
@@ -0,0 +1,138 @@
+// Copyright (c) 2012 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/memory/weak_ptr.h"
+
+namespace base {
+
+struct Producer : SupportsWeakPtr<Producer> {};
+struct DerivedProducer : Producer {};
+struct OtherDerivedProducer : Producer {};
+struct MultiplyDerivedProducer : Producer,
+ SupportsWeakPtr<MultiplyDerivedProducer> {};
+struct Unrelated {};
+struct DerivedUnrelated : Unrelated {};
+
+#if defined(NCTEST_AUTO_DOWNCAST) // [r"invalid conversion from"]
+
+void WontCompile() {
+ Producer f;
+ WeakPtr<Producer> ptr = f.AsWeakPtr();
+ WeakPtr<DerivedProducer> derived_ptr = ptr;
+}
+
+#elif defined(NCTEST_STATIC_DOWNCAST) // [r"invalid conversion from"]
+
+void WontCompile() {
+ Producer f;
+ WeakPtr<Producer> ptr = f.AsWeakPtr();
+ WeakPtr<DerivedProducer> derived_ptr =
+ static_cast<WeakPtr<DerivedProducer> >(ptr);
+}
+
+#elif defined(NCTEST_AUTO_REF_DOWNCAST) // [r"invalid initialization of reference"]
+
+void WontCompile() {
+ Producer f;
+ WeakPtr<Producer> ptr = f.AsWeakPtr();
+ WeakPtr<DerivedProducer>& derived_ptr = ptr;
+}
+
+#elif defined(NCTEST_STATIC_REF_DOWNCAST) // [r"invalid static_cast"]
+
+void WontCompile() {
+ Producer f;
+ WeakPtr<Producer> ptr = f.AsWeakPtr();
+ WeakPtr<DerivedProducer>& derived_ptr =
+ static_cast<WeakPtr<DerivedProducer>&>(ptr);
+}
+
+#elif defined(NCTEST_STATIC_ASWEAKPTR_DOWNCAST) // [r"no matching function"]
+
+void WontCompile() {
+ Producer f;
+ WeakPtr<DerivedProducer> ptr =
+ SupportsWeakPtr<Producer>::StaticAsWeakPtr<DerivedProducer>(&f);
+}
+
+#elif defined(NCTEST_UNSAFE_HELPER_DOWNCAST) // [r"invalid conversion from"]
+
+void WontCompile() {
+ Producer f;
+ WeakPtr<DerivedProducer> ptr = AsWeakPtr(&f);
+}
+
+#elif defined(NCTEST_UNSAFE_INSTANTIATED_HELPER_DOWNCAST) // [r"no matching function"]
+
+void WontCompile() {
+ Producer f;
+ WeakPtr<DerivedProducer> ptr = AsWeakPtr<DerivedProducer>(&f);
+}
+
+#elif defined(NCTEST_UNSAFE_WRONG_INSANTIATED_HELPER_DOWNCAST) // [r"invalid conversion from"]
+
+void WontCompile() {
+ Producer f;
+ WeakPtr<DerivedProducer> ptr = AsWeakPtr<Producer>(&f);
+}
+
+#elif defined(NCTEST_UNSAFE_HELPER_CAST) // [r"cannot convert"]
+
+void WontCompile() {
+ DerivedProducer f;
+ WeakPtr<OtherDerivedProducer> ptr = AsWeakPtr(&f);
+}
+
+#elif defined(NCTEST_UNSAFE_INSTANTIATED_HELPER_SIDECAST) // [r"no matching function"]
+
+void WontCompile() {
+ DerivedProducer f;
+ WeakPtr<OtherDerivedProducer> ptr = AsWeakPtr<OtherDerivedProducer>(&f);
+}
+
+#elif defined(NCTEST_UNSAFE_WRONG_INSTANTIATED_HELPER_SIDECAST) // [r"cannot convert"]
+
+void WontCompile() {
+ DerivedProducer f;
+ WeakPtr<OtherDerivedProducer> ptr = AsWeakPtr<DerivedProducer>(&f);
+}
+
+#elif defined(NCTEST_UNRELATED_HELPER) // [r"cannot convert"]
+
+void WontCompile() {
+ DerivedProducer f;
+ WeakPtr<Unrelated> ptr = AsWeakPtr(&f);
+}
+
+#elif defined(NCTEST_UNRELATED_INSTANTIATED_HELPER) // [r"no matching function"]
+
+void WontCompile() {
+ DerivedProducer f;
+ WeakPtr<Unrelated> ptr = AsWeakPtr<Unrelated>(&f);
+}
+
+#elif defined(NCTEST_COMPLETELY_UNRELATED_HELPER) // [r"array with negative size"]
+
+void WontCompile() {
+ Unrelated f;
+ WeakPtr<Unrelated> ptr = AsWeakPtr(&f);
+}
+
+#elif defined(NCTEST_DERIVED_COMPLETELY_UNRELATED_HELPER) // [r"array with negative size"]
+
+void WontCompile() {
+ DerivedUnrelated f;
+ WeakPtr<Unrelated> ptr = AsWeakPtr(&f);
+}
+
+#elif defined(NCTEST_AMBIGUOUS_ANCESTORS) // [r"ambiguous base"]
+
+void WontCompile() {
+ MultiplyDerivedProducer f;
+ WeakPtr<MultiplyDerivedProducer> ptr = AsWeakPtr(&f);
+}
+
+#endif
+
+}
diff --git a/src/base/message_loop.cc b/src/base/message_loop.cc
new file mode 100644
index 0000000..2f8b032
--- /dev/null
+++ b/src/base/message_loop.cc
@@ -0,0 +1,864 @@
+// Copyright (c) 2012 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/message_loop.h"
+
+#include <algorithm>
+
+#include "base/atomic_sequence_num.h"
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/debug/alias.h"
+#include "base/debug/trace_event.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop_proxy_impl.h"
+#include "base/message_pump_default.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/run_loop.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/threading/thread_local.h"
+#include "base/time.h"
+#include "base/tracked_objects.h"
+
+#if defined(OS_MACOSX)
+#include "base/message_pump_mac.h"
+#endif
+#if defined(OS_POSIX) && !defined(OS_IOS) && \
+ (!defined(__LB_SHELL__) || defined(__LB_ANDROID__))
+#include "base/message_pump_libevent.h"
+#endif
+#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
+#include "base/message_pump_android.h"
+#endif
+#if defined(OS_STARBOARD)
+#include "base/message_pump_io_starboard.h"
+#include "base/message_pump_ui_starboard.h"
+#endif
+#if defined(__LB_SHELL__) && !defined(__LB_ANDROID__)
+#include "base/message_pump_shell.h"
+#endif
+
+#if defined(TOOLKIT_GTK)
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#endif
+
+using base::PendingTask;
+using base::TimeDelta;
+using base::TimeTicks;
+
+namespace {
+
+// A lazily created thread local storage for quick access to a thread's message
+// loop, if one exists. This should be safe and free of static constructors.
+base::LazyInstance<base::ThreadLocalPointer<MessageLoop> > lazy_tls_ptr =
+ LAZY_INSTANCE_INITIALIZER;
+
+// Assign a sequential number to each message loops ever created so that
+// we can use that as a unique ID for them, for debug tracing purposes.
+base::LazyInstance<base::AtomicSequenceNumber> s_id_generator =
+ LAZY_INSTANCE_INITIALIZER;
+
+// Logical events for Histogram profiling. Run with -message-loop-histogrammer
+// to get an accounting of messages and actions taken on each thread.
+const int kTaskRunEvent = 0x1;
+const int kTimerEvent = 0x2;
+
+// Provide range of message IDs for use in histogramming and debug display.
+const int kLeastNonZeroMessageId = 1;
+const int kMaxMessageId = 1099;
+const int kNumberOfDistinctMessagesDisplayed = 1100;
+
+// Provide a macro that takes an expression (such as a constant, or macro
+// constant) and creates a pair to initalize an array of pairs. In this case,
+// our pair consists of the expressions value, and the "stringized" version
+// of the expression (i.e., the exrpression put in quotes). For example, if
+// we have:
+// #define FOO 2
+// #define BAR 5
+// then the following:
+// VALUE_TO_NUMBER_AND_NAME(FOO + BAR)
+// will expand to:
+// {7, "FOO + BAR"}
+// We use the resulting array as an argument to our histogram, which reads the
+// number as a bucket identifier, and proceeds to use the corresponding name
+// in the pair (i.e., the quoted string) when printing out a histogram.
+#define VALUE_TO_NUMBER_AND_NAME(name) {name, #name},
+
+const base::LinearHistogram::DescriptionPair event_descriptions_[] = {
+ // Provide some pretty print capability in our histogram for our internal
+ // messages.
+
+ // A few events we handle (kindred to messages), and used to profile actions.
+ VALUE_TO_NUMBER_AND_NAME(kTaskRunEvent)
+ VALUE_TO_NUMBER_AND_NAME(kTimerEvent)
+
+ {-1, NULL} // The list must be null terminated, per API to histogram.
+};
+
+bool enable_histogrammer_ = false;
+
+MessageLoop::MessagePumpFactory* message_pump_for_ui_factory_ = NULL;
+
+// Create a process-wide unique ID to represent this task in trace events. This
+// will be mangled with a Process ID hash to reduce the likelyhood of colliding
+// with MessageLoop pointers on other processes.
+uint64 GetTaskTraceID(const PendingTask& task, MessageLoop* loop) {
+ return (static_cast<uint64>(task.sequence_num) << 32) |
+ loop->id();
+}
+
+} // namespace
+
+//------------------------------------------------------------------------------
+
+#if defined(OS_WIN)
+
+// Upon a SEH exception in this thread, it restores the original unhandled
+// exception filter.
+static int SEHFilter(LPTOP_LEVEL_EXCEPTION_FILTER old_filter) {
+ ::SetUnhandledExceptionFilter(old_filter);
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+// Retrieves a pointer to the current unhandled exception filter. There
+// is no standalone getter method.
+static LPTOP_LEVEL_EXCEPTION_FILTER GetTopSEHFilter() {
+ LPTOP_LEVEL_EXCEPTION_FILTER top_filter = NULL;
+ top_filter = ::SetUnhandledExceptionFilter(0);
+ ::SetUnhandledExceptionFilter(top_filter);
+ return top_filter;
+}
+
+#endif // defined(OS_WIN)
+
+//------------------------------------------------------------------------------
+
+MessageLoop::TaskObserver::TaskObserver() {
+}
+
+MessageLoop::TaskObserver::~TaskObserver() {
+}
+
+MessageLoop::DestructionObserver::~DestructionObserver() {
+}
+
+//------------------------------------------------------------------------------
+
+MessageLoop::MessageLoop(Type type)
+ : type_(type),
+ nestable_tasks_allowed_(true),
+ exception_restoration_(false),
+ message_histogram_(NULL),
+ run_loop_(NULL),
+#ifdef OS_WIN
+ os_modal_loop_(false),
+#endif // OS_WIN
+ next_sequence_num_(0) {
+ DCHECK(!current()) << "should only have one message loop per thread";
+ lazy_tls_ptr.Pointer()->Set(this);
+
+ id_ = s_id_generator.Get().GetNext();
+
+ message_loop_proxy_ = new base::MessageLoopProxyImpl();
+ thread_task_runner_handle_.reset(
+ new base::ThreadTaskRunnerHandle(message_loop_proxy_));
+
+// TODO(rvargas): Get rid of the OS guards.
+#if defined(OS_WIN)
+#define MESSAGE_PUMP_UI new base::MessagePumpForUI()
+#define MESSAGE_PUMP_IO new base::MessagePumpForIO()
+#elif defined(OS_IOS)
+#define MESSAGE_PUMP_UI base::MessagePumpMac::Create()
+#define MESSAGE_PUMP_IO new base::MessagePumpIOSForIO()
+#elif defined(OS_MACOSX)
+#define MESSAGE_PUMP_UI base::MessagePumpMac::Create()
+#define MESSAGE_PUMP_IO new base::MessagePumpLibevent()
+#elif defined(OS_NACL)
+// Currently NaCl doesn't have a UI MessageLoop.
+// TODO(abarth): Figure out if we need this.
+#define MESSAGE_PUMP_UI NULL
+// ipc_channel_nacl.cc uses a worker thread to do socket reads currently, and
+// doesn't require extra support for watching file descriptors.
+#define MESSAGE_PUMP_IO new base::MessagePumpDefault();
+#elif defined(OS_STARBOARD)
+#define MESSAGE_PUMP_UI new base::MessagePumpUIStarboard()
+#define MESSAGE_PUMP_IO new base::MessagePumpIOStarboard()
+#elif defined(__LB_SHELL__) && !defined(__LB_ANDROID__)
+#define MESSAGE_PUMP_UI new base::MessagePumpShell()
+#define MESSAGE_PUMP_IO new base::MessagePumpShell()
+#elif defined(OS_POSIX) // POSIX but not MACOSX.
+#define MESSAGE_PUMP_UI new base::MessagePumpForUI()
+#define MESSAGE_PUMP_IO new base::MessagePumpLibevent()
+#else
+#error Not implemented
+#endif
+
+ if (type_ == TYPE_UI) {
+ if (message_pump_for_ui_factory_)
+ pump_ = message_pump_for_ui_factory_();
+ else
+ pump_ = MESSAGE_PUMP_UI;
+ } else if (type_ == TYPE_IO) {
+ pump_ = MESSAGE_PUMP_IO;
+ } else {
+ DCHECK_EQ(TYPE_DEFAULT, type_);
+ pump_ = new base::MessagePumpDefault();
+ }
+}
+
+MessageLoop::~MessageLoop() {
+ DCHECK_EQ(this, current());
+
+ DCHECK(!run_loop_);
+
+ // Clean up any unprocessed tasks, but take care: deleting a task could
+ // result in the addition of more tasks (e.g., via DeleteSoon). We set a
+ // limit on the number of times we will allow a deleted task to generate more
+ // tasks. Normally, we should only pass through this loop once or twice. If
+ // we end up hitting the loop limit, then it is probably due to one task that
+ // is being stubborn. Inspect the queues to see who is left.
+ bool did_work;
+ for (int i = 0; i < 100; ++i) {
+ DeletePendingTasks();
+ ReloadWorkQueue();
+ // If we end up with empty queues, then break out of the loop.
+ did_work = DeletePendingTasks();
+ if (!did_work)
+ break;
+ }
+ DCHECK(!did_work);
+
+ // Let interested parties have one last shot at accessing this.
+ FOR_EACH_OBSERVER(DestructionObserver, destruction_observers_,
+ WillDestroyCurrentMessageLoop());
+
+ thread_task_runner_handle_.reset();
+
+ // Tell the message_loop_proxy that we are dying.
+ static_cast<base::MessageLoopProxyImpl*>(message_loop_proxy_.get())->
+ WillDestroyCurrentMessageLoop();
+ message_loop_proxy_ = NULL;
+
+ // OK, now make it so that no one can find us.
+ lazy_tls_ptr.Pointer()->Set(NULL);
+
+#if defined(OS_WIN)
+ // If we left the high-resolution timer activated, deactivate it now.
+ // Doing this is not-critical, it is mainly to make sure we track
+ // the high resolution timer activations properly in our unit tests.
+ if (!high_resolution_timer_expiration_.is_null()) {
+ base::Time::ActivateHighResolutionTimer(false);
+ high_resolution_timer_expiration_ = base::TimeTicks();
+ }
+#endif
+}
+
+// static
+MessageLoop* MessageLoop::current() {
+ // TODO(darin): sadly, we cannot enable this yet since people call us even
+ // when they have no intention of using us.
+ // DCHECK(loop) << "Ouch, did you forget to initialize me?";
+ return lazy_tls_ptr.Pointer()->Get();
+}
+
+// static
+void MessageLoop::EnableHistogrammer(bool enable) {
+ enable_histogrammer_ = enable;
+}
+
+// static
+void MessageLoop::InitMessagePumpForUIFactory(MessagePumpFactory* factory) {
+ DCHECK(!message_pump_for_ui_factory_);
+ message_pump_for_ui_factory_ = factory;
+}
+
+void MessageLoop::AddDestructionObserver(
+ DestructionObserver* destruction_observer) {
+ DCHECK_EQ(this, current());
+ destruction_observers_.AddObserver(destruction_observer);
+}
+
+void MessageLoop::RemoveDestructionObserver(
+ DestructionObserver* destruction_observer) {
+ DCHECK_EQ(this, current());
+ destruction_observers_.RemoveObserver(destruction_observer);
+}
+
+void MessageLoop::PostTask(
+ const tracked_objects::Location& from_here, const base::Closure& task) {
+ DCHECK(!task.is_null()) << from_here.ToString();
+ PendingTask pending_task(
+ from_here, task, CalculateDelayedRuntime(TimeDelta()), true);
+ AddToIncomingQueue(&pending_task);
+}
+
+void MessageLoop::PostDelayedTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ TimeDelta delay) {
+ DCHECK(!task.is_null()) << from_here.ToString();
+ PendingTask pending_task(
+ from_here, task, CalculateDelayedRuntime(delay), true);
+ AddToIncomingQueue(&pending_task);
+}
+
+void MessageLoop::PostNonNestableTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ DCHECK(!task.is_null()) << from_here.ToString();
+ PendingTask pending_task(
+ from_here, task, CalculateDelayedRuntime(TimeDelta()), false);
+ AddToIncomingQueue(&pending_task);
+}
+
+void MessageLoop::PostNonNestableDelayedTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ TimeDelta delay) {
+ DCHECK(!task.is_null()) << from_here.ToString();
+ PendingTask pending_task(
+ from_here, task, CalculateDelayedRuntime(delay), false);
+ AddToIncomingQueue(&pending_task);
+}
+
+void MessageLoop::Run() {
+ base::RunLoop run_loop;
+ run_loop.Run();
+}
+
+void MessageLoop::RunUntilIdle() {
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+}
+
+void MessageLoop::QuitWhenIdle() {
+ DCHECK_EQ(this, current());
+ if (run_loop_) {
+ run_loop_->quit_when_idle_received_ = true;
+#if defined(OS_STARBOARD)
+ // The IO loop may have already been idle, in which case it's waiting on the
+ // SbSocketWaiter. This call to ScheduleWork() ensures that the Idle
+ // delegate callback will be called again soon. This should be safe and
+ // appropriate to call here, even on non-Starboard message pumps.
+ pump_->ScheduleWork();
+#endif
+ } else {
+ NOTREACHED() << "Must be inside Run to call Quit";
+ }
+}
+
+void MessageLoop::QuitNow() {
+ DCHECK_EQ(this, current());
+ if (run_loop_) {
+ pump_->Quit();
+ } else {
+ NOTREACHED() << "Must be inside Run to call Quit";
+ }
+}
+
+bool MessageLoop::IsType(Type type) const {
+ return type_ == type;
+}
+
+static void QuitCurrentWhenIdle() {
+ MessageLoop::current()->QuitWhenIdle();
+}
+
+// static
+base::Closure MessageLoop::QuitWhenIdleClosure() {
+ return base::Bind(&QuitCurrentWhenIdle);
+}
+
+void MessageLoop::SetNestableTasksAllowed(bool allowed) {
+ if (nestable_tasks_allowed_ != allowed) {
+ nestable_tasks_allowed_ = allowed;
+ if (!nestable_tasks_allowed_)
+ return;
+ // Start the native pump if we are not already pumping.
+ pump_->ScheduleWork();
+ }
+}
+
+bool MessageLoop::NestableTasksAllowed() const {
+ return nestable_tasks_allowed_;
+}
+
+bool MessageLoop::IsNested() {
+ return run_loop_->run_depth_ > 1;
+}
+
+void MessageLoop::AddTaskObserver(TaskObserver* task_observer) {
+ DCHECK_EQ(this, current());
+ task_observers_.AddObserver(task_observer);
+}
+
+void MessageLoop::RemoveTaskObserver(TaskObserver* task_observer) {
+ DCHECK_EQ(this, current());
+ task_observers_.RemoveObserver(task_observer);
+}
+
+void MessageLoop::AssertIdle() const {
+ // We only check |incoming_queue_|, since we don't want to lock |work_queue_|.
+ base::AutoLock lock(incoming_queue_lock_);
+ DCHECK(incoming_queue_.empty());
+}
+
+bool MessageLoop::is_running() const {
+ DCHECK_EQ(this, current());
+ return run_loop_ != NULL;
+}
+
+//------------------------------------------------------------------------------
+
+// Runs the loop in two different SEH modes:
+// enable_SEH_restoration_ = false : any unhandled exception goes to the last
+// one that calls SetUnhandledExceptionFilter().
+// enable_SEH_restoration_ = true : any unhandled exception goes to the filter
+// that was existed before the loop was run.
+void MessageLoop::RunHandler() {
+#if defined(OS_WIN)
+ if (exception_restoration_) {
+ RunInternalInSEHFrame();
+ return;
+ }
+#endif
+
+ RunInternal();
+}
+
+#if defined(OS_WIN)
+__declspec(noinline) void MessageLoop::RunInternalInSEHFrame() {
+ LPTOP_LEVEL_EXCEPTION_FILTER current_filter = GetTopSEHFilter();
+ __try {
+ RunInternal();
+ } __except(SEHFilter(current_filter)) {
+ }
+ return;
+}
+#endif
+
+void MessageLoop::RunInternal() {
+ DCHECK_EQ(this, current());
+
+ StartHistogrammer();
+
+#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && !defined(__LB_ANDROID__) && \
+ !defined(OS_STARBOARD)
+ if (run_loop_->dispatcher_ && type() == TYPE_UI) {
+ static_cast<base::MessagePumpForUI*>(pump_.get())->
+ RunWithDispatcher(this, run_loop_->dispatcher_);
+ return;
+ }
+#endif
+
+ pump_->Run(this);
+}
+
+bool MessageLoop::ProcessNextDelayedNonNestableTask() {
+ if (run_loop_->run_depth_ != 1)
+ return false;
+
+ if (deferred_non_nestable_work_queue_.empty())
+ return false;
+
+ PendingTask pending_task = deferred_non_nestable_work_queue_.front();
+ deferred_non_nestable_work_queue_.pop();
+
+ RunTask(pending_task);
+ return true;
+}
+
+void MessageLoop::RunTask(const PendingTask& pending_task) {
+ TRACE_EVENT_FLOW_END0("task", "MessageLoop::PostTask",
+ TRACE_ID_MANGLE(GetTaskTraceID(pending_task, this)));
+ TRACE_EVENT2("task", "MessageLoop::RunTask",
+ "src_file", pending_task.posted_from.file_name(),
+ "src_func", pending_task.posted_from.function_name());
+ DCHECK(nestable_tasks_allowed_);
+ // Execute the task and assume the worst: It is probably not reentrant.
+ nestable_tasks_allowed_ = false;
+
+ // Before running the task, store the program counter where it was posted
+ // and deliberately alias it to ensure it is on the stack if the task
+ // crashes. Be careful not to assume that the variable itself will have the
+ // expected value when displayed by the optimizer in an optimized build.
+ // Look at a memory dump of the stack.
+ const void* program_counter =
+ pending_task.posted_from.program_counter();
+ base::debug::Alias(&program_counter);
+
+ HistogramEvent(kTaskRunEvent);
+
+ tracked_objects::TrackedTime start_time =
+ tracked_objects::ThreadData::NowForStartOfRun(pending_task.birth_tally);
+
+ FOR_EACH_OBSERVER(TaskObserver, task_observers_,
+ WillProcessTask(pending_task.time_posted));
+ pending_task.task.Run();
+ FOR_EACH_OBSERVER(TaskObserver, task_observers_,
+ DidProcessTask(pending_task.time_posted));
+
+ tracked_objects::ThreadData::TallyRunOnNamedThreadIfTracking(pending_task,
+ start_time, tracked_objects::ThreadData::NowForEndOfRun());
+
+ nestable_tasks_allowed_ = true;
+}
+
+bool MessageLoop::DeferOrRunPendingTask(const PendingTask& pending_task) {
+ if (pending_task.nestable || run_loop_->run_depth_ == 1) {
+ RunTask(pending_task);
+ // Show that we ran a task (Note: a new one might arrive as a
+ // consequence!).
+ return true;
+ }
+
+ // We couldn't run the task now because we're in a nested message loop
+ // and the task isn't nestable.
+ deferred_non_nestable_work_queue_.push(pending_task);
+ return false;
+}
+
+void MessageLoop::AddToDelayedWorkQueue(const PendingTask& pending_task) {
+ // Move to the delayed work queue.
+ delayed_work_queue_.push(pending_task);
+}
+
+void MessageLoop::ReloadWorkQueue() {
+ // We can improve performance of our loading tasks from incoming_queue_ to
+ // work_queue_ by waiting until the last minute (work_queue_ is empty) to
+ // load. That reduces the number of locks-per-task significantly when our
+ // queues get large.
+ if (!work_queue_.empty())
+ return; // Wait till we *really* need to lock and load.
+
+ // Acquire all we can from the inter-thread queue with one lock acquisition.
+ {
+ base::AutoLock lock(incoming_queue_lock_);
+ if (incoming_queue_.empty())
+ return;
+ incoming_queue_.Swap(&work_queue_); // Constant time
+ DCHECK(incoming_queue_.empty());
+ }
+}
+
+bool MessageLoop::DeletePendingTasks() {
+ bool did_work = !work_queue_.empty();
+ while (!work_queue_.empty()) {
+ PendingTask pending_task = work_queue_.front();
+ work_queue_.pop();
+ if (!pending_task.delayed_run_time.is_null()) {
+ // We want to delete delayed tasks in the same order in which they would
+ // normally be deleted in case of any funny dependencies between delayed
+ // tasks.
+ AddToDelayedWorkQueue(pending_task);
+ }
+ }
+ did_work |= !deferred_non_nestable_work_queue_.empty();
+ while (!deferred_non_nestable_work_queue_.empty()) {
+ deferred_non_nestable_work_queue_.pop();
+ }
+ did_work |= !delayed_work_queue_.empty();
+
+ // Historically, we always delete the task regardless of valgrind status. It's
+ // not completely clear why we want to leak them in the loops above. This
+ // code is replicating legacy behavior, and should not be considered
+ // absolutely "correct" behavior. See TODO above about deleting all tasks
+ // when it's safe.
+ while (!delayed_work_queue_.empty()) {
+ delayed_work_queue_.pop();
+ }
+ return did_work;
+}
+
+TimeTicks MessageLoop::CalculateDelayedRuntime(TimeDelta delay) {
+ TimeTicks delayed_run_time;
+ if (delay > TimeDelta()) {
+ delayed_run_time = TimeTicks::Now() + delay;
+
+#if defined(OS_WIN)
+ if (high_resolution_timer_expiration_.is_null()) {
+ // Windows timers are granular to 15.6ms. If we only set high-res
+ // timers for those under 15.6ms, then a 18ms timer ticks at ~32ms,
+ // which as a percentage is pretty inaccurate. So enable high
+ // res timers for any timer which is within 2x of the granularity.
+ // This is a tradeoff between accuracy and power management.
+ bool needs_high_res_timers = delay.InMilliseconds() <
+ (2 * base::Time::kMinLowResolutionThresholdMs);
+ if (needs_high_res_timers) {
+ if (base::Time::ActivateHighResolutionTimer(true)) {
+ high_resolution_timer_expiration_ = TimeTicks::Now() +
+ TimeDelta::FromMilliseconds(kHighResolutionTimerModeLeaseTimeMs);
+ }
+ }
+ }
+#endif
+ } else {
+ DCHECK_EQ(delay.InMilliseconds(), 0) << "delay should not be negative";
+ }
+
+#if defined(OS_WIN)
+ if (!high_resolution_timer_expiration_.is_null()) {
+ if (TimeTicks::Now() > high_resolution_timer_expiration_) {
+ base::Time::ActivateHighResolutionTimer(false);
+ high_resolution_timer_expiration_ = TimeTicks();
+ }
+ }
+#endif
+
+ return delayed_run_time;
+}
+
+// Possibly called on a background thread!
+void MessageLoop::AddToIncomingQueue(PendingTask* pending_task) {
+ // Warning: Don't try to short-circuit, and handle this thread's tasks more
+ // directly, as it could starve handling of foreign threads. Put every task
+ // into this queue.
+
+ scoped_refptr<base::MessagePump> pump;
+ {
+ base::AutoLock locked(incoming_queue_lock_);
+
+ // Initialize the sequence number. The sequence number is used for delayed
+ // tasks (to faciliate FIFO sorting when two tasks have the same
+ // delayed_run_time value) and for identifying the task in about:tracing.
+ pending_task->sequence_num = next_sequence_num_++;
+
+ TRACE_EVENT_FLOW_BEGIN0("task", "MessageLoop::PostTask",
+ TRACE_ID_MANGLE(GetTaskTraceID(*pending_task, this)));
+
+ bool was_empty = incoming_queue_.empty();
+ incoming_queue_.push(*pending_task);
+ pending_task->task.Reset();
+ if (!was_empty)
+ return; // Someone else should have started the sub-pump.
+
+ pump = pump_;
+ }
+ // Since the incoming_queue_ may contain a task that destroys this message
+ // loop, we cannot exit incoming_queue_lock_ until we are done with |this|.
+ // We use a stack-based reference to the message pump so that we can call
+ // ScheduleWork outside of incoming_queue_lock_.
+
+ pump->ScheduleWork();
+}
+
+//------------------------------------------------------------------------------
+// Method and data for histogramming events and actions taken by each instance
+// on each thread.
+
+void MessageLoop::StartHistogrammer() {
+#if !defined(OS_NACL) // NaCl build has no metrics code.
+ if (enable_histogrammer_ && !message_histogram_
+ && base::StatisticsRecorder::IsActive()) {
+ DCHECK(!thread_name_.empty());
+ message_histogram_ = base::LinearHistogram::FactoryGetWithRangeDescription(
+ "MsgLoop:" + thread_name_,
+ kLeastNonZeroMessageId, kMaxMessageId,
+ kNumberOfDistinctMessagesDisplayed,
+ message_histogram_->kHexRangePrintingFlag,
+ event_descriptions_);
+ }
+#endif
+}
+
+void MessageLoop::HistogramEvent(int event) {
+#if !defined(OS_NACL)
+ if (message_histogram_)
+ message_histogram_->Add(event);
+#endif
+}
+
+bool MessageLoop::DoWork() {
+ if (!nestable_tasks_allowed_) {
+ // Task can't be executed right now.
+ return false;
+ }
+
+ for (;;) {
+ ReloadWorkQueue();
+ if (work_queue_.empty())
+ break;
+
+ // Execute oldest task.
+ do {
+ PendingTask pending_task = work_queue_.front();
+ work_queue_.pop();
+ if (!pending_task.delayed_run_time.is_null()) {
+ AddToDelayedWorkQueue(pending_task);
+ // If we changed the topmost task, then it is time to reschedule.
+ if (delayed_work_queue_.top().task.Equals(pending_task.task))
+ pump_->ScheduleDelayedWork(pending_task.delayed_run_time);
+ } else {
+ if (DeferOrRunPendingTask(pending_task))
+ return true;
+ }
+ } while (!work_queue_.empty());
+ }
+
+ // Nothing happened.
+ return false;
+}
+
+bool MessageLoop::DoDelayedWork(TimeTicks* next_delayed_work_time) {
+ if (!nestable_tasks_allowed_ || delayed_work_queue_.empty()) {
+ recent_time_ = *next_delayed_work_time = TimeTicks();
+ return false;
+ }
+
+ // When we "fall behind," there will be a lot of tasks in the delayed work
+ // queue that are ready to run. To increase efficiency when we fall behind,
+ // we will only call Time::Now() intermittently, and then process all tasks
+ // that are ready to run before calling it again. As a result, the more we
+ // fall behind (and have a lot of ready-to-run delayed tasks), the more
+ // efficient we'll be at handling the tasks.
+
+ TimeTicks next_run_time = delayed_work_queue_.top().delayed_run_time;
+ if (next_run_time > recent_time_) {
+ recent_time_ = TimeTicks::Now(); // Get a better view of Now();
+ if (next_run_time > recent_time_) {
+ *next_delayed_work_time = next_run_time;
+ return false;
+ }
+ }
+
+ PendingTask pending_task = delayed_work_queue_.top();
+ delayed_work_queue_.pop();
+
+ if (!delayed_work_queue_.empty())
+ *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time;
+
+ return DeferOrRunPendingTask(pending_task);
+}
+
+bool MessageLoop::DoIdleWork() {
+ if (ProcessNextDelayedNonNestableTask())
+ return true;
+
+ if (run_loop_->quit_when_idle_received_)
+ pump_->Quit();
+
+ return false;
+}
+
+void MessageLoop::DeleteSoonInternal(const tracked_objects::Location& from_here,
+ void(*deleter)(const void*),
+ const void* object) {
+ PostNonNestableTask(from_here, base::Bind(deleter, object));
+}
+
+void MessageLoop::ReleaseSoonInternal(
+ const tracked_objects::Location& from_here,
+ void(*releaser)(const void*),
+ const void* object) {
+ PostNonNestableTask(from_here, base::Bind(releaser, object));
+}
+
+//------------------------------------------------------------------------------
+// MessageLoopForUI
+
+#if defined(OS_WIN)
+void MessageLoopForUI::DidProcessMessage(const MSG& message) {
+ pump_win()->DidProcessMessage(message);
+}
+#endif // defined(OS_WIN)
+
+#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
+void MessageLoopForUI::Start() {
+ // No Histogram support for UI message loop as it is managed by Java side
+ static_cast<base::MessagePumpForUI*>(pump_.get())->Start(this);
+}
+#endif
+
+#if defined(OS_STARBOARD)
+void MessageLoopForUI::Start() {
+ // No Histogram support for UI message loop as it is managed by Starboard.
+ static_cast<base::MessagePumpUIStarboard*>(pump_.get())->Start(this);
+}
+#endif
+
+#if defined(OS_IOS)
+void MessageLoopForUI::Attach() {
+ static_cast<base::MessagePumpUIApplication*>(pump_.get())->Attach(this);
+}
+#endif
+
+#if !defined(OS_MACOSX) && !defined(OS_NACL) && !defined(OS_ANDROID) && \
+ !defined(__LB_ANDROID__) && !defined(OS_STARBOARD)
+void MessageLoopForUI::AddObserver(Observer* observer) {
+ pump_ui()->AddObserver(observer);
+}
+
+void MessageLoopForUI::RemoveObserver(Observer* observer) {
+ pump_ui()->RemoveObserver(observer);
+}
+#endif // !defined(OS_MACOSX) && !defined(OS_NACL) && !defined(OS_ANDROID)
+
+//------------------------------------------------------------------------------
+// MessageLoopForIO
+
+#if defined(OS_WIN)
+
+void MessageLoopForIO::RegisterIOHandler(HANDLE file, IOHandler* handler) {
+ pump_io()->RegisterIOHandler(file, handler);
+}
+
+bool MessageLoopForIO::RegisterJobObject(HANDLE job, IOHandler* handler) {
+ return pump_io()->RegisterJobObject(job, handler);
+}
+
+bool MessageLoopForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) {
+ return pump_io()->WaitForIOCompletion(timeout, filter);
+}
+
+#elif defined(__LB_SHELL__) && !defined(__LB_ANDROID__)
+
+bool MessageLoopForIO::WatchSocket(int sock,
+ bool persistent,
+ Mode mode,
+ FileDescriptorWatcher *controller,
+ Watcher *delegate) {
+ return pump_io()->WatchSocket(
+ sock,
+ persistent,
+ mode,
+ controller,
+ delegate);
+}
+
+#elif defined(OS_IOS)
+
+bool MessageLoopForIO::WatchFileDescriptor(int fd,
+ bool persistent,
+ Mode mode,
+ FileDescriptorWatcher *controller,
+ Watcher *delegate) {
+ return pump_io()->WatchFileDescriptor(
+ fd,
+ persistent,
+ mode,
+ controller,
+ delegate);
+}
+
+#elif defined(OS_POSIX) && !defined(OS_NACL)
+
+bool MessageLoopForIO::WatchFileDescriptor(int fd,
+ bool persistent,
+ Mode mode,
+ FileDescriptorWatcher *controller,
+ Watcher *delegate) {
+ return pump_libevent()->WatchFileDescriptor(
+ fd,
+ persistent,
+ mode,
+ controller,
+ delegate);
+}
+
+#endif
diff --git a/src/base/message_loop.h b/src/base/message_loop.h
new file mode 100644
index 0000000..70a15b7
--- /dev/null
+++ b/src/base/message_loop.h
@@ -0,0 +1,778 @@
+// Copyright (c) 2012 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_MESSAGE_LOOP_H_
+#define BASE_MESSAGE_LOOP_H_
+
+#include <queue>
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop_proxy.h"
+#include "base/message_pump.h"
+#include "base/observer_list.h"
+#include "base/pending_task.h"
+#include "base/sequenced_task_runner_helpers.h"
+#include "base/synchronization/lock.h"
+#include "base/tracking_info.h"
+#include "base/time.h"
+
+#if defined(OS_WIN)
+// We need this to declare base::MessagePumpWin::Dispatcher, which we should
+// really just eliminate.
+#include "base/message_pump_win.h"
+#elif defined(OS_STARBOARD)
+#include "base/message_pump_io_starboard.h"
+#elif defined(__LB_SHELL__) && !defined(__LB_ANDROID__)
+#include "base/message_pump_shell.h"
+#elif defined(OS_IOS)
+#include "base/message_pump_io_ios.h"
+#elif defined(OS_POSIX)
+#include "base/message_pump_libevent.h"
+#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && !defined(__LB_ANDROID__)
+
+#if defined(USE_AURA) && defined(USE_X11) && !defined(OS_NACL)
+#include "base/message_pump_aurax11.h"
+#else
+#include "base/message_pump_gtk.h"
+#endif
+
+#endif
+#endif
+
+namespace base {
+class Histogram;
+class RunLoop;
+class ThreadTaskRunnerHandle;
+#if defined(OS_ANDROID)
+class MessagePumpForUI;
+#endif
+#if defined(OS_STARBOARD)
+class MessagePumpUIStarboard;
+#endif
+} // namespace base
+
+// A MessageLoop is used to process events for a particular thread. There is
+// at most one MessageLoop instance per thread.
+//
+// Events include at a minimum Task instances submitted to PostTask or those
+// managed by TimerManager. Depending on the type of message pump used by the
+// MessageLoop other events such as UI messages may be processed. On Windows
+// APC calls (as time permits) and signals sent to a registered set of HANDLEs
+// may also be processed.
+//
+// NOTE: Unless otherwise specified, a MessageLoop's methods may only be called
+// on the thread where the MessageLoop's Run method executes.
+//
+// NOTE: MessageLoop has task reentrancy protection. This means that if a
+// task is being processed, a second task cannot start until the first task is
+// finished. Reentrancy can happen when processing a task, and an inner
+// message pump is created. That inner pump then processes native messages
+// which could implicitly start an inner task. Inner message pumps are created
+// with dialogs (DialogBox), common dialogs (GetOpenFileName), OLE functions
+// (DoDragDrop), printer functions (StartDoc) and *many* others.
+//
+// Sample workaround when inner task processing is needed:
+// HRESULT hr;
+// {
+// MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
+// hr = DoDragDrop(...); // Implicitly runs a modal message loop.
+// }
+// // Process |hr| (the result returned by DoDragDrop()).
+//
+// Please be SURE your task is reentrant (nestable) and all global variables
+// are stable and accessible before calling SetNestableTasksAllowed(true).
+//
+class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate {
+ public:
+#if (defined(__LB_SHELL__) && !defined(__LB_ANDROID__))
+ typedef base::MessagePumpShell::Dispatcher Dispatcher;
+ typedef base::MessagePumpShell::Observer Observer;
+#elif !defined(OS_MACOSX) && !defined(OS_ANDROID) && \
+ !defined(__LB_ANDROID__) && !defined(OS_STARBOARD)
+ typedef base::MessagePumpDispatcher Dispatcher;
+ typedef base::MessagePumpObserver Observer;
+#endif
+
+ // A MessageLoop has a particular type, which indicates the set of
+ // asynchronous events it may process in addition to tasks and timers.
+ //
+ // TYPE_DEFAULT
+ // This type of ML only supports tasks and timers.
+ //
+ // TYPE_UI
+ // This type of ML also supports native UI events (e.g., Windows messages).
+ // See also MessageLoopForUI.
+ //
+ // TYPE_IO
+ // This type of ML also supports asynchronous IO. See also
+ // MessageLoopForIO.
+ //
+ enum Type {
+ TYPE_DEFAULT,
+ TYPE_UI,
+ TYPE_IO
+ };
+
+ // Normally, it is not necessary to instantiate a MessageLoop. Instead, it
+ // is typical to make use of the current thread's MessageLoop instance.
+ explicit MessageLoop(Type type = TYPE_DEFAULT);
+ virtual ~MessageLoop();
+
+ // Returns the MessageLoop object for the current thread, or null if none.
+ static MessageLoop* current();
+
+ static void EnableHistogrammer(bool enable_histogrammer);
+
+ typedef base::MessagePump* (MessagePumpFactory)();
+ // Using the given base::MessagePumpForUIFactory to override the default
+ // MessagePump implementation for 'TYPE_UI'.
+ static void InitMessagePumpForUIFactory(MessagePumpFactory* factory);
+
+#if (defined(__LB_SHELL__) && !defined(__LB_ANDROID__)) || defined(OS_STARBOARD)
+ inline std::size_t Size() const {
+ return work_queue_.size() + delayed_work_queue_.size() +
+ deferred_non_nestable_work_queue_.size() + incoming_queue_.size();
+ }
+#endif
+
+ // A DestructionObserver is notified when the current MessageLoop is being
+ // destroyed. These observers are notified prior to MessageLoop::current()
+ // being changed to return NULL. This gives interested parties the chance to
+ // do final cleanup that depends on the MessageLoop.
+ //
+ // NOTE: Any tasks posted to the MessageLoop during this notification will
+ // not be run. Instead, they will be deleted.
+ //
+ class BASE_EXPORT DestructionObserver {
+ public:
+ virtual void WillDestroyCurrentMessageLoop() = 0;
+
+ protected:
+ virtual ~DestructionObserver();
+ };
+
+ // Add a DestructionObserver, which will start receiving notifications
+ // immediately.
+ void AddDestructionObserver(DestructionObserver* destruction_observer);
+
+ // Remove a DestructionObserver. It is safe to call this method while a
+ // DestructionObserver is receiving a notification callback.
+ void RemoveDestructionObserver(DestructionObserver* destruction_observer);
+
+ // The "PostTask" family of methods call the task's Run method asynchronously
+ // from within a message loop at some point in the future.
+ //
+ // With the PostTask variant, tasks are invoked in FIFO order, inter-mixed
+ // with normal UI or IO event processing. With the PostDelayedTask variant,
+ // tasks are called after at least approximately 'delay_ms' have elapsed.
+ //
+ // The NonNestable variants work similarly except that they promise never to
+ // dispatch the task from a nested invocation of MessageLoop::Run. Instead,
+ // such tasks get deferred until the top-most MessageLoop::Run is executing.
+ //
+ // The MessageLoop takes ownership of the Task, and deletes it after it has
+ // been Run().
+ //
+ // PostTask(from_here, task) is equivalent to
+ // PostDelayedTask(from_here, task, 0).
+ //
+ // NOTE: These methods may be called on any thread. The Task will be invoked
+ // on the thread that executes MessageLoop::Run().
+ void PostTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task);
+
+ void PostDelayedTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay);
+
+ void PostNonNestableTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task);
+
+ void PostNonNestableDelayedTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay);
+
+ // A variant on PostTask that deletes the given object. This is useful
+ // if the object needs to live until the next run of the MessageLoop (for
+ // example, deleting a RenderProcessHost from within an IPC callback is not
+ // good).
+ //
+ // NOTE: This method may be called on any thread. The object will be deleted
+ // on the thread that executes MessageLoop::Run(). If this is not the same
+ // as the thread that calls PostDelayedTask(FROM_HERE, ), then T MUST inherit
+ // from RefCountedThreadSafe<T>!
+ template <class T>
+ void DeleteSoon(const tracked_objects::Location& from_here, const T* object) {
+ base::subtle::DeleteHelperInternal<T, void>::DeleteViaSequencedTaskRunner(
+ this, from_here, object);
+ }
+
+ // A variant on PostTask that releases the given reference counted object
+ // (by calling its Release method). This is useful if the object needs to
+ // live until the next run of the MessageLoop, or if the object needs to be
+ // released on a particular thread.
+ //
+ // NOTE: This method may be called on any thread. The object will be
+ // released (and thus possibly deleted) on the thread that executes
+ // MessageLoop::Run(). If this is not the same as the thread that calls
+ // PostDelayedTask(FROM_HERE, ), then T MUST inherit from
+ // RefCountedThreadSafe<T>!
+ template <class T>
+ void ReleaseSoon(const tracked_objects::Location& from_here,
+ const T* object) {
+ base::subtle::ReleaseHelperInternal<T, void>::ReleaseViaSequencedTaskRunner(
+ this, from_here, object);
+ }
+
+ // Deprecated: use RunLoop instead.
+ // Run the message loop.
+ void Run();
+
+ // Deprecated: use RunLoop instead.
+ // Process all pending tasks, windows messages, etc., but don't wait/sleep.
+ // Return as soon as all items that can be run are taken care of.
+ void RunUntilIdle();
+
+ // TODO(jbates) remove this. crbug.com/131220. See RunUntilIdle().
+ void RunAllPending() { RunUntilIdle(); }
+
+ // TODO(jbates) remove this. crbug.com/131220. See QuitWhenIdle().
+ void Quit() { QuitWhenIdle(); }
+
+ // Deprecated: use RunLoop instead.
+ //
+ // Signals the Run method to return when it becomes idle. It will continue to
+ // process pending messages and future messages as long as they are enqueued.
+ // Warning: if the MessageLoop remains busy, it may never quit. Only use this
+ // Quit method when looping procedures (such as web pages) have been shut
+ // down.
+ //
+ // This method may only be called on the same thread that called Run, and Run
+ // must still be on the call stack.
+ //
+ // Use QuitClosure variants if you need to Quit another thread's MessageLoop,
+ // but note that doing so is fairly dangerous if the target thread makes
+ // nested calls to MessageLoop::Run. The problem being that you won't know
+ // which nested run loop you are quitting, so be careful!
+ void QuitWhenIdle();
+
+ // Deprecated: use RunLoop instead.
+ //
+ // This method is a variant of Quit, that does not wait for pending messages
+ // to be processed before returning from Run.
+ void QuitNow();
+
+ // TODO(jbates) remove this. crbug.com/131220. See QuitWhenIdleClosure().
+ static base::Closure QuitClosure() { return QuitWhenIdleClosure(); }
+
+ // Deprecated: use RunLoop instead.
+ // Construct a Closure that will call QuitWhenIdle(). Useful to schedule an
+ // arbitrary MessageLoop to QuitWhenIdle.
+ static base::Closure QuitWhenIdleClosure();
+
+ // Returns true if this loop is |type|. This allows subclasses (especially
+ // those in tests) to specialize how they are identified.
+ virtual bool IsType(Type type) const;
+
+ // Returns the type passed to the constructor.
+ Type type() const { return type_; }
+
+ // Optional call to connect the thread name with this loop.
+ void set_thread_name(const std::string& thread_name) {
+ DCHECK(thread_name_.empty()) << "Should not rename this thread!";
+ thread_name_ = thread_name;
+ }
+ const std::string& thread_name() const { return thread_name_; }
+
+ // Gets the message loop proxy associated with this message loop.
+ scoped_refptr<base::MessageLoopProxy> message_loop_proxy() {
+ return message_loop_proxy_.get();
+ }
+
+ // Enables or disables the recursive task processing. This happens in the case
+ // of recursive message loops. Some unwanted message loop may occurs when
+ // using common controls or printer functions. By default, recursive task
+ // processing is disabled.
+ //
+ // Please utilize |ScopedNestableTaskAllower| instead of calling these methods
+ // directly. In general nestable message loops are to be avoided. They are
+ // dangerous and difficult to get right, so please use with extreme caution.
+ //
+ // The specific case where tasks get queued is:
+ // - The thread is running a message loop.
+ // - It receives a task #1 and execute it.
+ // - The task #1 implicitly start a message loop, like a MessageBox in the
+ // unit test. This can also be StartDoc or GetSaveFileName.
+ // - The thread receives a task #2 before or while in this second message
+ // loop.
+ // - With NestableTasksAllowed set to true, the task #2 will run right away.
+ // Otherwise, it will get executed right after task #1 completes at "thread
+ // message loop level".
+ void SetNestableTasksAllowed(bool allowed);
+ bool NestableTasksAllowed() const;
+
+ // Enables nestable tasks on |loop| while in scope.
+ class ScopedNestableTaskAllower {
+ public:
+ explicit ScopedNestableTaskAllower(MessageLoop* loop)
+ : loop_(loop),
+ old_state_(loop_->NestableTasksAllowed()) {
+ loop_->SetNestableTasksAllowed(true);
+ }
+ ~ScopedNestableTaskAllower() {
+ loop_->SetNestableTasksAllowed(old_state_);
+ }
+
+ private:
+ MessageLoop* loop_;
+ bool old_state_;
+ };
+
+ // Enables or disables the restoration during an exception of the unhandled
+ // exception filter that was active when Run() was called. This can happen
+ // if some third party code call SetUnhandledExceptionFilter() and never
+ // restores the previous filter.
+ void set_exception_restoration(bool restore) {
+ exception_restoration_ = restore;
+ }
+
+ // Returns true if we are currently running a nested message loop.
+ bool IsNested();
+
+ // A TaskObserver is an object that receives task notifications from the
+ // MessageLoop.
+ //
+ // NOTE: A TaskObserver implementation should be extremely fast!
+ class BASE_EXPORT TaskObserver {
+ public:
+ TaskObserver();
+
+ // This method is called before processing a task.
+ virtual void WillProcessTask(base::TimeTicks time_posted) = 0;
+
+ // This method is called after processing a task.
+ virtual void DidProcessTask(base::TimeTicks time_posted) = 0;
+
+ protected:
+ virtual ~TaskObserver();
+ };
+
+ // These functions can only be called on the same thread that |this| is
+ // running on.
+ void AddTaskObserver(TaskObserver* task_observer);
+ void RemoveTaskObserver(TaskObserver* task_observer);
+
+ // Returns true if the message loop has high resolution timers enabled.
+ // Provided for testing.
+ bool high_resolution_timers_enabled() {
+#if defined(OS_WIN)
+ return !high_resolution_timer_expiration_.is_null();
+#else
+ return true;
+#endif
+ }
+
+ // When we go into high resolution timer mode, we will stay in hi-res mode
+ // for at least 1s.
+ static const int kHighResolutionTimerModeLeaseTimeMs = 1000;
+
+ // Asserts that the MessageLoop is "idle".
+ void AssertIdle() const;
+
+#if defined(OS_WIN)
+ void set_os_modal_loop(bool os_modal_loop) {
+ os_modal_loop_ = os_modal_loop;
+ }
+
+ bool os_modal_loop() const {
+ return os_modal_loop_;
+ }
+#endif // OS_WIN
+
+ // Can only be called from the thread that owns the MessageLoop.
+ bool is_running() const;
+
+ int id() { return id_; }
+
+ //----------------------------------------------------------------------------
+ protected:
+ friend class base::RunLoop;
+
+#if defined(OS_WIN)
+ base::MessagePumpWin* pump_win() {
+ return static_cast<base::MessagePumpWin*>(pump_.get());
+ }
+#elif defined(OS_POSIX) && !defined(OS_IOS) && \
+ (!defined(__LB_SHELL__) || defined(__LB_ANDROID__))
+ base::MessagePumpLibevent* pump_libevent() {
+ return static_cast<base::MessagePumpLibevent*>(pump_.get());
+ }
+#endif
+
+ // A function to encapsulate all the exception handling capability in the
+ // stacks around the running of a main message loop. It will run the message
+ // loop in a SEH try block or not depending on the set_SEH_restoration()
+ // flag invoking respectively RunInternalInSEHFrame() or RunInternal().
+ void RunHandler();
+
+#if defined(OS_WIN)
+ __declspec(noinline) void RunInternalInSEHFrame();
+#endif
+
+ // A surrounding stack frame around the running of the message loop that
+ // supports all saving and restoring of state, as is needed for any/all (ugly)
+ // recursive calls.
+ void RunInternal();
+
+ // Called to process any delayed non-nestable tasks.
+ bool ProcessNextDelayedNonNestableTask();
+
+ // Runs the specified PendingTask.
+ void RunTask(const base::PendingTask& pending_task);
+
+ // Calls RunTask or queues the pending_task on the deferred task list if it
+ // cannot be run right now. Returns true if the task was run.
+ bool DeferOrRunPendingTask(const base::PendingTask& pending_task);
+
+ // Adds the pending task to delayed_work_queue_.
+ void AddToDelayedWorkQueue(const base::PendingTask& pending_task);
+
+ // Adds the pending task to our incoming_queue_.
+ //
+ // Caller retains ownership of |pending_task|, but this function will
+ // reset the value of pending_task->task. This is needed to ensure
+ // that the posting call stack does not retain pending_task->task
+ // beyond this function call.
+ void AddToIncomingQueue(base::PendingTask* pending_task);
+
+ // Load tasks from the incoming_queue_ into work_queue_ if the latter is
+ // empty. The former requires a lock to access, while the latter is directly
+ // accessible on this thread.
+ void ReloadWorkQueue();
+
+ // Delete tasks that haven't run yet without running them. Used in the
+ // destructor to make sure all the task's destructors get called. Returns
+ // true if some work was done.
+ bool DeletePendingTasks();
+
+ // Calculates the time at which a PendingTask should run.
+ base::TimeTicks CalculateDelayedRuntime(base::TimeDelta delay);
+
+ // Start recording histogram info about events and action IF it was enabled
+ // and IF the statistics recorder can accept a registration of our histogram.
+ void StartHistogrammer();
+
+ // Add occurrence of event to our histogram, so that we can see what is being
+ // done in a specific MessageLoop instance (i.e., specific thread).
+ // If message_histogram_ is NULL, this is a no-op.
+ void HistogramEvent(int event);
+
+ // base::MessagePump::Delegate methods:
+ virtual bool DoWork() OVERRIDE;
+ virtual bool DoDelayedWork(base::TimeTicks* next_delayed_work_time) OVERRIDE;
+ virtual bool DoIdleWork() OVERRIDE;
+
+ Type type_;
+
+ // A list of tasks that need to be processed by this instance. Note that
+ // this queue is only accessed (push/pop) by our current thread.
+ base::TaskQueue work_queue_;
+
+ // Contains delayed tasks, sorted by their 'delayed_run_time' property.
+ base::DelayedTaskQueue delayed_work_queue_;
+
+ // A recent snapshot of Time::Now(), used to check delayed_work_queue_.
+ base::TimeTicks recent_time_;
+
+ // A queue of non-nestable tasks that we had to defer because when it came
+ // time to execute them we were in a nested message loop. They will execute
+ // once we're out of nested message loops.
+ base::TaskQueue deferred_non_nestable_work_queue_;
+
+ scoped_refptr<base::MessagePump> pump_;
+
+ ObserverList<DestructionObserver> destruction_observers_;
+
+ // A recursion block that prevents accidentally running additional tasks when
+ // insider a (accidentally induced?) nested message pump.
+ bool nestable_tasks_allowed_;
+
+ bool exception_restoration_;
+
+ std::string thread_name_;
+ // A profiling histogram showing the counts of various messages and events.
+ base::Histogram* message_histogram_;
+
+ // A null terminated list which creates an incoming_queue of tasks that are
+ // acquired under a mutex for processing on this instance's thread. These
+ // tasks have not yet been sorted out into items for our work_queue_ vs items
+ // that will be handled by the TimerManager.
+ base::TaskQueue incoming_queue_;
+ // Protect access to incoming_queue_.
+ mutable base::Lock incoming_queue_lock_;
+
+ base::RunLoop* run_loop_;
+
+#if defined(OS_WIN)
+ base::TimeTicks high_resolution_timer_expiration_;
+ // Should be set to true before calling Windows APIs like TrackPopupMenu, etc
+ // which enter a modal message loop.
+ bool os_modal_loop_;
+#endif
+
+ // The next sequence number to use for delayed tasks. Updating this counter is
+ // protected by incoming_queue_lock_.
+ int next_sequence_num_;
+
+ ObserverList<TaskObserver> task_observers_;
+
+ // The message loop proxy associated with this message loop, if one exists.
+ scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
+ scoped_ptr<base::ThreadTaskRunnerHandle> thread_task_runner_handle_;
+
+ // A unique ID (over time) identifying this message loop.
+ int id_;
+
+ private:
+ template <class T, class R> friend class base::subtle::DeleteHelperInternal;
+ template <class T, class R> friend class base::subtle::ReleaseHelperInternal;
+
+ void DeleteSoonInternal(const tracked_objects::Location& from_here,
+ void(*deleter)(const void*),
+ const void* object);
+ void ReleaseSoonInternal(const tracked_objects::Location& from_here,
+ void(*releaser)(const void*),
+ const void* object);
+
+ DISALLOW_COPY_AND_ASSIGN(MessageLoop);
+};
+
+//-----------------------------------------------------------------------------
+// MessageLoopForUI extends MessageLoop with methods that are particular to a
+// MessageLoop instantiated with TYPE_UI.
+//
+// This class is typically used like so:
+// MessageLoopForUI::current()->...call some method...
+//
+class BASE_EXPORT MessageLoopForUI : public MessageLoop {
+ public:
+#if defined(OS_WIN)
+ typedef base::MessagePumpForUI::MessageFilter MessageFilter;
+#endif
+
+ MessageLoopForUI() : MessageLoop(TYPE_UI) {
+ }
+
+ // Returns the MessageLoopForUI of the current thread.
+ static MessageLoopForUI* current() {
+ MessageLoop* loop = MessageLoop::current();
+ DCHECK(loop);
+ DCHECK_EQ(MessageLoop::TYPE_UI, loop->type());
+ return static_cast<MessageLoopForUI*>(loop);
+ }
+
+#if defined(OS_WIN)
+ void DidProcessMessage(const MSG& message);
+#endif // defined(OS_WIN)
+
+#if defined(OS_IOS)
+ // On iOS, the main message loop cannot be Run(). Instead call Attach(),
+ // which connects this MessageLoop to the UI thread's CFRunLoop and allows
+ // PostTask() to work.
+ void Attach();
+#endif
+
+#if defined(OS_ANDROID) || defined(__LB_ANDROID__) || defined(OS_STARBOARD)
+ // On Android, the UI message loop is handled by Java side. So Run() should
+ // never be called. Instead use Start(), which will forward all the native UI
+ // events to the Java message loop.
+ // Also Starboard.
+ void Start();
+#elif !defined(OS_MACOSX)
+ // Please see message_pump_win/message_pump_glib for definitions of these
+ // methods.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+#if defined(OS_WIN)
+ // Plese see MessagePumpForUI for definitions of this method.
+ void SetMessageFilter(scoped_ptr<MessageFilter> message_filter) {
+ pump_ui()->SetMessageFilter(message_filter.Pass());
+ }
+#endif
+
+ protected:
+#if defined(USE_AURA) && defined(USE_X11) && !defined(OS_NACL)
+ friend class base::MessagePumpAuraX11;
+#endif
+
+ // TODO(rvargas): Make this platform independent.
+ base::MessagePumpForUI* pump_ui() {
+ return static_cast<base::MessagePumpForUI*>(pump_.get());
+ }
+#endif // !defined(OS_MACOSX)
+};
+
+// Do not add any member variables to MessageLoopForUI! This is important b/c
+// MessageLoopForUI is often allocated via MessageLoop(TYPE_UI). Any extra
+// data that you need should be stored on the MessageLoop's pump_ instance.
+COMPILE_ASSERT(sizeof(MessageLoop) == sizeof(MessageLoopForUI),
+ MessageLoopForUI_should_not_have_extra_member_variables);
+
+//-----------------------------------------------------------------------------
+// MessageLoopForIO extends MessageLoop with methods that are particular to a
+// MessageLoop instantiated with TYPE_IO.
+//
+// This class is typically used like so:
+// MessageLoopForIO::current()->...call some method...
+//
+class BASE_EXPORT MessageLoopForIO : public MessageLoop {
+ public:
+#if defined(OS_WIN)
+ typedef base::MessagePumpForIO::IOHandler IOHandler;
+ typedef base::MessagePumpForIO::IOContext IOContext;
+ typedef base::MessagePumpForIO::IOObserver IOObserver;
+#elif defined(OS_STARBOARD)
+ typedef base::MessagePumpIOStarboard::Watcher Watcher;
+ typedef base::MessagePumpIOStarboard::SocketWatcher SocketWatcher;
+ typedef base::MessagePumpIOStarboard::IOObserver IOObserver;
+
+ enum Mode{WATCH_READ = base::MessagePumpIOStarboard::WATCH_READ,
+ WATCH_WRITE = base::MessagePumpIOStarboard::WATCH_WRITE,
+ WATCH_READ_WRITE = base::MessagePumpIOStarboard::WATCH_READ_WRITE};
+#elif defined(__LB_SHELL__) && !defined(__LB_ANDROID__)
+ typedef base::MessagePumpShell::Watcher Watcher;
+ typedef base::MessagePumpShell::FileDescriptorWatcher FileDescriptorWatcher;
+ typedef base::MessagePumpShell::IOObserver IOObserver;
+
+ enum Mode {
+ WATCH_READ = base::MessagePumpShell::WATCH_READ,
+ WATCH_WRITE = base::MessagePumpShell::WATCH_WRITE,
+ WATCH_READ_WRITE = base::MessagePumpShell::WATCH_READ_WRITE
+ };
+#elif defined(OS_IOS)
+ typedef base::MessagePumpIOSForIO::Watcher Watcher;
+ typedef base::MessagePumpIOSForIO::FileDescriptorWatcher
+ FileDescriptorWatcher;
+ typedef base::MessagePumpIOSForIO::IOObserver IOObserver;
+
+ enum Mode {
+ WATCH_READ = base::MessagePumpIOSForIO::WATCH_READ,
+ WATCH_WRITE = base::MessagePumpIOSForIO::WATCH_WRITE,
+ WATCH_READ_WRITE = base::MessagePumpIOSForIO::WATCH_READ_WRITE
+ };
+#elif defined(OS_POSIX)
+ typedef base::MessagePumpLibevent::Watcher Watcher;
+ typedef base::MessagePumpLibevent::FileDescriptorWatcher
+ FileDescriptorWatcher;
+ typedef base::MessagePumpLibevent::IOObserver IOObserver;
+
+ enum Mode {
+ WATCH_READ = base::MessagePumpLibevent::WATCH_READ,
+ WATCH_WRITE = base::MessagePumpLibevent::WATCH_WRITE,
+ WATCH_READ_WRITE = base::MessagePumpLibevent::WATCH_READ_WRITE
+ };
+
+#endif
+
+ MessageLoopForIO() : MessageLoop(TYPE_IO) {
+ }
+
+ // Returns the MessageLoopForIO of the current thread.
+ static MessageLoopForIO* current() {
+ MessageLoop* loop = MessageLoop::current();
+ DCHECK_EQ(MessageLoop::TYPE_IO, loop->type());
+ return static_cast<MessageLoopForIO*>(loop);
+ }
+
+ void AddIOObserver(IOObserver* io_observer) {
+ pump_io()->AddIOObserver(io_observer);
+ }
+
+ void RemoveIOObserver(IOObserver* io_observer) {
+ pump_io()->RemoveIOObserver(io_observer);
+ }
+
+#if defined(OS_WIN)
+ // Please see MessagePumpWin for definitions of these methods.
+ void RegisterIOHandler(HANDLE file, IOHandler* handler);
+ bool RegisterJobObject(HANDLE job, IOHandler* handler);
+ bool WaitForIOCompletion(DWORD timeout, IOHandler* filter);
+
+ protected:
+ // TODO(rvargas): Make this platform independent.
+ base::MessagePumpForIO* pump_io() {
+ return static_cast<base::MessagePumpForIO*>(pump_.get());
+ }
+
+#elif defined(OS_STARBOARD)
+ bool Watch(SbSocket socket,
+ bool persistent,
+ int mode,
+ SocketWatcher* controller,
+ Watcher* delegate) {
+ return pump_io()->Watch(socket, persistent, mode, controller, delegate);
+ }
+
+ protected:
+ base::MessagePumpIOStarboard* pump_io() {
+ return static_cast<base::MessagePumpIOStarboard*>(pump_.get());
+ }
+
+#elif defined(__LB_SHELL__) && !defined(__LB_ANDROID__)
+ bool WatchSocket(int sock,
+ bool persistent,
+ Mode mode,
+ FileDescriptorWatcher *controller,
+ Watcher *delegate);
+
+ protected:
+ base::MessagePumpForIO* pump_io() {
+ return static_cast<base::MessagePumpForIO*>(pump_.get());
+ }
+
+#elif defined(OS_IOS)
+ // Please see MessagePumpIOSForIO for definition.
+ bool WatchFileDescriptor(int fd,
+ bool persistent,
+ Mode mode,
+ FileDescriptorWatcher *controller,
+ Watcher *delegate);
+
+ private:
+ base::MessagePumpIOSForIO* pump_io() {
+ return static_cast<base::MessagePumpIOSForIO*>(pump_.get());
+ }
+
+#elif defined(OS_POSIX)
+ // Please see MessagePumpLibevent for definition.
+ bool WatchFileDescriptor(int fd,
+ bool persistent,
+ Mode mode,
+ FileDescriptorWatcher* controller,
+ Watcher* delegate);
+
+ private:
+ base::MessagePumpLibevent* pump_io() {
+ return static_cast<base::MessagePumpLibevent*>(pump_.get());
+ }
+#endif // defined(OS_POSIX)
+};
+
+// Do not add any member variables to MessageLoopForIO! This is important b/c
+// MessageLoopForIO is often allocated via MessageLoop(TYPE_IO). Any extra
+// data that you need should be stored on the MessageLoop's pump_ instance.
+COMPILE_ASSERT(sizeof(MessageLoop) == sizeof(MessageLoopForIO),
+ MessageLoopForIO_should_not_have_extra_member_variables);
+
+#endif // BASE_MESSAGE_LOOP_H_
diff --git a/src/base/message_loop_proxy.cc b/src/base/message_loop_proxy.cc
new file mode 100644
index 0000000..755564b
--- /dev/null
+++ b/src/base/message_loop_proxy.cc
@@ -0,0 +1,17 @@
+// Copyright (c) 2012 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/message_loop_proxy.h"
+
+#include "base/bind.h"
+
+namespace base {
+
+MessageLoopProxy::MessageLoopProxy() {
+}
+
+MessageLoopProxy::~MessageLoopProxy() {
+}
+
+} // namespace base
diff --git a/src/base/message_loop_proxy.h b/src/base/message_loop_proxy.h
new file mode 100644
index 0000000..ce949dd
--- /dev/null
+++ b/src/base/message_loop_proxy.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 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_MESSAGE_LOOP_PROXY_H_
+#define BASE_MESSAGE_LOOP_PROXY_H_
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+
+namespace base {
+
+// This class provides a thread-safe refcounted interface to the Post* methods
+// of a message loop. This class can outlive the target message loop.
+// MessageLoopProxy objects are constructed automatically for all MessageLoops.
+// So, to access them, you can use any of the following:
+// Thread::message_loop_proxy()
+// MessageLoop::current()->message_loop_proxy()
+// MessageLoopProxy::current()
+//
+// TODO(akalin): Now that we have the *TaskRunner interfaces, we can
+// merge this with MessageLoopProxyImpl.
+class BASE_EXPORT MessageLoopProxy : public SingleThreadTaskRunner {
+ public:
+ // Gets the MessageLoopProxy for the current message loop, creating one if
+ // needed.
+ static scoped_refptr<MessageLoopProxy> current();
+
+ protected:
+ MessageLoopProxy();
+ virtual ~MessageLoopProxy();
+};
+
+} // namespace base
+
+#endif // BASE_MESSAGE_LOOP_PROXY_H_
diff --git a/src/base/message_loop_proxy_impl.cc b/src/base/message_loop_proxy_impl.cc
new file mode 100644
index 0000000..c3efe26
--- /dev/null
+++ b/src/base/message_loop_proxy_impl.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 2012 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/message_loop_proxy_impl.h"
+
+#include "base/location.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace base {
+
+MessageLoopProxyImpl::~MessageLoopProxyImpl() {
+}
+
+bool MessageLoopProxyImpl::PostDelayedTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) {
+ return PostTaskHelper(from_here, task, delay, true);
+}
+
+bool MessageLoopProxyImpl::PostNonNestableDelayedTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) {
+ return PostTaskHelper(from_here, task, delay, false);
+}
+
+bool MessageLoopProxyImpl::RunsTasksOnCurrentThread() const {
+ return thread_id_ == PlatformThread::CurrentId();
+}
+
+// MessageLoop::DestructionObserver implementation
+void MessageLoopProxyImpl::WillDestroyCurrentMessageLoop() {
+ AutoLock lock(message_loop_lock_);
+ target_message_loop_ = NULL;
+}
+
+void MessageLoopProxyImpl::OnDestruct() const {
+ // We shouldn't use MessageLoop::current() since it uses LazyInstance which
+ // may be deleted by ~AtExitManager when a WorkerPool thread calls this
+ // function.
+ // http://crbug.com/63678
+ bool delete_later = false;
+ {
+ AutoLock lock(message_loop_lock_);
+ if (target_message_loop_) {
+ base::ThreadRestrictions::ScopedAllowSingleton allow_singleton;
+ if (MessageLoop::current() != target_message_loop_) {
+ target_message_loop_->DeleteSoon(FROM_HERE, this);
+ delete_later = true;
+ }
+ }
+ }
+ if (!delete_later)
+ delete this;
+}
+
+MessageLoopProxyImpl::MessageLoopProxyImpl()
+ : thread_id_(PlatformThread::CurrentId()),
+ target_message_loop_(MessageLoop::current()) {}
+
+bool MessageLoopProxyImpl::PostTaskHelper(
+ const tracked_objects::Location& from_here, const base::Closure& task,
+ base::TimeDelta delay, bool nestable) {
+ AutoLock lock(message_loop_lock_);
+ if (target_message_loop_) {
+ if (nestable) {
+ target_message_loop_->PostDelayedTask(from_here, task, delay);
+ } else {
+ target_message_loop_->PostNonNestableDelayedTask(from_here, task, delay);
+ }
+ return true;
+ }
+ return false;
+}
+
+scoped_refptr<MessageLoopProxy>
+MessageLoopProxy::current() {
+ MessageLoop* cur_loop = MessageLoop::current();
+ if (!cur_loop)
+ return NULL;
+ return cur_loop->message_loop_proxy();
+}
+
+} // namespace base
diff --git a/src/base/message_loop_proxy_impl.h b/src/base/message_loop_proxy_impl.h
new file mode 100644
index 0000000..a01b415
--- /dev/null
+++ b/src/base/message_loop_proxy_impl.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2012 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_MESSAGE_LOOP_PROXY_IMPL_H_
+#define BASE_MESSAGE_LOOP_PROXY_IMPL_H_
+
+#include "base/base_export.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+// A stock implementation of MessageLoopProxy that is created and managed by a
+// MessageLoop. For now a MessageLoopProxyImpl can only be created as part of a
+// MessageLoop.
+class BASE_EXPORT MessageLoopProxyImpl : public MessageLoopProxy {
+ public:
+ // MessageLoopProxy implementation
+ virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) OVERRIDE;
+ virtual bool PostNonNestableDelayedTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) OVERRIDE;
+ virtual bool RunsTasksOnCurrentThread() const OVERRIDE;
+
+ protected:
+ virtual ~MessageLoopProxyImpl();
+
+ // Override OnDestruct so that we can delete the object on the target message
+ // loop if it still exists.
+ virtual void OnDestruct() const OVERRIDE;
+
+ private:
+ // Allow the MessageLoop to create a MessageLoopProxyImpl.
+ friend class ::MessageLoop;
+ friend class DeleteHelper<MessageLoopProxyImpl>;
+
+ MessageLoopProxyImpl();
+
+ // Called directly by MessageLoop::~MessageLoop.
+ virtual void WillDestroyCurrentMessageLoop();
+
+
+ bool PostTaskHelper(const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay,
+ bool nestable);
+
+ PlatformThreadId thread_id_;
+
+ // The lock that protects access to target_message_loop_.
+ mutable base::Lock message_loop_lock_;
+ MessageLoop* target_message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessageLoopProxyImpl);
+};
+
+} // namespace base
+
+#endif // BASE_MESSAGE_LOOP_PROXY_IMPL_H_
diff --git a/src/base/message_loop_proxy_impl_unittest.cc b/src/base/message_loop_proxy_impl_unittest.cc
new file mode 100644
index 0000000..e97d8f1
--- /dev/null
+++ b/src/base/message_loop_proxy_impl_unittest.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2012 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/message_loop_proxy_impl.h"
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+
+class MessageLoopProxyImplTest : public testing::Test {
+ public:
+ void Release() const {
+ AssertOnIOThread();
+ Quit();
+ }
+
+ void Quit() const {
+ loop_.PostTask(FROM_HERE, MessageLoop::QuitClosure());
+ }
+
+ void AssertOnIOThread() const {
+ ASSERT_TRUE(io_thread_->message_loop_proxy()->BelongsToCurrentThread());
+ ASSERT_EQ(io_thread_->message_loop_proxy(),
+ base::MessageLoopProxy::current());
+ }
+
+ void AssertOnFileThread() const {
+ ASSERT_TRUE(file_thread_->message_loop_proxy()->BelongsToCurrentThread());
+ ASSERT_EQ(file_thread_->message_loop_proxy(),
+ base::MessageLoopProxy::current());
+ }
+
+ protected:
+ virtual void SetUp() OVERRIDE {
+ io_thread_.reset(new base::Thread("MessageLoopProxyImplTest_IO"));
+ file_thread_.reset(new base::Thread("MessageLoopProxyImplTest_File"));
+ io_thread_->Start();
+ file_thread_->Start();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ io_thread_->Stop();
+ file_thread_->Stop();
+ }
+
+ static void BasicFunction(MessageLoopProxyImplTest* test) {
+ test->AssertOnFileThread();
+ test->Quit();
+ }
+
+ static void AssertNotRun() {
+ FAIL() << "Callback Should not get executed.";
+ }
+
+ class DeletedOnFile {
+ public:
+ explicit DeletedOnFile(MessageLoopProxyImplTest* test) : test_(test) {}
+
+ ~DeletedOnFile() {
+ test_->AssertOnFileThread();
+ test_->Quit();
+ }
+
+ private:
+ MessageLoopProxyImplTest* test_;
+ };
+
+ scoped_ptr<base::Thread> io_thread_;
+ scoped_ptr<base::Thread> file_thread_;
+
+ private:
+ mutable MessageLoop loop_;
+};
+
+TEST_F(MessageLoopProxyImplTest, Release) {
+ EXPECT_TRUE(io_thread_->message_loop_proxy()->ReleaseSoon(FROM_HERE, this));
+ MessageLoop::current()->Run();
+}
+
+TEST_F(MessageLoopProxyImplTest, Delete) {
+ DeletedOnFile* deleted_on_file = new DeletedOnFile(this);
+ EXPECT_TRUE(file_thread_->message_loop_proxy()->DeleteSoon(
+ FROM_HERE, deleted_on_file));
+ MessageLoop::current()->Run();
+}
+
+TEST_F(MessageLoopProxyImplTest, PostTask) {
+ EXPECT_TRUE(file_thread_->message_loop_proxy()->PostTask(
+ FROM_HERE, base::Bind(&MessageLoopProxyImplTest::BasicFunction,
+ base::Unretained(this))));
+ MessageLoop::current()->Run();
+}
+
+TEST_F(MessageLoopProxyImplTest, PostTaskAfterThreadExits) {
+ scoped_ptr<base::Thread> test_thread(
+ new base::Thread("MessageLoopProxyImplTest_Dummy"));
+ test_thread->Start();
+ scoped_refptr<base::MessageLoopProxy> message_loop_proxy =
+ test_thread->message_loop_proxy();
+ test_thread->Stop();
+
+ bool ret = message_loop_proxy->PostTask(
+ FROM_HERE,
+ base::Bind(&MessageLoopProxyImplTest::AssertNotRun));
+ EXPECT_FALSE(ret);
+}
+
+TEST_F(MessageLoopProxyImplTest, PostTaskAfterThreadIsDeleted) {
+ scoped_refptr<base::MessageLoopProxy> message_loop_proxy;
+ {
+ scoped_ptr<base::Thread> test_thread(
+ new base::Thread("MessageLoopProxyImplTest_Dummy"));
+ test_thread->Start();
+ message_loop_proxy = test_thread->message_loop_proxy();
+ }
+ bool ret = message_loop_proxy->PostTask(
+ FROM_HERE,
+ base::Bind(&MessageLoopProxyImplTest::AssertNotRun));
+ EXPECT_FALSE(ret);
+}
diff --git a/src/base/message_loop_proxy_unittest.cc b/src/base/message_loop_proxy_unittest.cc
new file mode 100644
index 0000000..4776502
--- /dev/null
+++ b/src/base/message_loop_proxy_unittest.cc
@@ -0,0 +1,266 @@
+// Copyright (c) 2012 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/message_loop_proxy.h"
+
+#include "base/atomic_sequence_num.h"
+#include "base/bind.h"
+#include "base/debug/leak_annotations.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class MessageLoopProxyTest : public testing::Test {
+ public:
+ MessageLoopProxyTest()
+ : current_loop_(new MessageLoop()),
+ task_thread_("task_thread"),
+ thread_sync_(true, false) {
+ }
+
+ void DeleteCurrentMessageLoop() {
+ current_loop_.reset();
+ }
+
+ protected:
+ virtual void SetUp() OVERRIDE {
+ // Use SetUp() instead of the constructor to avoid posting a task to a
+ // partialy constructed object.
+ task_thread_.Start();
+
+ // Allow us to pause the |task_thread_|'s MessageLoop.
+ task_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ Bind(&MessageLoopProxyTest::BlockTaskThreadHelper, Unretained(this)));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ // Make sure the |task_thread_| is not blocked, and stop the thread
+ // fully before destuction because its tasks may still depend on the
+ // |thread_sync_| event.
+ thread_sync_.Signal();
+ task_thread_.Stop();
+ DeleteCurrentMessageLoop();
+ }
+
+ // Make LoopRecorder threadsafe so that there is defined behavior even if a
+ // threading mistake sneaks into the PostTaskAndReplyRelay implementation.
+ class LoopRecorder : public RefCountedThreadSafe<LoopRecorder> {
+ public:
+ LoopRecorder(MessageLoop** run_on, MessageLoop** deleted_on,
+ int* destruct_order)
+ : run_on_(run_on),
+ deleted_on_(deleted_on),
+ destruct_order_(destruct_order) {
+ }
+
+ void RecordRun() {
+ *run_on_ = MessageLoop::current();
+ }
+
+ private:
+ friend class RefCountedThreadSafe<LoopRecorder>;
+ ~LoopRecorder() {
+ *deleted_on_ = MessageLoop::current();
+ *destruct_order_ = g_order.GetNext();
+ }
+
+ MessageLoop** run_on_;
+ MessageLoop** deleted_on_;
+ int* destruct_order_;
+ };
+
+ static void RecordLoop(scoped_refptr<LoopRecorder> recorder) {
+ recorder->RecordRun();
+ }
+
+ static void RecordLoopAndQuit(scoped_refptr<LoopRecorder> recorder) {
+ recorder->RecordRun();
+ MessageLoop::current()->Quit();
+ }
+
+ void UnblockTaskThread() {
+ thread_sync_.Signal();
+ }
+
+ void BlockTaskThreadHelper() {
+ thread_sync_.Wait();
+ }
+
+ static StaticAtomicSequenceNumber g_order;
+
+ scoped_ptr<MessageLoop> current_loop_;
+ Thread task_thread_;
+
+ private:
+ base::WaitableEvent thread_sync_;
+};
+
+StaticAtomicSequenceNumber MessageLoopProxyTest::g_order;
+
+TEST_F(MessageLoopProxyTest, PostTaskAndReply_Basic) {
+ MessageLoop* task_run_on = NULL;
+ MessageLoop* task_deleted_on = NULL;
+ int task_delete_order = -1;
+ MessageLoop* reply_run_on = NULL;
+ MessageLoop* reply_deleted_on = NULL;
+ int reply_delete_order = -1;
+
+ scoped_refptr<LoopRecorder> task_recoder =
+ new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
+ scoped_refptr<LoopRecorder> reply_recoder =
+ new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
+
+ ASSERT_TRUE(task_thread_.message_loop_proxy()->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&RecordLoop, task_recoder),
+ Bind(&RecordLoopAndQuit, reply_recoder)));
+
+ // Die if base::Bind doesn't retain a reference to the recorders.
+ task_recoder = NULL;
+ reply_recoder = NULL;
+ ASSERT_FALSE(task_deleted_on);
+ ASSERT_FALSE(reply_deleted_on);
+
+ UnblockTaskThread();
+ current_loop_->Run();
+
+ EXPECT_EQ(task_thread_.message_loop(), task_run_on);
+ EXPECT_EQ(current_loop_.get(), task_deleted_on);
+ EXPECT_EQ(current_loop_.get(), reply_run_on);
+ EXPECT_EQ(current_loop_.get(), reply_deleted_on);
+ EXPECT_LT(task_delete_order, reply_delete_order);
+}
+
+TEST_F(MessageLoopProxyTest, PostTaskAndReplyOnDeletedThreadDoesNotLeak) {
+ MessageLoop* task_run_on = NULL;
+ MessageLoop* task_deleted_on = NULL;
+ int task_delete_order = -1;
+ MessageLoop* reply_run_on = NULL;
+ MessageLoop* reply_deleted_on = NULL;
+ int reply_delete_order = -1;
+
+ scoped_refptr<LoopRecorder> task_recoder =
+ new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
+ scoped_refptr<LoopRecorder> reply_recoder =
+ new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
+
+ // Grab a MessageLoopProxy to a dead MessageLoop.
+ scoped_refptr<MessageLoopProxy> task_loop_proxy =
+ task_thread_.message_loop_proxy();
+ UnblockTaskThread();
+ task_thread_.Stop();
+
+ ASSERT_FALSE(task_loop_proxy->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&RecordLoop, task_recoder),
+ Bind(&RecordLoopAndQuit, reply_recoder)));
+
+ // The relay should have properly deleted its resources leaving us as the only
+ // reference.
+ EXPECT_EQ(task_delete_order, reply_delete_order);
+ ASSERT_TRUE(task_recoder->HasOneRef());
+ ASSERT_TRUE(reply_recoder->HasOneRef());
+
+ // Nothing should have run though.
+ EXPECT_FALSE(task_run_on);
+ EXPECT_FALSE(reply_run_on);
+}
+
+TEST_F(MessageLoopProxyTest, PostTaskAndReply_SameLoop) {
+ MessageLoop* task_run_on = NULL;
+ MessageLoop* task_deleted_on = NULL;
+ int task_delete_order = -1;
+ MessageLoop* reply_run_on = NULL;
+ MessageLoop* reply_deleted_on = NULL;
+ int reply_delete_order = -1;
+
+ scoped_refptr<LoopRecorder> task_recoder =
+ new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
+ scoped_refptr<LoopRecorder> reply_recoder =
+ new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
+
+ // Enqueue the relay.
+ ASSERT_TRUE(current_loop_->message_loop_proxy()->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&RecordLoop, task_recoder),
+ Bind(&RecordLoopAndQuit, reply_recoder)));
+
+ // Die if base::Bind doesn't retain a reference to the recorders.
+ task_recoder = NULL;
+ reply_recoder = NULL;
+ ASSERT_FALSE(task_deleted_on);
+ ASSERT_FALSE(reply_deleted_on);
+
+ current_loop_->Run();
+
+ EXPECT_EQ(current_loop_.get(), task_run_on);
+ EXPECT_EQ(current_loop_.get(), task_deleted_on);
+ EXPECT_EQ(current_loop_.get(), reply_run_on);
+ EXPECT_EQ(current_loop_.get(), reply_deleted_on);
+ EXPECT_LT(task_delete_order, reply_delete_order);
+}
+
+TEST_F(MessageLoopProxyTest, PostTaskAndReply_DeadReplyLoopDoesNotDelete) {
+ // Annotate the scope as having memory leaks to suppress heapchecker reports.
+ ANNOTATE_SCOPED_MEMORY_LEAK;
+ MessageLoop* task_run_on = NULL;
+ MessageLoop* task_deleted_on = NULL;
+ int task_delete_order = -1;
+ MessageLoop* reply_run_on = NULL;
+ MessageLoop* reply_deleted_on = NULL;
+ int reply_delete_order = -1;
+
+ scoped_refptr<LoopRecorder> task_recoder =
+ new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order);
+ scoped_refptr<LoopRecorder> reply_recoder =
+ new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order);
+
+ // Enqueue the relay.
+ task_thread_.message_loop_proxy()->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&RecordLoop, task_recoder),
+ Bind(&RecordLoopAndQuit, reply_recoder));
+
+ // Die if base::Bind doesn't retain a reference to the recorders.
+ task_recoder = NULL;
+ reply_recoder = NULL;
+ ASSERT_FALSE(task_deleted_on);
+ ASSERT_FALSE(reply_deleted_on);
+
+ UnblockTaskThread();
+
+ // Mercilessly whack the current loop before |reply| gets to run.
+ current_loop_.reset();
+
+ // This should ensure the relay has been run. We need to record the
+ // MessageLoop pointer before stopping the thread because Thread::Stop() will
+ // NULL out its own pointer.
+ MessageLoop* task_loop = task_thread_.message_loop();
+ task_thread_.Stop();
+
+ EXPECT_EQ(task_loop, task_run_on);
+ ASSERT_FALSE(task_deleted_on);
+ EXPECT_FALSE(reply_run_on);
+ ASSERT_FALSE(reply_deleted_on);
+ EXPECT_EQ(task_delete_order, reply_delete_order);
+
+ // The PostTaskAndReplyRelay is leaked here. Even if we had a reference to
+ // it, we cannot just delete it because PostTaskAndReplyRelay's destructor
+ // checks that MessageLoop::current() is the the same as when the
+ // PostTaskAndReplyRelay object was constructed. However, this loop must have
+ // aleady been deleted in order to perform this test. See
+ // http://crbug.com/86301.
+}
+
+} // namespace
+
+} // namespace base
diff --git a/src/base/message_loop_unittest.cc b/src/base/message_loop_unittest.cc
new file mode 100644
index 0000000..b4bf2f3
--- /dev/null
+++ b/src/base/message_loop_unittest.cc
@@ -0,0 +1,2152 @@
+// Copyright (c) 2012 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 <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/run_loop.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include "base/message_pump_win.h"
+#include "base/win/scoped_handle.h"
+#endif
+
+using base::PlatformThread;
+using base::Thread;
+using base::Time;
+using base::TimeDelta;
+using base::TimeTicks;
+
+// TODO(darin): Platform-specific MessageLoop tests should be grouped together
+// to avoid chopping this file up with so many #ifdefs.
+
+namespace {
+
+class Foo : public base::RefCounted<Foo> {
+ public:
+ Foo() : test_count_(0) {
+ }
+
+ void Test0() {
+ ++test_count_;
+ }
+
+ void Test1ConstRef(const std::string& a) {
+ ++test_count_;
+ result_.append(a);
+ }
+
+ void Test1Ptr(std::string* a) {
+ ++test_count_;
+ result_.append(*a);
+ }
+
+ void Test1Int(int a) {
+ test_count_ += a;
+ }
+
+ void Test2Ptr(std::string* a, std::string* b) {
+ ++test_count_;
+ result_.append(*a);
+ result_.append(*b);
+ }
+
+ void Test2Mixed(const std::string& a, std::string* b) {
+ ++test_count_;
+ result_.append(a);
+ result_.append(*b);
+ }
+
+ int test_count() const { return test_count_; }
+ const std::string& result() const { return result_; }
+
+ private:
+ friend class base::RefCounted<Foo>;
+
+ ~Foo() {}
+
+ int test_count_;
+ std::string result_;
+};
+
+void RunTest_PostTask(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ // Add tests to message loop
+ scoped_refptr<Foo> foo(new Foo());
+ std::string a("a"), b("b"), c("c"), d("d");
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &Foo::Test0, foo.get()));
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &Foo::Test1ConstRef, foo.get(), a));
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &Foo::Test1Ptr, foo.get(), &b));
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &Foo::Test1Int, foo.get(), 100));
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &Foo::Test2Ptr, foo.get(), &a, &c));
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &Foo::Test2Mixed, foo.get(), a, &d));
+
+ // After all tests, post a message that will shut down the message loop
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &MessageLoop::Quit, base::Unretained(MessageLoop::current())));
+
+ // Now kick things off
+ MessageLoop::current()->Run();
+
+ EXPECT_EQ(foo->test_count(), 105);
+ EXPECT_EQ(foo->result(), "abacad");
+}
+
+void RunTest_PostTask_SEH(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ // Add tests to message loop
+ scoped_refptr<Foo> foo(new Foo());
+ std::string a("a"), b("b"), c("c"), d("d");
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &Foo::Test0, foo.get()));
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &Foo::Test1ConstRef, foo.get(), a));
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &Foo::Test1Ptr, foo.get(), &b));
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &Foo::Test1Int, foo.get(), 100));
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &Foo::Test2Ptr, foo.get(), &a, &c));
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &Foo::Test2Mixed, foo.get(), a, &d));
+
+ // After all tests, post a message that will shut down the message loop
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &MessageLoop::Quit, base::Unretained(MessageLoop::current())));
+
+ // Now kick things off with the SEH block active.
+ MessageLoop::current()->set_exception_restoration(true);
+ MessageLoop::current()->Run();
+ MessageLoop::current()->set_exception_restoration(false);
+
+ EXPECT_EQ(foo->test_count(), 105);
+ EXPECT_EQ(foo->result(), "abacad");
+}
+
+// This function runs slowly to simulate a large amount of work being done.
+static void SlowFunc(TimeDelta pause, int* quit_counter) {
+ PlatformThread::Sleep(pause);
+ if (--(*quit_counter) == 0)
+ MessageLoop::current()->Quit();
+}
+
+// This function records the time when Run was called in a Time object, which is
+// useful for building a variety of MessageLoop tests.
+static void RecordRunTimeFunc(Time* run_time, int* quit_counter) {
+ *run_time = Time::Now();
+
+ // Cause our Run function to take some time to execute. As a result we can
+ // count on subsequent RecordRunTimeFunc()s running at a future time,
+ // without worry about the resolution of our system clock being an issue.
+ SlowFunc(TimeDelta::FromMilliseconds(10), quit_counter);
+}
+
+void RunTest_PostDelayedTask_Basic(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ // Test that PostDelayedTask results in a delayed task.
+
+ const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
+
+ int num_tasks = 1;
+ Time run_time;
+
+ loop.PostDelayedTask(
+ FROM_HERE, base::Bind(&RecordRunTimeFunc, &run_time, &num_tasks),
+ kDelay);
+
+ Time time_before_run = Time::Now();
+ loop.Run();
+ Time time_after_run = Time::Now();
+
+ EXPECT_EQ(0, num_tasks);
+ EXPECT_LT(kDelay, time_after_run - time_before_run);
+}
+
+void RunTest_PostDelayedTask_InDelayOrder(
+ MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ // Test that two tasks with different delays run in the right order.
+ int num_tasks = 2;
+ Time run_time1, run_time2;
+
+ loop.PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&RecordRunTimeFunc, &run_time1, &num_tasks),
+ TimeDelta::FromMilliseconds(200));
+ // If we get a large pause in execution (due to a context switch) here, this
+ // test could fail.
+ loop.PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&RecordRunTimeFunc, &run_time2, &num_tasks),
+ TimeDelta::FromMilliseconds(10));
+
+ loop.Run();
+ EXPECT_EQ(0, num_tasks);
+
+ EXPECT_TRUE(run_time2 < run_time1);
+}
+
+void RunTest_PostDelayedTask_InPostOrder(
+ MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ // Test that two tasks with the same delay run in the order in which they
+ // were posted.
+ //
+ // NOTE: This is actually an approximate test since the API only takes a
+ // "delay" parameter, so we are not exactly simulating two tasks that get
+ // posted at the exact same time. It would be nice if the API allowed us to
+ // specify the desired run time.
+
+ const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
+
+ int num_tasks = 2;
+ Time run_time1, run_time2;
+
+ loop.PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&RecordRunTimeFunc, &run_time1, &num_tasks), kDelay);
+ loop.PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&RecordRunTimeFunc, &run_time2, &num_tasks), kDelay);
+
+ loop.Run();
+ EXPECT_EQ(0, num_tasks);
+
+ EXPECT_TRUE(run_time1 < run_time2);
+}
+
+void RunTest_PostDelayedTask_InPostOrder_2(
+ MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ // Test that a delayed task still runs after a normal tasks even if the
+ // normal tasks take a long time to run.
+
+ const TimeDelta kPause = TimeDelta::FromMilliseconds(50);
+
+ int num_tasks = 2;
+ Time run_time;
+
+ loop.PostTask(FROM_HERE, base::Bind(&SlowFunc, kPause, &num_tasks));
+ loop.PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&RecordRunTimeFunc, &run_time, &num_tasks),
+ TimeDelta::FromMilliseconds(10));
+
+ Time time_before_run = Time::Now();
+ loop.Run();
+ Time time_after_run = Time::Now();
+
+ EXPECT_EQ(0, num_tasks);
+
+ EXPECT_LT(kPause, time_after_run - time_before_run);
+}
+
+void RunTest_PostDelayedTask_InPostOrder_3(
+ MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ // Test that a delayed task still runs after a pile of normal tasks. The key
+ // difference between this test and the previous one is that here we return
+ // the MessageLoop a lot so we give the MessageLoop plenty of opportunities
+ // to maybe run the delayed task. It should know not to do so until the
+ // delayed task's delay has passed.
+
+ int num_tasks = 11;
+ Time run_time1, run_time2;
+
+ // Clutter the ML with tasks.
+ for (int i = 1; i < num_tasks; ++i)
+ loop.PostTask(FROM_HERE,
+ base::Bind(&RecordRunTimeFunc, &run_time1, &num_tasks));
+
+ loop.PostDelayedTask(
+ FROM_HERE, base::Bind(&RecordRunTimeFunc, &run_time2, &num_tasks),
+ TimeDelta::FromMilliseconds(1));
+
+ loop.Run();
+ EXPECT_EQ(0, num_tasks);
+
+ EXPECT_TRUE(run_time2 > run_time1);
+}
+
+void RunTest_PostDelayedTask_SharedTimer(
+ MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ // Test that the interval of the timer, used to run the next delayed task, is
+ // set to a value corresponding to when the next delayed task should run.
+
+ // By setting num_tasks to 1, we ensure that the first task to run causes the
+ // run loop to exit.
+ int num_tasks = 1;
+ Time run_time1, run_time2;
+
+ loop.PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&RecordRunTimeFunc, &run_time1, &num_tasks),
+ TimeDelta::FromSeconds(1000));
+ loop.PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&RecordRunTimeFunc, &run_time2, &num_tasks),
+ TimeDelta::FromMilliseconds(10));
+
+ Time start_time = Time::Now();
+
+ loop.Run();
+ EXPECT_EQ(0, num_tasks);
+
+ // Ensure that we ran in far less time than the slower timer.
+ TimeDelta total_time = Time::Now() - start_time;
+ EXPECT_GT(5000, total_time.InMilliseconds());
+
+ // In case both timers somehow run at nearly the same time, sleep a little
+ // and then run all pending to force them both to have run. This is just
+ // encouraging flakiness if there is any.
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+ loop.RunUntilIdle();
+
+ EXPECT_TRUE(run_time1.is_null());
+ EXPECT_FALSE(run_time2.is_null());
+}
+
+#if defined(OS_WIN)
+
+void SubPumpFunc() {
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ MSG msg;
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ MessageLoop::current()->Quit();
+}
+
+void RunTest_PostDelayedTask_SharedTimer_SubPump() {
+ MessageLoop loop(MessageLoop::TYPE_UI);
+
+ // Test that the interval of the timer, used to run the next delayed task, is
+ // set to a value corresponding to when the next delayed task should run.
+
+ // By setting num_tasks to 1, we ensure that the first task to run causes the
+ // run loop to exit.
+ int num_tasks = 1;
+ Time run_time;
+
+ loop.PostTask(FROM_HERE, base::Bind(&SubPumpFunc));
+
+ // This very delayed task should never run.
+ loop.PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&RecordRunTimeFunc, &run_time, &num_tasks),
+ TimeDelta::FromSeconds(1000));
+
+ // This slightly delayed task should run from within SubPumpFunc).
+ loop.PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&PostQuitMessage, 0),
+ TimeDelta::FromMilliseconds(10));
+
+ Time start_time = Time::Now();
+
+ loop.Run();
+ EXPECT_EQ(1, num_tasks);
+
+ // Ensure that we ran in far less time than the slower timer.
+ TimeDelta total_time = Time::Now() - start_time;
+ EXPECT_GT(5000, total_time.InMilliseconds());
+
+ // In case both timers somehow run at nearly the same time, sleep a little
+ // and then run all pending to force them both to have run. This is just
+ // encouraging flakiness if there is any.
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+ loop.RunUntilIdle();
+
+ EXPECT_TRUE(run_time.is_null());
+}
+
+#endif // defined(OS_WIN)
+
+// This is used to inject a test point for recording the destructor calls for
+// Closure objects send to MessageLoop::PostTask(). It is awkward usage since we
+// are trying to hook the actual destruction, which is not a common operation.
+class RecordDeletionProbe : public base::RefCounted<RecordDeletionProbe> {
+ public:
+ RecordDeletionProbe(RecordDeletionProbe* post_on_delete, bool* was_deleted)
+ : post_on_delete_(post_on_delete), was_deleted_(was_deleted) {
+ }
+ void Run() {}
+
+ private:
+ friend class base::RefCounted<RecordDeletionProbe>;
+
+ ~RecordDeletionProbe() {
+ *was_deleted_ = true;
+ if (post_on_delete_)
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&RecordDeletionProbe::Run, post_on_delete_.get()));
+ }
+
+ scoped_refptr<RecordDeletionProbe> post_on_delete_;
+ bool* was_deleted_;
+};
+
+void RunTest_EnsureDeletion(MessageLoop::Type message_loop_type) {
+ bool a_was_deleted = false;
+ bool b_was_deleted = false;
+ {
+ MessageLoop loop(message_loop_type);
+ loop.PostTask(
+ FROM_HERE, base::Bind(&RecordDeletionProbe::Run,
+ new RecordDeletionProbe(NULL, &a_was_deleted)));
+ // TODO(ajwong): Do we really need 1000ms here?
+ loop.PostDelayedTask(
+ FROM_HERE, base::Bind(&RecordDeletionProbe::Run,
+ new RecordDeletionProbe(NULL, &b_was_deleted)),
+ TimeDelta::FromMilliseconds(1000));
+ }
+ EXPECT_TRUE(a_was_deleted);
+ EXPECT_TRUE(b_was_deleted);
+}
+
+void RunTest_EnsureDeletion_Chain(MessageLoop::Type message_loop_type) {
+ bool a_was_deleted = false;
+ bool b_was_deleted = false;
+ bool c_was_deleted = false;
+ {
+ MessageLoop loop(message_loop_type);
+ // The scoped_refptr for each of the below is held either by the chained
+ // RecordDeletionProbe, or the bound RecordDeletionProbe::Run() callback.
+ RecordDeletionProbe* a = new RecordDeletionProbe(NULL, &a_was_deleted);
+ RecordDeletionProbe* b = new RecordDeletionProbe(a, &b_was_deleted);
+ RecordDeletionProbe* c = new RecordDeletionProbe(b, &c_was_deleted);
+ loop.PostTask(FROM_HERE, base::Bind(&RecordDeletionProbe::Run, c));
+ }
+ EXPECT_TRUE(a_was_deleted);
+ EXPECT_TRUE(b_was_deleted);
+ EXPECT_TRUE(c_was_deleted);
+}
+
+void NestingFunc(int* depth) {
+ if (*depth > 0) {
+ *depth -= 1;
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&NestingFunc, depth));
+
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ MessageLoop::current()->Run();
+ }
+ MessageLoop::current()->Quit();
+}
+
+#if defined(OS_WIN)
+
+LONG WINAPI BadExceptionHandler(EXCEPTION_POINTERS *ex_info) {
+ ADD_FAILURE() << "bad exception handler";
+ ::ExitProcess(ex_info->ExceptionRecord->ExceptionCode);
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+// This task throws an SEH exception: initially write to an invalid address.
+// If the right SEH filter is installed, it will fix the error.
+class Crasher : public base::RefCounted<Crasher> {
+ public:
+ // Ctor. If trash_SEH_handler is true, the task will override the unhandled
+ // exception handler with one sure to crash this test.
+ explicit Crasher(bool trash_SEH_handler)
+ : trash_SEH_handler_(trash_SEH_handler) {
+ }
+
+ void Run() {
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(1));
+ if (trash_SEH_handler_)
+ ::SetUnhandledExceptionFilter(&BadExceptionHandler);
+ // Generate a SEH fault. We do it in asm to make sure we know how to undo
+ // the damage.
+
+#if defined(_M_IX86)
+
+ __asm {
+ mov eax, dword ptr [Crasher::bad_array_]
+ mov byte ptr [eax], 66
+ }
+
+#elif defined(_M_X64)
+
+ bad_array_[0] = 66;
+
+#else
+#error "needs architecture support"
+#endif
+
+ MessageLoop::current()->Quit();
+ }
+ // Points the bad array to a valid memory location.
+ static void FixError() {
+ bad_array_ = &valid_store_;
+ }
+
+ private:
+ bool trash_SEH_handler_;
+ static volatile char* bad_array_;
+ static char valid_store_;
+};
+
+volatile char* Crasher::bad_array_ = 0;
+char Crasher::valid_store_ = 0;
+
+// This SEH filter fixes the problem and retries execution. Fixing requires
+// that the last instruction: mov eax, [Crasher::bad_array_] to be retried
+// so we move the instruction pointer 5 bytes back.
+LONG WINAPI HandleCrasherException(EXCEPTION_POINTERS *ex_info) {
+ if (ex_info->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
+ return EXCEPTION_EXECUTE_HANDLER;
+
+ Crasher::FixError();
+
+#if defined(_M_IX86)
+
+ ex_info->ContextRecord->Eip -= 5;
+
+#elif defined(_M_X64)
+
+ ex_info->ContextRecord->Rip -= 5;
+
+#endif
+
+ return EXCEPTION_CONTINUE_EXECUTION;
+}
+
+void RunTest_Crasher(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ if (::IsDebuggerPresent())
+ return;
+
+ LPTOP_LEVEL_EXCEPTION_FILTER old_SEH_filter =
+ ::SetUnhandledExceptionFilter(&HandleCrasherException);
+
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&Crasher::Run, new Crasher(false)));
+ MessageLoop::current()->set_exception_restoration(true);
+ MessageLoop::current()->Run();
+ MessageLoop::current()->set_exception_restoration(false);
+
+ ::SetUnhandledExceptionFilter(old_SEH_filter);
+}
+
+void RunTest_CrasherNasty(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ if (::IsDebuggerPresent())
+ return;
+
+ LPTOP_LEVEL_EXCEPTION_FILTER old_SEH_filter =
+ ::SetUnhandledExceptionFilter(&HandleCrasherException);
+
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&Crasher::Run, new Crasher(true)));
+ MessageLoop::current()->set_exception_restoration(true);
+ MessageLoop::current()->Run();
+ MessageLoop::current()->set_exception_restoration(false);
+
+ ::SetUnhandledExceptionFilter(old_SEH_filter);
+}
+
+#endif // defined(OS_WIN)
+
+void RunTest_Nesting(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ int depth = 100;
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&NestingFunc, &depth));
+ MessageLoop::current()->Run();
+ EXPECT_EQ(depth, 0);
+}
+
+#if defined(OS_WIN)
+const wchar_t* const kMessageBoxTitle = L"MessageLoop Unit Test";
+#endif
+
+enum TaskType {
+ MESSAGEBOX,
+ ENDDIALOG,
+ RECURSIVE,
+ TIMEDMESSAGELOOP,
+ QUITMESSAGELOOP,
+ ORDERED,
+ PUMPS,
+ SLEEP,
+ RUNS,
+};
+
+// Saves the order in which the tasks executed.
+struct TaskItem {
+ TaskItem(TaskType t, int c, bool s)
+ : type(t),
+ cookie(c),
+ start(s) {
+ }
+
+ TaskType type;
+ int cookie;
+ bool start;
+
+ bool operator == (const TaskItem& other) const {
+ return type == other.type && cookie == other.cookie && start == other.start;
+ }
+};
+
+std::ostream& operator <<(std::ostream& os, TaskType type) {
+ switch (type) {
+ case MESSAGEBOX: os << "MESSAGEBOX"; break;
+ case ENDDIALOG: os << "ENDDIALOG"; break;
+ case RECURSIVE: os << "RECURSIVE"; break;
+ case TIMEDMESSAGELOOP: os << "TIMEDMESSAGELOOP"; break;
+ case QUITMESSAGELOOP: os << "QUITMESSAGELOOP"; break;
+ case ORDERED: os << "ORDERED"; break;
+ case PUMPS: os << "PUMPS"; break;
+ case SLEEP: os << "SLEEP"; break;
+ default:
+ NOTREACHED();
+ os << "Unknown TaskType";
+ break;
+ }
+ return os;
+}
+
+std::ostream& operator <<(std::ostream& os, const TaskItem& item) {
+ if (item.start)
+ return os << item.type << " " << item.cookie << " starts";
+ else
+ return os << item.type << " " << item.cookie << " ends";
+}
+
+class TaskList {
+ public:
+ void RecordStart(TaskType type, int cookie) {
+ TaskItem item(type, cookie, true);
+ DVLOG(1) << item;
+ task_list_.push_back(item);
+ }
+
+ void RecordEnd(TaskType type, int cookie) {
+ TaskItem item(type, cookie, false);
+ DVLOG(1) << item;
+ task_list_.push_back(item);
+ }
+
+ size_t Size() {
+ return task_list_.size();
+ }
+
+ TaskItem Get(int n) {
+ return task_list_[n];
+ }
+
+ private:
+ std::vector<TaskItem> task_list_;
+};
+
+// Saves the order the tasks ran.
+void OrderedFunc(TaskList* order, int cookie) {
+ order->RecordStart(ORDERED, cookie);
+ order->RecordEnd(ORDERED, cookie);
+}
+
+#if defined(OS_WIN)
+
+// MessageLoop implicitly start a "modal message loop". Modal dialog boxes,
+// common controls (like OpenFile) and StartDoc printing function can cause
+// implicit message loops.
+void MessageBoxFunc(TaskList* order, int cookie, bool is_reentrant) {
+ order->RecordStart(MESSAGEBOX, cookie);
+ if (is_reentrant)
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ MessageBox(NULL, L"Please wait...", kMessageBoxTitle, MB_OK);
+ order->RecordEnd(MESSAGEBOX, cookie);
+}
+
+// Will end the MessageBox.
+void EndDialogFunc(TaskList* order, int cookie) {
+ order->RecordStart(ENDDIALOG, cookie);
+ HWND window = GetActiveWindow();
+ if (window != NULL) {
+ EXPECT_NE(EndDialog(window, IDCONTINUE), 0);
+ // Cheap way to signal that the window wasn't found if RunEnd() isn't
+ // called.
+ order->RecordEnd(ENDDIALOG, cookie);
+ }
+}
+
+#endif // defined(OS_WIN)
+
+void RecursiveFunc(TaskList* order, int cookie, int depth,
+ bool is_reentrant) {
+ order->RecordStart(RECURSIVE, cookie);
+ if (depth > 0) {
+ if (is_reentrant)
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&RecursiveFunc, order, cookie, depth - 1, is_reentrant));
+ }
+ order->RecordEnd(RECURSIVE, cookie);
+}
+
+void RecursiveSlowFunc(TaskList* order, int cookie, int depth,
+ bool is_reentrant) {
+ RecursiveFunc(order, cookie, depth, is_reentrant);
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(10));
+}
+
+void QuitFunc(TaskList* order, int cookie) {
+ order->RecordStart(QUITMESSAGELOOP, cookie);
+ MessageLoop::current()->Quit();
+ order->RecordEnd(QUITMESSAGELOOP, cookie);
+}
+
+void SleepFunc(TaskList* order, int cookie, TimeDelta delay) {
+ order->RecordStart(SLEEP, cookie);
+ PlatformThread::Sleep(delay);
+ order->RecordEnd(SLEEP, cookie);
+}
+
+#if defined(OS_WIN)
+void RecursiveFuncWin(MessageLoop* target,
+ HANDLE event,
+ bool expect_window,
+ TaskList* order,
+ bool is_reentrant) {
+ target->PostTask(FROM_HERE,
+ base::Bind(&RecursiveFunc, order, 1, 2, is_reentrant));
+ target->PostTask(FROM_HERE,
+ base::Bind(&MessageBoxFunc, order, 2, is_reentrant));
+ target->PostTask(FROM_HERE,
+ base::Bind(&RecursiveFunc, order, 3, 2, is_reentrant));
+ // The trick here is that for recursive task processing, this task will be
+ // ran _inside_ the MessageBox message loop, dismissing the MessageBox
+ // without a chance.
+ // For non-recursive task processing, this will be executed _after_ the
+ // MessageBox will have been dismissed by the code below, where
+ // expect_window_ is true.
+ target->PostTask(FROM_HERE,
+ base::Bind(&EndDialogFunc, order, 4));
+ target->PostTask(FROM_HERE,
+ base::Bind(&QuitFunc, order, 5));
+
+ // Enforce that every tasks are sent before starting to run the main thread
+ // message loop.
+ ASSERT_TRUE(SetEvent(event));
+
+ // Poll for the MessageBox. Don't do this at home! At the speed we do it,
+ // you will never realize one MessageBox was shown.
+ for (; expect_window;) {
+ HWND window = FindWindow(L"#32770", kMessageBoxTitle);
+ if (window) {
+ // Dismiss it.
+ for (;;) {
+ HWND button = FindWindowEx(window, NULL, L"Button", NULL);
+ if (button != NULL) {
+ EXPECT_EQ(0, SendMessage(button, WM_LBUTTONDOWN, 0, 0));
+ EXPECT_EQ(0, SendMessage(button, WM_LBUTTONUP, 0, 0));
+ break;
+ }
+ }
+ break;
+ }
+ }
+}
+
+#endif // defined(OS_WIN)
+
+void RunTest_RecursiveDenial1(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
+ TaskList order;
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&RecursiveFunc, &order, 1, 2, false));
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&RecursiveFunc, &order, 2, 2, false));
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&QuitFunc, &order, 3));
+
+ MessageLoop::current()->Run();
+
+ // FIFO order.
+ ASSERT_EQ(14U, order.Size());
+ EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true));
+ EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false));
+ EXPECT_EQ(order.Get(6), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(7), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 2, false));
+}
+
+void RunTest_RecursiveDenial3(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
+ TaskList order;
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&RecursiveSlowFunc, &order, 1, 2, false));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&RecursiveSlowFunc, &order, 2, 2, false));
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&OrderedFunc, &order, 3),
+ TimeDelta::FromMilliseconds(5));
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&QuitFunc, &order, 4),
+ TimeDelta::FromMilliseconds(5));
+
+ MessageLoop::current()->Run();
+
+ // FIFO order.
+ ASSERT_EQ(16U, order.Size());
+ EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(5), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(6), TaskItem(ORDERED, 3, true));
+ EXPECT_EQ(order.Get(7), TaskItem(ORDERED, 3, false));
+#if (defined(__LB_SHELL__) && !defined(__LB_ANDROID__)) || defined(OS_STARBOARD)
+ if (message_loop_type == MessageLoop::TYPE_DEFAULT) {
+ // lbshell messagepump gives delayed tasks higher priority, which causes
+ // the process order to be a bit different. the messagepump src code is in
+ // external\chromium\base\message_pump_shell.cc, function
+ // void MessagePumpShell::Run(Delegate * delegate);
+#endif
+ EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order.Get(10), TaskItem(QUITMESSAGELOOP, 4, true));
+ EXPECT_EQ(order.Get(11), TaskItem(QUITMESSAGELOOP, 4, false));
+#if (defined(__LB_SHELL__) && !defined(__LB_ANDROID__)) || defined(OS_STARBOARD)
+ } else {
+ EXPECT_EQ(order.Get(8), TaskItem(QUITMESSAGELOOP, 4, true));
+ EXPECT_EQ(order.Get(9), TaskItem(QUITMESSAGELOOP, 4, false));
+ EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 2, false));
+ }
+#endif
+ EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 2, false));
+}
+
+void RunTest_RecursiveSupport1(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ TaskList order;
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&RecursiveFunc, &order, 1, 2, true));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&RecursiveFunc, &order, 2, 2, true));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&QuitFunc, &order, 3));
+
+ MessageLoop::current()->Run();
+
+ // FIFO order.
+ ASSERT_EQ(14U, order.Size());
+ EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true));
+ EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false));
+ EXPECT_EQ(order.Get(6), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(7), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 2, false));
+}
+
+#if defined(OS_WIN)
+// TODO(darin): These tests need to be ported since they test critical
+// message loop functionality.
+
+// A side effect of this test is the generation a beep. Sorry.
+void RunTest_RecursiveDenial2(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ Thread worker("RecursiveDenial2_worker");
+ Thread::Options options;
+ options.message_loop_type = message_loop_type;
+ ASSERT_EQ(true, worker.StartWithOptions(options));
+ TaskList order;
+ base::win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL));
+ worker.message_loop()->PostTask(FROM_HERE,
+ base::Bind(&RecursiveFuncWin,
+ MessageLoop::current(),
+ event.Get(),
+ true,
+ &order,
+ false));
+ // Let the other thread execute.
+ WaitForSingleObject(event, INFINITE);
+ MessageLoop::current()->Run();
+
+ ASSERT_EQ(order.Size(), 17);
+ EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(2), TaskItem(MESSAGEBOX, 2, true));
+ EXPECT_EQ(order.Get(3), TaskItem(MESSAGEBOX, 2, false));
+ EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 3, true));
+ EXPECT_EQ(order.Get(5), TaskItem(RECURSIVE, 3, false));
+ // When EndDialogFunc is processed, the window is already dismissed, hence no
+ // "end" entry.
+ EXPECT_EQ(order.Get(6), TaskItem(ENDDIALOG, 4, true));
+ EXPECT_EQ(order.Get(7), TaskItem(QUITMESSAGELOOP, 5, true));
+ EXPECT_EQ(order.Get(8), TaskItem(QUITMESSAGELOOP, 5, false));
+ EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 3, true));
+ EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 3, false));
+ EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 3, true));
+ EXPECT_EQ(order.Get(16), TaskItem(RECURSIVE, 3, false));
+}
+
+// A side effect of this test is the generation a beep. Sorry. This test also
+// needs to process windows messages on the current thread.
+void RunTest_RecursiveSupport2(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ Thread worker("RecursiveSupport2_worker");
+ Thread::Options options;
+ options.message_loop_type = message_loop_type;
+ ASSERT_EQ(true, worker.StartWithOptions(options));
+ TaskList order;
+ base::win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL));
+ worker.message_loop()->PostTask(FROM_HERE,
+ base::Bind(&RecursiveFuncWin,
+ MessageLoop::current(),
+ event.Get(),
+ false,
+ &order,
+ true));
+ // Let the other thread execute.
+ WaitForSingleObject(event, INFINITE);
+ MessageLoop::current()->Run();
+
+ ASSERT_EQ(order.Size(), 18);
+ EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(2), TaskItem(MESSAGEBOX, 2, true));
+ // Note that this executes in the MessageBox modal loop.
+ EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 3, true));
+ EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 3, false));
+ EXPECT_EQ(order.Get(5), TaskItem(ENDDIALOG, 4, true));
+ EXPECT_EQ(order.Get(6), TaskItem(ENDDIALOG, 4, false));
+ EXPECT_EQ(order.Get(7), TaskItem(MESSAGEBOX, 2, false));
+ /* The order can subtly change here. The reason is that when RecursiveFunc(1)
+ is called in the main thread, if it is faster than getting to the
+ PostTask(FROM_HERE, base::Bind(&QuitFunc) execution, the order of task
+ execution can change. We don't care anyway that the order isn't correct.
+ EXPECT_EQ(order.Get(8), TaskItem(QUITMESSAGELOOP, 5, true));
+ EXPECT_EQ(order.Get(9), TaskItem(QUITMESSAGELOOP, 5, false));
+ EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false));
+ */
+ EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 3, true));
+ EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 3, false));
+ EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(16), TaskItem(RECURSIVE, 3, true));
+ EXPECT_EQ(order.Get(17), TaskItem(RECURSIVE, 3, false));
+}
+
+#endif // defined(OS_WIN)
+
+void FuncThatPumps(TaskList* order, int cookie) {
+ order->RecordStart(PUMPS, cookie);
+ {
+ MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
+ MessageLoop::current()->RunUntilIdle();
+ }
+ order->RecordEnd(PUMPS, cookie);
+}
+
+void FuncThatRuns(TaskList* order, int cookie, base::RunLoop* run_loop) {
+ order->RecordStart(RUNS, cookie);
+ {
+ MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
+ run_loop->Run();
+ }
+ order->RecordEnd(RUNS, cookie);
+}
+
+void FuncThatQuitsNow() {
+ MessageLoop::current()->QuitNow();
+}
+
+// Tests that non nestable tasks run in FIFO if there are no nested loops.
+void RunTest_NonNestableWithNoNesting(
+ MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ TaskList order;
+
+ MessageLoop::current()->PostNonNestableTask(
+ FROM_HERE,
+ base::Bind(&OrderedFunc, &order, 1));
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&OrderedFunc, &order, 2));
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&QuitFunc, &order, 3));
+ MessageLoop::current()->Run();
+
+ // FIFO order.
+ ASSERT_EQ(6U, order.Size());
+ EXPECT_EQ(order.Get(0), TaskItem(ORDERED, 1, true));
+ EXPECT_EQ(order.Get(1), TaskItem(ORDERED, 1, false));
+ EXPECT_EQ(order.Get(2), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(3), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true));
+ EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false));
+}
+
+// Tests that non nestable tasks don't run when there's code in the call stack.
+void RunTest_NonNestableInNestedLoop(MessageLoop::Type message_loop_type,
+ bool use_delayed) {
+ MessageLoop loop(message_loop_type);
+
+ TaskList order;
+
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&FuncThatPumps, &order, 1));
+ if (use_delayed) {
+ MessageLoop::current()->PostNonNestableDelayedTask(
+ FROM_HERE,
+ base::Bind(&OrderedFunc, &order, 2),
+ TimeDelta::FromMilliseconds(1));
+ } else {
+ MessageLoop::current()->PostNonNestableTask(
+ FROM_HERE,
+ base::Bind(&OrderedFunc, &order, 2));
+ }
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&OrderedFunc, &order, 3));
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&SleepFunc, &order, 4, TimeDelta::FromMilliseconds(50)));
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&OrderedFunc, &order, 5));
+ if (use_delayed) {
+ MessageLoop::current()->PostNonNestableDelayedTask(
+ FROM_HERE,
+ base::Bind(&QuitFunc, &order, 6),
+ TimeDelta::FromMilliseconds(2));
+ } else {
+ MessageLoop::current()->PostNonNestableTask(
+ FROM_HERE,
+ base::Bind(&QuitFunc, &order, 6));
+ }
+
+ MessageLoop::current()->Run();
+
+ // FIFO order.
+ ASSERT_EQ(12U, order.Size());
+ EXPECT_EQ(order.Get(0), TaskItem(PUMPS, 1, true));
+ EXPECT_EQ(order.Get(1), TaskItem(ORDERED, 3, true));
+ EXPECT_EQ(order.Get(2), TaskItem(ORDERED, 3, false));
+ EXPECT_EQ(order.Get(3), TaskItem(SLEEP, 4, true));
+ EXPECT_EQ(order.Get(4), TaskItem(SLEEP, 4, false));
+ EXPECT_EQ(order.Get(5), TaskItem(ORDERED, 5, true));
+ EXPECT_EQ(order.Get(6), TaskItem(ORDERED, 5, false));
+ EXPECT_EQ(order.Get(7), TaskItem(PUMPS, 1, false));
+ EXPECT_EQ(order.Get(8), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(9), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(order.Get(10), TaskItem(QUITMESSAGELOOP, 6, true));
+ EXPECT_EQ(order.Get(11), TaskItem(QUITMESSAGELOOP, 6, false));
+}
+
+// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
+void RunTest_QuitNow(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ TaskList order;
+
+ base::RunLoop run_loop;
+
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&FuncThatRuns, &order, 1, base::Unretained(&run_loop)));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&OrderedFunc, &order, 2));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&FuncThatQuitsNow));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&OrderedFunc, &order, 3));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&FuncThatQuitsNow));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&OrderedFunc, &order, 4)); // never runs
+
+ MessageLoop::current()->Run();
+
+ ASSERT_EQ(6U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// Tests RunLoopQuit works before RunWithID.
+void RunTest_RunLoopQuitOrderBefore(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ TaskList order;
+
+ base::RunLoop run_loop;
+
+ run_loop.Quit();
+
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&OrderedFunc, &order, 1)); // never runs
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&FuncThatQuitsNow)); // never runs
+
+ run_loop.Run();
+
+ ASSERT_EQ(0U, order.Size());
+}
+
+// Tests RunLoopQuit works during RunWithID.
+void RunTest_RunLoopQuitOrderDuring(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ TaskList order;
+
+ base::RunLoop run_loop;
+
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&OrderedFunc, &order, 1));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, run_loop.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&OrderedFunc, &order, 2)); // never runs
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&FuncThatQuitsNow)); // never runs
+
+ run_loop.Run();
+
+ ASSERT_EQ(2U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// Tests RunLoopQuit works after RunWithID.
+void RunTest_RunLoopQuitOrderAfter(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ TaskList order;
+
+ base::RunLoop run_loop;
+
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&FuncThatRuns, &order, 1, base::Unretained(&run_loop)));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&OrderedFunc, &order, 2));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&FuncThatQuitsNow));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&OrderedFunc, &order, 3));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, run_loop.QuitClosure()); // has no affect
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&OrderedFunc, &order, 4));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&FuncThatQuitsNow));
+
+ base::RunLoop outer_run_loop;
+ outer_run_loop.Run();
+
+ ASSERT_EQ(8U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
+void RunTest_RunLoopQuitTop(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ TaskList order;
+
+ base::RunLoop outer_run_loop;
+ base::RunLoop nested_run_loop;
+
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&FuncThatRuns, &order, 1, base::Unretained(&nested_run_loop)));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, outer_run_loop.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&OrderedFunc, &order, 2));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, nested_run_loop.QuitClosure());
+
+ outer_run_loop.Run();
+
+ ASSERT_EQ(4U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
+void RunTest_RunLoopQuitNested(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ TaskList order;
+
+ base::RunLoop outer_run_loop;
+ base::RunLoop nested_run_loop;
+
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&FuncThatRuns, &order, 1, base::Unretained(&nested_run_loop)));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, nested_run_loop.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&OrderedFunc, &order, 2));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, outer_run_loop.QuitClosure());
+
+ outer_run_loop.Run();
+
+ ASSERT_EQ(4U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
+void RunTest_RunLoopQuitBogus(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ TaskList order;
+
+ base::RunLoop outer_run_loop;
+ base::RunLoop nested_run_loop;
+ base::RunLoop bogus_run_loop;
+
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&FuncThatRuns, &order, 1, base::Unretained(&nested_run_loop)));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, bogus_run_loop.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&OrderedFunc, &order, 2));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, outer_run_loop.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, nested_run_loop.QuitClosure());
+
+ outer_run_loop.Run();
+
+ ASSERT_EQ(4U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
+void RunTest_RunLoopQuitDeep(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ TaskList order;
+
+ base::RunLoop outer_run_loop;
+ base::RunLoop nested_loop1;
+ base::RunLoop nested_loop2;
+ base::RunLoop nested_loop3;
+ base::RunLoop nested_loop4;
+
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&FuncThatRuns, &order, 1, base::Unretained(&nested_loop1)));
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&FuncThatRuns, &order, 2, base::Unretained(&nested_loop2)));
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&FuncThatRuns, &order, 3, base::Unretained(&nested_loop3)));
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&FuncThatRuns, &order, 4, base::Unretained(&nested_loop4)));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&OrderedFunc, &order, 5));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, outer_run_loop.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&OrderedFunc, &order, 6));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, nested_loop1.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&OrderedFunc, &order, 7));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, nested_loop2.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&OrderedFunc, &order, 8));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, nested_loop3.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&OrderedFunc, &order, 9));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, nested_loop4.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&OrderedFunc, &order, 10));
+
+ outer_run_loop.Run();
+
+ ASSERT_EQ(18U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 2, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 3, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 4, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 5, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 5, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 6, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 6, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 7, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 7, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 8, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 8, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 9, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 9, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 4, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 3, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 2, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+void PostNTasksThenQuit(int posts_remaining) {
+ if (posts_remaining > 1) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&PostNTasksThenQuit, posts_remaining - 1));
+ } else {
+ MessageLoop::current()->QuitWhenIdle();
+ }
+}
+
+void RunTest_RecursivePosts(MessageLoop::Type message_loop_type,
+ int num_times) {
+ MessageLoop loop(message_loop_type);
+ loop.PostTask(FROM_HERE, base::Bind(&PostNTasksThenQuit, num_times));
+ loop.Run();
+}
+
+#if defined(OS_WIN)
+
+class DispatcherImpl : public MessageLoopForUI::Dispatcher {
+ public:
+ DispatcherImpl() : dispatch_count_(0) {}
+
+ virtual bool Dispatch(const base::NativeEvent& msg) OVERRIDE {
+ ::TranslateMessage(&msg);
+ ::DispatchMessage(&msg);
+ // Do not count WM_TIMER since it is not what we post and it will cause
+ // flakiness.
+ if (msg.message != WM_TIMER)
+ ++dispatch_count_;
+ // We treat WM_LBUTTONUP as the last message.
+ return msg.message != WM_LBUTTONUP;
+ }
+
+ int dispatch_count_;
+};
+
+void MouseDownUp() {
+ PostMessage(NULL, WM_LBUTTONDOWN, 0, 0);
+ PostMessage(NULL, WM_LBUTTONUP, 'A', 0);
+}
+
+void RunTest_Dispatcher(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&MouseDownUp),
+ TimeDelta::FromMilliseconds(100));
+ DispatcherImpl dispatcher;
+ base::RunLoop run_loop(&dispatcher);
+ run_loop.Run();
+ ASSERT_EQ(2, dispatcher.dispatch_count_);
+}
+
+LRESULT CALLBACK MsgFilterProc(int code, WPARAM wparam, LPARAM lparam) {
+ if (code == base::MessagePumpForUI::kMessageFilterCode) {
+ MSG* msg = reinterpret_cast<MSG*>(lparam);
+ if (msg->message == WM_LBUTTONDOWN)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void RunTest_DispatcherWithMessageHook(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&MouseDownUp),
+ TimeDelta::FromMilliseconds(100));
+ HHOOK msg_hook = SetWindowsHookEx(WH_MSGFILTER,
+ MsgFilterProc,
+ NULL,
+ GetCurrentThreadId());
+ DispatcherImpl dispatcher;
+ base::RunLoop run_loop(&dispatcher);
+ run_loop.Run();
+ ASSERT_EQ(1, dispatcher.dispatch_count_);
+ UnhookWindowsHookEx(msg_hook);
+}
+
+class TestIOHandler : public MessageLoopForIO::IOHandler {
+ public:
+ TestIOHandler(const wchar_t* name, HANDLE signal, bool wait);
+
+ virtual void OnIOCompleted(MessageLoopForIO::IOContext* context,
+ DWORD bytes_transfered, DWORD error);
+
+ void Init();
+ void WaitForIO();
+ OVERLAPPED* context() { return &context_.overlapped; }
+ DWORD size() { return sizeof(buffer_); }
+
+ private:
+ char buffer_[48];
+ MessageLoopForIO::IOContext context_;
+ HANDLE signal_;
+ base::win::ScopedHandle file_;
+ bool wait_;
+};
+
+TestIOHandler::TestIOHandler(const wchar_t* name, HANDLE signal, bool wait)
+ : signal_(signal), wait_(wait) {
+ memset(buffer_, 0, sizeof(buffer_));
+ memset(&context_, 0, sizeof(context_));
+ context_.handler = this;
+
+ file_.Set(CreateFile(name, GENERIC_READ, 0, NULL, OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED, NULL));
+ EXPECT_TRUE(file_.IsValid());
+}
+
+void TestIOHandler::Init() {
+ MessageLoopForIO::current()->RegisterIOHandler(file_, this);
+
+ DWORD read;
+ EXPECT_FALSE(ReadFile(file_, buffer_, size(), &read, context()));
+ EXPECT_EQ(ERROR_IO_PENDING, GetLastError());
+ if (wait_)
+ WaitForIO();
+}
+
+void TestIOHandler::OnIOCompleted(MessageLoopForIO::IOContext* context,
+ DWORD bytes_transfered, DWORD error) {
+ ASSERT_TRUE(context == &context_);
+ ASSERT_TRUE(SetEvent(signal_));
+}
+
+void TestIOHandler::WaitForIO() {
+ EXPECT_TRUE(MessageLoopForIO::current()->WaitForIOCompletion(300, this));
+ EXPECT_TRUE(MessageLoopForIO::current()->WaitForIOCompletion(400, this));
+}
+
+void RunTest_IOHandler() {
+ base::win::ScopedHandle callback_called(CreateEvent(NULL, TRUE, FALSE, NULL));
+ ASSERT_TRUE(callback_called.IsValid());
+
+ const wchar_t* kPipeName = L"\\\\.\\pipe\\iohandler_pipe";
+ base::win::ScopedHandle server(
+ CreateNamedPipe(kPipeName, PIPE_ACCESS_OUTBOUND, 0, 1, 0, 0, 0, NULL));
+ ASSERT_TRUE(server.IsValid());
+
+ Thread thread("IOHandler test");
+ Thread::Options options;
+ options.message_loop_type = MessageLoop::TYPE_IO;
+ ASSERT_TRUE(thread.StartWithOptions(options));
+
+ MessageLoop* thread_loop = thread.message_loop();
+ ASSERT_TRUE(NULL != thread_loop);
+
+ TestIOHandler handler(kPipeName, callback_called, false);
+ thread_loop->PostTask(FROM_HERE, base::Bind(&TestIOHandler::Init,
+ base::Unretained(&handler)));
+ // Make sure the thread runs and sleeps for lack of work.
+ base::PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+
+ const char buffer[] = "Hello there!";
+ DWORD written;
+ EXPECT_TRUE(WriteFile(server, buffer, sizeof(buffer), &written, NULL));
+
+ DWORD result = WaitForSingleObject(callback_called, 1000);
+ EXPECT_EQ(WAIT_OBJECT_0, result);
+
+ thread.Stop();
+}
+
+void RunTest_WaitForIO() {
+ base::win::ScopedHandle callback1_called(
+ CreateEvent(NULL, TRUE, FALSE, NULL));
+ base::win::ScopedHandle callback2_called(
+ CreateEvent(NULL, TRUE, FALSE, NULL));
+ ASSERT_TRUE(callback1_called.IsValid());
+ ASSERT_TRUE(callback2_called.IsValid());
+
+ const wchar_t* kPipeName1 = L"\\\\.\\pipe\\iohandler_pipe1";
+ const wchar_t* kPipeName2 = L"\\\\.\\pipe\\iohandler_pipe2";
+ base::win::ScopedHandle server1(
+ CreateNamedPipe(kPipeName1, PIPE_ACCESS_OUTBOUND, 0, 1, 0, 0, 0, NULL));
+ base::win::ScopedHandle server2(
+ CreateNamedPipe(kPipeName2, PIPE_ACCESS_OUTBOUND, 0, 1, 0, 0, 0, NULL));
+ ASSERT_TRUE(server1.IsValid());
+ ASSERT_TRUE(server2.IsValid());
+
+ Thread thread("IOHandler test");
+ Thread::Options options;
+ options.message_loop_type = MessageLoop::TYPE_IO;
+ ASSERT_TRUE(thread.StartWithOptions(options));
+
+ MessageLoop* thread_loop = thread.message_loop();
+ ASSERT_TRUE(NULL != thread_loop);
+
+ TestIOHandler handler1(kPipeName1, callback1_called, false);
+ TestIOHandler handler2(kPipeName2, callback2_called, true);
+ thread_loop->PostTask(FROM_HERE, base::Bind(&TestIOHandler::Init,
+ base::Unretained(&handler1)));
+ // TODO(ajwong): Do we really need such long Sleeps in ths function?
+ // Make sure the thread runs and sleeps for lack of work.
+ TimeDelta delay = TimeDelta::FromMilliseconds(100);
+ base::PlatformThread::Sleep(delay);
+ thread_loop->PostTask(FROM_HERE, base::Bind(&TestIOHandler::Init,
+ base::Unretained(&handler2)));
+ base::PlatformThread::Sleep(delay);
+
+ // At this time handler1 is waiting to be called, and the thread is waiting
+ // on the Init method of handler2, filtering only handler2 callbacks.
+
+ const char buffer[] = "Hello there!";
+ DWORD written;
+ EXPECT_TRUE(WriteFile(server1, buffer, sizeof(buffer), &written, NULL));
+ base::PlatformThread::Sleep(2 * delay);
+ EXPECT_EQ(WAIT_TIMEOUT, WaitForSingleObject(callback1_called, 0)) <<
+ "handler1 has not been called";
+
+ EXPECT_TRUE(WriteFile(server2, buffer, sizeof(buffer), &written, NULL));
+
+ HANDLE objects[2] = { callback1_called.Get(), callback2_called.Get() };
+ DWORD result = WaitForMultipleObjects(2, objects, TRUE, 1000);
+ EXPECT_EQ(WAIT_OBJECT_0, result);
+
+ thread.Stop();
+}
+
+#endif // defined(OS_WIN)
+
+} // namespace
+
+//-----------------------------------------------------------------------------
+// Each test is run against each type of MessageLoop. That way we are sure
+// that message loops work properly in all configurations. Of course, in some
+// cases, a unit test may only be for a particular type of loop.
+
+TEST(MessageLoopTest, PostTask) {
+ RunTest_PostTask(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_PostTask(MessageLoop::TYPE_UI);
+#endif
+ RunTest_PostTask(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, PostTask_SEH) {
+ RunTest_PostTask_SEH(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_PostTask_SEH(MessageLoop::TYPE_UI);
+#endif
+ RunTest_PostTask_SEH(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, PostDelayedTask_Basic) {
+ RunTest_PostDelayedTask_Basic(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_PostDelayedTask_Basic(MessageLoop::TYPE_UI);
+#endif
+ RunTest_PostDelayedTask_Basic(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, PostDelayedTask_InDelayOrder) {
+ RunTest_PostDelayedTask_InDelayOrder(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_PostDelayedTask_InDelayOrder(MessageLoop::TYPE_UI);
+#endif
+ RunTest_PostDelayedTask_InDelayOrder(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, PostDelayedTask_InPostOrder) {
+ RunTest_PostDelayedTask_InPostOrder(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_PostDelayedTask_InPostOrder(MessageLoop::TYPE_UI);
+#endif
+ RunTest_PostDelayedTask_InPostOrder(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, PostDelayedTask_InPostOrder_2) {
+ RunTest_PostDelayedTask_InPostOrder_2(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_PostDelayedTask_InPostOrder_2(MessageLoop::TYPE_UI);
+#endif
+ RunTest_PostDelayedTask_InPostOrder_2(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, PostDelayedTask_InPostOrder_3) {
+ RunTest_PostDelayedTask_InPostOrder_3(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_PostDelayedTask_InPostOrder_3(MessageLoop::TYPE_UI);
+#endif
+ RunTest_PostDelayedTask_InPostOrder_3(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, PostDelayedTask_SharedTimer) {
+ RunTest_PostDelayedTask_SharedTimer(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_PostDelayedTask_SharedTimer(MessageLoop::TYPE_UI);
+#endif
+ RunTest_PostDelayedTask_SharedTimer(MessageLoop::TYPE_IO);
+}
+
+#if defined(OS_WIN)
+TEST(MessageLoopTest, PostDelayedTask_SharedTimer_SubPump) {
+ RunTest_PostDelayedTask_SharedTimer_SubPump();
+}
+#endif
+
+// TODO(darin): MessageLoop does not support deleting all tasks in the
+// destructor.
+// Fails, http://crbug.com/50272.
+TEST(MessageLoopTest, DISABLED_EnsureDeletion) {
+ RunTest_EnsureDeletion(MessageLoop::TYPE_DEFAULT);
+ RunTest_EnsureDeletion(MessageLoop::TYPE_UI);
+ RunTest_EnsureDeletion(MessageLoop::TYPE_IO);
+}
+
+// TODO(darin): MessageLoop does not support deleting all tasks in the
+// destructor.
+// Fails, http://crbug.com/50272.
+TEST(MessageLoopTest, DISABLED_EnsureDeletion_Chain) {
+ RunTest_EnsureDeletion_Chain(MessageLoop::TYPE_DEFAULT);
+ RunTest_EnsureDeletion_Chain(MessageLoop::TYPE_UI);
+ RunTest_EnsureDeletion_Chain(MessageLoop::TYPE_IO);
+}
+
+#if defined(OS_WIN)
+TEST(MessageLoopTest, Crasher) {
+ RunTest_Crasher(MessageLoop::TYPE_DEFAULT);
+ RunTest_Crasher(MessageLoop::TYPE_UI);
+ RunTest_Crasher(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, CrasherNasty) {
+ RunTest_CrasherNasty(MessageLoop::TYPE_DEFAULT);
+ RunTest_CrasherNasty(MessageLoop::TYPE_UI);
+ RunTest_CrasherNasty(MessageLoop::TYPE_IO);
+}
+#endif // defined(OS_WIN)
+
+TEST(MessageLoopTest, Nesting) {
+ RunTest_Nesting(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_Nesting(MessageLoop::TYPE_UI);
+#endif
+ RunTest_Nesting(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, RecursiveDenial1) {
+ RunTest_RecursiveDenial1(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_RecursiveDenial1(MessageLoop::TYPE_UI);
+#endif
+ RunTest_RecursiveDenial1(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, RecursiveDenial3) {
+ RunTest_RecursiveDenial3(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_RecursiveDenial3(MessageLoop::TYPE_UI);
+#endif
+ RunTest_RecursiveDenial3(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, RecursiveSupport1) {
+ RunTest_RecursiveSupport1(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_RecursiveSupport1(MessageLoop::TYPE_UI);
+#endif
+ RunTest_RecursiveSupport1(MessageLoop::TYPE_IO);
+}
+
+#if defined(OS_WIN)
+// This test occasionally hangs http://crbug.com/44567
+TEST(MessageLoopTest, DISABLED_RecursiveDenial2) {
+ RunTest_RecursiveDenial2(MessageLoop::TYPE_DEFAULT);
+ RunTest_RecursiveDenial2(MessageLoop::TYPE_UI);
+ RunTest_RecursiveDenial2(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, RecursiveSupport2) {
+ // This test requires a UI loop
+ RunTest_RecursiveSupport2(MessageLoop::TYPE_UI);
+}
+#endif // defined(OS_WIN)
+
+TEST(MessageLoopTest, NonNestableWithNoNesting) {
+ RunTest_NonNestableWithNoNesting(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_NonNestableWithNoNesting(MessageLoop::TYPE_UI);
+#endif
+ RunTest_NonNestableWithNoNesting(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, NonNestableInNestedLoop) {
+ RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_DEFAULT, false);
+#if !defined(OS_STARBOARD)
+ RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_UI, false);
+#endif
+ RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_IO, false);
+}
+
+TEST(MessageLoopTest, NonNestableDelayedInNestedLoop) {
+ RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_DEFAULT, true);
+#if !defined(OS_STARBOARD)
+ RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_UI, true);
+#endif
+ RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_IO, true);
+}
+
+TEST(MessageLoopTest, QuitNow) {
+ RunTest_QuitNow(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_QuitNow(MessageLoop::TYPE_UI);
+#endif
+ RunTest_QuitNow(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, RunLoopQuitTop) {
+ RunTest_RunLoopQuitTop(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_RunLoopQuitTop(MessageLoop::TYPE_UI);
+#endif
+ RunTest_RunLoopQuitTop(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, RunLoopQuitNested) {
+ RunTest_RunLoopQuitNested(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_RunLoopQuitNested(MessageLoop::TYPE_UI);
+#endif
+ RunTest_RunLoopQuitNested(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, RunLoopQuitBogus) {
+ RunTest_RunLoopQuitBogus(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_RunLoopQuitBogus(MessageLoop::TYPE_UI);
+#endif
+ RunTest_RunLoopQuitBogus(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, RunLoopQuitDeep) {
+ RunTest_RunLoopQuitDeep(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_RunLoopQuitDeep(MessageLoop::TYPE_UI);
+#endif
+ RunTest_RunLoopQuitDeep(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, RunLoopQuitOrderBefore) {
+ RunTest_RunLoopQuitOrderBefore(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_RunLoopQuitOrderBefore(MessageLoop::TYPE_UI);
+#endif
+ RunTest_RunLoopQuitOrderBefore(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, RunLoopQuitOrderDuring) {
+ RunTest_RunLoopQuitOrderDuring(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_RunLoopQuitOrderDuring(MessageLoop::TYPE_UI);
+#endif
+ RunTest_RunLoopQuitOrderDuring(MessageLoop::TYPE_IO);
+}
+
+TEST(MessageLoopTest, RunLoopQuitOrderAfter) {
+ RunTest_RunLoopQuitOrderAfter(MessageLoop::TYPE_DEFAULT);
+#if !defined(OS_STARBOARD)
+ RunTest_RunLoopQuitOrderAfter(MessageLoop::TYPE_UI);
+#endif
+ RunTest_RunLoopQuitOrderAfter(MessageLoop::TYPE_IO);
+}
+
+class DummyTaskObserver : public MessageLoop::TaskObserver {
+ public:
+ explicit DummyTaskObserver(int num_tasks)
+ : num_tasks_started_(0),
+ num_tasks_processed_(0),
+ num_tasks_(num_tasks) {}
+
+ virtual ~DummyTaskObserver() {}
+
+ virtual void WillProcessTask(TimeTicks time_posted) OVERRIDE {
+ num_tasks_started_++;
+ EXPECT_TRUE(time_posted != TimeTicks());
+ EXPECT_LE(num_tasks_started_, num_tasks_);
+ EXPECT_EQ(num_tasks_started_, num_tasks_processed_ + 1);
+ }
+
+ virtual void DidProcessTask(TimeTicks time_posted) OVERRIDE {
+ num_tasks_processed_++;
+ EXPECT_TRUE(time_posted != TimeTicks());
+ EXPECT_LE(num_tasks_started_, num_tasks_);
+ EXPECT_EQ(num_tasks_started_, num_tasks_processed_);
+ }
+
+ int num_tasks_started() const { return num_tasks_started_; }
+ int num_tasks_processed() const { return num_tasks_processed_; }
+
+ private:
+ int num_tasks_started_;
+ int num_tasks_processed_;
+ const int num_tasks_;
+
+ DISALLOW_COPY_AND_ASSIGN(DummyTaskObserver);
+};
+
+TEST(MessageLoopTest, TaskObserver) {
+ const int kNumPosts = 6;
+ DummyTaskObserver observer(kNumPosts);
+
+ MessageLoop loop;
+ loop.AddTaskObserver(&observer);
+ loop.PostTask(FROM_HERE, base::Bind(&PostNTasksThenQuit, kNumPosts));
+ loop.Run();
+ loop.RemoveTaskObserver(&observer);
+
+ EXPECT_EQ(kNumPosts, observer.num_tasks_started());
+ EXPECT_EQ(kNumPosts, observer.num_tasks_processed());
+}
+
+#if defined(OS_WIN)
+TEST(MessageLoopTest, Dispatcher) {
+ // This test requires a UI loop
+ RunTest_Dispatcher(MessageLoop::TYPE_UI);
+}
+
+TEST(MessageLoopTest, DispatcherWithMessageHook) {
+ // This test requires a UI loop
+ RunTest_DispatcherWithMessageHook(MessageLoop::TYPE_UI);
+}
+
+TEST(MessageLoopTest, IOHandler) {
+ RunTest_IOHandler();
+}
+
+TEST(MessageLoopTest, WaitForIO) {
+ RunTest_WaitForIO();
+}
+
+TEST(MessageLoopTest, HighResolutionTimer) {
+ MessageLoop loop;
+
+ const TimeDelta kFastTimer = TimeDelta::FromMilliseconds(5);
+ const TimeDelta kSlowTimer = TimeDelta::FromMilliseconds(100);
+
+ EXPECT_FALSE(loop.high_resolution_timers_enabled());
+
+ // Post a fast task to enable the high resolution timers.
+ loop.PostDelayedTask(FROM_HERE, base::Bind(&PostNTasksThenQuit, 1),
+ kFastTimer);
+ loop.Run();
+ EXPECT_TRUE(loop.high_resolution_timers_enabled());
+
+ // Post a slow task and verify high resolution timers
+ // are still enabled.
+ loop.PostDelayedTask(FROM_HERE, base::Bind(&PostNTasksThenQuit, 1),
+ kSlowTimer);
+ loop.Run();
+ EXPECT_TRUE(loop.high_resolution_timers_enabled());
+
+ // Wait for a while so that high-resolution mode elapses.
+ base::PlatformThread::Sleep(TimeDelta::FromMilliseconds(
+ MessageLoop::kHighResolutionTimerModeLeaseTimeMs));
+
+ // Post a slow task to disable the high resolution timers.
+ loop.PostDelayedTask(FROM_HERE, base::Bind(&PostNTasksThenQuit, 1),
+ kSlowTimer);
+ loop.Run();
+ EXPECT_FALSE(loop.high_resolution_timers_enabled());
+}
+
+#endif // defined(OS_WIN)
+
+#if defined(OS_POSIX) && !defined(OS_NACL)
+
+namespace {
+
+class QuitDelegate : public MessageLoopForIO::Watcher {
+ public:
+ virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {
+ MessageLoop::current()->Quit();
+ }
+ virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {
+ MessageLoop::current()->Quit();
+ }
+};
+
+// LB_SHELL platforms don't implement WatchFileDescriptor().
+// pipe() may not exist on some platforms.
+#if !defined(__LB_SHELL__) && !defined(OS_STARBOARD)
+TEST(MessageLoopTest, FileDescriptorWatcherOutlivesMessageLoop) {
+ // Simulate a MessageLoop that dies before an FileDescriptorWatcher.
+ // This could happen when people use the Singleton pattern or atexit.
+
+ // Create a file descriptor. Doesn't need to be readable or writable,
+ // as we don't need to actually get any notifications.
+ // pipe() is just the easiest way to do it.
+ int pipefds[2];
+ int err = pipe(pipefds);
+ ASSERT_EQ(0, err);
+ int fd = pipefds[1];
+ {
+ // Arrange for controller to live longer than message loop.
+ MessageLoopForIO::FileDescriptorWatcher controller;
+ {
+ MessageLoopForIO message_loop;
+
+ QuitDelegate delegate;
+ message_loop.WatchFileDescriptor(fd,
+ true, MessageLoopForIO::WATCH_WRITE, &controller, &delegate);
+ // and don't run the message loop, just destroy it.
+ }
+ }
+ if (HANDLE_EINTR(close(pipefds[0])) < 0)
+ PLOG(ERROR) << "close";
+ if (HANDLE_EINTR(close(pipefds[1])) < 0)
+ PLOG(ERROR) << "close";
+}
+
+TEST(MessageLoopTest, FileDescriptorWatcherDoubleStop) {
+ // Verify that it's ok to call StopWatchingFileDescriptor().
+ // (Errors only showed up in valgrind.)
+ int pipefds[2];
+ int err = pipe(pipefds);
+ ASSERT_EQ(0, err);
+ int fd = pipefds[1];
+ {
+ // Arrange for message loop to live longer than controller.
+ MessageLoopForIO message_loop;
+ {
+ MessageLoopForIO::FileDescriptorWatcher controller;
+
+ QuitDelegate delegate;
+ message_loop.WatchFileDescriptor(fd,
+ true, MessageLoopForIO::WATCH_WRITE, &controller, &delegate);
+ controller.StopWatchingFileDescriptor();
+ }
+ }
+ if (HANDLE_EINTR(close(pipefds[0])) < 0)
+ PLOG(ERROR) << "close";
+ if (HANDLE_EINTR(close(pipefds[1])) < 0)
+ PLOG(ERROR) << "close";
+}
+#endif
+
+} // namespace
+
+#endif // defined(OS_POSIX) && !defined(OS_NACL)
+
+namespace {
+// Inject a test point for recording the destructor calls for Closure objects
+// send to MessageLoop::PostTask(). It is awkward usage since we are trying to
+// hook the actual destruction, which is not a common operation.
+class DestructionObserverProbe :
+ public base::RefCounted<DestructionObserverProbe> {
+ public:
+ DestructionObserverProbe(bool* task_destroyed,
+ bool* destruction_observer_called)
+ : task_destroyed_(task_destroyed),
+ destruction_observer_called_(destruction_observer_called) {
+ }
+ virtual void Run() {
+ // This task should never run.
+ ADD_FAILURE();
+ }
+ private:
+ friend class base::RefCounted<DestructionObserverProbe>;
+
+ virtual ~DestructionObserverProbe() {
+ EXPECT_FALSE(*destruction_observer_called_);
+ *task_destroyed_ = true;
+ }
+
+ bool* task_destroyed_;
+ bool* destruction_observer_called_;
+};
+
+class MLDestructionObserver : public MessageLoop::DestructionObserver {
+ public:
+ MLDestructionObserver(bool* task_destroyed, bool* destruction_observer_called)
+ : task_destroyed_(task_destroyed),
+ destruction_observer_called_(destruction_observer_called),
+ task_destroyed_before_message_loop_(false) {
+ }
+ virtual void WillDestroyCurrentMessageLoop() OVERRIDE {
+ task_destroyed_before_message_loop_ = *task_destroyed_;
+ *destruction_observer_called_ = true;
+ }
+ bool task_destroyed_before_message_loop() const {
+ return task_destroyed_before_message_loop_;
+ }
+ private:
+ bool* task_destroyed_;
+ bool* destruction_observer_called_;
+ bool task_destroyed_before_message_loop_;
+};
+
+} // namespace
+
+TEST(MessageLoopTest, DestructionObserverTest) {
+ // Verify that the destruction observer gets called at the very end (after
+ // all the pending tasks have been destroyed).
+ MessageLoop* loop = new MessageLoop;
+ const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
+
+ bool task_destroyed = false;
+ bool destruction_observer_called = false;
+
+ MLDestructionObserver observer(&task_destroyed, &destruction_observer_called);
+ loop->AddDestructionObserver(&observer);
+ loop->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&DestructionObserverProbe::Run,
+ new DestructionObserverProbe(&task_destroyed,
+ &destruction_observer_called)),
+ kDelay);
+ delete loop;
+ EXPECT_TRUE(observer.task_destroyed_before_message_loop());
+ // The task should have been destroyed when we deleted the loop.
+ EXPECT_TRUE(task_destroyed);
+ EXPECT_TRUE(destruction_observer_called);
+}
+
+
+// Verify that MessageLoop sets ThreadMainTaskRunner::current() and it
+// posts tasks on that message loop.
+TEST(MessageLoopTest, ThreadMainTaskRunner) {
+ MessageLoop loop;
+
+ scoped_refptr<Foo> foo(new Foo());
+ std::string a("a");
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::Bind(
+ &Foo::Test1ConstRef, foo.get(), a));
+
+ // Post quit task;
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &MessageLoop::Quit, base::Unretained(MessageLoop::current())));
+
+ // Now kick things off
+ MessageLoop::current()->Run();
+
+ EXPECT_EQ(foo->test_count(), 1);
+ EXPECT_EQ(foo->result(), "a");
+}
+
+TEST(MessageLoopTest, IsType) {
+ MessageLoop loop(MessageLoop::TYPE_UI);
+ EXPECT_TRUE(loop.IsType(MessageLoop::TYPE_UI));
+ EXPECT_FALSE(loop.IsType(MessageLoop::TYPE_IO));
+ EXPECT_FALSE(loop.IsType(MessageLoop::TYPE_DEFAULT));
+}
+
+TEST(MessageLoopTest, RecursivePosts) {
+ // There was a bug in the MessagePumpGLib where posting tasks recursively
+ // caused the message loop to hang, due to the buffer of the internal pipe
+ // becoming full. Test all MessageLoop types to ensure this issue does not
+ // exist in other MessagePumps.
+
+ // On Linux, the pipe buffer size is 64KiB by default. The bug caused one
+ // byte accumulated in the pipe per two posts, so we should repeat 128K
+ // times to reproduce the bug.
+ const int kNumTimes = 1 << 17;
+ RunTest_RecursivePosts(MessageLoop::TYPE_DEFAULT, kNumTimes);
+#if !defined(OS_STARBOARD)
+ RunTest_RecursivePosts(MessageLoop::TYPE_UI, kNumTimes);
+#endif
+ RunTest_RecursivePosts(MessageLoop::TYPE_IO, kNumTimes);
+}
diff --git a/src/base/message_pump.cc b/src/base/message_pump.cc
new file mode 100644
index 0000000..de7c517
--- /dev/null
+++ b/src/base/message_pump.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/message_pump.h"
+
+namespace base {
+
+MessagePump::MessagePump() {
+}
+
+MessagePump::~MessagePump() {
+}
+
+} // namespace base
diff --git a/src/base/message_pump.h b/src/base/message_pump.h
new file mode 100644
index 0000000..b2e0a42
--- /dev/null
+++ b/src/base/message_pump.h
@@ -0,0 +1,129 @@
+// Copyright (c) 2012 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_MESSAGE_PUMP_H_
+#define BASE_MESSAGE_PUMP_H_
+
+#include "base/base_export.h"
+#include "base/memory/ref_counted.h"
+
+namespace base {
+
+class TimeTicks;
+
+class BASE_EXPORT MessagePump : public RefCountedThreadSafe<MessagePump> {
+ public:
+ // Please see the comments above the Run method for an illustration of how
+ // these delegate methods are used.
+ class BASE_EXPORT Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // Called from within Run in response to ScheduleWork or when the message
+ // pump would otherwise call DoDelayedWork. Returns true to indicate that
+ // work was done. DoDelayedWork will still be called if DoWork returns
+ // true, but DoIdleWork will not.
+ virtual bool DoWork() = 0;
+
+ // Called from within Run in response to ScheduleDelayedWork or when the
+ // message pump would otherwise sleep waiting for more work. Returns true
+ // to indicate that delayed work was done. DoIdleWork will not be called
+ // if DoDelayedWork returns true. Upon return |next_delayed_work_time|
+ // indicates the time when DoDelayedWork should be called again. If
+ // |next_delayed_work_time| is null (per Time::is_null), then the queue of
+ // future delayed work (timer events) is currently empty, and no additional
+ // calls to this function need to be scheduled.
+ virtual bool DoDelayedWork(TimeTicks* next_delayed_work_time) = 0;
+
+ // Called from within Run just before the message pump goes to sleep.
+ // Returns true to indicate that idle work was done.
+ virtual bool DoIdleWork() = 0;
+ };
+
+ MessagePump();
+
+ // The Run method is called to enter the message pump's run loop.
+ //
+ // Within the method, the message pump is responsible for processing native
+ // messages as well as for giving cycles to the delegate periodically. The
+ // message pump should take care to mix delegate callbacks with native
+ // message processing so neither type of event starves the other of cycles.
+ //
+ // The anatomy of a typical run loop:
+ //
+ // for (;;) {
+ // bool did_work = DoInternalWork();
+ // if (should_quit_)
+ // break;
+ //
+ // did_work |= delegate_->DoWork();
+ // if (should_quit_)
+ // break;
+ //
+ // TimeTicks next_time;
+ // did_work |= delegate_->DoDelayedWork(&next_time);
+ // if (should_quit_)
+ // break;
+ //
+ // if (did_work)
+ // continue;
+ //
+ // did_work = delegate_->DoIdleWork();
+ // if (should_quit_)
+ // break;
+ //
+ // if (did_work)
+ // continue;
+ //
+ // WaitForWork();
+ // }
+ //
+ // Here, DoInternalWork is some private method of the message pump that is
+ // responsible for dispatching the next UI message or notifying the next IO
+ // completion (for example). WaitForWork is a private method that simply
+ // blocks until there is more work of any type to do.
+ //
+ // Notice that the run loop cycles between calling DoInternalWork, DoWork,
+ // and DoDelayedWork methods. This helps ensure that none of these work
+ // queues starve the others. This is important for message pumps that are
+ // used to drive animations, for example.
+ //
+ // Notice also that after each callout to foreign code, the run loop checks
+ // to see if it should quit. The Quit method is responsible for setting this
+ // flag. No further work is done once the quit flag is set.
+ //
+ // NOTE: Care must be taken to handle Run being called again from within any
+ // of the callouts to foreign code. Native message pumps may also need to
+ // deal with other native message pumps being run outside their control
+ // (e.g., the MessageBox API on Windows pumps UI messages!). To be specific,
+ // the callouts (DoWork and DoDelayedWork) MUST still be provided even in
+ // nested sub-loops that are "seemingly" outside the control of this message
+ // pump. DoWork in particular must never be starved for time slices unless
+ // it returns false (meaning it has run out of things to do).
+ //
+ virtual void Run(Delegate* delegate) = 0;
+
+ // Quit immediately from the most recently entered run loop. This method may
+ // only be used on the thread that called Run.
+ virtual void Quit() = 0;
+
+ // Schedule a DoWork callback to happen reasonably soon. Does nothing if a
+ // DoWork callback is already scheduled. This method may be called from any
+ // thread. Once this call is made, DoWork should not be "starved" at least
+ // until it returns a value of false.
+ virtual void ScheduleWork() = 0;
+
+ // Schedule a DoDelayedWork callback to happen at the specified time,
+ // cancelling any pending DoDelayedWork callback. This method may only be
+ // used on the thread that called Run.
+ virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) = 0;
+
+ protected:
+ virtual ~MessagePump();
+ friend class RefCountedThreadSafe<MessagePump>;
+};
+
+} // namespace base
+
+#endif // BASE_MESSAGE_PUMP_H_
diff --git a/src/base/message_pump_android.cc b/src/base/message_pump_android.cc
new file mode 100644
index 0000000..f02da67
--- /dev/null
+++ b/src/base/message_pump_android.cc
@@ -0,0 +1,139 @@
+// Copyright (c) 2012 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/message_pump_android.h"
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "base/time.h"
+#include "jni/SystemMessageHandler_jni.h"
+
+using base::android::ScopedJavaLocalRef;
+
+namespace {
+
+base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >
+ g_system_message_handler_obj = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+// ----------------------------------------------------------------------------
+// Native JNI methods called by Java.
+// ----------------------------------------------------------------------------
+// This method can not move to anonymous namespace as it has been declared as
+// 'static' in system_message_handler_jni.h.
+static jboolean DoRunLoopOnce(JNIEnv* env, jobject obj, jint native_delegate) {
+ base::MessagePump::Delegate* delegate =
+ reinterpret_cast<base::MessagePump::Delegate*>(native_delegate);
+ DCHECK(delegate);
+ // This is based on MessagePumpForUI::DoRunLoop() from desktop.
+ // Note however that our system queue is handled in the java side.
+ // In desktop we inspect and process a single system message and then
+ // we call DoWork() / DoDelayedWork().
+ // On Android, the java message queue may contain messages for other handlers
+ // that will be processed before calling here again.
+ bool more_work_is_plausible = delegate->DoWork();
+
+ // This is the time when we need to do delayed work.
+ base::TimeTicks delayed_work_time;
+ more_work_is_plausible |= delegate->DoDelayedWork(&delayed_work_time);
+
+ // This is a major difference between android and other platforms: since we
+ // can't inspect it and process just one single message, instead we'll yeld
+ // the callstack, and post a message to call us back soon.
+ if (more_work_is_plausible)
+ return true;
+
+ more_work_is_plausible = delegate->DoIdleWork();
+ if (!more_work_is_plausible && !delayed_work_time.is_null()) {
+ // We only set the timer here as returning true would post a message.
+ jlong millis =
+ (delayed_work_time - base::TimeTicks::Now()).InMillisecondsRoundedUp();
+ Java_SystemMessageHandler_setDelayedTimer(env, obj, millis);
+ }
+ return more_work_is_plausible;
+}
+
+namespace base {
+
+MessagePumpForUI::MessagePumpForUI()
+ : run_loop_(NULL) {
+}
+
+MessagePumpForUI::~MessagePumpForUI() {
+}
+
+void MessagePumpForUI::Run(Delegate* delegate) {
+ NOTREACHED() << "UnitTests should rely on MessagePumpForUIStub in"
+ " test_stub_android.h";
+}
+
+void MessagePumpForUI::Start(Delegate* delegate) {
+ run_loop_ = new base::RunLoop();
+ // Since the RunLoop was just created above, BeforeRun should be guaranteed to
+ // return true (it only returns false if the RunLoop has been Quit already).
+ if (!run_loop_->BeforeRun())
+ NOTREACHED();
+
+ DCHECK(g_system_message_handler_obj.Get().is_null());
+
+ JNIEnv* env = base::android::AttachCurrentThread();
+ DCHECK(env);
+
+ g_system_message_handler_obj.Get().Reset(
+ Java_SystemMessageHandler_create(env, reinterpret_cast<jint>(delegate)));
+}
+
+void MessagePumpForUI::Quit() {
+ if (!g_system_message_handler_obj.Get().is_null()) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ DCHECK(env);
+
+ Java_SystemMessageHandler_removeTimer(env,
+ g_system_message_handler_obj.Get().obj());
+ g_system_message_handler_obj.Get().Reset();
+ }
+
+ if (run_loop_) {
+ run_loop_->AfterRun();
+ delete run_loop_;
+ run_loop_ = NULL;
+ }
+}
+
+void MessagePumpForUI::ScheduleWork() {
+ DCHECK(!g_system_message_handler_obj.Get().is_null());
+
+ JNIEnv* env = base::android::AttachCurrentThread();
+ DCHECK(env);
+
+ Java_SystemMessageHandler_setTimer(env,
+ g_system_message_handler_obj.Get().obj());
+}
+
+void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
+ DCHECK(!g_system_message_handler_obj.Get().is_null());
+
+ JNIEnv* env = base::android::AttachCurrentThread();
+ DCHECK(env);
+
+ jlong millis =
+ (delayed_work_time - base::TimeTicks::Now()).InMillisecondsRoundedUp();
+ // Note that we're truncating to milliseconds as required by the java side,
+ // even though delayed_work_time is microseconds resolution.
+ Java_SystemMessageHandler_setDelayedTimer(env,
+ g_system_message_handler_obj.Get().obj(), millis);
+}
+
+// static
+bool MessagePumpForUI::RegisterBindings(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace base
diff --git a/src/base/message_pump_android.h b/src/base/message_pump_android.h
new file mode 100644
index 0000000..5ef4bd6
--- /dev/null
+++ b/src/base/message_pump_android.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 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_MESSAGE_PUMP_ANDROID_H_
+#define BASE_MESSAGE_PUMP_ANDROID_H_
+
+#include <jni.h>
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/message_pump.h"
+
+namespace base {
+
+class RunLoop;
+class TimeTicks;
+
+// This class implements a MessagePump needed for TYPE_UI MessageLoops on
+// OS_ANDROID platform.
+class BASE_EXPORT MessagePumpForUI : public MessagePump {
+ public:
+ MessagePumpForUI();
+
+ virtual void Run(Delegate* delegate) OVERRIDE;
+ virtual void Quit() OVERRIDE;
+ virtual void ScheduleWork() OVERRIDE;
+ virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) OVERRIDE;
+
+ virtual void Start(Delegate* delegate);
+
+ static bool RegisterBindings(JNIEnv* env);
+
+ protected:
+ virtual ~MessagePumpForUI();
+
+ private:
+ base::RunLoop* run_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI);
+};
+
+} // namespace base
+
+#endif // BASE_MESSAGE_PUMP_ANDROID_H_
diff --git a/src/base/message_pump_aurax11.cc b/src/base/message_pump_aurax11.cc
new file mode 100644
index 0000000..9ba7989
--- /dev/null
+++ b/src/base/message_pump_aurax11.cc
@@ -0,0 +1,307 @@
+// Copyright (c) 2012 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/message_pump_aurax11.h"
+
+#include <glib.h>
+#include <X11/X.h>
+#include <X11/extensions/XInput2.h>
+#include <X11/XKBlib.h>
+
+#include "base/basictypes.h"
+#include "base/message_loop.h"
+
+namespace {
+
+gboolean XSourcePrepare(GSource* source, gint* timeout_ms) {
+ if (XPending(base::MessagePumpAuraX11::GetDefaultXDisplay()))
+ *timeout_ms = 0;
+ else
+ *timeout_ms = -1;
+ return FALSE;
+}
+
+gboolean XSourceCheck(GSource* source) {
+ return XPending(base::MessagePumpAuraX11::GetDefaultXDisplay());
+}
+
+gboolean XSourceDispatch(GSource* source,
+ GSourceFunc unused_func,
+ gpointer data) {
+ base::MessagePumpAuraX11* pump = static_cast<base::MessagePumpAuraX11*>(data);
+ return pump->DispatchXEvents();
+}
+
+GSourceFuncs XSourceFuncs = {
+ XSourcePrepare,
+ XSourceCheck,
+ XSourceDispatch,
+ NULL
+};
+
+// The connection is essentially a global that's accessed through a static
+// method and destroyed whenever ~MessagePumpAuraX11() is called. We do this
+// for historical reasons so user code can call
+// MessagePumpForUI::GetDefaultXDisplay() where MessagePumpForUI is a typedef
+// to whatever type in the current build.
+//
+// TODO(erg): This can be changed to something more sane like
+// MessagePumpAuraX11::Current()->display() once MessagePumpGtk goes away.
+Display* g_xdisplay = NULL;
+int g_xinput_opcode = -1;
+
+bool InitializeXInput2Internal() {
+ Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
+ if (!display)
+ return false;
+
+ int event, err;
+
+ int xiopcode;
+ if (!XQueryExtension(display, "XInputExtension", &xiopcode, &event, &err)) {
+ DVLOG(1) << "X Input extension not available.";
+ return false;
+ }
+ g_xinput_opcode = xiopcode;
+
+#if defined(USE_XI2_MT)
+ // USE_XI2_MT also defines the required XI2 minor minimum version.
+ int major = 2, minor = USE_XI2_MT;
+#else
+ int major = 2, minor = 0;
+#endif
+ if (XIQueryVersion(display, &major, &minor) == BadRequest) {
+ DVLOG(1) << "XInput2 not supported in the server.";
+ return false;
+ }
+#if defined(USE_XI2_MT)
+ if (major < 2 || (major == 2 && minor < USE_XI2_MT)) {
+ DVLOG(1) << "XI version on server is " << major << "." << minor << ". "
+ << "But 2." << USE_XI2_MT << " is required.";
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+Window FindEventTarget(const base::NativeEvent& xev) {
+ Window target = xev->xany.window;
+ if (xev->type == GenericEvent &&
+ static_cast<XIEvent*>(xev->xcookie.data)->extension == g_xinput_opcode) {
+ target = static_cast<XIDeviceEvent*>(xev->xcookie.data)->event;
+ }
+ return target;
+}
+
+bool InitializeXInput2() {
+ static bool xinput2_supported = InitializeXInput2Internal();
+ return xinput2_supported;
+}
+
+bool InitializeXkb() {
+ Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
+ if (!display)
+ return false;
+
+ int opcode, event, error;
+ int major = XkbMajorVersion;
+ int minor = XkbMinorVersion;
+ if (!XkbQueryExtension(display, &opcode, &event, &error, &major, &minor)) {
+ DVLOG(1) << "Xkb extension not available.";
+ return false;
+ }
+
+ // Ask the server not to send KeyRelease event when the user holds down a key.
+ // crbug.com/138092
+ Bool supported_return;
+ if (!XkbSetDetectableAutoRepeat(display, True, &supported_return)) {
+ DVLOG(1) << "XKB not supported in the server.";
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace base {
+
+MessagePumpAuraX11::MessagePumpAuraX11() : MessagePumpGlib(),
+ x_source_(NULL) {
+ InitializeXInput2();
+ InitializeXkb();
+ InitXSource();
+
+ // Can't put this in the initializer list because g_xdisplay may not exist
+ // until after InitXSource().
+ x_root_window_ = DefaultRootWindow(g_xdisplay);
+}
+
+// static
+Display* MessagePumpAuraX11::GetDefaultXDisplay() {
+ if (!g_xdisplay)
+ g_xdisplay = XOpenDisplay(NULL);
+ return g_xdisplay;
+}
+
+// static
+bool MessagePumpAuraX11::HasXInput2() {
+ return InitializeXInput2();
+}
+
+// static
+MessagePumpAuraX11* MessagePumpAuraX11::Current() {
+ MessageLoopForUI* loop = MessageLoopForUI::current();
+ return static_cast<MessagePumpAuraX11*>(loop->pump_ui());
+}
+
+void MessagePumpAuraX11::AddDispatcherForWindow(
+ MessagePumpDispatcher* dispatcher,
+ unsigned long xid) {
+ dispatchers_.insert(std::make_pair(xid, dispatcher));
+}
+
+void MessagePumpAuraX11::RemoveDispatcherForWindow(unsigned long xid) {
+ dispatchers_.erase(xid);
+}
+
+void MessagePumpAuraX11::AddDispatcherForRootWindow(
+ MessagePumpDispatcher* dispatcher) {
+ root_window_dispatchers_.AddObserver(dispatcher);
+}
+
+void MessagePumpAuraX11::RemoveDispatcherForRootWindow(
+ MessagePumpDispatcher* dispatcher) {
+ root_window_dispatchers_.RemoveObserver(dispatcher);
+}
+
+bool MessagePumpAuraX11::DispatchXEvents() {
+ Display* display = GetDefaultXDisplay();
+ DCHECK(display);
+ MessagePumpDispatcher* dispatcher =
+ GetDispatcher() ? GetDispatcher() : this;
+
+ // In the general case, we want to handle all pending events before running
+ // the tasks. This is what happens in the message_pump_glib case.
+ while (XPending(display)) {
+ XEvent xev;
+ XNextEvent(display, &xev);
+ if (dispatcher && ProcessXEvent(dispatcher, &xev))
+ return TRUE;
+ }
+ return TRUE;
+}
+
+void MessagePumpAuraX11::BlockUntilWindowMapped(unsigned long xid) {
+ XEvent event;
+
+ Display* display = GetDefaultXDisplay();
+ DCHECK(display);
+
+ MessagePumpDispatcher* dispatcher =
+ GetDispatcher() ? GetDispatcher() : this;
+
+ do {
+ // Block until there's a message of |event_mask| type on |w|. Then remove
+ // it from the queue and stuff it in |event|.
+ XWindowEvent(display, xid, StructureNotifyMask, &event);
+ ProcessXEvent(dispatcher, &event);
+ } while (event.type != MapNotify);
+}
+
+MessagePumpAuraX11::~MessagePumpAuraX11() {
+ g_source_destroy(x_source_);
+ g_source_unref(x_source_);
+ XCloseDisplay(g_xdisplay);
+ g_xdisplay = NULL;
+}
+
+void MessagePumpAuraX11::InitXSource() {
+ // CHECKs are to help track down crbug.com/113106.
+ CHECK(!x_source_);
+ Display* display = GetDefaultXDisplay();
+ CHECK(display) << "Unable to get connection to X server";
+ x_poll_.reset(new GPollFD());
+ CHECK(x_poll_.get());
+ x_poll_->fd = ConnectionNumber(display);
+ x_poll_->events = G_IO_IN;
+
+ x_source_ = g_source_new(&XSourceFuncs, sizeof(GSource));
+ g_source_add_poll(x_source_, x_poll_.get());
+ g_source_set_can_recurse(x_source_, TRUE);
+ g_source_set_callback(x_source_, NULL, this, NULL);
+ g_source_attach(x_source_, g_main_context_default());
+}
+
+bool MessagePumpAuraX11::ProcessXEvent(MessagePumpDispatcher* dispatcher,
+ XEvent* xev) {
+ bool should_quit = false;
+
+ bool have_cookie = false;
+ if (xev->type == GenericEvent &&
+ XGetEventData(xev->xgeneric.display, &xev->xcookie)) {
+ have_cookie = true;
+ }
+
+ if (!WillProcessXEvent(xev)) {
+ if (!dispatcher->Dispatch(xev)) {
+ should_quit = true;
+ Quit();
+ }
+ DidProcessXEvent(xev);
+ }
+
+ if (have_cookie) {
+ XFreeEventData(xev->xgeneric.display, &xev->xcookie);
+ }
+
+ return should_quit;
+}
+
+bool MessagePumpAuraX11::WillProcessXEvent(XEvent* xevent) {
+ if (!observers().might_have_observers())
+ return false;
+ ObserverListBase<MessagePumpObserver>::Iterator it(observers());
+ MessagePumpObserver* obs;
+ while ((obs = it.GetNext()) != NULL) {
+ if (obs->WillProcessEvent(xevent))
+ return true;
+ }
+ return false;
+}
+
+void MessagePumpAuraX11::DidProcessXEvent(XEvent* xevent) {
+ FOR_EACH_OBSERVER(MessagePumpObserver, observers(), DidProcessEvent(xevent));
+}
+
+MessagePumpDispatcher* MessagePumpAuraX11::GetDispatcherForXEvent(
+ const base::NativeEvent& xev) const {
+ ::Window x_window = FindEventTarget(xev);
+ DispatchersMap::const_iterator it = dispatchers_.find(x_window);
+ return it != dispatchers_.end() ? it->second : NULL;
+}
+
+bool MessagePumpAuraX11::Dispatch(const base::NativeEvent& xev) {
+ // MappingNotify events (meaning that the keyboard or pointer buttons have
+ // been remapped) aren't associated with a window; send them to all
+ // dispatchers.
+ if (xev->type == MappingNotify) {
+ for (DispatchersMap::const_iterator it = dispatchers_.begin();
+ it != dispatchers_.end(); ++it) {
+ it->second->Dispatch(xev);
+ }
+ return true;
+ }
+
+ if (FindEventTarget(xev) == x_root_window_) {
+ FOR_EACH_OBSERVER(MessagePumpDispatcher, root_window_dispatchers_,
+ Dispatch(xev));
+ return true;
+ }
+ MessagePumpDispatcher* dispatcher = GetDispatcherForXEvent(xev);
+ return dispatcher ? dispatcher->Dispatch(xev) : true;
+}
+
+} // namespace base
diff --git a/src/base/message_pump_aurax11.h b/src/base/message_pump_aurax11.h
new file mode 100644
index 0000000..486de4f
--- /dev/null
+++ b/src/base/message_pump_aurax11.h
@@ -0,0 +1,123 @@
+// Copyright (c) 2012 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_MESSAGE_PUMP_AURAX11_H
+#define BASE_MESSAGE_PUMP_AURAX11_H
+
+#include <bitset>
+#include <map>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_pump.h"
+#include "base/message_pump_glib.h"
+#include "base/message_pump_dispatcher.h"
+#include "base/message_pump_observer.h"
+#include "base/observer_list.h"
+
+// It would be nice to include the X11 headers here so that we use Window
+// instead of its typedef of unsigned long, but we can't because everything in
+// chrome includes us through base/message_loop.h, and X11's crappy #define
+// heavy headers muck up half of chrome.
+
+typedef struct _GPollFD GPollFD;
+typedef struct _GSource GSource;
+typedef struct _XDisplay Display;
+
+namespace base {
+
+// This class implements a message-pump for dispatching X events.
+//
+// If there's a current dispatcher given through RunWithDispatcher(), that
+// dispatcher receives events. Otherwise, we route to messages to dispatchers
+// who have subscribed to messages from a specific X11 window.
+class BASE_EXPORT MessagePumpAuraX11 : public MessagePumpGlib,
+ public MessagePumpDispatcher {
+ public:
+ MessagePumpAuraX11();
+
+ // Returns default X Display.
+ static Display* GetDefaultXDisplay();
+
+ // Returns true if the system supports XINPUT2.
+ static bool HasXInput2();
+
+ // Returns the UI message pump.
+ static MessagePumpAuraX11* Current();
+
+ // Adds/Removes |dispatcher| for the |xid|. This will route all messages from
+ // the window |xid| to |dispatcher.
+ void AddDispatcherForWindow(MessagePumpDispatcher* dispatcher,
+ unsigned long xid);
+ void RemoveDispatcherForWindow(unsigned long xid);
+
+ // Adds/Removes |dispatcher| to receive all events sent to the X root
+ // window. A root window can have multiple dispatchers, and events on root
+ // windows will be dispatched to all.
+ void AddDispatcherForRootWindow(MessagePumpDispatcher* dispatcher);
+ void RemoveDispatcherForRootWindow(MessagePumpDispatcher* dispatcher);
+
+ // Internal function. Called by the glib source dispatch function. Processes
+ // all available X events.
+ bool DispatchXEvents();
+
+ // Blocks on the X11 event queue until we receive notification from the
+ // xserver that |w| has been mapped; StructureNotifyMask events on |w| are
+ // pulled out from the queue and dispatched out of order.
+ //
+ // For those that know X11, this is really a wrapper around XWindowEvent
+ // which still makes sure the preempted event is dispatched instead of
+ // dropped on the floor. This method exists because mapping a window is
+ // asynchronous (and we receive an XEvent when mapped), while there are also
+ // functions which require a mapped window.
+ void BlockUntilWindowMapped(unsigned long xid);
+
+ protected:
+ virtual ~MessagePumpAuraX11();
+
+ private:
+ typedef std::map<unsigned long, MessagePumpDispatcher*> DispatchersMap;
+
+ // Initializes the glib event source for X.
+ void InitXSource();
+
+ // Dispatches the XEvent and returns true if we should exit the current loop
+ // of message processing.
+ bool ProcessXEvent(MessagePumpDispatcher* dispatcher, XEvent* event);
+
+ // Sends the event to the observers. If an observer returns true, then it does
+ // not send the event to any other observers and returns true. Returns false
+ // if no observer returns true.
+ bool WillProcessXEvent(XEvent* xevent);
+ void DidProcessXEvent(XEvent* xevent);
+
+ // Returns the Dispatcher based on the event's target window.
+ MessagePumpDispatcher* GetDispatcherForXEvent(
+ const base::NativeEvent& xev) const;
+
+ // Overridden from MessagePumpDispatcher:
+ virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE;
+
+ // The event source for X events.
+ GSource* x_source_;
+
+ // The poll attached to |x_source_|.
+ scoped_ptr<GPollFD> x_poll_;
+
+ DispatchersMap dispatchers_;
+
+ // Dispatch calls can cause addition of new dispatchers as we iterate
+ // through them. Use ObserverList to ensure the iterator remains valid across
+ // additions.
+ ObserverList<MessagePumpDispatcher> root_window_dispatchers_;
+
+ unsigned long x_root_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpAuraX11);
+};
+
+typedef MessagePumpAuraX11 MessagePumpForUI;
+
+} // namespace base
+
+#endif // BASE_MESSAGE_PUMP_AURAX11_H
diff --git a/src/base/message_pump_default.cc b/src/base/message_pump_default.cc
new file mode 100644
index 0000000..649e2aa
--- /dev/null
+++ b/src/base/message_pump_default.cc
@@ -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/message_pump_default.h"
+
+#include "base/logging.h"
+#include "base/threading/thread_restrictions.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_nsautorelease_pool.h"
+#endif
+
+namespace base {
+
+MessagePumpDefault::MessagePumpDefault()
+ : keep_running_(true),
+ event_(false, false) {
+}
+
+void MessagePumpDefault::Run(Delegate* delegate) {
+ DCHECK(keep_running_) << "Quit must have been called outside of Run!";
+
+ for (;;) {
+#if defined(OS_MACOSX)
+ mac::ScopedNSAutoreleasePool autorelease_pool;
+#endif
+
+ bool did_work = delegate->DoWork();
+ if (!keep_running_)
+ break;
+
+ did_work |= delegate->DoDelayedWork(&delayed_work_time_);
+ if (!keep_running_)
+ break;
+
+ if (did_work)
+ continue;
+
+ did_work = delegate->DoIdleWork();
+ if (!keep_running_)
+ break;
+
+ if (did_work)
+ continue;
+
+ base::ThreadRestrictions::ScopedAllowWait allow_wait;
+ if (delayed_work_time_.is_null()) {
+ event_.Wait();
+ } else {
+ TimeDelta delay = delayed_work_time_ - TimeTicks::Now();
+ if (delay > TimeDelta()) {
+ event_.TimedWait(delay);
+ } else {
+ // It looks like delayed_work_time_ indicates a time in the past, so we
+ // need to call DoDelayedWork now.
+ delayed_work_time_ = TimeTicks();
+ }
+ }
+ // Since event_ is auto-reset, we don't need to do anything special here
+ // other than service each delegate method.
+ }
+
+ keep_running_ = true;
+}
+
+void MessagePumpDefault::Quit() {
+ keep_running_ = false;
+}
+
+void MessagePumpDefault::ScheduleWork() {
+ // Since this can be called on any thread, we need to ensure that our Run
+ // loop wakes up.
+ event_.Signal();
+}
+
+void MessagePumpDefault::ScheduleDelayedWork(
+ const TimeTicks& delayed_work_time) {
+ // We know that we can't be blocked on Wait right now since this method can
+ // only be called on the same thread as Run, so we only need to update our
+ // record of how long to sleep when we do sleep.
+ delayed_work_time_ = delayed_work_time;
+}
+
+} // namespace base
diff --git a/src/base/message_pump_default.h b/src/base/message_pump_default.h
new file mode 100644
index 0000000..6deec99
--- /dev/null
+++ b/src/base/message_pump_default.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2012 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_MESSAGE_PUMP_DEFAULT_H_
+#define BASE_MESSAGE_PUMP_DEFAULT_H_
+
+#include "base/message_pump.h"
+#include "base/time.h"
+#include "base/synchronization/waitable_event.h"
+
+namespace base {
+
+class MessagePumpDefault : public MessagePump {
+ public:
+ MessagePumpDefault();
+
+ // MessagePump methods:
+ virtual void Run(Delegate* delegate) OVERRIDE;
+ virtual void Quit() OVERRIDE;
+ virtual void ScheduleWork() OVERRIDE;
+ virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) OVERRIDE;
+
+ protected:
+ virtual ~MessagePumpDefault() {}
+
+ private:
+ // This flag is set to false when Run should return.
+ bool keep_running_;
+
+ // Used to sleep until there is more work to do.
+ WaitableEvent event_;
+
+ // The time at which we should call DoDelayedWork.
+ TimeTicks delayed_work_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpDefault);
+};
+
+} // namespace base
+
+#endif // BASE_MESSAGE_PUMP_DEFAULT_H_
diff --git a/src/base/message_pump_dispatcher.h b/src/base/message_pump_dispatcher.h
new file mode 100644
index 0000000..c3ccf3c
--- /dev/null
+++ b/src/base/message_pump_dispatcher.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 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_MESSAGE_PUMP_DISPATCHER_H
+#define BASE_MESSAGE_PUMP_DISPATCHER_H
+
+#include "base/base_export.h"
+#include "base/event_types.h"
+
+namespace base {
+
+// Dispatcher is used during a nested invocation of Run to dispatch events when
+// |RunLoop(dispatcher).Run()| is used. If |RunLoop().Run()| is invoked,
+// MessageLoop does not dispatch events (or invoke TranslateMessage), rather
+// every message is passed to Dispatcher's Dispatch method for dispatch. It is
+// up to the Dispatcher whether or not to dispatch the event.
+//
+// The nested loop is exited by either posting a quit, or returning false
+// from Dispatch.
+class BASE_EXPORT MessagePumpDispatcher {
+ public:
+ virtual ~MessagePumpDispatcher() {}
+
+ // Dispatches the event. If true is returned processing continues as
+ // normal. If false is returned, the nested loop exits immediately.
+ virtual bool Dispatch(const NativeEvent& event) = 0;
+};
+
+} // namespace base
+
+#endif // BASE_MESSAGE_PUMP_DISPATCHER_H
diff --git a/src/base/message_pump_glib.cc b/src/base/message_pump_glib.cc
new file mode 100644
index 0000000..7436558
--- /dev/null
+++ b/src/base/message_pump_glib.cc
@@ -0,0 +1,335 @@
+// Copyright (c) 2012 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/message_pump_glib.h"
+
+#include <fcntl.h>
+#include <math.h>
+
+#include <glib.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/threading/platform_thread.h"
+
+namespace {
+
+// Return a timeout suitable for the glib loop, -1 to block forever,
+// 0 to return right away, or a timeout in milliseconds from now.
+int GetTimeIntervalMilliseconds(const base::TimeTicks& from) {
+ if (from.is_null())
+ return -1;
+
+ // Be careful here. TimeDelta has a precision of microseconds, but we want a
+ // value in milliseconds. If there are 5.5ms left, should the delay be 5 or
+ // 6? It should be 6 to avoid executing delayed work too early.
+ int delay = static_cast<int>(
+ ceil((from - base::TimeTicks::Now()).InMillisecondsF()));
+
+ // If this value is negative, then we need to run delayed work soon.
+ return delay < 0 ? 0 : delay;
+}
+
+// A brief refresher on GLib:
+// GLib sources have four callbacks: Prepare, Check, Dispatch and Finalize.
+// On each iteration of the GLib pump, it calls each source's Prepare function.
+// This function should return TRUE if it wants GLib to call its Dispatch, and
+// FALSE otherwise. It can also set a timeout in this case for the next time
+// Prepare should be called again (it may be called sooner).
+// After the Prepare calls, GLib does a poll to check for events from the
+// system. File descriptors can be attached to the sources. The poll may block
+// if none of the Prepare calls returned TRUE. It will block indefinitely, or
+// by the minimum time returned by a source in Prepare.
+// After the poll, GLib calls Check for each source that returned FALSE
+// from Prepare. The return value of Check has the same meaning as for Prepare,
+// making Check a second chance to tell GLib we are ready for Dispatch.
+// Finally, GLib calls Dispatch for each source that is ready. If Dispatch
+// returns FALSE, GLib will destroy the source. Dispatch calls may be recursive
+// (i.e., you can call Run from them), but Prepare and Check cannot.
+// Finalize is called when the source is destroyed.
+// NOTE: It is common for subsytems to want to process pending events while
+// doing intensive work, for example the flash plugin. They usually use the
+// following pattern (recommended by the GTK docs):
+// while (gtk_events_pending()) {
+// gtk_main_iteration();
+// }
+//
+// gtk_events_pending just calls g_main_context_pending, which does the
+// following:
+// - Call prepare on all the sources.
+// - Do the poll with a timeout of 0 (not blocking).
+// - Call check on all the sources.
+// - *Does not* call dispatch on the sources.
+// - Return true if any of prepare() or check() returned true.
+//
+// gtk_main_iteration just calls g_main_context_iteration, which does the whole
+// thing, respecting the timeout for the poll (and block, although it is
+// expected not to if gtk_events_pending returned true), and call dispatch.
+//
+// Thus it is important to only return true from prepare or check if we
+// actually have events or work to do. We also need to make sure we keep
+// internal state consistent so that if prepare/check return true when called
+// from gtk_events_pending, they will still return true when called right
+// after, from gtk_main_iteration.
+//
+// For the GLib pump we try to follow the Windows UI pump model:
+// - Whenever we receive a wakeup event or the timer for delayed work expires,
+// we run DoWork and/or DoDelayedWork. That part will also run in the other
+// event pumps.
+// - We also run DoWork, DoDelayedWork, and possibly DoIdleWork in the main
+// loop, around event handling.
+
+struct WorkSource : public GSource {
+ base::MessagePumpGlib* pump;
+};
+
+gboolean WorkSourcePrepare(GSource* source,
+ gint* timeout_ms) {
+ *timeout_ms = static_cast<WorkSource*>(source)->pump->HandlePrepare();
+ // We always return FALSE, so that our timeout is honored. If we were
+ // to return TRUE, the timeout would be considered to be 0 and the poll
+ // would never block. Once the poll is finished, Check will be called.
+ return FALSE;
+}
+
+gboolean WorkSourceCheck(GSource* source) {
+ // Only return TRUE if Dispatch should be called.
+ return static_cast<WorkSource*>(source)->pump->HandleCheck();
+}
+
+gboolean WorkSourceDispatch(GSource* source,
+ GSourceFunc unused_func,
+ gpointer unused_data) {
+
+ static_cast<WorkSource*>(source)->pump->HandleDispatch();
+ // Always return TRUE so our source stays registered.
+ return TRUE;
+}
+
+// I wish these could be const, but g_source_new wants non-const.
+GSourceFuncs WorkSourceFuncs = {
+ WorkSourcePrepare,
+ WorkSourceCheck,
+ WorkSourceDispatch,
+ NULL
+};
+
+} // namespace
+
+
+namespace base {
+
+struct MessagePumpGlib::RunState {
+ Delegate* delegate;
+ MessagePumpDispatcher* dispatcher;
+
+ // Used to flag that the current Run() invocation should return ASAP.
+ bool should_quit;
+
+ // Used to count how many Run() invocations are on the stack.
+ int run_depth;
+
+ // This keeps the state of whether the pump got signaled that there was new
+ // work to be done. Since we eat the message on the wake up pipe as soon as
+ // we get it, we keep that state here to stay consistent.
+ bool has_work;
+};
+
+MessagePumpGlib::MessagePumpGlib()
+ : state_(NULL),
+ context_(g_main_context_default()),
+ wakeup_gpollfd_(new GPollFD) {
+ // Create our wakeup pipe, which is used to flag when work was scheduled.
+ int fds[2];
+ int ret = pipe(fds);
+ DCHECK_EQ(ret, 0);
+ (void)ret; // Prevent warning in release mode.
+
+ wakeup_pipe_read_ = fds[0];
+ wakeup_pipe_write_ = fds[1];
+ wakeup_gpollfd_->fd = wakeup_pipe_read_;
+ wakeup_gpollfd_->events = G_IO_IN;
+
+ work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource));
+ static_cast<WorkSource*>(work_source_)->pump = this;
+ g_source_add_poll(work_source_, wakeup_gpollfd_.get());
+ // Use a low priority so that we let other events in the queue go first.
+ g_source_set_priority(work_source_, G_PRIORITY_DEFAULT_IDLE);
+ // This is needed to allow Run calls inside Dispatch.
+ g_source_set_can_recurse(work_source_, TRUE);
+ g_source_attach(work_source_, context_);
+}
+
+void MessagePumpGlib::RunWithDispatcher(Delegate* delegate,
+ MessagePumpDispatcher* dispatcher) {
+#ifndef NDEBUG
+ // Make sure we only run this on one thread. X/GTK only has one message pump
+ // so we can only have one UI loop per process.
+ static base::PlatformThreadId thread_id = base::PlatformThread::CurrentId();
+ DCHECK(thread_id == base::PlatformThread::CurrentId()) <<
+ "Running MessagePumpGlib on two different threads; "
+ "this is unsupported by GLib!";
+#endif
+
+ RunState state;
+ state.delegate = delegate;
+ state.dispatcher = dispatcher;
+ state.should_quit = false;
+ state.run_depth = state_ ? state_->run_depth + 1 : 1;
+ state.has_work = false;
+
+ RunState* previous_state = state_;
+ state_ = &state;
+
+ // We really only do a single task for each iteration of the loop. If we
+ // have done something, assume there is likely something more to do. This
+ // will mean that we don't block on the message pump until there was nothing
+ // more to do. We also set this to true to make sure not to block on the
+ // first iteration of the loop, so RunUntilIdle() works correctly.
+ bool more_work_is_plausible = true;
+
+ // We run our own loop instead of using g_main_loop_quit in one of the
+ // callbacks. This is so we only quit our own loops, and we don't quit
+ // nested loops run by others. TODO(deanm): Is this what we want?
+ for (;;) {
+ // Don't block if we think we have more work to do.
+ bool block = !more_work_is_plausible;
+
+ more_work_is_plausible = g_main_context_iteration(context_, block);
+ if (state_->should_quit)
+ break;
+
+ more_work_is_plausible |= state_->delegate->DoWork();
+ if (state_->should_quit)
+ break;
+
+ more_work_is_plausible |=
+ state_->delegate->DoDelayedWork(&delayed_work_time_);
+ if (state_->should_quit)
+ break;
+
+ if (more_work_is_plausible)
+ continue;
+
+ more_work_is_plausible = state_->delegate->DoIdleWork();
+ if (state_->should_quit)
+ break;
+ }
+
+ state_ = previous_state;
+}
+
+// Return the timeout we want passed to poll.
+int MessagePumpGlib::HandlePrepare() {
+ // We know we have work, but we haven't called HandleDispatch yet. Don't let
+ // the pump block so that we can do some processing.
+ if (state_ && // state_ may be null during tests.
+ state_->has_work)
+ return 0;
+
+ // We don't think we have work to do, but make sure not to block
+ // longer than the next time we need to run delayed work.
+ return GetTimeIntervalMilliseconds(delayed_work_time_);
+}
+
+bool MessagePumpGlib::HandleCheck() {
+ if (!state_) // state_ may be null during tests.
+ return false;
+
+ // We usually have a single message on the wakeup pipe, since we are only
+ // signaled when the queue went from empty to non-empty, but there can be
+ // two messages if a task posted a task, hence we read at most two bytes.
+ // The glib poll will tell us whether there was data, so this read
+ // shouldn't block.
+ if (wakeup_gpollfd_->revents & G_IO_IN) {
+ char msg[2];
+ const int num_bytes = HANDLE_EINTR(read(wakeup_pipe_read_, msg, 2));
+ if (num_bytes < 1) {
+ NOTREACHED() << "Error reading from the wakeup pipe.";
+ }
+ DCHECK((num_bytes == 1 && msg[0] == '!') ||
+ (num_bytes == 2 && msg[0] == '!' && msg[1] == '!'));
+ // Since we ate the message, we need to record that we have more work,
+ // because HandleCheck() may be called without HandleDispatch being called
+ // afterwards.
+ state_->has_work = true;
+ }
+
+ if (state_->has_work)
+ return true;
+
+ if (GetTimeIntervalMilliseconds(delayed_work_time_) == 0) {
+ // The timer has expired. That condition will stay true until we process
+ // that delayed work, so we don't need to record this differently.
+ return true;
+ }
+
+ return false;
+}
+
+void MessagePumpGlib::HandleDispatch() {
+ state_->has_work = false;
+ if (state_->delegate->DoWork()) {
+ // NOTE: on Windows at this point we would call ScheduleWork (see
+ // MessagePumpGlib::HandleWorkMessage in message_pump_win.cc). But here,
+ // instead of posting a message on the wakeup pipe, we can avoid the
+ // syscalls and just signal that we have more work.
+ state_->has_work = true;
+ }
+
+ if (state_->should_quit)
+ return;
+
+ state_->delegate->DoDelayedWork(&delayed_work_time_);
+}
+
+void MessagePumpGlib::AddObserver(MessagePumpObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void MessagePumpGlib::RemoveObserver(MessagePumpObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void MessagePumpGlib::Run(Delegate* delegate) {
+ RunWithDispatcher(delegate, NULL);
+}
+
+void MessagePumpGlib::Quit() {
+ if (state_) {
+ state_->should_quit = true;
+ } else {
+ NOTREACHED() << "Quit called outside Run!";
+ }
+}
+
+void MessagePumpGlib::ScheduleWork() {
+ // This can be called on any thread, so we don't want to touch any state
+ // variables as we would then need locks all over. This ensures that if
+ // we are sleeping in a poll that we will wake up.
+ char msg = '!';
+ if (HANDLE_EINTR(write(wakeup_pipe_write_, &msg, 1)) != 1) {
+ NOTREACHED() << "Could not write to the UI message loop wakeup pipe!";
+ }
+}
+
+void MessagePumpGlib::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
+ // We need to wake up the loop in case the poll timeout needs to be
+ // adjusted. This will cause us to try to do work, but that's ok.
+ delayed_work_time_ = delayed_work_time;
+ ScheduleWork();
+}
+
+MessagePumpGlib::~MessagePumpGlib() {
+ g_source_destroy(work_source_);
+ g_source_unref(work_source_);
+ close(wakeup_pipe_read_);
+ close(wakeup_pipe_write_);
+}
+
+MessagePumpDispatcher* MessagePumpGlib::GetDispatcher() {
+ return state_ ? state_->dispatcher : NULL;
+}
+
+} // namespace base
diff --git a/src/base/message_pump_glib.h b/src/base/message_pump_glib.h
new file mode 100644
index 0000000..48afb55
--- /dev/null
+++ b/src/base/message_pump_glib.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2012 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_MESSAGE_PUMP_GLIB_H_
+#define BASE_MESSAGE_PUMP_GLIB_H_
+
+#include "base/base_export.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_pump.h"
+#include "base/observer_list.h"
+#include "base/time.h"
+
+typedef struct _GMainContext GMainContext;
+typedef struct _GPollFD GPollFD;
+typedef struct _GSource GSource;
+
+namespace base {
+
+// MessagePumpObserver is notified prior to an event being dispatched. As
+// Observers are notified of every change, they have to be FAST! The platform
+// specific implementation of the class is in message_pump_gtk/message_pump_x.
+class MessagePumpObserver;
+
+// MessagePumpDispatcher is used during a nested invocation of Run to dispatch
+// events. If Run is invoked with a non-NULL MessagePumpDispatcher, MessageLoop
+// does not dispatch events (or invoke gtk_main_do_event), rather every event is
+// passed to Dispatcher's Dispatch method for dispatch. It is up to the
+// Dispatcher to dispatch, or not, the event. The platform specific
+// implementation of the class is in message_pump_gtk/message_pump_x.
+class MessagePumpDispatcher;
+
+// This class implements a base MessagePump needed for TYPE_UI MessageLoops on
+// platforms using GLib.
+class BASE_EXPORT MessagePumpGlib : public MessagePump {
+ public:
+ MessagePumpGlib();
+
+ // Like MessagePump::Run, but events are routed through dispatcher.
+ virtual void RunWithDispatcher(Delegate* delegate,
+ MessagePumpDispatcher* dispatcher);
+
+ // Internal methods used for processing the pump callbacks. They are
+ // public for simplicity but should not be used directly. HandlePrepare
+ // is called during the prepare step of glib, and returns a timeout that
+ // will be passed to the poll. HandleCheck is called after the poll
+ // has completed, and returns whether or not HandleDispatch should be called.
+ // HandleDispatch is called if HandleCheck returned true.
+ int HandlePrepare();
+ bool HandleCheck();
+ void HandleDispatch();
+
+ // Adds an Observer, which will start receiving notifications immediately.
+ void AddObserver(MessagePumpObserver* observer);
+
+ // Removes an Observer. It is safe to call this method while an Observer is
+ // receiving a notification callback.
+ void RemoveObserver(MessagePumpObserver* observer);
+
+ // Overridden from MessagePump:
+ virtual void Run(Delegate* delegate) OVERRIDE;
+ virtual void Quit() OVERRIDE;
+ virtual void ScheduleWork() OVERRIDE;
+ virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) OVERRIDE;
+
+ protected:
+ virtual ~MessagePumpGlib();
+
+ // Returns the dispatcher for the current run state (|state_->dispatcher|).
+ MessagePumpDispatcher* GetDispatcher();
+
+ ObserverList<MessagePumpObserver>& observers() { return observers_; }
+
+ private:
+ // We may make recursive calls to Run, so we save state that needs to be
+ // separate between them in this structure type.
+ struct RunState;
+
+ RunState* state_;
+
+ // This is a GLib structure that we can add event sources to. We use the
+ // default GLib context, which is the one to which all GTK events are
+ // dispatched.
+ GMainContext* context_;
+
+ // This is the time when we need to do delayed work.
+ TimeTicks delayed_work_time_;
+
+ // The work source. It is shared by all calls to Run and destroyed when
+ // the message pump is destroyed.
+ GSource* work_source_;
+
+ // We use a wakeup pipe to make sure we'll get out of the glib polling phase
+ // when another thread has scheduled us to do some work. There is a glib
+ // mechanism g_main_context_wakeup, but this won't guarantee that our event's
+ // Dispatch() will be called.
+ int wakeup_pipe_read_;
+ int wakeup_pipe_write_;
+ // Use a scoped_ptr to avoid needing the definition of GPollFD in the header.
+ scoped_ptr<GPollFD> wakeup_gpollfd_;
+
+ // List of observers.
+ ObserverList<MessagePumpObserver> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpGlib);
+};
+
+} // namespace base
+
+#endif // BASE_MESSAGE_PUMP_GLIB_H_
diff --git a/src/base/message_pump_glib_unittest.cc b/src/base/message_pump_glib_unittest.cc
new file mode 100644
index 0000000..e23cc1e
--- /dev/null
+++ b/src/base/message_pump_glib_unittest.cc
@@ -0,0 +1,577 @@
+// Copyright (c) 2012 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/message_pump_glib.h"
+
+#include <glib.h>
+#include <math.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(TOOLKIT_GTK)
+#include <gtk/gtk.h>
+#endif
+
+namespace {
+
+// This class injects dummy "events" into the GLib loop. When "handled" these
+// events can run tasks. This is intended to mock gtk events (the corresponding
+// GLib source runs at the same priority).
+class EventInjector {
+ public:
+ EventInjector() : processed_events_(0) {
+ source_ = static_cast<Source*>(g_source_new(&SourceFuncs, sizeof(Source)));
+ source_->injector = this;
+ g_source_attach(source_, NULL);
+ g_source_set_can_recurse(source_, TRUE);
+ }
+
+ ~EventInjector() {
+ g_source_destroy(source_);
+ g_source_unref(source_);
+ }
+
+ int HandlePrepare() {
+ // If the queue is empty, block.
+ if (events_.empty())
+ return -1;
+ base::TimeDelta delta = events_[0].time - base::Time::NowFromSystemTime();
+ return std::max(0, static_cast<int>(ceil(delta.InMillisecondsF())));
+ }
+
+ bool HandleCheck() {
+ if (events_.empty())
+ return false;
+ return events_[0].time <= base::Time::NowFromSystemTime();
+ }
+
+ void HandleDispatch() {
+ if (events_.empty())
+ return;
+ Event event = events_[0];
+ events_.erase(events_.begin());
+ ++processed_events_;
+ if (!event.callback.is_null())
+ event.callback.Run();
+ else if (!event.task.is_null())
+ event.task.Run();
+ }
+
+ // Adds an event to the queue. When "handled", executes |callback|.
+ // delay_ms is relative to the last event if any, or to Now() otherwise.
+ void AddEvent(int delay_ms, const base::Closure& callback) {
+ AddEventHelper(delay_ms, callback, base::Closure());
+ }
+
+ void AddDummyEvent(int delay_ms) {
+ AddEventHelper(delay_ms, base::Closure(), base::Closure());
+ }
+
+ void AddEventAsTask(int delay_ms, const base::Closure& task) {
+ AddEventHelper(delay_ms, base::Closure(), task);
+ }
+
+ void Reset() {
+ processed_events_ = 0;
+ events_.clear();
+ }
+
+ int processed_events() const { return processed_events_; }
+
+ private:
+ struct Event {
+ base::Time time;
+ base::Closure callback;
+ base::Closure task;
+ };
+
+ struct Source : public GSource {
+ EventInjector* injector;
+ };
+
+ void AddEventHelper(
+ int delay_ms, const base::Closure& callback, const base::Closure& task) {
+ base::Time last_time;
+ if (!events_.empty())
+ last_time = (events_.end()-1)->time;
+ else
+ last_time = base::Time::NowFromSystemTime();
+
+ base::Time future = last_time + base::TimeDelta::FromMilliseconds(delay_ms);
+ EventInjector::Event event = {future, callback, task};
+ events_.push_back(event);
+ }
+
+ static gboolean Prepare(GSource* source, gint* timeout_ms) {
+ *timeout_ms = static_cast<Source*>(source)->injector->HandlePrepare();
+ return FALSE;
+ }
+
+ static gboolean Check(GSource* source) {
+ return static_cast<Source*>(source)->injector->HandleCheck();
+ }
+
+ static gboolean Dispatch(GSource* source,
+ GSourceFunc unused_func,
+ gpointer unused_data) {
+ static_cast<Source*>(source)->injector->HandleDispatch();
+ return TRUE;
+ }
+
+ Source* source_;
+ std::vector<Event> events_;
+ int processed_events_;
+ static GSourceFuncs SourceFuncs;
+ DISALLOW_COPY_AND_ASSIGN(EventInjector);
+};
+
+GSourceFuncs EventInjector::SourceFuncs = {
+ EventInjector::Prepare,
+ EventInjector::Check,
+ EventInjector::Dispatch,
+ NULL
+};
+
+void IncrementInt(int *value) {
+ ++*value;
+}
+
+// Checks how many events have been processed by the injector.
+void ExpectProcessedEvents(EventInjector* injector, int count) {
+ EXPECT_EQ(injector->processed_events(), count);
+}
+
+// Posts a task on the current message loop.
+void PostMessageLoopTask(const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ MessageLoop::current()->PostTask(from_here, task);
+}
+
+// Test fixture.
+class MessagePumpGLibTest : public testing::Test {
+ public:
+ MessagePumpGLibTest() : loop_(NULL), injector_(NULL) { }
+
+ // Overridden from testing::Test:
+ virtual void SetUp() OVERRIDE {
+ loop_ = new MessageLoop(MessageLoop::TYPE_UI);
+ injector_ = new EventInjector();
+ }
+ virtual void TearDown() OVERRIDE {
+ delete injector_;
+ injector_ = NULL;
+ delete loop_;
+ loop_ = NULL;
+ }
+
+ MessageLoop* loop() const { return loop_; }
+ EventInjector* injector() const { return injector_; }
+
+ private:
+ MessageLoop* loop_;
+ EventInjector* injector_;
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpGLibTest);
+};
+
+} // namespace
+
+TEST_F(MessagePumpGLibTest, TestQuit) {
+ // Checks that Quit works and that the basic infrastructure is working.
+
+ // Quit from a task
+ loop()->PostTask(FROM_HERE, MessageLoop::QuitClosure());
+ loop()->Run();
+ EXPECT_EQ(0, injector()->processed_events());
+
+ injector()->Reset();
+ // Quit from an event
+ injector()->AddEvent(0, MessageLoop::QuitClosure());
+ loop()->Run();
+ EXPECT_EQ(1, injector()->processed_events());
+}
+
+TEST_F(MessagePumpGLibTest, TestEventTaskInterleave) {
+ // Checks that tasks posted by events are executed before the next event if
+ // the posted task queue is empty.
+ // MessageLoop doesn't make strong guarantees that it is the case, but the
+ // current implementation ensures it and the tests below rely on it.
+ // If changes cause this test to fail, it is reasonable to change it, but
+ // TestWorkWhileWaitingForEvents and TestEventsWhileWaitingForWork have to be
+ // changed accordingly, otherwise they can become flaky.
+ injector()->AddEventAsTask(0, base::Bind(&base::DoNothing));
+ base::Closure check_task =
+ base::Bind(&ExpectProcessedEvents, base::Unretained(injector()), 2);
+ base::Closure posted_task =
+ base::Bind(&PostMessageLoopTask, FROM_HERE, check_task);
+ injector()->AddEventAsTask(0, posted_task);
+ injector()->AddEventAsTask(0, base::Bind(&base::DoNothing));
+ injector()->AddEvent(0, MessageLoop::QuitClosure());
+ loop()->Run();
+ EXPECT_EQ(4, injector()->processed_events());
+
+ injector()->Reset();
+ injector()->AddEventAsTask(0, base::Bind(&base::DoNothing));
+ check_task =
+ base::Bind(&ExpectProcessedEvents, base::Unretained(injector()), 2);
+ posted_task = base::Bind(&PostMessageLoopTask, FROM_HERE, check_task);
+ injector()->AddEventAsTask(0, posted_task);
+ injector()->AddEventAsTask(10, base::Bind(&base::DoNothing));
+ injector()->AddEvent(0, MessageLoop::QuitClosure());
+ loop()->Run();
+ EXPECT_EQ(4, injector()->processed_events());
+}
+
+TEST_F(MessagePumpGLibTest, TestWorkWhileWaitingForEvents) {
+ int task_count = 0;
+ // Tests that we process tasks while waiting for new events.
+ // The event queue is empty at first.
+ for (int i = 0; i < 10; ++i) {
+ loop()->PostTask(FROM_HERE, base::Bind(&IncrementInt, &task_count));
+ }
+ // After all the previous tasks have executed, enqueue an event that will
+ // quit.
+ loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&EventInjector::AddEvent, base::Unretained(injector()), 0,
+ MessageLoop::QuitClosure()));
+ loop()->Run();
+ ASSERT_EQ(10, task_count);
+ EXPECT_EQ(1, injector()->processed_events());
+
+ // Tests that we process delayed tasks while waiting for new events.
+ injector()->Reset();
+ task_count = 0;
+ for (int i = 0; i < 10; ++i) {
+ loop()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&IncrementInt, &task_count),
+ base::TimeDelta::FromMilliseconds(10*i));
+ }
+ // After all the previous tasks have executed, enqueue an event that will
+ // quit.
+ // This relies on the fact that delayed tasks are executed in delay order.
+ // That is verified in message_loop_unittest.cc.
+ loop()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&EventInjector::AddEvent, base::Unretained(injector()), 10,
+ MessageLoop::QuitClosure()),
+ base::TimeDelta::FromMilliseconds(150));
+ loop()->Run();
+ ASSERT_EQ(10, task_count);
+ EXPECT_EQ(1, injector()->processed_events());
+}
+
+TEST_F(MessagePumpGLibTest, TestEventsWhileWaitingForWork) {
+ // Tests that we process events while waiting for work.
+ // The event queue is empty at first.
+ for (int i = 0; i < 10; ++i) {
+ injector()->AddDummyEvent(0);
+ }
+ // After all the events have been processed, post a task that will check that
+ // the events have been processed (note: the task executes after the event
+ // that posted it has been handled, so we expect 11 at that point).
+ base::Closure check_task =
+ base::Bind(&ExpectProcessedEvents, base::Unretained(injector()), 11);
+ base::Closure posted_task =
+ base::Bind(&PostMessageLoopTask, FROM_HERE, check_task);
+ injector()->AddEventAsTask(10, posted_task);
+
+ // And then quit (relies on the condition tested by TestEventTaskInterleave).
+ injector()->AddEvent(10, MessageLoop::QuitClosure());
+ loop()->Run();
+
+ EXPECT_EQ(12, injector()->processed_events());
+}
+
+namespace {
+
+// This class is a helper for the concurrent events / posted tasks test below.
+// It will quit the main loop once enough tasks and events have been processed,
+// while making sure there is always work to do and events in the queue.
+class ConcurrentHelper : public base::RefCounted<ConcurrentHelper> {
+ public:
+ explicit ConcurrentHelper(EventInjector* injector)
+ : injector_(injector),
+ event_count_(kStartingEventCount),
+ task_count_(kStartingTaskCount) {
+ }
+
+ void FromTask() {
+ if (task_count_ > 0) {
+ --task_count_;
+ }
+ if (task_count_ == 0 && event_count_ == 0) {
+ MessageLoop::current()->Quit();
+ } else {
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&ConcurrentHelper::FromTask, this));
+ }
+ }
+
+ void FromEvent() {
+ if (event_count_ > 0) {
+ --event_count_;
+ }
+ if (task_count_ == 0 && event_count_ == 0) {
+ MessageLoop::current()->Quit();
+ } else {
+ injector_->AddEventAsTask(
+ 0, base::Bind(&ConcurrentHelper::FromEvent, this));
+ }
+ }
+
+ int event_count() const { return event_count_; }
+ int task_count() const { return task_count_; }
+
+ private:
+ friend class base::RefCounted<ConcurrentHelper>;
+
+ ~ConcurrentHelper() {}
+
+ static const int kStartingEventCount = 20;
+ static const int kStartingTaskCount = 20;
+
+ EventInjector* injector_;
+ int event_count_;
+ int task_count_;
+};
+
+} // namespace
+
+TEST_F(MessagePumpGLibTest, TestConcurrentEventPostedTask) {
+ // Tests that posted tasks don't starve events, nor the opposite.
+ // We use the helper class above. We keep both event and posted task queues
+ // full, the helper verifies that both tasks and events get processed.
+ // If that is not the case, either event_count_ or task_count_ will not get
+ // to 0, and MessageLoop::Quit() will never be called.
+ scoped_refptr<ConcurrentHelper> helper = new ConcurrentHelper(injector());
+
+ // Add 2 events to the queue to make sure it is always full (when we remove
+ // the event before processing it).
+ injector()->AddEventAsTask(
+ 0, base::Bind(&ConcurrentHelper::FromEvent, helper.get()));
+ injector()->AddEventAsTask(
+ 0, base::Bind(&ConcurrentHelper::FromEvent, helper.get()));
+
+ // Similarly post 2 tasks.
+ loop()->PostTask(
+ FROM_HERE, base::Bind(&ConcurrentHelper::FromTask, helper.get()));
+ loop()->PostTask(
+ FROM_HERE, base::Bind(&ConcurrentHelper::FromTask, helper.get()));
+
+ loop()->Run();
+ EXPECT_EQ(0, helper->event_count());
+ EXPECT_EQ(0, helper->task_count());
+}
+
+namespace {
+
+void AddEventsAndDrainGLib(EventInjector* injector) {
+ // Add a couple of dummy events
+ injector->AddDummyEvent(0);
+ injector->AddDummyEvent(0);
+ // Then add an event that will quit the main loop.
+ injector->AddEvent(0, MessageLoop::QuitClosure());
+
+ // Post a couple of dummy tasks
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&base::DoNothing));
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&base::DoNothing));
+
+ // Drain the events
+ while (g_main_context_pending(NULL)) {
+ g_main_context_iteration(NULL, FALSE);
+ }
+}
+
+} // namespace
+
+TEST_F(MessagePumpGLibTest, TestDrainingGLib) {
+ // Tests that draining events using GLib works.
+ loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&AddEventsAndDrainGLib, base::Unretained(injector())));
+ loop()->Run();
+
+ EXPECT_EQ(3, injector()->processed_events());
+}
+
+
+namespace {
+
+#if defined(TOOLKIT_GTK)
+void AddEventsAndDrainGtk(EventInjector* injector) {
+ // Add a couple of dummy events
+ injector->AddDummyEvent(0);
+ injector->AddDummyEvent(0);
+ // Then add an event that will quit the main loop.
+ injector->AddEvent(0, MessageLoop::QuitClosure());
+
+ // Post a couple of dummy tasks
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&base::DoNothing));
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&base::DoNothing));
+
+ // Drain the events
+ while (gtk_events_pending()) {
+ gtk_main_iteration();
+ }
+}
+#endif
+
+} // namespace
+
+#if defined(TOOLKIT_GTK)
+TEST_F(MessagePumpGLibTest, TestDrainingGtk) {
+ // Tests that draining events using Gtk works.
+ loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&AddEventsAndDrainGtk, base::Unretained(injector())));
+ loop()->Run();
+
+ EXPECT_EQ(3, injector()->processed_events());
+}
+#endif
+
+namespace {
+
+// Helper class that lets us run the GLib message loop.
+class GLibLoopRunner : public base::RefCounted<GLibLoopRunner> {
+ public:
+ GLibLoopRunner() : quit_(false) { }
+
+ void RunGLib() {
+ while (!quit_) {
+ g_main_context_iteration(NULL, TRUE);
+ }
+ }
+
+ void RunLoop() {
+#if defined(TOOLKIT_GTK)
+ while (!quit_) {
+ gtk_main_iteration();
+ }
+#else
+ while (!quit_) {
+ g_main_context_iteration(NULL, TRUE);
+ }
+#endif
+ }
+
+ void Quit() {
+ quit_ = true;
+ }
+
+ void Reset() {
+ quit_ = false;
+ }
+
+ private:
+ friend class base::RefCounted<GLibLoopRunner>;
+
+ ~GLibLoopRunner() {}
+
+ bool quit_;
+};
+
+void TestGLibLoopInternal(EventInjector* injector) {
+ // Allow tasks to be processed from 'native' event loops.
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner();
+
+ int task_count = 0;
+ // Add a couple of dummy events
+ injector->AddDummyEvent(0);
+ injector->AddDummyEvent(0);
+ // Post a couple of dummy tasks
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&IncrementInt, &task_count));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&IncrementInt, &task_count));
+ // Delayed events
+ injector->AddDummyEvent(10);
+ injector->AddDummyEvent(10);
+ // Delayed work
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&IncrementInt, &task_count),
+ base::TimeDelta::FromMilliseconds(30));
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&GLibLoopRunner::Quit, runner.get()),
+ base::TimeDelta::FromMilliseconds(40));
+
+ // Run a nested, straight GLib message loop.
+ runner->RunGLib();
+
+ ASSERT_EQ(3, task_count);
+ EXPECT_EQ(4, injector->processed_events());
+ MessageLoop::current()->Quit();
+}
+
+void TestGtkLoopInternal(EventInjector* injector) {
+ // Allow tasks to be processed from 'native' event loops.
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner();
+
+ int task_count = 0;
+ // Add a couple of dummy events
+ injector->AddDummyEvent(0);
+ injector->AddDummyEvent(0);
+ // Post a couple of dummy tasks
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&IncrementInt, &task_count));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&IncrementInt, &task_count));
+ // Delayed events
+ injector->AddDummyEvent(10);
+ injector->AddDummyEvent(10);
+ // Delayed work
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&IncrementInt, &task_count),
+ base::TimeDelta::FromMilliseconds(30));
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&GLibLoopRunner::Quit, runner.get()),
+ base::TimeDelta::FromMilliseconds(40));
+
+ // Run a nested, straight Gtk message loop.
+ runner->RunLoop();
+
+ ASSERT_EQ(3, task_count);
+ EXPECT_EQ(4, injector->processed_events());
+ MessageLoop::current()->Quit();
+}
+
+} // namespace
+
+TEST_F(MessagePumpGLibTest, TestGLibLoop) {
+ // Tests that events and posted tasks are correctly executed if the message
+ // loop is not run by MessageLoop::Run() but by a straight GLib loop.
+ // Note that in this case we don't make strong guarantees about niceness
+ // between events and posted tasks.
+ loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&TestGLibLoopInternal, base::Unretained(injector())));
+ loop()->Run();
+}
+
+TEST_F(MessagePumpGLibTest, TestGtkLoop) {
+ // Tests that events and posted tasks are correctly executed if the message
+ // loop is not run by MessageLoop::Run() but by a straight Gtk loop.
+ // Note that in this case we don't make strong guarantees about niceness
+ // between events and posted tasks.
+ loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&TestGtkLoopInternal, base::Unretained(injector())));
+ loop()->Run();
+}
diff --git a/src/base/message_pump_gtk.cc b/src/base/message_pump_gtk.cc
new file mode 100644
index 0000000..780b4d8
--- /dev/null
+++ b/src/base/message_pump_gtk.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2012 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/message_pump_gtk.h"
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+
+#include "base/profiler/scoped_profile.h"
+#include "base/debug/trace_event.h"
+
+namespace {
+
+const char* EventToTypeString(const GdkEvent* event) {
+ switch (event->type) {
+ case GDK_NOTHING: return "GDK_NOTHING";
+ case GDK_DELETE: return "GDK_DELETE";
+ case GDK_DESTROY: return "GDK_DESTROY";
+ case GDK_EXPOSE: return "GDK_EXPOSE";
+ case GDK_MOTION_NOTIFY: return "GDK_MOTION_NOTIFY";
+ case GDK_BUTTON_PRESS: return "GDK_BUTTON_PRESS";
+ case GDK_2BUTTON_PRESS: return "GDK_2BUTTON_PRESS";
+ case GDK_3BUTTON_PRESS: return "GDK_3BUTTON_PRESS";
+ case GDK_BUTTON_RELEASE: return "GDK_BUTTON_RELEASE";
+ case GDK_KEY_PRESS: return "GDK_KEY_PRESS";
+ case GDK_KEY_RELEASE: return "GDK_KEY_RELEASE";
+ case GDK_ENTER_NOTIFY: return "GDK_ENTER_NOTIFY";
+ case GDK_LEAVE_NOTIFY: return "GDK_LEAVE_NOTIFY";
+ case GDK_FOCUS_CHANGE: return "GDK_FOCUS_CHANGE";
+ case GDK_CONFIGURE: return "GDK_CONFIGURE";
+ case GDK_MAP: return "GDK_MAP";
+ case GDK_UNMAP: return "GDK_UNMAP";
+ case GDK_PROPERTY_NOTIFY: return "GDK_PROPERTY_NOTIFY";
+ case GDK_SELECTION_CLEAR: return "GDK_SELECTION_CLEAR";
+ case GDK_SELECTION_REQUEST: return "GDK_SELECTION_REQUEST";
+ case GDK_SELECTION_NOTIFY: return "GDK_SELECTION_NOTIFY";
+ case GDK_PROXIMITY_IN: return "GDK_PROXIMITY_IN";
+ case GDK_PROXIMITY_OUT: return "GDK_PROXIMITY_OUT";
+ case GDK_DRAG_ENTER: return "GDK_DRAG_ENTER";
+ case GDK_DRAG_LEAVE: return "GDK_DRAG_LEAVE";
+ case GDK_DRAG_MOTION: return "GDK_DRAG_MOTION";
+ case GDK_DRAG_STATUS: return "GDK_DRAG_STATUS";
+ case GDK_DROP_START: return "GDK_DROP_START";
+ case GDK_DROP_FINISHED: return "GDK_DROP_FINISHED";
+ case GDK_CLIENT_EVENT: return "GDK_CLIENT_EVENT";
+ case GDK_VISIBILITY_NOTIFY: return "GDK_VISIBILITY_NOTIFY";
+ case GDK_NO_EXPOSE: return "GDK_NO_EXPOSE";
+ case GDK_SCROLL: return "GDK_SCROLL";
+ case GDK_WINDOW_STATE: return "GDK_WINDOW_STATE";
+ case GDK_SETTING: return "GDK_SETTING";
+ case GDK_OWNER_CHANGE: return "GDK_OWNER_CHANGE";
+ case GDK_GRAB_BROKEN: return "GDK_GRAB_BROKEN";
+ case GDK_DAMAGE: return "GDK_DAMAGE";
+ default:
+ return "Unknown Gdk Event";
+ }
+}
+
+}
+
+namespace base {
+
+MessagePumpGtk::MessagePumpGtk() : MessagePumpGlib() {
+ gdk_event_handler_set(&EventDispatcher, this, NULL);
+}
+
+void MessagePumpGtk::DispatchEvents(GdkEvent* event) {
+ UNSHIPPED_TRACE_EVENT1("task", "MessagePumpGtk::DispatchEvents",
+ "type", EventToTypeString(event));
+
+ WillProcessEvent(event);
+
+ MessagePumpDispatcher* dispatcher = GetDispatcher();
+ if (!dispatcher)
+ gtk_main_do_event(event);
+ else if (!dispatcher->Dispatch(event))
+ Quit();
+
+ DidProcessEvent(event);
+}
+
+// static
+Display* MessagePumpGtk::GetDefaultXDisplay() {
+ static GdkDisplay* display = gdk_display_get_default();
+ if (!display) {
+ // GTK / GDK has not been initialized, which is a decision we wish to
+ // support, for example for the GPU process.
+ static Display* xdisplay = XOpenDisplay(NULL);
+ return xdisplay;
+ }
+ return GDK_DISPLAY_XDISPLAY(display);
+}
+
+MessagePumpGtk::~MessagePumpGtk() {
+ gdk_event_handler_set(reinterpret_cast<GdkEventFunc>(gtk_main_do_event),
+ this, NULL);
+}
+
+void MessagePumpGtk::WillProcessEvent(GdkEvent* event) {
+ FOR_EACH_OBSERVER(MessagePumpObserver, observers(), WillProcessEvent(event));
+}
+
+void MessagePumpGtk::DidProcessEvent(GdkEvent* event) {
+ FOR_EACH_OBSERVER(MessagePumpObserver, observers(), DidProcessEvent(event));
+}
+
+// static
+void MessagePumpGtk::EventDispatcher(GdkEvent* event, gpointer data) {
+ MessagePumpGtk* message_pump = reinterpret_cast<MessagePumpGtk*>(data);
+ message_pump->DispatchEvents(event);
+}
+
+} // namespace base
diff --git a/src/base/message_pump_gtk.h b/src/base/message_pump_gtk.h
new file mode 100644
index 0000000..ba5cff1
--- /dev/null
+++ b/src/base/message_pump_gtk.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2012 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_MESSAGE_PUMP_GTK_H_
+#define BASE_MESSAGE_PUMP_GTK_H_
+
+#include "base/message_pump_glib.h"
+
+typedef union _GdkEvent GdkEvent;
+typedef struct _XDisplay Display;
+
+namespace base {
+
+// The documentation for this class is in message_pump_glib.h
+class MessagePumpObserver {
+ public:
+ // This method is called before processing a message.
+ virtual void WillProcessEvent(GdkEvent* event) = 0;
+
+ // This method is called after processing a message.
+ virtual void DidProcessEvent(GdkEvent* event) = 0;
+
+ protected:
+ virtual ~MessagePumpObserver() {}
+};
+
+// The documentation for this class is in message_pump_glib.h
+//
+// The nested loop is exited by either posting a quit, or returning false
+// from Dispatch.
+class MessagePumpDispatcher {
+ public:
+ // Dispatches the event. If true is returned processing continues as
+ // normal. If false is returned, the nested loop exits immediately.
+ virtual bool Dispatch(GdkEvent* event) = 0;
+
+ protected:
+ virtual ~MessagePumpDispatcher() {}
+};
+
+// This class implements a message-pump for dispatching GTK events.
+class BASE_EXPORT MessagePumpGtk : public MessagePumpGlib {
+ public:
+ MessagePumpGtk();
+
+ // Dispatch an available GdkEvent. Essentially this allows a subclass to do
+ // some task before/after calling the default handler (EventDispatcher).
+ void DispatchEvents(GdkEvent* event);
+
+ // Returns default X Display.
+ static Display* GetDefaultXDisplay();
+
+ protected:
+ virtual ~MessagePumpGtk();
+
+ private:
+ // Invoked from EventDispatcher. Notifies all observers we're about to
+ // process an event.
+ void WillProcessEvent(GdkEvent* event);
+
+ // Invoked from EventDispatcher. Notifies all observers we processed an
+ // event.
+ void DidProcessEvent(GdkEvent* event);
+
+ // Callback prior to gdk dispatching an event.
+ static void EventDispatcher(GdkEvent* event, void* data);
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpGtk);
+};
+
+typedef MessagePumpGtk MessagePumpForUI;
+
+} // namespace base
+
+#endif // BASE_MESSAGE_PUMP_GTK_H_
diff --git a/src/base/message_pump_io_ios.cc b/src/base/message_pump_io_ios.cc
new file mode 100644
index 0000000..ca800ff
--- /dev/null
+++ b/src/base/message_pump_io_ios.cc
@@ -0,0 +1,209 @@
+// Copyright 2012 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/message_pump_io_ios.h"
+
+namespace base {
+
+MessagePumpIOSForIO::FileDescriptorWatcher::FileDescriptorWatcher()
+ : is_persistent_(false),
+ fdref_(NULL),
+ callback_types_(0),
+ fd_source_(NULL),
+ pump_(NULL),
+ watcher_(NULL) {
+}
+
+MessagePumpIOSForIO::FileDescriptorWatcher::~FileDescriptorWatcher() {
+ StopWatchingFileDescriptor();
+}
+
+bool MessagePumpIOSForIO::FileDescriptorWatcher::StopWatchingFileDescriptor() {
+ if (fdref_ == NULL)
+ return true;
+
+ CFFileDescriptorDisableCallBacks(fdref_, callback_types_);
+ pump_->RemoveRunLoopSource(fd_source_);
+ fd_source_.reset();
+ fdref_.reset();
+ callback_types_ = 0;
+ pump_ = NULL;
+ watcher_ = NULL;
+ return true;
+}
+
+void MessagePumpIOSForIO::FileDescriptorWatcher::Init(
+ CFFileDescriptorRef fdref,
+ CFOptionFlags callback_types,
+ CFRunLoopSourceRef fd_source,
+ bool is_persistent) {
+ DCHECK(fdref);
+ DCHECK(!fdref_);
+
+ is_persistent_ = is_persistent;
+ fdref_.reset(fdref);
+ callback_types_ = callback_types;
+ fd_source_.reset(fd_source);
+}
+
+void MessagePumpIOSForIO::FileDescriptorWatcher::OnFileCanReadWithoutBlocking(
+ int fd,
+ MessagePumpIOSForIO* pump) {
+ DCHECK(callback_types_ & kCFFileDescriptorReadCallBack);
+ pump->WillProcessIOEvent();
+ watcher_->OnFileCanReadWithoutBlocking(fd);
+ pump->DidProcessIOEvent();
+}
+
+void MessagePumpIOSForIO::FileDescriptorWatcher::OnFileCanWriteWithoutBlocking(
+ int fd,
+ MessagePumpIOSForIO* pump) {
+ DCHECK(callback_types_ & kCFFileDescriptorWriteCallBack);
+ pump->WillProcessIOEvent();
+ watcher_->OnFileCanWriteWithoutBlocking(fd);
+ pump->DidProcessIOEvent();
+}
+
+MessagePumpIOSForIO::MessagePumpIOSForIO() {
+}
+
+MessagePumpIOSForIO::~MessagePumpIOSForIO() {
+}
+
+bool MessagePumpIOSForIO::WatchFileDescriptor(
+ int fd,
+ bool persistent,
+ int mode,
+ FileDescriptorWatcher *controller,
+ Watcher *delegate) {
+ DCHECK_GE(fd, 0);
+ DCHECK(controller);
+ DCHECK(delegate);
+ DCHECK(mode == WATCH_READ || mode == WATCH_WRITE || mode == WATCH_READ_WRITE);
+
+ // WatchFileDescriptor should be called on the pump thread. It is not
+ // threadsafe, and your watcher may never be registered.
+ DCHECK(watch_file_descriptor_caller_checker_.CalledOnValidThread());
+
+ CFFileDescriptorContext source_context = {0};
+ source_context.info = controller;
+
+ CFOptionFlags callback_types = 0;
+ if (mode & WATCH_READ) {
+ callback_types |= kCFFileDescriptorReadCallBack;
+ }
+ if (mode & WATCH_WRITE) {
+ callback_types |= kCFFileDescriptorWriteCallBack;
+ }
+
+ CFFileDescriptorRef fdref = controller->fdref_;
+ if (fdref == NULL) {
+ base::mac::ScopedCFTypeRef<CFFileDescriptorRef> scoped_fdref(
+ CFFileDescriptorCreate(kCFAllocatorDefault, fd, false, HandleFdIOEvent,
+ &source_context));
+ if (scoped_fdref == NULL) {
+ NOTREACHED() << "CFFileDescriptorCreate failed";
+ return false;
+ }
+
+ CFFileDescriptorEnableCallBacks(scoped_fdref, callback_types);
+
+ // TODO(wtc): what should the 'order' argument be?
+ base::mac::ScopedCFTypeRef<CFRunLoopSourceRef> scoped_fd_source(
+ CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault,
+ scoped_fdref,
+ 0));
+ if (scoped_fd_source == NULL) {
+ NOTREACHED() << "CFFileDescriptorCreateRunLoopSource failed";
+ return false;
+ }
+ CFRunLoopAddSource(run_loop(), scoped_fd_source, kCFRunLoopCommonModes);
+
+ // Transfer ownership of scoped_fdref and fd_source to controller.
+ controller->Init(scoped_fdref.release(), callback_types,
+ scoped_fd_source.release(), persistent);
+ } else {
+ // It's illegal to use this function to listen on 2 separate fds with the
+ // same |controller|.
+ if (CFFileDescriptorGetNativeDescriptor(fdref) != fd) {
+ NOTREACHED() << "FDs don't match: "
+ << CFFileDescriptorGetNativeDescriptor(fdref)
+ << " != " << fd;
+ return false;
+ }
+ if (persistent != controller->is_persistent_) {
+ NOTREACHED() << "persistent doesn't match";
+ return false;
+ }
+
+ // Combine old/new event masks.
+ CFFileDescriptorDisableCallBacks(fdref, controller->callback_types_);
+ controller->callback_types_ |= callback_types;
+ CFFileDescriptorEnableCallBacks(fdref, controller->callback_types_);
+ }
+
+ controller->set_watcher(delegate);
+ controller->set_pump(this);
+
+ return true;
+}
+
+void MessagePumpIOSForIO::RemoveRunLoopSource(CFRunLoopSourceRef source) {
+ CFRunLoopRemoveSource(run_loop(), source, kCFRunLoopCommonModes);
+}
+
+void MessagePumpIOSForIO::AddIOObserver(IOObserver *obs) {
+ io_observers_.AddObserver(obs);
+}
+
+void MessagePumpIOSForIO::RemoveIOObserver(IOObserver *obs) {
+ io_observers_.RemoveObserver(obs);
+}
+
+void MessagePumpIOSForIO::WillProcessIOEvent() {
+ FOR_EACH_OBSERVER(IOObserver, io_observers_, WillProcessIOEvent());
+}
+
+void MessagePumpIOSForIO::DidProcessIOEvent() {
+ FOR_EACH_OBSERVER(IOObserver, io_observers_, DidProcessIOEvent());
+}
+
+// static
+void MessagePumpIOSForIO::HandleFdIOEvent(CFFileDescriptorRef fdref,
+ CFOptionFlags callback_types,
+ void* context) {
+ FileDescriptorWatcher* controller =
+ static_cast<FileDescriptorWatcher*>(context);
+ DCHECK_EQ(fdref, controller->fdref_);
+
+ // Ensure that |fdref| will remain live for the duration of this function
+ // call even if |controller| is deleted or |StopWatchingFileDescriptor()| is
+ // called, either of which will cause |fdref| to be released.
+ mac::ScopedCFTypeRef<CFFileDescriptorRef> scoped_fdref(
+ fdref, base::scoped_policy::RETAIN);
+
+ int fd = CFFileDescriptorGetNativeDescriptor(fdref);
+ MessagePumpIOSForIO* pump = controller->pump();
+ if (callback_types & kCFFileDescriptorWriteCallBack)
+ controller->OnFileCanWriteWithoutBlocking(fd, pump);
+
+ // Perform the read callback only if the file descriptor has not been
+ // invalidated in the write callback. As |FileDescriptorWatcher| invalidates
+ // its file descriptor on destruction, the file descriptor being valid also
+ // guarantees that |controller| has not been deleted.
+ if (callback_types & kCFFileDescriptorReadCallBack &&
+ CFFileDescriptorIsValid(fdref)) {
+ DCHECK_EQ(fdref, controller->fdref_);
+ controller->OnFileCanReadWithoutBlocking(fd, pump);
+ }
+
+ // Re-enable callbacks after the read/write if the file descriptor is still
+ // valid and the controller is persistent.
+ if (CFFileDescriptorIsValid(fdref) && controller->is_persistent_) {
+ DCHECK_EQ(fdref, controller->fdref_);
+ CFFileDescriptorEnableCallBacks(fdref, callback_types);
+ }
+}
+
+} // namespace base
diff --git a/src/base/message_pump_io_ios.h b/src/base/message_pump_io_ios.h
new file mode 100644
index 0000000..81f529d
--- /dev/null
+++ b/src/base/message_pump_io_ios.h
@@ -0,0 +1,142 @@
+// Copyright 2012 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_MESSAGE_PUMP_IO_IOS_H_
+#define BASE_MESSAGE_PUMP_IO_IOS_H_
+
+#include "base/base_export.h"
+#include "base/mac/scoped_cffiledescriptorref.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_pump_mac.h"
+#include "base/observer_list.h"
+
+namespace base {
+
+// This file introduces a class to monitor sockets and issue callbacks when
+// sockets are ready for I/O on iOS.
+class BASE_EXPORT MessagePumpIOSForIO : public MessagePumpNSRunLoop {
+ public:
+ class IOObserver {
+ public:
+ IOObserver() {}
+
+ // An IOObserver is an object that receives IO notifications from the
+ // MessagePump.
+ //
+ // NOTE: An IOObserver implementation should be extremely fast!
+ virtual void WillProcessIOEvent() = 0;
+ virtual void DidProcessIOEvent() = 0;
+
+ protected:
+ virtual ~IOObserver() {}
+ };
+
+ // Used with WatchFileDescriptor to asynchronously monitor the I/O readiness
+ // of a file descriptor.
+ class Watcher {
+ public:
+ // Called from MessageLoop::Run when an FD can be read from/written to
+ // without blocking
+ virtual void OnFileCanReadWithoutBlocking(int fd) = 0;
+ virtual void OnFileCanWriteWithoutBlocking(int fd) = 0;
+
+ protected:
+ virtual ~Watcher() {}
+ };
+
+ // Object returned by WatchFileDescriptor to manage further watching.
+ class FileDescriptorWatcher {
+ public:
+ FileDescriptorWatcher();
+ ~FileDescriptorWatcher(); // Implicitly calls StopWatchingFileDescriptor.
+
+ // NOTE: These methods aren't called StartWatching()/StopWatching() to
+ // avoid confusion with the win32 ObjectWatcher class.
+
+ // Stop watching the FD, always safe to call. No-op if there's nothing
+ // to do.
+ bool StopWatchingFileDescriptor();
+
+ private:
+ friend class MessagePumpIOSForIO;
+ friend class MessagePumpIOSForIOTest;
+
+ // Called by MessagePumpIOSForIO, ownership of |fdref| and |fd_source|
+ // is transferred to this object.
+ void Init(CFFileDescriptorRef fdref,
+ CFOptionFlags callback_types,
+ CFRunLoopSourceRef fd_source,
+ bool is_persistent);
+
+ void set_pump(MessagePumpIOSForIO* pump) { pump_ = pump; }
+ MessagePumpIOSForIO* pump() const { return pump_; }
+
+ void set_watcher(Watcher* watcher) { watcher_ = watcher; }
+
+ void OnFileCanReadWithoutBlocking(int fd, MessagePumpIOSForIO* pump);
+ void OnFileCanWriteWithoutBlocking(int fd, MessagePumpIOSForIO* pump);
+
+ bool is_persistent_; // false if this event is one-shot.
+ base::mac::ScopedCFFileDescriptorRef fdref_;
+ CFOptionFlags callback_types_;
+ base::mac::ScopedCFTypeRef<CFRunLoopSourceRef> fd_source_;
+ scoped_refptr<MessagePumpIOSForIO> pump_;
+ Watcher* watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcher);
+ };
+
+ enum Mode {
+ WATCH_READ = 1 << 0,
+ WATCH_WRITE = 1 << 1,
+ WATCH_READ_WRITE = WATCH_READ | WATCH_WRITE
+ };
+
+ MessagePumpIOSForIO();
+
+ // Have the current thread's message loop watch for a a situation in which
+ // reading/writing to the FD can be performed without blocking.
+ // Callers must provide a preallocated FileDescriptorWatcher object which
+ // can later be used to manage the lifetime of this event.
+ // If a FileDescriptorWatcher is passed in which is already attached to
+ // an event, then the effect is cumulative i.e. after the call |controller|
+ // will watch both the previous event and the new one.
+ // If an error occurs while calling this method in a cumulative fashion, the
+ // event previously attached to |controller| is aborted.
+ // Returns true on success.
+ // Must be called on the same thread the message_pump is running on.
+ bool WatchFileDescriptor(int fd,
+ bool persistent,
+ int mode,
+ FileDescriptorWatcher *controller,
+ Watcher *delegate);
+
+ void RemoveRunLoopSource(CFRunLoopSourceRef source);
+
+ void AddIOObserver(IOObserver* obs);
+ void RemoveIOObserver(IOObserver* obs);
+
+ protected:
+ virtual ~MessagePumpIOSForIO();
+
+ private:
+ friend class MessagePumpIOSForIOTest;
+
+ void WillProcessIOEvent();
+ void DidProcessIOEvent();
+
+ static void HandleFdIOEvent(CFFileDescriptorRef fdref,
+ CFOptionFlags callback_types,
+ void* context);
+
+ ObserverList<IOObserver> io_observers_;
+ ThreadChecker watch_file_descriptor_caller_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpIOSForIO);
+};
+
+} // namespace base
+
+#endif // BASE_MESSAGE_PUMP_IO_IOS_H_
diff --git a/src/base/message_pump_io_ios_unittest.cc b/src/base/message_pump_io_ios_unittest.cc
new file mode 100644
index 0000000..7a00d11
--- /dev/null
+++ b/src/base/message_pump_io_ios_unittest.cc
@@ -0,0 +1,188 @@
+// Copyright 2012 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/message_pump_io_ios.h"
+
+#include <unistd.h>
+
+#include "base/message_loop.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+class MessagePumpIOSForIOTest : public testing::Test {
+ protected:
+ MessagePumpIOSForIOTest()
+ : ui_loop_(MessageLoop::TYPE_UI),
+ io_thread_("MessagePumpIOSForIOTestIOThread") {}
+ virtual ~MessagePumpIOSForIOTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ Thread::Options options(MessageLoop::TYPE_IO, 0);
+ ASSERT_TRUE(io_thread_.StartWithOptions(options));
+ ASSERT_EQ(MessageLoop::TYPE_IO, io_thread_.message_loop()->type());
+ int ret = pipe(pipefds_);
+ ASSERT_EQ(0, ret);
+ ret = pipe(alternate_pipefds_);
+ ASSERT_EQ(0, ret);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ if (HANDLE_EINTR(close(pipefds_[0])) < 0)
+ PLOG(ERROR) << "close";
+ if (HANDLE_EINTR(close(pipefds_[1])) < 0)
+ PLOG(ERROR) << "close";
+ }
+
+ MessageLoop* ui_loop() { return &ui_loop_; }
+ MessageLoopForIO* io_loop() const {
+ return static_cast<MessageLoopForIO*>(io_thread_.message_loop());
+ }
+
+ void HandleFdIOEvent(MessageLoopForIO::FileDescriptorWatcher* watcher) {
+ MessagePumpIOSForIO::HandleFdIOEvent(watcher->fdref_,
+ kCFFileDescriptorReadCallBack | kCFFileDescriptorWriteCallBack,
+ watcher);
+ }
+
+ int pipefds_[2];
+ int alternate_pipefds_[2];
+
+ private:
+ MessageLoop ui_loop_;
+ Thread io_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpIOSForIOTest);
+};
+
+namespace {
+
+// Concrete implementation of MessagePumpIOSForIO::Watcher that does
+// nothing useful.
+class StupidWatcher : public MessagePumpIOSForIO::Watcher {
+ public:
+ virtual ~StupidWatcher() {}
+
+ // base:MessagePumpIOSForIO::Watcher interface
+ virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {}
+ virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {}
+};
+
+#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+
+// Test to make sure that we catch calling WatchFileDescriptor off of the
+// wrong thread.
+TEST_F(MessagePumpIOSForIOTest, TestWatchingFromBadThread) {
+ MessagePumpIOSForIO::FileDescriptorWatcher watcher;
+ StupidWatcher delegate;
+
+ ASSERT_DEBUG_DEATH(io_loop()->WatchFileDescriptor(
+ STDOUT_FILENO, false, MessageLoopForIO::WATCH_READ, &watcher, &delegate),
+ "Check failed: "
+ "watch_file_descriptor_caller_checker_.CalledOnValidThread()");
+}
+
+#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+
+class BaseWatcher : public MessagePumpIOSForIO::Watcher {
+ public:
+ BaseWatcher(MessagePumpIOSForIO::FileDescriptorWatcher* controller)
+ : controller_(controller) {
+ DCHECK(controller_);
+ }
+ virtual ~BaseWatcher() {}
+
+ // MessagePumpIOSForIO::Watcher interface
+ virtual void OnFileCanReadWithoutBlocking(int /* fd */) OVERRIDE {
+ NOTREACHED();
+ }
+
+ virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE {
+ NOTREACHED();
+ }
+
+ protected:
+ MessagePumpIOSForIO::FileDescriptorWatcher* controller_;
+};
+
+class DeleteWatcher : public BaseWatcher {
+ public:
+ explicit DeleteWatcher(
+ MessagePumpIOSForIO::FileDescriptorWatcher* controller)
+ : BaseWatcher(controller) {}
+
+ virtual ~DeleteWatcher() {
+ DCHECK(!controller_);
+ }
+
+ virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE {
+ DCHECK(controller_);
+ delete controller_;
+ controller_ = NULL;
+ }
+};
+
+TEST_F(MessagePumpIOSForIOTest, DeleteWatcher) {
+ scoped_refptr<MessagePumpIOSForIO> pump(new MessagePumpIOSForIO);
+ MessagePumpIOSForIO::FileDescriptorWatcher* watcher =
+ new MessagePumpIOSForIO::FileDescriptorWatcher;
+ DeleteWatcher delegate(watcher);
+ pump->WatchFileDescriptor(pipefds_[1],
+ false, MessagePumpIOSForIO::WATCH_READ_WRITE, watcher, &delegate);
+
+ // Spoof a callback.
+ HandleFdIOEvent(watcher);
+}
+
+class StopWatcher : public BaseWatcher {
+ public:
+ StopWatcher(MessagePumpIOSForIO::FileDescriptorWatcher* controller,
+ MessagePumpIOSForIO* pump,
+ int fd_to_start_watching = -1)
+ : BaseWatcher(controller),
+ pump_(pump),
+ fd_to_start_watching_(fd_to_start_watching) {}
+
+ virtual ~StopWatcher() {}
+
+ virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE {
+ controller_->StopWatchingFileDescriptor();
+ if (fd_to_start_watching_ >= 0) {
+ pump_->WatchFileDescriptor(fd_to_start_watching_,
+ false, MessagePumpIOSForIO::WATCH_READ_WRITE, controller_, this);
+ }
+ }
+
+ private:
+ MessagePumpIOSForIO* pump_;
+ int fd_to_start_watching_;
+};
+
+TEST_F(MessagePumpIOSForIOTest, StopWatcher) {
+ scoped_refptr<MessagePumpIOSForIO> pump(new MessagePumpIOSForIO);
+ MessagePumpIOSForIO::FileDescriptorWatcher watcher;
+ StopWatcher delegate(&watcher, pump);
+ pump->WatchFileDescriptor(pipefds_[1],
+ false, MessagePumpIOSForIO::WATCH_READ_WRITE, &watcher, &delegate);
+
+ // Spoof a callback.
+ HandleFdIOEvent(&watcher);
+}
+
+TEST_F(MessagePumpIOSForIOTest, StopWatcherAndWatchSomethingElse) {
+ scoped_refptr<MessagePumpIOSForIO> pump(new MessagePumpIOSForIO);
+ MessagePumpIOSForIO::FileDescriptorWatcher watcher;
+ StopWatcher delegate(&watcher, pump, alternate_pipefds_[1]);
+ pump->WatchFileDescriptor(pipefds_[1],
+ false, MessagePumpIOSForIO::WATCH_READ_WRITE, &watcher, &delegate);
+
+ // Spoof a callback.
+ HandleFdIOEvent(&watcher);
+}
+
+} // namespace
+
+} // namespace base
diff --git a/src/base/message_pump_io_starboard.cc b/src/base/message_pump_io_starboard.cc
new file mode 100644
index 0000000..bbd1707
--- /dev/null
+++ b/src/base/message_pump_io_starboard.cc
@@ -0,0 +1,289 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/message_pump_io_starboard.h"
+
+#include "base/auto_reset.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/time.h"
+#include "starboard/socket.h"
+#include "starboard/socket_waiter.h"
+
+namespace base {
+
+MessagePumpIOStarboard::SocketWatcher::SocketWatcher()
+ : socket_(kSbSocketInvalid),
+ pump_(NULL),
+ watcher_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {}
+
+MessagePumpIOStarboard::SocketWatcher::~SocketWatcher() {
+ if (SbSocketIsValid(socket_)) {
+ StopWatchingSocket();
+ }
+}
+
+bool MessagePumpIOStarboard::SocketWatcher::StopWatchingSocket() {
+ SbSocket socket = Release();
+ bool result = true;
+ if (SbSocketIsValid(socket)) {
+ DCHECK(pump_);
+ result = pump_->StopWatching(socket);
+ }
+ pump_ = NULL;
+ watcher_ = NULL;
+ return result;
+}
+
+void MessagePumpIOStarboard::SocketWatcher::Init(SbSocket socket,
+ bool persistent) {
+ DCHECK(socket);
+ DCHECK(!socket_);
+ socket_ = socket;
+ persistent_ = persistent;
+}
+
+SbSocket MessagePumpIOStarboard::SocketWatcher::Release() {
+ SbSocket socket = socket_;
+ socket_ = kSbSocketInvalid;
+ return socket;
+}
+
+void MessagePumpIOStarboard::SocketWatcher::OnSocketReadyToRead(
+ SbSocket socket,
+ MessagePumpIOStarboard* pump) {
+ if (!watcher_)
+ return;
+ pump->WillProcessIOEvent();
+ watcher_->OnSocketReadyToRead(socket);
+ pump->DidProcessIOEvent();
+}
+
+void MessagePumpIOStarboard::SocketWatcher::OnSocketReadyToWrite(
+ SbSocket socket,
+ MessagePumpIOStarboard* pump) {
+ if (!watcher_)
+ return;
+ pump->WillProcessIOEvent();
+ watcher_->OnSocketReadyToWrite(socket);
+ pump->DidProcessIOEvent();
+}
+
+MessagePumpIOStarboard::MessagePumpIOStarboard()
+ : keep_running_(true),
+ in_run_(false),
+ processed_io_events_(false),
+ waiter_(SbSocketWaiterCreate()) {}
+
+MessagePumpIOStarboard::~MessagePumpIOStarboard() {
+ DCHECK(SbSocketWaiterIsValid(waiter_));
+ SbSocketWaiterDestroy(waiter_);
+}
+
+bool MessagePumpIOStarboard::Watch(SbSocket socket,
+ bool persistent,
+ int mode,
+ SocketWatcher* controller,
+ Watcher* delegate) {
+ DCHECK(SbSocketIsValid(socket));
+ DCHECK(controller);
+ DCHECK(delegate);
+ DCHECK(mode == WATCH_READ || mode == WATCH_WRITE || mode == WATCH_READ_WRITE);
+ // Watch should be called on the pump thread. It is not threadsafe, and your
+ // watcher may never be registered.
+ DCHECK(watch_socket_caller_checker_.CalledOnValidThread());
+
+ int interests = kSbSocketWaiterInterestNone;
+ if (mode & WATCH_READ) {
+ interests |= kSbSocketWaiterInterestRead;
+ }
+ if (mode & WATCH_WRITE) {
+ interests |= kSbSocketWaiterInterestWrite;
+ }
+
+ SbSocket old_socket = controller->Release();
+ if (SbSocketIsValid(old_socket)) {
+ // It's illegal to use this function to listen on 2 separate fds with the
+ // same |controller|.
+ if (old_socket != socket) {
+ NOTREACHED() << "Sockets don't match" << old_socket << "!=" << socket;
+ return false;
+ }
+
+ // Make sure we don't pick up any funky internal masks.
+ int old_interest_mask =
+ controller->interests() &
+ (kSbSocketWaiterInterestRead | kSbSocketWaiterInterestWrite);
+
+ // Combine old/new event masks.
+ interests |= old_interest_mask;
+
+ // Must disarm the event before we can reuse it.
+ SbSocketWaiterRemove(waiter_, old_socket);
+ }
+
+ // Set current interest mask and waiter for this event.
+ bool result =
+ SbSocketWaiterAdd(waiter_, socket, controller, OnSocketWaiterNotification,
+ interests, persistent);
+ DCHECK(result);
+
+ controller->Init(socket, persistent);
+ controller->set_watcher(delegate);
+ controller->set_pump(this);
+
+ return true;
+}
+
+bool MessagePumpIOStarboard::StopWatching(SbSocket socket) {
+ return SbSocketWaiterRemove(waiter_, socket);
+}
+
+void MessagePumpIOStarboard::AddIOObserver(IOObserver* obs) {
+ io_observers_.AddObserver(obs);
+}
+
+void MessagePumpIOStarboard::RemoveIOObserver(IOObserver* obs) {
+ io_observers_.RemoveObserver(obs);
+}
+
+// Reentrant!
+void MessagePumpIOStarboard::Run(Delegate* delegate) {
+ DCHECK(keep_running_) << "Quit must have been called outside of Run!";
+ AutoReset<bool> auto_reset_in_run(&in_run_, true);
+
+ for (;;) {
+ bool did_work = delegate->DoWork();
+ if (!keep_running_)
+ break;
+
+ // NOTE: We need to have a wake-up pending any time there is work queued,
+ // and the MessageLoop only wakes up the pump when the work queue goes from
+ // 0 tasks to 1 task. If any work is scheduled on this MessageLoop (from
+ // another thread) anywhere in between the call to DoWork() above and the
+ // call to SbSocketWaiterWaitTimed() below, SbSocketWaiterWaitTimed() will
+ // consume a wake-up, but leave the work queued. This will cause the
+ // blocking wait further below to hang forever, no matter how many more
+ // items are added to the queue. To resolve this, if this wait consumes a
+ // wake-up, we set did_work to true so we will jump back to the top of the
+ // loop and call delegate->DoWork() before we decide to block.
+
+ SbSocketWaiterResult result = SbSocketWaiterWaitTimed(waiter_, 0);
+ DCHECK_NE(kSbSocketWaiterResultInvalid, result);
+ did_work |=
+ (result == kSbSocketWaiterResultWokenUp) || processed_io_events_;
+ processed_io_events_ = false;
+ if (!keep_running_)
+ break;
+
+ // Let's play catchup on all delayed work before we loop. This fixes bug
+ // #5534709 by processing a large number of short delayed tasks quickly
+ // before looping back to process non-delayed tasks (like paint).
+ bool did_delayed_work = false;
+ do {
+ did_delayed_work = delegate->DoDelayedWork(&delayed_work_time_);
+ did_work |= did_delayed_work;
+ } while (did_delayed_work && keep_running_);
+
+ if (!keep_running_)
+ break;
+
+ if (did_work)
+ continue;
+
+ did_work = delegate->DoIdleWork();
+ if (!keep_running_)
+ break;
+
+ if (did_work)
+ continue;
+
+ if (delayed_work_time_.is_null()) {
+ SbSocketWaiterWait(waiter_);
+ } else {
+ TimeDelta delay = delayed_work_time_ - TimeTicks::Now();
+ if (delay > TimeDelta()) {
+ SbSocketWaiterWaitTimed(waiter_, delay.ToSbTime());
+ } else {
+ // It looks like delayed_work_time_ indicates a time in the past, so we
+ // need to call DoDelayedWork now.
+ delayed_work_time_ = TimeTicks();
+ }
+ }
+ }
+
+ keep_running_ = true;
+}
+
+void MessagePumpIOStarboard::Quit() {
+ DCHECK(in_run_);
+ // Tell both the SbObjectWaiter and Run that they should break out of their
+ // loops.
+ keep_running_ = false;
+ ScheduleWork();
+}
+
+void MessagePumpIOStarboard::ScheduleWork() {
+ SbSocketWaiterWakeUp(waiter_);
+}
+
+void MessagePumpIOStarboard::ScheduleDelayedWork(
+ const TimeTicks& delayed_work_time) {
+ // We know that we can't be blocked on Wait right now since this method can
+ // only be called on the same thread as Run, so we only need to update our
+ // record of how long to sleep when we do sleep.
+ delayed_work_time_ = delayed_work_time;
+ ScheduleWork();
+}
+
+void MessagePumpIOStarboard::WillProcessIOEvent() {
+ FOR_EACH_OBSERVER(IOObserver, io_observers_, WillProcessIOEvent());
+}
+
+void MessagePumpIOStarboard::DidProcessIOEvent() {
+ FOR_EACH_OBSERVER(IOObserver, io_observers_, DidProcessIOEvent());
+}
+
+// static
+void MessagePumpIOStarboard::OnSocketWaiterNotification(SbSocketWaiter waiter,
+ SbSocket socket,
+ void* context,
+ int ready_interests) {
+ base::WeakPtr<SocketWatcher> controller =
+ static_cast<SocketWatcher*>(context)->weak_factory_.GetWeakPtr();
+ DCHECK(controller.get());
+
+ MessagePumpIOStarboard* pump = controller->pump();
+ pump->processed_io_events_ = true;
+
+ // If not persistent, the watch has been released at this point.
+ if (!controller->persistent()) {
+ controller->Release();
+ }
+
+ if (ready_interests & kSbSocketWaiterInterestWrite) {
+ controller->OnSocketReadyToWrite(socket, pump);
+ }
+
+ // Check |controller| in case it's been deleted previously.
+ if (controller.get() && ready_interests & kSbSocketWaiterInterestRead) {
+ controller->OnSocketReadyToRead(socket, pump);
+ }
+}
+
+} // namespace base
diff --git a/src/base/message_pump_io_starboard.h b/src/base/message_pump_io_starboard.h
new file mode 100644
index 0000000..c840181
--- /dev/null
+++ b/src/base/message_pump_io_starboard.h
@@ -0,0 +1,179 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef BASE_MESSAGE_PUMP_IO_STARBOARD_H_
+#define BASE_MESSAGE_PUMP_IO_STARBOARD_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_pump.h"
+#include "base/observer_list.h"
+#include "base/threading/thread_checker.h"
+#include "base/time.h"
+#include "starboard/socket.h"
+#include "starboard/socket_waiter.h"
+
+namespace base {
+
+// Class to monitor sockets and issue callbacks when sockets are ready for I/O.
+class BASE_EXPORT MessagePumpIOStarboard : public MessagePump {
+ public:
+ class IOObserver {
+ public:
+ IOObserver() {}
+
+ // An IOObserver is an object that receives IO notifications from the
+ // MessagePump.
+ //
+ // NOTE: An IOObserver should not do much work, it should return extremely
+ // quickly!
+ virtual void WillProcessIOEvent() = 0;
+ virtual void DidProcessIOEvent() = 0;
+
+ protected:
+ virtual ~IOObserver() {}
+ };
+
+ // Used with WatchFileDescriptor to asynchronously monitor the I/O readiness
+ // of a file descriptor.
+ class Watcher {
+ public:
+ // These methods are called from MessageLoop::Run when a socket can be
+ // interacted with without blocking.
+ virtual void OnSocketReadyToRead(SbSocket /*socket*/) {}
+ virtual void OnSocketReadyToWrite(SbSocket /*socket*/) {}
+
+ protected:
+ virtual ~Watcher() {}
+ };
+
+ // Object returned by WatchSocket to manage further watching.
+ class SocketWatcher {
+ public:
+ SocketWatcher();
+ ~SocketWatcher(); // Implicitly calls StopWatchingSocket.
+
+ // NOTE: These methods aren't called StartWatching()/StopWatching() to avoid
+ // confusion with the win32 ObjectWatcher class.
+
+ // Stops watching the socket, always safe to call. No-op if there's nothing
+ // to do.
+ bool StopWatchingSocket();
+
+ bool persistent() const { return persistent_; }
+
+ private:
+ friend class MessagePumpIOStarboard;
+ friend class MessagePumpIOStarboardTest;
+
+ // Called by MessagePumpIOStarboard.
+ void Init(SbSocket socket, bool persistent);
+ SbSocket Release();
+
+ int interests() const { return interests_; }
+
+ void set_pump(MessagePumpIOStarboard* pump) { pump_ = pump; }
+ MessagePumpIOStarboard* pump() const { return pump_; }
+
+ void set_watcher(Watcher* watcher) { watcher_ = watcher; }
+
+ void OnSocketReadyToRead(SbSocket socket, MessagePumpIOStarboard* pump);
+ void OnSocketReadyToWrite(SbSocket socket, MessagePumpIOStarboard* pump);
+
+ int interests_;
+ SbSocket socket_;
+ bool persistent_;
+ MessagePumpIOStarboard* pump_;
+ Watcher* watcher_;
+ base::WeakPtrFactory<SocketWatcher> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SocketWatcher);
+ };
+
+ enum Mode {
+ WATCH_READ = 1 << 0,
+ WATCH_WRITE = 1 << 1,
+ WATCH_READ_WRITE = WATCH_READ | WATCH_WRITE
+ };
+
+ MessagePumpIOStarboard();
+
+ // Have the current thread's message loop watch for a a situation in which
+ // reading/writing to the socket can be performed without blocking. Callers
+ // must provide a preallocated SocketWatcher object which can later be used to
+ // manage the lifetime of this event. If a SocketWatcher is passed in which
+ // is already attached to a socket, then the effect is cumulative i.e. after
+ // the call |controller| will watch both the previous event and the new one.
+ // If an error occurs while calling this method in a cumulative fashion, the
+ // event previously attached to |controller| is aborted. Returns true on
+ // success. Must be called on the same thread the message_pump is running on.
+ bool Watch(SbSocket socket,
+ bool persistent,
+ int mode,
+ SocketWatcher* controller,
+ Watcher* delegate);
+
+ // Stops watching the socket.
+ bool StopWatching(SbSocket socket);
+
+ void AddIOObserver(IOObserver* obs);
+ void RemoveIOObserver(IOObserver* obs);
+
+ // MessagePump methods:
+ virtual void Run(Delegate* delegate) OVERRIDE;
+ virtual void Quit() OVERRIDE;
+ virtual void ScheduleWork() OVERRIDE;
+ virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) OVERRIDE;
+
+ protected:
+ virtual ~MessagePumpIOStarboard();
+
+ private:
+ friend class MessagePumpIOStarboardTest;
+
+ void WillProcessIOEvent();
+ void DidProcessIOEvent();
+
+ // Called by SbSocketWaiter to tell us a registered socket can be read and/or
+ // written to.
+ static void OnSocketWaiterNotification(SbSocketWaiter waiter,
+ SbSocket socket,
+ void* context,
+ int ready_interests);
+
+ // This flag is set to false when Run should return.
+ bool keep_running_;
+
+ // This flag is set when inside Run.
+ bool in_run_;
+
+ // This flag is set if the Socket Waiter has processed I/O events.
+ bool processed_io_events_;
+
+ // The time at which we should call DoDelayedWork.
+ TimeTicks delayed_work_time_;
+
+ // Starboard socket waiter dispatcher. Waits for all sockets registered with
+ // it, and sends readiness callbacks when a socket is ready for I/O.
+ SbSocketWaiter waiter_;
+
+ ObserverList<IOObserver> io_observers_;
+ ThreadChecker watch_socket_caller_checker_;
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpIOStarboard);
+};
+
+} // namespace base
+
+#endif // BASE_MESSAGE_PUMP_IO_STARBOARD_H_
diff --git a/src/base/message_pump_libevent.cc b/src/base/message_pump_libevent.cc
new file mode 100644
index 0000000..14a4715
--- /dev/null
+++ b/src/base/message_pump_libevent.cc
@@ -0,0 +1,383 @@
+// Copyright (c) 2012 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/message_pump_libevent.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "base/auto_reset.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_nsautorelease_pool.h"
+#endif
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/time.h"
+#if defined(USE_SYSTEM_LIBEVENT)
+#include <event.h>
+#else
+#include "third_party/libevent/event.h"
+#endif
+
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_nsautorelease_pool.h"
+#endif
+
+// Lifecycle of struct event
+// Libevent uses two main data structures:
+// struct event_base (of which there is one per message pump), and
+// struct event (of which there is roughly one per socket).
+// The socket's struct event is created in
+// MessagePumpLibevent::WatchFileDescriptor(),
+// is owned by the FileDescriptorWatcher, and is destroyed in
+// StopWatchingFileDescriptor().
+// It is moved into and out of lists in struct event_base by
+// the libevent functions event_add() and event_del().
+//
+// TODO(dkegel):
+// At the moment bad things happen if a FileDescriptorWatcher
+// is active after its MessagePumpLibevent has been destroyed.
+// See MessageLoopTest.FileDescriptorWatcherOutlivesMessageLoop
+// Not clear yet whether that situation occurs in practice,
+// but if it does, we need to fix it.
+
+namespace base {
+
+// Return 0 on success
+// Too small a function to bother putting in a library?
+static int SetNonBlocking(int fd) {
+ int flags = fcntl(fd, F_GETFL, 0);
+ if (flags == -1)
+ flags = 0;
+ return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+}
+
+MessagePumpLibevent::FileDescriptorWatcher::FileDescriptorWatcher()
+ : event_(NULL),
+ pump_(NULL),
+ watcher_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
+}
+
+MessagePumpLibevent::FileDescriptorWatcher::~FileDescriptorWatcher() {
+ if (event_) {
+ StopWatchingFileDescriptor();
+ }
+}
+
+bool MessagePumpLibevent::FileDescriptorWatcher::StopWatchingFileDescriptor() {
+ event* e = ReleaseEvent();
+ if (e == NULL)
+ return true;
+
+ // event_del() is a no-op if the event isn't active.
+ int rv = event_del(e);
+ delete e;
+ pump_ = NULL;
+ watcher_ = NULL;
+ return (rv == 0);
+}
+
+void MessagePumpLibevent::FileDescriptorWatcher::Init(event *e) {
+ DCHECK(e);
+ DCHECK(!event_);
+
+ event_ = e;
+}
+
+event *MessagePumpLibevent::FileDescriptorWatcher::ReleaseEvent() {
+ struct event *e = event_;
+ event_ = NULL;
+ return e;
+}
+
+void MessagePumpLibevent::FileDescriptorWatcher::OnFileCanReadWithoutBlocking(
+ int fd, MessagePumpLibevent* pump) {
+ // Since OnFileCanWriteWithoutBlocking() gets called first, it can stop
+ // watching the file descriptor.
+ if (!watcher_)
+ return;
+ pump->WillProcessIOEvent();
+ watcher_->OnFileCanReadWithoutBlocking(fd);
+ pump->DidProcessIOEvent();
+}
+
+void MessagePumpLibevent::FileDescriptorWatcher::OnFileCanWriteWithoutBlocking(
+ int fd, MessagePumpLibevent* pump) {
+ DCHECK(watcher_);
+ pump->WillProcessIOEvent();
+ watcher_->OnFileCanWriteWithoutBlocking(fd);
+ pump->DidProcessIOEvent();
+}
+
+MessagePumpLibevent::MessagePumpLibevent()
+ : keep_running_(true),
+ in_run_(false),
+ processed_io_events_(false),
+ event_base_(event_base_new()),
+ wakeup_pipe_in_(-1),
+ wakeup_pipe_out_(-1) {
+ if (!Init())
+ NOTREACHED();
+}
+
+MessagePumpLibevent::~MessagePumpLibevent() {
+ DCHECK(wakeup_event_);
+ DCHECK(event_base_);
+ event_del(wakeup_event_);
+ delete wakeup_event_;
+ if (wakeup_pipe_in_ >= 0) {
+ if (HANDLE_EINTR(close(wakeup_pipe_in_)) < 0)
+ DPLOG(ERROR) << "close";
+ }
+ if (wakeup_pipe_out_ >= 0) {
+ if (HANDLE_EINTR(close(wakeup_pipe_out_)) < 0)
+ DPLOG(ERROR) << "close";
+ }
+ event_base_free(event_base_);
+}
+
+bool MessagePumpLibevent::WatchFileDescriptor(int fd,
+ bool persistent,
+ int mode,
+ FileDescriptorWatcher *controller,
+ Watcher *delegate) {
+ DCHECK_GE(fd, 0);
+ DCHECK(controller);
+ DCHECK(delegate);
+ DCHECK(mode == WATCH_READ || mode == WATCH_WRITE || mode == WATCH_READ_WRITE);
+ // WatchFileDescriptor should be called on the pump thread. It is not
+ // threadsafe, and your watcher may never be registered.
+ DCHECK(watch_file_descriptor_caller_checker_.CalledOnValidThread());
+
+ int event_mask = persistent ? EV_PERSIST : 0;
+ if (mode & WATCH_READ) {
+ event_mask |= EV_READ;
+ }
+ if (mode & WATCH_WRITE) {
+ event_mask |= EV_WRITE;
+ }
+
+ scoped_ptr<event> evt(controller->ReleaseEvent());
+ if (evt.get() == NULL) {
+ // Ownership is transferred to the controller.
+ evt.reset(new event);
+ } else {
+ // Make sure we don't pick up any funky internal libevent masks.
+ int old_interest_mask = evt.get()->ev_events &
+ (EV_READ | EV_WRITE | EV_PERSIST);
+
+ // Combine old/new event masks.
+ event_mask |= old_interest_mask;
+
+ // Must disarm the event before we can reuse it.
+ event_del(evt.get());
+
+ // It's illegal to use this function to listen on 2 separate fds with the
+ // same |controller|.
+ if (EVENT_FD(evt.get()) != fd) {
+ NOTREACHED() << "FDs don't match" << EVENT_FD(evt.get()) << "!=" << fd;
+ return false;
+ }
+ }
+
+ // Set current interest mask and message pump for this event.
+ event_set(evt.get(), fd, event_mask, OnLibeventNotification, controller);
+
+ // Tell libevent which message pump this socket will belong to when we add it.
+ if (event_base_set(event_base_, evt.get())) {
+ return false;
+ }
+
+ // Add this socket to the list of monitored sockets.
+ if (event_add(evt.get(), NULL)) {
+ return false;
+ }
+
+ // Transfer ownership of evt to controller.
+ controller->Init(evt.release());
+
+ controller->set_watcher(delegate);
+ controller->set_pump(this);
+
+ return true;
+}
+
+void MessagePumpLibevent::AddIOObserver(IOObserver *obs) {
+ io_observers_.AddObserver(obs);
+}
+
+void MessagePumpLibevent::RemoveIOObserver(IOObserver *obs) {
+ io_observers_.RemoveObserver(obs);
+}
+
+// Tell libevent to break out of inner loop.
+static void timer_callback(int fd, short events, void *context)
+{
+ event_base_loopbreak((struct event_base *)context);
+}
+
+// Reentrant!
+void MessagePumpLibevent::Run(Delegate* delegate) {
+ DCHECK(keep_running_) << "Quit must have been called outside of Run!";
+ base::AutoReset<bool> auto_reset_in_run(&in_run_, true);
+
+ // event_base_loopexit() + EVLOOP_ONCE is leaky, see http://crbug.com/25641.
+ // Instead, make our own timer and reuse it on each call to event_base_loop().
+ scoped_ptr<event> timer_event(new event);
+
+ for (;;) {
+#if defined(OS_MACOSX)
+ mac::ScopedNSAutoreleasePool autorelease_pool;
+#endif
+
+ bool did_work = delegate->DoWork();
+ if (!keep_running_)
+ break;
+
+ event_base_loop(event_base_, EVLOOP_NONBLOCK);
+ did_work |= processed_io_events_;
+ processed_io_events_ = false;
+ if (!keep_running_)
+ break;
+
+ did_work |= delegate->DoDelayedWork(&delayed_work_time_);
+ if (!keep_running_)
+ break;
+
+ if (did_work)
+ continue;
+
+ did_work = delegate->DoIdleWork();
+ if (!keep_running_)
+ break;
+
+ if (did_work)
+ continue;
+
+ // EVLOOP_ONCE tells libevent to only block once,
+ // but to service all pending events when it wakes up.
+ if (delayed_work_time_.is_null()) {
+ event_base_loop(event_base_, EVLOOP_ONCE);
+ } else {
+ TimeDelta delay = delayed_work_time_ - TimeTicks::Now();
+ if (delay > TimeDelta()) {
+ struct timeval poll_tv;
+ poll_tv.tv_sec = delay.InSeconds();
+ poll_tv.tv_usec = delay.InMicroseconds() % Time::kMicrosecondsPerSecond;
+ event_set(timer_event.get(), -1, 0, timer_callback, event_base_);
+ event_base_set(event_base_, timer_event.get());
+ event_add(timer_event.get(), &poll_tv);
+ event_base_loop(event_base_, EVLOOP_ONCE);
+ event_del(timer_event.get());
+ } else {
+ // It looks like delayed_work_time_ indicates a time in the past, so we
+ // need to call DoDelayedWork now.
+ delayed_work_time_ = TimeTicks();
+ }
+ }
+ }
+
+ keep_running_ = true;
+}
+
+void MessagePumpLibevent::Quit() {
+ DCHECK(in_run_);
+ // Tell both libevent and Run that they should break out of their loops.
+ keep_running_ = false;
+ ScheduleWork();
+}
+
+void MessagePumpLibevent::ScheduleWork() {
+ // Tell libevent (in a threadsafe way) that it should break out of its loop.
+ char buf = 0;
+ int nwrite = HANDLE_EINTR(write(wakeup_pipe_in_, &buf, 1));
+ DCHECK(nwrite == 1 || errno == EAGAIN)
+ << "[nwrite:" << nwrite << "] [errno:" << errno << "]";
+}
+
+void MessagePumpLibevent::ScheduleDelayedWork(
+ const TimeTicks& delayed_work_time) {
+ // We know that we can't be blocked on Wait right now since this method can
+ // only be called on the same thread as Run, so we only need to update our
+ // record of how long to sleep when we do sleep.
+ delayed_work_time_ = delayed_work_time;
+}
+
+void MessagePumpLibevent::WillProcessIOEvent() {
+ FOR_EACH_OBSERVER(IOObserver, io_observers_, WillProcessIOEvent());
+}
+
+void MessagePumpLibevent::DidProcessIOEvent() {
+ FOR_EACH_OBSERVER(IOObserver, io_observers_, DidProcessIOEvent());
+}
+
+bool MessagePumpLibevent::Init() {
+ int fds[2];
+ if (pipe(fds)) {
+ DLOG(ERROR) << "pipe() failed, errno: " << errno;
+ return false;
+ }
+ if (SetNonBlocking(fds[0])) {
+ DLOG(ERROR) << "SetNonBlocking for pipe fd[0] failed, errno: " << errno;
+ return false;
+ }
+ if (SetNonBlocking(fds[1])) {
+ DLOG(ERROR) << "SetNonBlocking for pipe fd[1] failed, errno: " << errno;
+ return false;
+ }
+ wakeup_pipe_out_ = fds[0];
+ wakeup_pipe_in_ = fds[1];
+
+ wakeup_event_ = new event;
+ event_set(wakeup_event_, wakeup_pipe_out_, EV_READ | EV_PERSIST,
+ OnWakeup, this);
+ event_base_set(event_base_, wakeup_event_);
+
+ if (event_add(wakeup_event_, 0))
+ return false;
+ return true;
+}
+
+// static
+void MessagePumpLibevent::OnLibeventNotification(int fd, short flags,
+ void* context) {
+ base::WeakPtr<FileDescriptorWatcher> controller =
+ static_cast<FileDescriptorWatcher*>(context)->weak_factory_.GetWeakPtr();
+ DCHECK(controller.get());
+
+ MessagePumpLibevent* pump = controller->pump();
+ pump->processed_io_events_ = true;
+
+ if (flags & EV_WRITE) {
+ controller->OnFileCanWriteWithoutBlocking(fd, pump);
+ }
+ // Check |controller| in case it's been deleted in
+ // controller->OnFileCanWriteWithoutBlocking().
+ if (controller.get() && flags & EV_READ) {
+ controller->OnFileCanReadWithoutBlocking(fd, pump);
+ }
+}
+
+// Called if a byte is received on the wakeup pipe.
+// static
+void MessagePumpLibevent::OnWakeup(int socket, short flags, void* context) {
+ base::MessagePumpLibevent* that =
+ static_cast<base::MessagePumpLibevent*>(context);
+ DCHECK(that->wakeup_pipe_out_ == socket);
+
+ // Remove and discard the wakeup byte.
+ char buf;
+ int nread = HANDLE_EINTR(read(socket, &buf, 1));
+ DCHECK_EQ(nread, 1);
+ that->processed_io_events_ = true;
+ // Tell libevent to break out of inner loop.
+ event_base_loopbreak(that->event_base_);
+}
+
+} // namespace base
diff --git a/src/base/message_pump_libevent.h b/src/base/message_pump_libevent.h
new file mode 100644
index 0000000..90e8510
--- /dev/null
+++ b/src/base/message_pump_libevent.h
@@ -0,0 +1,179 @@
+// Copyright (c) 2012 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_MESSAGE_PUMP_LIBEVENT_H_
+#define BASE_MESSAGE_PUMP_LIBEVENT_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_pump.h"
+#include "base/observer_list.h"
+#include "base/threading/thread_checker.h"
+#include "base/time.h"
+
+// Declare structs we need from libevent.h rather than including it
+struct event_base;
+struct event;
+
+namespace base {
+
+// Class to monitor sockets and issue callbacks when sockets are ready for I/O
+// TODO(dkegel): add support for background file IO somehow
+class BASE_EXPORT MessagePumpLibevent : public MessagePump {
+ public:
+ class IOObserver {
+ public:
+ IOObserver() {}
+
+ // An IOObserver is an object that receives IO notifications from the
+ // MessagePump.
+ //
+ // NOTE: An IOObserver implementation should be extremely fast!
+ virtual void WillProcessIOEvent() = 0;
+ virtual void DidProcessIOEvent() = 0;
+
+ protected:
+ virtual ~IOObserver() {}
+ };
+
+ // Used with WatchFileDescriptor to asynchronously monitor the I/O readiness
+ // of a file descriptor.
+ class Watcher {
+ public:
+ // Called from MessageLoop::Run when an FD can be read from/written to
+ // without blocking
+ virtual void OnFileCanReadWithoutBlocking(int fd) = 0;
+ virtual void OnFileCanWriteWithoutBlocking(int fd) = 0;
+
+ protected:
+ virtual ~Watcher() {}
+ };
+
+ // Object returned by WatchFileDescriptor to manage further watching.
+ class FileDescriptorWatcher {
+ public:
+ FileDescriptorWatcher();
+ ~FileDescriptorWatcher(); // Implicitly calls StopWatchingFileDescriptor.
+
+ // NOTE: These methods aren't called StartWatching()/StopWatching() to
+ // avoid confusion with the win32 ObjectWatcher class.
+
+ // Stop watching the FD, always safe to call. No-op if there's nothing
+ // to do.
+ bool StopWatchingFileDescriptor();
+
+ private:
+ friend class MessagePumpLibevent;
+ friend class MessagePumpLibeventTest;
+
+ // Called by MessagePumpLibevent, ownership of |e| is transferred to this
+ // object.
+ void Init(event* e);
+
+ // Used by MessagePumpLibevent to take ownership of event_.
+ event* ReleaseEvent();
+
+ void set_pump(MessagePumpLibevent* pump) { pump_ = pump; }
+ MessagePumpLibevent* pump() const { return pump_; }
+
+ void set_watcher(Watcher* watcher) { watcher_ = watcher; }
+
+ void OnFileCanReadWithoutBlocking(int fd, MessagePumpLibevent* pump);
+ void OnFileCanWriteWithoutBlocking(int fd, MessagePumpLibevent* pump);
+
+ event* event_;
+ MessagePumpLibevent* pump_;
+ Watcher* watcher_;
+ base::WeakPtrFactory<FileDescriptorWatcher> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcher);
+ };
+
+ enum Mode {
+ WATCH_READ = 1 << 0,
+ WATCH_WRITE = 1 << 1,
+ WATCH_READ_WRITE = WATCH_READ | WATCH_WRITE
+ };
+
+ MessagePumpLibevent();
+
+ // Have the current thread's message loop watch for a a situation in which
+ // reading/writing to the FD can be performed without blocking.
+ // Callers must provide a preallocated FileDescriptorWatcher object which
+ // can later be used to manage the lifetime of this event.
+ // If a FileDescriptorWatcher is passed in which is already attached to
+ // an event, then the effect is cumulative i.e. after the call |controller|
+ // will watch both the previous event and the new one.
+ // If an error occurs while calling this method in a cumulative fashion, the
+ // event previously attached to |controller| is aborted.
+ // Returns true on success.
+ // Must be called on the same thread the message_pump is running on.
+ // TODO(dkegel): switch to edge-triggered readiness notification
+ bool WatchFileDescriptor(int fd,
+ bool persistent,
+ int mode,
+ FileDescriptorWatcher *controller,
+ Watcher *delegate);
+
+ void AddIOObserver(IOObserver* obs);
+ void RemoveIOObserver(IOObserver* obs);
+
+ // MessagePump methods:
+ virtual void Run(Delegate* delegate) OVERRIDE;
+ virtual void Quit() OVERRIDE;
+ virtual void ScheduleWork() OVERRIDE;
+ virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) OVERRIDE;
+
+ protected:
+ virtual ~MessagePumpLibevent();
+
+ private:
+ friend class MessagePumpLibeventTest;
+
+ void WillProcessIOEvent();
+ void DidProcessIOEvent();
+
+ // Risky part of constructor. Returns true on success.
+ bool Init();
+
+ // Called by libevent to tell us a registered FD can be read/written to.
+ static void OnLibeventNotification(int fd, short flags,
+ void* context);
+
+ // Unix pipe used to implement ScheduleWork()
+ // ... callback; called by libevent inside Run() when pipe is ready to read
+ static void OnWakeup(int socket, short flags, void* context);
+
+ // This flag is set to false when Run should return.
+ bool keep_running_;
+
+ // This flag is set when inside Run.
+ bool in_run_;
+
+ // This flag is set if libevent has processed I/O events.
+ bool processed_io_events_;
+
+ // The time at which we should call DoDelayedWork.
+ TimeTicks delayed_work_time_;
+
+ // Libevent dispatcher. Watches all sockets registered with it, and sends
+ // readiness callbacks when a socket is ready for I/O.
+ event_base* event_base_;
+
+ // ... write end; ScheduleWork() writes a single byte to it
+ int wakeup_pipe_in_;
+ // ... read end; OnWakeup reads it and then breaks Run() out of its sleep
+ int wakeup_pipe_out_;
+ // ... libevent wrapper for read end
+ event* wakeup_event_;
+
+ ObserverList<IOObserver> io_observers_;
+ ThreadChecker watch_file_descriptor_caller_checker_;
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpLibevent);
+};
+
+} // namespace base
+
+#endif // BASE_MESSAGE_PUMP_LIBEVENT_H_
diff --git a/src/base/message_pump_libevent_unittest.cc b/src/base/message_pump_libevent_unittest.cc
new file mode 100644
index 0000000..e48a9c5
--- /dev/null
+++ b/src/base/message_pump_libevent_unittest.cc
@@ -0,0 +1,167 @@
+// Copyright (c) 2012 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/message_pump_libevent.h"
+
+#include <unistd.h>
+
+#include "base/message_loop.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(USE_SYSTEM_LIBEVENT)
+#include <event.h>
+#else
+#include "third_party/libevent/event.h"
+#endif
+
+namespace base {
+
+class MessagePumpLibeventTest : public testing::Test {
+ protected:
+ MessagePumpLibeventTest()
+ : ui_loop_(MessageLoop::TYPE_UI),
+ io_thread_("MessagePumpLibeventTestIOThread") {}
+ virtual ~MessagePumpLibeventTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ Thread::Options options(MessageLoop::TYPE_IO, 0);
+ ASSERT_TRUE(io_thread_.StartWithOptions(options));
+ ASSERT_EQ(MessageLoop::TYPE_IO, io_thread_.message_loop()->type());
+ int ret = pipe(pipefds_);
+ ASSERT_EQ(0, ret);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ if (HANDLE_EINTR(close(pipefds_[0])) < 0)
+ PLOG(ERROR) << "close";
+ if (HANDLE_EINTR(close(pipefds_[1])) < 0)
+ PLOG(ERROR) << "close";
+ }
+
+ MessageLoop* ui_loop() { return &ui_loop_; }
+ MessageLoopForIO* io_loop() const {
+ return static_cast<MessageLoopForIO*>(io_thread_.message_loop());
+ }
+
+ void OnLibeventNotification(
+ MessagePumpLibevent* pump,
+ MessagePumpLibevent::FileDescriptorWatcher* controller) {
+ pump->OnLibeventNotification(0, EV_WRITE | EV_READ, controller);
+ }
+
+ int pipefds_[2];
+
+ private:
+ MessageLoop ui_loop_;
+ Thread io_thread_;
+};
+
+namespace {
+
+// Concrete implementation of MessagePumpLibevent::Watcher that does
+// nothing useful.
+class StupidWatcher : public MessagePumpLibevent::Watcher {
+ public:
+ virtual ~StupidWatcher() {}
+
+ // base:MessagePumpLibevent::Watcher interface
+ virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {}
+ virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {}
+};
+
+#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+
+// Test to make sure that we catch calling WatchFileDescriptor off of the
+// wrong thread.
+TEST_F(MessagePumpLibeventTest, TestWatchingFromBadThread) {
+ MessagePumpLibevent::FileDescriptorWatcher watcher;
+ StupidWatcher delegate;
+
+ ASSERT_DEATH(io_loop()->WatchFileDescriptor(
+ STDOUT_FILENO, false, MessageLoopForIO::WATCH_READ, &watcher, &delegate),
+ "Check failed: "
+ "watch_file_descriptor_caller_checker_.CalledOnValidThread()");
+}
+
+#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+
+class BaseWatcher : public MessagePumpLibevent::Watcher {
+ public:
+ BaseWatcher(MessagePumpLibevent::FileDescriptorWatcher* controller)
+ : controller_(controller) {
+ DCHECK(controller_);
+ }
+ virtual ~BaseWatcher() {}
+
+ // base:MessagePumpLibevent::Watcher interface
+ virtual void OnFileCanReadWithoutBlocking(int /* fd */) OVERRIDE {
+ NOTREACHED();
+ }
+
+ virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE {
+ NOTREACHED();
+ }
+
+ protected:
+ MessagePumpLibevent::FileDescriptorWatcher* controller_;
+};
+
+class DeleteWatcher : public BaseWatcher {
+ public:
+ explicit DeleteWatcher(
+ MessagePumpLibevent::FileDescriptorWatcher* controller)
+ : BaseWatcher(controller) {}
+
+ virtual ~DeleteWatcher() {
+ DCHECK(!controller_);
+ }
+
+ virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE {
+ DCHECK(controller_);
+ delete controller_;
+ controller_ = NULL;
+ }
+};
+
+TEST_F(MessagePumpLibeventTest, DeleteWatcher) {
+ scoped_refptr<MessagePumpLibevent> pump(new MessagePumpLibevent);
+ MessagePumpLibevent::FileDescriptorWatcher* watcher =
+ new MessagePumpLibevent::FileDescriptorWatcher;
+ DeleteWatcher delegate(watcher);
+ pump->WatchFileDescriptor(pipefds_[1],
+ false, MessagePumpLibevent::WATCH_READ_WRITE, watcher, &delegate);
+
+ // Spoof a libevent notification.
+ OnLibeventNotification(pump, watcher);
+}
+
+class StopWatcher : public BaseWatcher {
+ public:
+ explicit StopWatcher(
+ MessagePumpLibevent::FileDescriptorWatcher* controller)
+ : BaseWatcher(controller) {}
+
+ virtual ~StopWatcher() {}
+
+ virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE {
+ controller_->StopWatchingFileDescriptor();
+ }
+};
+
+TEST_F(MessagePumpLibeventTest, StopWatcher) {
+ scoped_refptr<MessagePumpLibevent> pump(new MessagePumpLibevent);
+ MessagePumpLibevent::FileDescriptorWatcher watcher;
+ StopWatcher delegate(&watcher);
+ pump->WatchFileDescriptor(pipefds_[1],
+ false, MessagePumpLibevent::WATCH_READ_WRITE, &watcher, &delegate);
+
+ // Spoof a libevent notification.
+ OnLibeventNotification(pump, &watcher);
+}
+
+} // namespace
+
+} // namespace base
diff --git a/src/base/message_pump_mac.h b/src/base/message_pump_mac.h
new file mode 100644
index 0000000..2dd5ecf
--- /dev/null
+++ b/src/base/message_pump_mac.h
@@ -0,0 +1,337 @@
+// Copyright (c) 2012 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.
+
+// The basis for all native run loops on the Mac is the CFRunLoop. It can be
+// used directly, it can be used as the driving force behind the similar
+// Foundation NSRunLoop, and it can be used to implement higher-level event
+// loops such as the NSApplication event loop.
+//
+// This file introduces a basic CFRunLoop-based implementation of the
+// MessagePump interface called CFRunLoopBase. CFRunLoopBase contains all
+// of the machinery necessary to dispatch events to a delegate, but does not
+// implement the specific run loop. Concrete subclasses must provide their
+// own DoRun and Quit implementations.
+//
+// A concrete subclass that just runs a CFRunLoop loop is provided in
+// MessagePumpCFRunLoop. For an NSRunLoop, the similar MessagePumpNSRunLoop
+// is provided.
+//
+// For the application's event loop, an implementation based on AppKit's
+// NSApplication event system is provided in MessagePumpNSApplication.
+//
+// Typically, MessagePumpNSApplication only makes sense on a Cocoa
+// application's main thread. If a CFRunLoop-based message pump is needed on
+// any other thread, one of the other concrete subclasses is preferrable.
+// MessagePumpMac::Create is defined, which returns a new NSApplication-based
+// or NSRunLoop-based MessagePump subclass depending on which thread it is
+// called on.
+
+#ifndef BASE_MESSAGE_PUMP_MAC_H_
+#define BASE_MESSAGE_PUMP_MAC_H_
+
+#include "base/message_pump.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#if !defined(__OBJC__)
+class NSAutoreleasePool;
+#else // !defined(__OBJC__)
+#if defined(OS_IOS)
+#import <Foundation/Foundation.h>
+#else
+#import <AppKit/AppKit.h>
+
+// Clients must subclass NSApplication and implement this protocol if they use
+// MessagePumpMac.
+@protocol CrAppProtocol
+// Must return true if -[NSApplication sendEvent:] is currently on the stack.
+// See the comment for |CreateAutoreleasePool()| in the cc file for why this is
+// necessary.
+- (BOOL)isHandlingSendEvent;
+@end
+#endif // !defined(OS_IOS)
+#endif // !defined(__OBJC__)
+
+namespace base {
+
+class RunLoop;
+class TimeTicks;
+
+class MessagePumpCFRunLoopBase : public MessagePump {
+ // Needs access to CreateAutoreleasePool.
+ friend class MessagePumpScopedAutoreleasePool;
+ public:
+ MessagePumpCFRunLoopBase();
+
+ // Subclasses should implement the work they need to do in MessagePump::Run
+ // in the DoRun method. MessagePumpCFRunLoopBase::Run calls DoRun directly.
+ // This arrangement is used because MessagePumpCFRunLoopBase needs to set
+ // up and tear down things before and after the "meat" of DoRun.
+ virtual void Run(Delegate* delegate) OVERRIDE;
+ virtual void DoRun(Delegate* delegate) = 0;
+
+ virtual void ScheduleWork() OVERRIDE;
+ virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) OVERRIDE;
+
+ protected:
+ virtual ~MessagePumpCFRunLoopBase();
+
+ // Accessors for private data members to be used by subclasses.
+ CFRunLoopRef run_loop() const { return run_loop_; }
+ int nesting_level() const { return nesting_level_; }
+ int run_nesting_level() const { return run_nesting_level_; }
+
+ // Sets this pump's delegate. Signals the appropriate sources if
+ // |delegateless_work_| is true. |delegate| can be NULL.
+ void SetDelegate(Delegate* delegate);
+
+ // Return an autorelease pool to wrap around any work being performed.
+ // In some cases, CreateAutoreleasePool may return nil intentionally to
+ // preventing an autorelease pool from being created, allowing any
+ // objects autoreleased by work to fall into the current autorelease pool.
+ virtual NSAutoreleasePool* CreateAutoreleasePool();
+
+ private:
+ // Timer callback scheduled by ScheduleDelayedWork. This does not do any
+ // work, but it signals work_source_ so that delayed work can be performed
+ // within the appropriate priority constraints.
+ static void RunDelayedWorkTimer(CFRunLoopTimerRef timer, void* info);
+
+ // Perform highest-priority work. This is associated with work_source_
+ // signalled by ScheduleWork or RunDelayedWorkTimer. The static method calls
+ // the instance method; the instance method returns true if it resignalled
+ // work_source_ to be called again from the loop.
+ static void RunWorkSource(void* info);
+ bool RunWork();
+
+ // Perform idle-priority work. This is normally called by PreWaitObserver,
+ // but is also associated with idle_work_source_. When this function
+ // actually does perform idle work, it will resignal that source. The
+ // static method calls the instance method; the instance method returns
+ // true if idle work was done.
+ static void RunIdleWorkSource(void* info);
+ bool RunIdleWork();
+
+ // Perform work that may have been deferred because it was not runnable
+ // within a nested run loop. This is associated with
+ // nesting_deferred_work_source_ and is signalled by
+ // MaybeScheduleNestingDeferredWork when returning from a nested loop,
+ // so that an outer loop will be able to perform the necessary tasks if it
+ // permits nestable tasks.
+ static void RunNestingDeferredWorkSource(void* info);
+ bool RunNestingDeferredWork();
+
+ // Schedules possible nesting-deferred work to be processed before the run
+ // loop goes to sleep, exits, or begins processing sources at the top of its
+ // loop. If this function detects that a nested loop had run since the
+ // previous attempt to schedule nesting-deferred work, it will schedule a
+ // call to RunNestingDeferredWorkSource.
+ void MaybeScheduleNestingDeferredWork();
+
+ // Observer callback responsible for performing idle-priority work, before
+ // the run loop goes to sleep. Associated with idle_work_observer_.
+ static void PreWaitObserver(CFRunLoopObserverRef observer,
+ CFRunLoopActivity activity, void* info);
+
+ // Observer callback called before the run loop processes any sources.
+ // Associated with pre_source_observer_.
+ static void PreSourceObserver(CFRunLoopObserverRef observer,
+ CFRunLoopActivity activity, void* info);
+
+ // Observer callback called when the run loop starts and stops, at the
+ // beginning and end of calls to CFRunLoopRun. This is used to maintain
+ // nesting_level_. Associated with enter_exit_observer_.
+ static void EnterExitObserver(CFRunLoopObserverRef observer,
+ CFRunLoopActivity activity, void* info);
+
+ // Called by EnterExitObserver after performing maintenance on nesting_level_.
+ // This allows subclasses an opportunity to perform additional processing on
+ // the basis of run loops starting and stopping.
+ virtual void EnterExitRunLoop(CFRunLoopActivity activity);
+
+ // The thread's run loop.
+ CFRunLoopRef run_loop_;
+
+ // The timer, sources, and observers are described above alongside their
+ // callbacks.
+ CFRunLoopTimerRef delayed_work_timer_;
+ CFRunLoopSourceRef work_source_;
+ CFRunLoopSourceRef idle_work_source_;
+ CFRunLoopSourceRef nesting_deferred_work_source_;
+ CFRunLoopObserverRef pre_wait_observer_;
+ CFRunLoopObserverRef pre_source_observer_;
+ CFRunLoopObserverRef enter_exit_observer_;
+
+ // (weak) Delegate passed as an argument to the innermost Run call.
+ Delegate* delegate_;
+
+ // The time that delayed_work_timer_ is scheduled to fire. This is tracked
+ // independently of CFRunLoopTimerGetNextFireDate(delayed_work_timer_)
+ // to be able to reset the timer properly after waking from system sleep.
+ // See PowerStateNotification.
+ CFAbsoluteTime delayed_work_fire_time_;
+
+ // The recursion depth of the currently-executing CFRunLoopRun loop on the
+ // run loop's thread. 0 if no run loops are running inside of whatever scope
+ // the object was created in.
+ int nesting_level_;
+
+ // The recursion depth (calculated in the same way as nesting_level_) of the
+ // innermost executing CFRunLoopRun loop started by a call to Run.
+ int run_nesting_level_;
+
+ // The deepest (numerically highest) recursion depth encountered since the
+ // most recent attempt to run nesting-deferred work.
+ int deepest_nesting_level_;
+
+ // "Delegateless" work flags are set when work is ready to be performed but
+ // must wait until a delegate is available to process it. This can happen
+ // when a MessagePumpCFRunLoopBase is instantiated and work arrives without
+ // any call to Run on the stack. The Run method will check for delegateless
+ // work on entry and redispatch it as needed once a delegate is available.
+ bool delegateless_work_;
+ bool delegateless_idle_work_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoopBase);
+};
+
+class MessagePumpCFRunLoop : public MessagePumpCFRunLoopBase {
+ public:
+ MessagePumpCFRunLoop();
+
+ virtual void DoRun(Delegate* delegate) OVERRIDE;
+ virtual void Quit() OVERRIDE;
+
+ protected:
+ virtual ~MessagePumpCFRunLoop();
+
+ private:
+ virtual void EnterExitRunLoop(CFRunLoopActivity activity) OVERRIDE;
+
+ // True if Quit is called to stop the innermost MessagePump
+ // (innermost_quittable_) but some other CFRunLoopRun loop (nesting_level_)
+ // is running inside the MessagePump's innermost Run call.
+ bool quit_pending_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoop);
+};
+
+class MessagePumpNSRunLoop : public MessagePumpCFRunLoopBase {
+ public:
+ BASE_EXPORT MessagePumpNSRunLoop();
+
+ virtual void DoRun(Delegate* delegate) OVERRIDE;
+ virtual void Quit() OVERRIDE;
+
+ protected:
+ virtual ~MessagePumpNSRunLoop();
+
+ private:
+ // A source that doesn't do anything but provide something signalable
+ // attached to the run loop. This source will be signalled when Quit
+ // is called, to cause the loop to wake up so that it can stop.
+ CFRunLoopSourceRef quit_source_;
+
+ // False after Quit is called.
+ bool keep_running_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpNSRunLoop);
+};
+
+#if defined(OS_IOS)
+// This is a fake message pump. It attaches sources to the main thread's
+// CFRunLoop, so PostTask() will work, but it is unable to drive the loop
+// directly, so calling Run() or Quit() are errors.
+class MessagePumpUIApplication : public MessagePumpCFRunLoopBase {
+ public:
+ MessagePumpUIApplication();
+ virtual void DoRun(Delegate* delegate) OVERRIDE;
+ virtual void Quit() OVERRIDE;
+
+ // This message pump can not spin the main message loop directly. Instead,
+ // call |Attach()| to set up a delegate. It is an error to call |Run()|.
+ virtual void Attach(Delegate* delegate);
+
+ protected:
+ virtual ~MessagePumpUIApplication();
+
+ private:
+ base::RunLoop* run_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpUIApplication);
+};
+
+#else
+
+class MessagePumpNSApplication : public MessagePumpCFRunLoopBase {
+ public:
+ MessagePumpNSApplication();
+
+ virtual void DoRun(Delegate* delegate) OVERRIDE;
+ virtual void Quit() OVERRIDE;
+
+ protected:
+ virtual ~MessagePumpNSApplication();
+
+ private:
+ // False after Quit is called.
+ bool keep_running_;
+
+ // True if DoRun is managing its own run loop as opposed to letting
+ // -[NSApplication run] handle it. The outermost run loop in the application
+ // is managed by -[NSApplication run], inner run loops are handled by a loop
+ // in DoRun.
+ bool running_own_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpNSApplication);
+};
+
+class MessagePumpCrApplication : public MessagePumpNSApplication {
+ public:
+ MessagePumpCrApplication();
+
+ protected:
+ virtual ~MessagePumpCrApplication() {}
+
+ // Returns nil if NSApp is currently in the middle of calling
+ // -sendEvent. Requires NSApp implementing CrAppProtocol.
+ virtual NSAutoreleasePool* CreateAutoreleasePool() OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpCrApplication);
+};
+#endif // !defined(OS_IOS)
+
+class MessagePumpMac {
+ public:
+ // If not on the main thread, returns a new instance of
+ // MessagePumpNSRunLoop.
+ //
+ // On the main thread, if NSApp exists and conforms to
+ // CrAppProtocol, creates an instances of MessagePumpCrApplication.
+ //
+ // Otherwise creates an instance of MessagePumpNSApplication using a
+ // default NSApplication.
+ static MessagePump* Create();
+
+#if !defined(OS_IOS)
+ // If a pump is created before the required CrAppProtocol is
+ // created, the wrong MessagePump subclass could be used.
+ // UsingCrApp() returns false if the message pump was created before
+ // NSApp was initialized, or if NSApp does not implement
+ // CrAppProtocol. NSApp must be initialized before calling.
+ BASE_EXPORT static bool UsingCrApp();
+
+ // Wrapper to query -[NSApp isHandlingSendEvent] from C++ code.
+ // Requires NSApp to implement CrAppProtocol.
+ BASE_EXPORT static bool IsHandlingSendEvent();
+#endif // !defined(OS_IOS)
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(MessagePumpMac);
+};
+
+} // namespace base
+
+#endif // BASE_MESSAGE_PUMP_MAC_H_
diff --git a/src/base/message_pump_mac.mm b/src/base/message_pump_mac.mm
new file mode 100644
index 0000000..3878eb8
--- /dev/null
+++ b/src/base/message_pump_mac.mm
@@ -0,0 +1,699 @@
+// Copyright (c) 2012 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.
+
+#import "base/message_pump_mac.h"
+
+#import <Foundation/Foundation.h>
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "base/time.h"
+
+#if !defined(OS_IOS)
+#import <AppKit/AppKit.h>
+#endif // !defined(OS_IOS)
+
+namespace {
+
+void NoOp(void* info) {
+}
+
+const CFTimeInterval kCFTimeIntervalMax =
+ std::numeric_limits<CFTimeInterval>::max();
+
+// Set to true if MessagePumpMac::Create() is called before NSApp is
+// initialized. Only accessed from the main thread.
+bool not_using_crapp = false;
+
+} // namespace
+
+namespace base {
+
+// A scoper for autorelease pools created from message pump run loops.
+// Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare
+// case where an autorelease pool needs to be passed in.
+class MessagePumpScopedAutoreleasePool {
+ public:
+ explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) :
+ pool_(pump->CreateAutoreleasePool()) {
+ }
+ ~MessagePumpScopedAutoreleasePool() {
+ [pool_ drain];
+ }
+
+ private:
+ NSAutoreleasePool* pool_;
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool);
+};
+
+// Must be called on the run loop thread.
+MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
+ : delegate_(NULL),
+ delayed_work_fire_time_(kCFTimeIntervalMax),
+ nesting_level_(0),
+ run_nesting_level_(0),
+ deepest_nesting_level_(0),
+ delegateless_work_(false),
+ delegateless_idle_work_(false) {
+ run_loop_ = CFRunLoopGetCurrent();
+ CFRetain(run_loop_);
+
+ // Set a repeating timer with a preposterous firing time and interval. The
+ // timer will effectively never fire as-is. The firing time will be adjusted
+ // as needed when ScheduleDelayedWork is called.
+ CFRunLoopTimerContext timer_context = CFRunLoopTimerContext();
+ timer_context.info = this;
+ delayed_work_timer_ = CFRunLoopTimerCreate(NULL, // allocator
+ kCFTimeIntervalMax, // fire time
+ kCFTimeIntervalMax, // interval
+ 0, // flags
+ 0, // priority
+ RunDelayedWorkTimer,
+ &timer_context);
+ CFRunLoopAddTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes);
+
+ CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
+ source_context.info = this;
+ source_context.perform = RunWorkSource;
+ work_source_ = CFRunLoopSourceCreate(NULL, // allocator
+ 1, // priority
+ &source_context);
+ CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes);
+
+ source_context.perform = RunIdleWorkSource;
+ idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator
+ 2, // priority
+ &source_context);
+ CFRunLoopAddSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes);
+
+ source_context.perform = RunNestingDeferredWorkSource;
+ nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator
+ 0, // priority
+ &source_context);
+ CFRunLoopAddSource(run_loop_, nesting_deferred_work_source_,
+ kCFRunLoopCommonModes);
+
+ CFRunLoopObserverContext observer_context = CFRunLoopObserverContext();
+ observer_context.info = this;
+ pre_wait_observer_ = CFRunLoopObserverCreate(NULL, // allocator
+ kCFRunLoopBeforeWaiting,
+ true, // repeat
+ 0, // priority
+ PreWaitObserver,
+ &observer_context);
+ CFRunLoopAddObserver(run_loop_, pre_wait_observer_, kCFRunLoopCommonModes);
+
+ pre_source_observer_ = CFRunLoopObserverCreate(NULL, // allocator
+ kCFRunLoopBeforeSources,
+ true, // repeat
+ 0, // priority
+ PreSourceObserver,
+ &observer_context);
+ CFRunLoopAddObserver(run_loop_, pre_source_observer_, kCFRunLoopCommonModes);
+
+ enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator
+ kCFRunLoopEntry |
+ kCFRunLoopExit,
+ true, // repeat
+ 0, // priority
+ EnterExitObserver,
+ &observer_context);
+ CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes);
+}
+
+// Ideally called on the run loop thread. If other run loops were running
+// lower on the run loop thread's stack when this object was created, the
+// same number of run loops must be running when this object is destroyed.
+MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() {
+ CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_,
+ kCFRunLoopCommonModes);
+ CFRelease(enter_exit_observer_);
+
+ CFRunLoopRemoveObserver(run_loop_, pre_source_observer_,
+ kCFRunLoopCommonModes);
+ CFRelease(pre_source_observer_);
+
+ CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_,
+ kCFRunLoopCommonModes);
+ CFRelease(pre_wait_observer_);
+
+ CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_,
+ kCFRunLoopCommonModes);
+ CFRelease(nesting_deferred_work_source_);
+
+ CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes);
+ CFRelease(idle_work_source_);
+
+ CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes);
+ CFRelease(work_source_);
+
+ CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes);
+ CFRelease(delayed_work_timer_);
+
+ CFRelease(run_loop_);
+}
+
+// Must be called on the run loop thread.
+void MessagePumpCFRunLoopBase::Run(Delegate* delegate) {
+ // nesting_level_ will be incremented in EnterExitRunLoop, so set
+ // run_nesting_level_ accordingly.
+ int last_run_nesting_level = run_nesting_level_;
+ run_nesting_level_ = nesting_level_ + 1;
+
+ Delegate* last_delegate = delegate_;
+ SetDelegate(delegate);
+
+ DoRun(delegate);
+
+ // Restore the previous state of the object.
+ SetDelegate(last_delegate);
+ run_nesting_level_ = last_run_nesting_level;
+}
+
+void MessagePumpCFRunLoopBase::SetDelegate(Delegate* delegate) {
+ delegate_ = delegate;
+
+ if (delegate) {
+ // If any work showed up but could not be dispatched for want of a
+ // delegate, set it up for dispatch again now that a delegate is
+ // available.
+ if (delegateless_work_) {
+ CFRunLoopSourceSignal(work_source_);
+ delegateless_work_ = false;
+ }
+ if (delegateless_idle_work_) {
+ CFRunLoopSourceSignal(idle_work_source_);
+ delegateless_idle_work_ = false;
+ }
+ }
+}
+
+// May be called on any thread.
+void MessagePumpCFRunLoopBase::ScheduleWork() {
+ CFRunLoopSourceSignal(work_source_);
+ CFRunLoopWakeUp(run_loop_);
+}
+
+// Must be called on the run loop thread.
+void MessagePumpCFRunLoopBase::ScheduleDelayedWork(
+ const TimeTicks& delayed_work_time) {
+ TimeDelta delta = delayed_work_time - TimeTicks::Now();
+ delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF();
+ CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_);
+}
+
+// Called from the run loop.
+// static
+void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer,
+ void* info) {
+ MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
+
+ // The timer won't fire again until it's reset.
+ self->delayed_work_fire_time_ = kCFTimeIntervalMax;
+
+ // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources.
+ // In order to establish the proper priority in which work and delayed work
+ // are processed one for one, the timer used to schedule delayed work must
+ // signal a CFRunLoopSource used to dispatch both work and delayed work.
+ CFRunLoopSourceSignal(self->work_source_);
+}
+
+// Called from the run loop.
+// static
+void MessagePumpCFRunLoopBase::RunWorkSource(void* info) {
+ MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
+ self->RunWork();
+}
+
+// Called by MessagePumpCFRunLoopBase::RunWorkSource.
+bool MessagePumpCFRunLoopBase::RunWork() {
+ if (!delegate_) {
+ // This point can be reached with a NULL delegate_ if Run is not on the
+ // stack but foreign code is spinning the CFRunLoop. Arrange to come back
+ // here when a delegate is available.
+ delegateless_work_ = true;
+ return false;
+ }
+
+ // The NSApplication-based run loop only drains the autorelease pool at each
+ // UI event (NSEvent). The autorelease pool is not drained for each
+ // CFRunLoopSource target that's run. Use a local pool for any autoreleased
+ // objects if the app is not currently handling a UI event to ensure they're
+ // released promptly even in the absence of UI events.
+ MessagePumpScopedAutoreleasePool autorelease_pool(this);
+
+ // Call DoWork and DoDelayedWork once, and if something was done, arrange to
+ // come back here again as long as the loop is still running.
+ bool did_work = delegate_->DoWork();
+ bool resignal_work_source = did_work;
+
+ TimeTicks next_time;
+ delegate_->DoDelayedWork(&next_time);
+ if (!did_work) {
+ // Determine whether there's more delayed work, and if so, if it needs to
+ // be done at some point in the future or if it's already time to do it.
+ // Only do these checks if did_work is false. If did_work is true, this
+ // function, and therefore any additional delayed work, will get another
+ // chance to run before the loop goes to sleep.
+ bool more_delayed_work = !next_time.is_null();
+ if (more_delayed_work) {
+ TimeDelta delay = next_time - TimeTicks::Now();
+ if (delay > TimeDelta()) {
+ // There's more delayed work to be done in the future.
+ ScheduleDelayedWork(next_time);
+ } else {
+ // There's more delayed work to be done, and its time is in the past.
+ // Arrange to come back here directly as long as the loop is still
+ // running.
+ resignal_work_source = true;
+ }
+ }
+ }
+
+ if (resignal_work_source) {
+ CFRunLoopSourceSignal(work_source_);
+ }
+
+ return resignal_work_source;
+}
+
+// Called from the run loop.
+// static
+void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) {
+ MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
+ self->RunIdleWork();
+}
+
+// Called by MessagePumpCFRunLoopBase::RunIdleWorkSource.
+bool MessagePumpCFRunLoopBase::RunIdleWork() {
+ if (!delegate_) {
+ // This point can be reached with a NULL delegate_ if Run is not on the
+ // stack but foreign code is spinning the CFRunLoop. Arrange to come back
+ // here when a delegate is available.
+ delegateless_idle_work_ = true;
+ return false;
+ }
+
+ // The NSApplication-based run loop only drains the autorelease pool at each
+ // UI event (NSEvent). The autorelease pool is not drained for each
+ // CFRunLoopSource target that's run. Use a local pool for any autoreleased
+ // objects if the app is not currently handling a UI event to ensure they're
+ // released promptly even in the absence of UI events.
+ MessagePumpScopedAutoreleasePool autorelease_pool(this);
+
+ // Call DoIdleWork once, and if something was done, arrange to come back here
+ // again as long as the loop is still running.
+ bool did_work = delegate_->DoIdleWork();
+ if (did_work) {
+ CFRunLoopSourceSignal(idle_work_source_);
+ }
+
+ return did_work;
+}
+
+// Called from the run loop.
+// static
+void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) {
+ MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
+ self->RunNestingDeferredWork();
+}
+
+// Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource.
+bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() {
+ if (!delegate_) {
+ // This point can be reached with a NULL delegate_ if Run is not on the
+ // stack but foreign code is spinning the CFRunLoop. There's no sense in
+ // attempting to do any work or signalling the work sources because
+ // without a delegate, work is not possible.
+ return false;
+ }
+
+ // Immediately try work in priority order.
+ if (!RunWork()) {
+ if (!RunIdleWork()) {
+ return false;
+ }
+ } else {
+ // Work was done. Arrange for the loop to try non-nestable idle work on
+ // a subsequent pass.
+ CFRunLoopSourceSignal(idle_work_source_);
+ }
+
+ return true;
+}
+
+// Called before the run loop goes to sleep or exits, or processes sources.
+void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() {
+ // deepest_nesting_level_ is set as run loops are entered. If the deepest
+ // level encountered is deeper than the current level, a nested loop
+ // (relative to the current level) ran since the last time nesting-deferred
+ // work was scheduled. When that situation is encountered, schedule
+ // nesting-deferred work in case any work was deferred because nested work
+ // was disallowed.
+ if (deepest_nesting_level_ > nesting_level_) {
+ deepest_nesting_level_ = nesting_level_;
+ CFRunLoopSourceSignal(nesting_deferred_work_source_);
+ }
+}
+
+// Called from the run loop.
+// static
+void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer,
+ CFRunLoopActivity activity,
+ void* info) {
+ MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
+
+ // Attempt to do some idle work before going to sleep.
+ self->RunIdleWork();
+
+ // The run loop is about to go to sleep. If any of the work done since it
+ // started or woke up resulted in a nested run loop running,
+ // nesting-deferred work may have accumulated. Schedule it for processing
+ // if appropriate.
+ self->MaybeScheduleNestingDeferredWork();
+}
+
+// Called from the run loop.
+// static
+void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer,
+ CFRunLoopActivity activity,
+ void* info) {
+ MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
+
+ // The run loop has reached the top of the loop and is about to begin
+ // processing sources. If the last iteration of the loop at this nesting
+ // level did not sleep or exit, nesting-deferred work may have accumulated
+ // if a nested loop ran. Schedule nesting-deferred work for processing if
+ // appropriate.
+ self->MaybeScheduleNestingDeferredWork();
+}
+
+// Called from the run loop.
+// static
+void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer,
+ CFRunLoopActivity activity,
+ void* info) {
+ MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
+
+ switch (activity) {
+ case kCFRunLoopEntry:
+ ++self->nesting_level_;
+ if (self->nesting_level_ > self->deepest_nesting_level_) {
+ self->deepest_nesting_level_ = self->nesting_level_;
+ }
+ break;
+
+ case kCFRunLoopExit:
+ // Not all run loops go to sleep. If a run loop is stopped before it
+ // goes to sleep due to a CFRunLoopStop call, or if the timeout passed
+ // to CFRunLoopRunInMode expires, the run loop may proceed directly from
+ // handling sources to exiting without any sleep. This most commonly
+ // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it
+ // to make a single pass through the loop and exit without sleep. Some
+ // native loops use CFRunLoop in this way. Because PreWaitObserver will
+ // not be called in these case, MaybeScheduleNestingDeferredWork needs
+ // to be called here, as the run loop exits.
+ //
+ // MaybeScheduleNestingDeferredWork consults self->nesting_level_
+ // to determine whether to schedule nesting-deferred work. It expects
+ // the nesting level to be set to the depth of the loop that is going
+ // to sleep or exiting. It must be called before decrementing the
+ // value so that the value still corresponds to the level of the exiting
+ // loop.
+ self->MaybeScheduleNestingDeferredWork();
+ --self->nesting_level_;
+ break;
+
+ default:
+ break;
+ }
+
+ self->EnterExitRunLoop(activity);
+}
+
+// Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default
+// implementation is a no-op.
+void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) {
+}
+
+// Base version returns a standard NSAutoreleasePool.
+NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
+ return [[NSAutoreleasePool alloc] init];
+}
+
+MessagePumpCFRunLoop::MessagePumpCFRunLoop()
+ : quit_pending_(false) {
+}
+
+MessagePumpCFRunLoop::~MessagePumpCFRunLoop() {}
+
+// Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were
+// running lower on the run loop thread's stack when this object was created,
+// the same number of CFRunLoopRun loops must be running for the outermost call
+// to Run. Run/DoRun are reentrant after that point.
+void MessagePumpCFRunLoop::DoRun(Delegate* delegate) {
+ // This is completely identical to calling CFRunLoopRun(), except autorelease
+ // pool management is introduced.
+ int result;
+ do {
+ MessagePumpScopedAutoreleasePool autorelease_pool(this);
+ result = CFRunLoopRunInMode(kCFRunLoopDefaultMode,
+ kCFTimeIntervalMax,
+ false);
+ } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished);
+}
+
+// Must be called on the run loop thread.
+void MessagePumpCFRunLoop::Quit() {
+ // Stop the innermost run loop managed by this MessagePumpCFRunLoop object.
+ if (nesting_level() == run_nesting_level()) {
+ // This object is running the innermost loop, just stop it.
+ CFRunLoopStop(run_loop());
+ } else {
+ // There's another loop running inside the loop managed by this object.
+ // In other words, someone else called CFRunLoopRunInMode on the same
+ // thread, deeper on the stack than the deepest Run call. Don't preempt
+ // other run loops, just mark this object to quit the innermost Run as
+ // soon as the other inner loops not managed by Run are done.
+ quit_pending_ = true;
+ }
+}
+
+// Called by MessagePumpCFRunLoopBase::EnterExitObserver.
+void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) {
+ if (activity == kCFRunLoopExit &&
+ nesting_level() == run_nesting_level() &&
+ quit_pending_) {
+ // Quit was called while loops other than those managed by this object
+ // were running further inside a run loop managed by this object. Now
+ // that all unmanaged inner run loops are gone, stop the loop running
+ // just inside Run.
+ CFRunLoopStop(run_loop());
+ quit_pending_ = false;
+ }
+}
+
+MessagePumpNSRunLoop::MessagePumpNSRunLoop()
+ : keep_running_(true) {
+ CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
+ source_context.perform = NoOp;
+ quit_source_ = CFRunLoopSourceCreate(NULL, // allocator
+ 0, // priority
+ &source_context);
+ CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
+}
+
+MessagePumpNSRunLoop::~MessagePumpNSRunLoop() {
+ CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
+ CFRelease(quit_source_);
+}
+
+void MessagePumpNSRunLoop::DoRun(Delegate* delegate) {
+ while (keep_running_) {
+ // NSRunLoop manages autorelease pools itself.
+ [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
+ beforeDate:[NSDate distantFuture]];
+ }
+
+ keep_running_ = true;
+}
+
+void MessagePumpNSRunLoop::Quit() {
+ keep_running_ = false;
+ CFRunLoopSourceSignal(quit_source_);
+ CFRunLoopWakeUp(run_loop());
+}
+
+#if defined(OS_IOS)
+MessagePumpUIApplication::MessagePumpUIApplication()
+ : run_loop_(NULL) {
+}
+
+MessagePumpUIApplication::~MessagePumpUIApplication() {}
+
+void MessagePumpUIApplication::DoRun(Delegate* delegate) {
+ NOTREACHED();
+}
+
+void MessagePumpUIApplication::Quit() {
+ NOTREACHED();
+}
+
+void MessagePumpUIApplication::Attach(Delegate* delegate) {
+ DCHECK(!run_loop_);
+ run_loop_ = new base::RunLoop();
+ CHECK(run_loop_->BeforeRun());
+ SetDelegate(delegate);
+}
+
+#else
+
+MessagePumpNSApplication::MessagePumpNSApplication()
+ : keep_running_(true),
+ running_own_loop_(false) {
+}
+
+MessagePumpNSApplication::~MessagePumpNSApplication() {}
+
+void MessagePumpNSApplication::DoRun(Delegate* delegate) {
+ bool last_running_own_loop_ = running_own_loop_;
+
+ // NSApp must be initialized by calling:
+ // [{some class which implements CrAppProtocol} sharedApplication]
+ // Most likely candidates are CrApplication or BrowserCrApplication.
+ // These can be initialized from C++ code by calling
+ // RegisterCrApp() or RegisterBrowserCrApp().
+ CHECK(NSApp);
+
+ if (![NSApp isRunning]) {
+ running_own_loop_ = false;
+ // NSApplication manages autorelease pools itself when run this way.
+ [NSApp run];
+ } else {
+ running_own_loop_ = true;
+ NSDate* distant_future = [NSDate distantFuture];
+ while (keep_running_) {
+ MessagePumpScopedAutoreleasePool autorelease_pool(this);
+ NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
+ untilDate:distant_future
+ inMode:NSDefaultRunLoopMode
+ dequeue:YES];
+ if (event) {
+ [NSApp sendEvent:event];
+ }
+ }
+ keep_running_ = true;
+ }
+
+ running_own_loop_ = last_running_own_loop_;
+}
+
+void MessagePumpNSApplication::Quit() {
+ if (!running_own_loop_) {
+ [[NSApplication sharedApplication] stop:nil];
+ } else {
+ keep_running_ = false;
+ }
+
+ // Send a fake event to wake the loop up.
+ [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
+ location:NSMakePoint(0, 0)
+ modifierFlags:0
+ timestamp:0
+ windowNumber:0
+ context:NULL
+ subtype:0
+ data1:0
+ data2:0]
+ atStart:NO];
+}
+
+MessagePumpCrApplication::MessagePumpCrApplication() {
+}
+
+// Prevents an autorelease pool from being created if the app is in the midst of
+// handling a UI event because various parts of AppKit depend on objects that
+// are created while handling a UI event to be autoreleased in the event loop.
+// An example of this is NSWindowController. When a window with a window
+// controller is closed it goes through a stack like this:
+// (Several stack frames elided for clarity)
+//
+// #0 [NSWindowController autorelease]
+// #1 DoAClose
+// #2 MessagePumpCFRunLoopBase::DoWork()
+// #3 [NSRunLoop run]
+// #4 [NSButton performClick:]
+// #5 [NSWindow sendEvent:]
+// #6 [NSApp sendEvent:]
+// #7 [NSApp run]
+//
+// -performClick: spins a nested run loop. If the pool created in DoWork was a
+// standard NSAutoreleasePool, it would release the objects that were
+// autoreleased into it once DoWork released it. This would cause the window
+// controller, which autoreleased itself in frame #0, to release itself, and
+// possibly free itself. Unfortunately this window controller controls the
+// window in frame #5. When the stack is unwound to frame #5, the window would
+// no longer exists and crashes may occur. Apple gets around this by never
+// releasing the pool it creates in frame #4, and letting frame #7 clean it up
+// when it cleans up the pool that wraps frame #7. When an autorelease pool is
+// released it releases all other pools that were created after it on the
+// autorelease pool stack.
+//
+// CrApplication is responsible for setting handlingSendEvent to true just
+// before it sends the event through the event handling mechanism, and
+// returning it to its previous value once the event has been sent.
+NSAutoreleasePool* MessagePumpCrApplication::CreateAutoreleasePool() {
+ if (MessagePumpMac::IsHandlingSendEvent())
+ return nil;
+ return MessagePumpNSApplication::CreateAutoreleasePool();
+}
+
+// static
+bool MessagePumpMac::UsingCrApp() {
+ DCHECK([NSThread isMainThread]);
+
+ // If NSApp is still not initialized, then the subclass used cannot
+ // be determined.
+ DCHECK(NSApp);
+
+ // The pump was created using MessagePumpNSApplication.
+ if (not_using_crapp)
+ return false;
+
+ return [NSApp conformsToProtocol:@protocol(CrAppProtocol)];
+}
+
+// static
+bool MessagePumpMac::IsHandlingSendEvent() {
+ DCHECK([NSApp conformsToProtocol:@protocol(CrAppProtocol)]);
+ NSObject<CrAppProtocol>* app = static_cast<NSObject<CrAppProtocol>*>(NSApp);
+ return [app isHandlingSendEvent];
+}
+#endif // !defined(OS_IOS)
+
+// static
+MessagePump* MessagePumpMac::Create() {
+ if ([NSThread isMainThread]) {
+#if defined(OS_IOS)
+ return new MessagePumpUIApplication;
+#else
+ if ([NSApp conformsToProtocol:@protocol(CrAppProtocol)])
+ return new MessagePumpCrApplication;
+
+ // The main-thread MessagePump implementations REQUIRE an NSApp.
+ // Executables which have specific requirements for their
+ // NSApplication subclass should initialize appropriately before
+ // creating an event loop.
+ [NSApplication sharedApplication];
+ not_using_crapp = true;
+ return new MessagePumpNSApplication;
+#endif
+ }
+
+ return new MessagePumpNSRunLoop;
+}
+
+} // namespace base
diff --git a/src/base/message_pump_observer.h b/src/base/message_pump_observer.h
new file mode 100644
index 0000000..a7d2aa7
--- /dev/null
+++ b/src/base/message_pump_observer.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2012 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_MESSAGE_PUMP_OBSERVER_H
+#define BASE_MESSAGE_PUMP_OBSERVER_H
+
+#include "base/base_export.h"
+#include "base/event_types.h"
+
+namespace base {
+
+enum EventStatus {
+ EVENT_CONTINUE, // The event should be dispatched as normal.
+#if defined(USE_X11)
+ EVENT_HANDLED // The event should not be processed any farther.
+#endif
+};
+
+// A MessagePumpObserver is an object that receives global
+// notifications from the UI MessageLoop with MessagePumpWin or
+// MessagePumpAuraX11.
+//
+// NOTE: An Observer implementation should be extremely fast!
+//
+// For use with MessagePumpAuraX11, please see message_pump_glib.h for more
+// info about how this is invoked in this environment.
+class BASE_EXPORT MessagePumpObserver {
+ public:
+ // This method is called before processing a NativeEvent. If the
+ // method returns EVENT_HANDLED, it indicates the event has already
+ // been handled, so the event is not processed any farther. If the
+ // method returns EVENT_CONTINUE, the event dispatching proceeds as
+ // normal.
+ virtual EventStatus WillProcessEvent(const NativeEvent& event) = 0;
+
+ // This method is called after processing a message. This method
+ // will not be called if WillProcessEvent returns EVENT_HANDLED.
+ virtual void DidProcessEvent(const NativeEvent& event) = 0;
+
+ protected:
+ virtual ~MessagePumpObserver() {}
+};
+
+} // namespace base
+
+#endif // BASE_MESSAGE_PUMP_OBSERVER_VIEWS_H
diff --git a/src/base/message_pump_shell.cc b/src/base/message_pump_shell.cc
new file mode 100644
index 0000000..c451f3d
--- /dev/null
+++ b/src/base/message_pump_shell.cc
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2012 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#if !defined(__LB_ANDROID__)
+#include "message_pump_shell.h"
+#include "base/logging.h"
+
+namespace base {
+
+MessagePumpShell::MessagePumpShell()
+ : keep_running_(true),
+ event_(false, false) {
+}
+
+bool MessagePumpShell::DoWork() {
+ NOTREACHED();
+ return false;
+}
+
+bool MessagePumpShell::DoDelayedWork(TimeTicks *next_delayed_work_time) {
+ NOTREACHED();
+ return false;
+}
+
+bool MessagePumpShell::DoIdleWork() {
+ NOTREACHED();
+ return false;
+}
+
+void MessagePumpShell::Run(Delegate * delegate) {
+ DCHECK(keep_running_) << "Quit must have been called outside of Run!";
+
+ for (;;) {
+ bool did_work = delegate->DoWork();
+ if (!keep_running_)
+ break;
+
+ bool did_delayed_work = false;
+ // Let's play catchup on all delayed work before we loop.
+ // This fixes bug #5534709 by processing a large number of
+ // short delayed tasks (like WebKitClientImpl's stuff)
+ // quickly before looping back to process non-delayed tasks
+ // (like paint). Otherwise, WebKitClientImpl's stuff
+ // (including everything in WebCore & JSC) gets starved out
+ // because the backlog of delayed tasks only gets processed
+ // at one task per drawn frame.
+ do {
+ did_delayed_work = delegate->DoDelayedWork(&delayed_work_time_);
+ did_work |= did_delayed_work;
+ } while (did_delayed_work && keep_running_);
+
+ if (!keep_running_)
+ break;
+
+ if (did_work)
+ continue;
+
+ did_work = delegate->DoIdleWork();
+ if (!keep_running_)
+ break;
+
+ if (did_work)
+ continue;
+
+ if (delayed_work_time_.is_null()) {
+ event_.Wait();
+ } else {
+ TimeDelta delay = delayed_work_time_ - TimeTicks::Now();
+ if (delay > TimeDelta()) {
+ event_.TimedWait(delay);
+ } else {
+ // It looks like delayed_work_time_ indicates a time in the past, so we
+ // need to call DoDelayedWork now.
+ delayed_work_time_ = TimeTicks();
+ }
+ }
+ // Since event_ is auto-reset, we don't need to do anything special here
+ // other than service each delegate method.
+ }
+ keep_running_ = true;
+}
+
+void MessagePumpShell::Quit() {
+ keep_running_ = false;
+}
+
+void MessagePumpShell::ScheduleWork() {
+ event_.Signal();
+}
+
+void MessagePumpShell::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
+ delayed_work_time_ = delayed_work_time;
+}
+
+void MessagePumpShell::RunWithDispatcher(Delegate* delegate, Dispatcher* dispatcher) {
+ NOTREACHED();
+}
+
+void MessagePumpShell::AddObserver(Observer* observer) {
+ NOTREACHED();
+}
+
+void MessagePumpShell::RemoveObserver(Observer* observer) {
+ NOTREACHED();
+}
+
+void MessagePumpShell::AddIOObserver(IOObserver* observer) {
+ NOTREACHED();
+}
+
+void MessagePumpShell::RemoveIOObserver(IOObserver* observer) {
+ NOTREACHED();
+}
+
+bool MessagePumpShell::WatchSocket(int s,
+ bool persistent,
+ int mode,
+ FileDescriptorWatcher *controller,
+ Watcher *del) {
+ NOTREACHED();
+ return false;
+}
+
+
+void MessagePumpShell::Watcher::OnFileCanReadWithoutBlocking(int fd)
+{
+ NOTREACHED();
+}
+
+void MessagePumpShell::Watcher::OnFileCanWriteWithoutBlocking(int fd)
+{
+ NOTREACHED();
+}
+
+
+} // namespace base
+#endif
diff --git a/src/base/message_pump_shell.h b/src/base/message_pump_shell.h
new file mode 100644
index 0000000..32356af
--- /dev/null
+++ b/src/base/message_pump_shell.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2012 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BASE_MESSAGE_PUMP_SHELL_H_
+#define BASE_MESSAGE_PUMP_SHELL_H_
+#pragma once
+
+#include "base/message_pump.h"
+#include "base/time.h"
+#include "base/synchronization/waitable_event.h"
+
+namespace base {
+
+class BASE_EXPORT MessagePumpShell : public MessagePump {
+ public:
+
+ MessagePumpShell();
+
+ class Dispatcher {
+ };
+
+ class Observer {
+ };
+
+ class IOObserver {
+ };
+
+ class Watcher {
+ public:
+ Watcher();
+ Watcher(Watcher &);
+ // __LB_SHELL__WRITE_ME__
+
+ virtual void OnFileCanReadWithoutBlocking(int fd);
+ virtual void OnFileCanWriteWithoutBlocking(int fd);
+ };
+
+ // this one only watches sockets, not all file descriptors
+ class FileDescriptorWatcher {
+ public:
+ FileDescriptorWatcher();
+ FileDescriptorWatcher(FileDescriptorWatcher &);
+ // __LB_SHELL__WRITE_ME__
+ bool StopWatchingFileDescriptor();
+ };
+
+ // borrowed from message_pump_libevent.h
+ enum Mode {
+ WATCH_READ = 1 << 0,
+ WATCH_WRITE = 1 << 1,
+ WATCH_READ_WRITE = WATCH_READ | WATCH_WRITE
+ };
+
+ // ------------------------------------------- UI
+
+ // cribbed from message_pump.h
+ virtual bool DoWork();
+ virtual bool DoDelayedWork(TimeTicks *next_delayed_work_time);
+ virtual bool DoIdleWork();
+
+ virtual void Run(Delegate * delegate);
+ virtual void Quit();
+ virtual void ScheduleWork();
+ virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time);
+
+ // cribbed from message_pump_glib.h
+ virtual void RunWithDispatcher(Delegate* delegate, Dispatcher* dispatcher);
+
+ // Add an Observer, which will start receiving notifications immediately.
+ void AddObserver(Observer* observer);
+
+ // Remove an Observer. It is safe to call this method while an Observer is
+ // receiving a notification callback.
+ void RemoveObserver(Observer* observer);
+
+ // ------------------------------------------- IO
+ void AddIOObserver(IOObserver* obs);
+ void RemoveIOObserver(IOObserver* obs);
+
+ bool WatchSocket(int s,
+ bool persistent,
+ int mode,
+ FileDescriptorWatcher *controller,
+ Watcher *del);
+
+ private:
+ // set to false when run should return
+ bool keep_running_;
+
+ WaitableEvent event_;
+
+ TimeTicks delayed_work_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpShell);
+};
+
+typedef MessagePumpShell MessagePumpForUI;
+typedef MessagePumpShell MessagePumpForIO;
+
+} // namespace base
+
+#endif // BASE_MESSAGE_PUMP_SHELL_H_
diff --git a/src/base/message_pump_ui_starboard.cc b/src/base/message_pump_ui_starboard.cc
new file mode 100644
index 0000000..74a71f5
--- /dev/null
+++ b/src/base/message_pump_ui_starboard.cc
@@ -0,0 +1,196 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/message_pump_ui_starboard.h"
+
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "base/time.h"
+
+#include "starboard/event.h"
+#include "starboard/system.h"
+
+namespace base {
+
+namespace {
+
+void CallMessagePumpImmediate(void* context) {
+ DCHECK(context);
+ MessagePumpUIStarboard* pump =
+ reinterpret_cast<MessagePumpUIStarboard*>(context);
+ pump->RunOneAndReschedule(false /*delayed*/);
+}
+
+void CallMessagePumpDelayed(void* context) {
+ DCHECK(context);
+ MessagePumpUIStarboard* pump =
+ reinterpret_cast<MessagePumpUIStarboard*>(context);
+ pump->RunOneAndReschedule(true /*delayed*/);
+}
+
+} // namespace
+
+MessagePumpUIStarboard::MessagePumpUIStarboard() : delegate_(NULL) {}
+
+void MessagePumpUIStarboard::RunOneAndReschedule(bool delayed) {
+ DCHECK(delegate_);
+ if (delayed) {
+ CancelDelayed();
+ } else {
+ CancelImmediate();
+ }
+
+ TimeTicks delayed_work_time;
+ for (;;) {
+ TimeTicks next_time;
+ if (!RunOne(&next_time)) {
+ delayed_work_time = next_time;
+ break;
+ }
+ }
+
+ if (!delayed_work_time.is_null()) {
+ ScheduleDelayedWork(delayed_work_time);
+ }
+}
+
+void MessagePumpUIStarboard::Run(Delegate* delegate) {
+ // This should never be called because we are not like a normal message pump
+ // where we loop until told to quit. We are providing a MessagePump interface
+ // on top of an externally-owned message pump. We want to exist and be able to
+ // schedule work, but the actual for(;;) loop is owned by Starboard.
+ NOTREACHED();
+}
+
+void MessagePumpUIStarboard::Start(Delegate* delegate) {
+ run_loop_.reset(new base::RunLoop());
+ delegate_ = delegate;
+
+ // Since the RunLoop was just created above, BeforeRun should be guaranteed to
+ // return true (it only returns false if the RunLoop has been Quit already).
+ if (!run_loop_->BeforeRun())
+ NOTREACHED();
+}
+
+void MessagePumpUIStarboard::Quit() {
+ delegate_ = NULL;
+ CancelAll();
+ if (run_loop_) {
+ run_loop_->AfterRun();
+ run_loop_.reset();
+ }
+}
+
+void MessagePumpUIStarboard::ScheduleWork() {
+ base::AutoLock auto_lock(outstanding_events_lock_);
+ if (!outstanding_events_.empty()) {
+ // No need, already an outstanding event.
+ return;
+ }
+
+ outstanding_events_.insert(
+ SbEventSchedule(&CallMessagePumpImmediate, this, 0));
+}
+
+void MessagePumpUIStarboard::ScheduleDelayedWork(
+ const base::TimeTicks& delayed_work_time) {
+ base::TimeDelta delay;
+ if (!delayed_work_time.is_null()) {
+ delay = delayed_work_time - base::TimeTicks::Now();
+
+ if (delay <= base::TimeDelta()) {
+ delay = base::TimeDelta();
+ }
+ }
+
+ {
+ base::AutoLock auto_lock(outstanding_events_lock_);
+ // Make sure any outstanding delayed event is canceled.
+ CancelDelayedLocked();
+ outstanding_delayed_events_.insert(
+ SbEventSchedule(&CallMessagePumpDelayed, this, delay.ToSbTime()));
+ }
+}
+
+void MessagePumpUIStarboard::CancelAll() {
+ base::AutoLock auto_lock(outstanding_events_lock_);
+ CancelImmediateLocked();
+ CancelDelayedLocked();
+}
+
+void MessagePumpUIStarboard::CancelImmediate() {
+ base::AutoLock auto_lock(outstanding_events_lock_);
+ CancelImmediateLocked();
+}
+
+void MessagePumpUIStarboard::CancelDelayed() {
+ base::AutoLock auto_lock(outstanding_events_lock_);
+ CancelDelayedLocked();
+}
+
+void MessagePumpUIStarboard::CancelImmediateLocked() {
+ outstanding_events_lock_.AssertAcquired();
+ for (SbEventIdSet::iterator it = outstanding_events_.begin();
+ it != outstanding_events_.end(); ++it) {
+ SbEventCancel(*it);
+ }
+ outstanding_events_.erase(outstanding_events_.begin(),
+ outstanding_events_.end());
+}
+
+void MessagePumpUIStarboard::CancelDelayedLocked() {
+ outstanding_events_lock_.AssertAcquired();
+ for (SbEventIdSet::iterator it = outstanding_delayed_events_.begin();
+ it != outstanding_delayed_events_.end(); ++it) {
+ SbEventCancel(*it);
+ }
+ outstanding_delayed_events_.erase(outstanding_delayed_events_.begin(),
+ outstanding_delayed_events_.end());
+}
+
+bool MessagePumpUIStarboard::RunOne(TimeTicks* out_delayed_work_time) {
+ DCHECK(out_delayed_work_time);
+
+ // We expect to start with a delegate, so we can DCHECK it, but any task we
+ // run could call Quit and remove it.
+ DCHECK(delegate_);
+
+ // Do immediate work.
+ bool did_work = delegate_->DoWork();
+
+ // Do all delayed work. Unlike Chromium, we drain all due delayed work before
+ // going back to the loop. See message_pump_io_starboard.cc for more
+ // information.
+ while (delegate_ && delegate_->DoDelayedWork(out_delayed_work_time)) {
+ did_work = true;
+ }
+
+ // If we did work, and we still have a delegate, return true, so we will be
+ // called again.
+ if (did_work) {
+ return !!delegate_;
+ }
+
+ // If the delegate has been removed, Quit() has been called, so no more work.
+ if (!delegate_) {
+ return false;
+ }
+
+ // No work was done, so only call back if there was idle work done, otherwise
+ // go to sleep. ScheduleWork or ScheduleDelayedWork will be called if new work
+ // is scheduled.
+ return delegate_->DoIdleWork();
+}
+
+} // namespace base
diff --git a/src/base/message_pump_ui_starboard.h b/src/base/message_pump_ui_starboard.h
new file mode 100644
index 0000000..08f6be4
--- /dev/null
+++ b/src/base/message_pump_ui_starboard.h
@@ -0,0 +1,106 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef BASE_MESSAGE_PUMP_UI_STARBOARD_H_
+#define BASE_MESSAGE_PUMP_UI_STARBOARD_H_
+
+#include <set>
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/message_pump.h"
+#include "base/run_loop.h"
+#include "base/synchronization/lock.h"
+#include "base/time.h"
+#include "starboard/event.h"
+
+namespace base {
+
+// MessagePump that integrates with the Starboard message pump. Starboard has
+// its own main loop, so the MessagePumpUIStarboard just piggybacks on that
+// rather than implementing its own pump.
+//
+// To adopt Starboard's message pump, one simply has to create a MessageLoop of
+// TYPE_UI from the Starboard message pump thread. A traditional place to do
+// this would be in the kSbEventStart event handler. One has to be sure to keep
+// the MessageLoop alive for as long as the application wants to use the
+// Starboard message pump as a MessageLoop. That would typically be for the
+// entire lifetime of the application, and in that case, the MessageLoop would
+// traditionally be deleted in the kSbEventStop handler.
+class BASE_EXPORT MessagePumpUIStarboard : public MessagePump {
+ public:
+ MessagePumpUIStarboard();
+
+ // Runs one iteration of the run loop, and reschedules another call, if
+ // necessary.
+ void RunOneAndReschedule(bool delayed);
+
+ // --- MessagePump Implementation ---
+
+ virtual void Run(Delegate* delegate) OVERRIDE;
+ virtual void Quit() OVERRIDE;
+ virtual void ScheduleWork() OVERRIDE;
+ virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) OVERRIDE;
+ virtual void Start(Delegate* delegate);
+
+ protected:
+ virtual ~MessagePumpUIStarboard() {}
+
+ private:
+ // Cancels all outstanding scheduled callback events, if any.
+ void CancelAll();
+
+ // Cancels immediate schedule callback events.
+ void CancelImmediate();
+
+ // Cancels delayed schedule callback events.
+ void CancelDelayed();
+
+ // Cancel workhorse that assumes |outstanding_events_lock_| is locked.
+ void CancelImmediateLocked();
+
+ // Cancel delayed workhorse that assumes |outstanding_events_lock_| is locked.
+ void CancelDelayedLocked();
+
+ // Runs one iteration of the run loop, returning whether to schedule another
+ // iteration or not. Places the delay, if any, in |out_delayed_work_time|.
+ bool RunOne(base::TimeTicks* out_delayed_work_time);
+
+ // The top-level RunLoop for the MessageLoopForUI. A MessageLoop needs a
+ // top-level RunLoop to be considered "running."
+ scoped_ptr<base::RunLoop> run_loop_;
+
+ // The MessagePump::Delegate configured in Start().
+ Delegate* delegate_;
+
+ // Lock protecting outstanding scheduled callback events.
+ base::Lock outstanding_events_lock_;
+
+ // A set of scheduled callback event IDs.
+ typedef std::set<SbEventId> SbEventIdSet;
+
+ // The set of outstanding scheduled callback events for immediate work.
+ SbEventIdSet outstanding_events_;
+
+ // The set of outstanding scheduled callback events for delayed work.
+ SbEventIdSet outstanding_delayed_events_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpUIStarboard);
+};
+
+} // namespace base
+
+#endif // BASE_MESSAGE_PUMP_UI_STARBOARD_H_
diff --git a/src/base/message_pump_win.cc b/src/base/message_pump_win.cc
new file mode 100644
index 0000000..fb962ca
--- /dev/null
+++ b/src/base/message_pump_win.cc
@@ -0,0 +1,680 @@
+// Copyright (c) 2012 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/message_pump_win.h"
+
+#include <math.h>
+
+#include "base/debug/trace_event.h"
+#include "base/message_loop.h"
+#include "base/metrics/histogram.h"
+#include "base/process_util.h"
+#include "base/win/wrapped_window_proc.h"
+
+namespace {
+
+enum MessageLoopProblems {
+ MESSAGE_POST_ERROR,
+ COMPLETION_POST_ERROR,
+ SET_TIMER_ERROR,
+ MESSAGE_LOOP_PROBLEM_MAX,
+};
+
+} // namespace
+
+namespace base {
+
+static const wchar_t kWndClass[] = L"Chrome_MessagePumpWindow";
+
+// Message sent to get an additional time slice for pumping (processing) another
+// task (a series of such messages creates a continuous task pump).
+static const int kMsgHaveWork = WM_USER + 1;
+
+//-----------------------------------------------------------------------------
+// MessagePumpWin public:
+
+void MessagePumpWin::AddObserver(MessagePumpObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void MessagePumpWin::RemoveObserver(MessagePumpObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void MessagePumpWin::WillProcessMessage(const MSG& msg) {
+ FOR_EACH_OBSERVER(MessagePumpObserver, observers_, WillProcessEvent(msg));
+}
+
+void MessagePumpWin::DidProcessMessage(const MSG& msg) {
+ FOR_EACH_OBSERVER(MessagePumpObserver, observers_, DidProcessEvent(msg));
+}
+
+void MessagePumpWin::RunWithDispatcher(
+ Delegate* delegate, MessagePumpDispatcher* dispatcher) {
+ RunState s;
+ s.delegate = delegate;
+ s.dispatcher = dispatcher;
+ s.should_quit = false;
+ s.run_depth = state_ ? state_->run_depth + 1 : 1;
+
+ RunState* previous_state = state_;
+ state_ = &s;
+
+ DoRunLoop();
+
+ state_ = previous_state;
+}
+
+void MessagePumpWin::Quit() {
+ DCHECK(state_);
+ state_->should_quit = true;
+}
+
+//-----------------------------------------------------------------------------
+// MessagePumpWin protected:
+
+int MessagePumpWin::GetCurrentDelay() const {
+ if (delayed_work_time_.is_null())
+ return -1;
+
+ // Be careful here. TimeDelta has a precision of microseconds, but we want a
+ // value in milliseconds. If there are 5.5ms left, should the delay be 5 or
+ // 6? It should be 6 to avoid executing delayed work too early.
+ double timeout =
+ ceil((delayed_work_time_ - TimeTicks::Now()).InMillisecondsF());
+
+ // If this value is negative, then we need to run delayed work soon.
+ int delay = static_cast<int>(timeout);
+ if (delay < 0)
+ delay = 0;
+
+ return delay;
+}
+
+//-----------------------------------------------------------------------------
+// MessagePumpForUI public:
+
+MessagePumpForUI::MessagePumpForUI()
+ : instance_(NULL),
+ message_filter_(new MessageFilter) {
+ InitMessageWnd();
+}
+
+MessagePumpForUI::~MessagePumpForUI() {
+ DestroyWindow(message_hwnd_);
+ UnregisterClass(kWndClass, instance_);
+}
+
+void MessagePumpForUI::ScheduleWork() {
+ if (InterlockedExchange(&have_work_, 1))
+ return; // Someone else continued the pumping.
+
+ // Make sure the MessagePump does some work for us.
+ BOOL ret = PostMessage(message_hwnd_, kMsgHaveWork,
+ reinterpret_cast<WPARAM>(this), 0);
+ if (ret)
+ return; // There was room in the Window Message queue.
+
+ // We have failed to insert a have-work message, so there is a chance that we
+ // will starve tasks/timers while sitting in a nested message loop. Nested
+ // loops only look at Windows Message queues, and don't look at *our* task
+ // queues, etc., so we might not get a time slice in such. :-(
+ // We could abort here, but the fear is that this failure mode is plausibly
+ // common (queue is full, of about 2000 messages), so we'll do a near-graceful
+ // recovery. Nested loops are pretty transient (we think), so this will
+ // probably be recoverable.
+ InterlockedExchange(&have_work_, 0); // Clarify that we didn't really insert.
+ UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", MESSAGE_POST_ERROR,
+ MESSAGE_LOOP_PROBLEM_MAX);
+}
+
+void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
+ //
+ // We would *like* to provide high resolution timers. Windows timers using
+ // SetTimer() have a 10ms granularity. We have to use WM_TIMER as a wakeup
+ // mechanism because the application can enter modal windows loops where it
+ // is not running our MessageLoop; the only way to have our timers fire in
+ // these cases is to post messages there.
+ //
+ // To provide sub-10ms timers, we process timers directly from our run loop.
+ // For the common case, timers will be processed there as the run loop does
+ // its normal work. However, we *also* set the system timer so that WM_TIMER
+ // events fire. This mops up the case of timers not being able to work in
+ // modal message loops. It is possible for the SetTimer to pop and have no
+ // pending timers, because they could have already been processed by the
+ // run loop itself.
+ //
+ // We use a single SetTimer corresponding to the timer that will expire
+ // soonest. As new timers are created and destroyed, we update SetTimer.
+ // Getting a spurrious SetTimer event firing is benign, as we'll just be
+ // processing an empty timer queue.
+ //
+ delayed_work_time_ = delayed_work_time;
+
+ int delay_msec = GetCurrentDelay();
+ DCHECK_GE(delay_msec, 0);
+ if (delay_msec < USER_TIMER_MINIMUM)
+ delay_msec = USER_TIMER_MINIMUM;
+
+ // Create a WM_TIMER event that will wake us up to check for any pending
+ // timers (in case we are running within a nested, external sub-pump).
+ BOOL ret = SetTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this),
+ delay_msec, NULL);
+ if (ret)
+ return;
+ // If we can't set timers, we are in big trouble... but cross our fingers for
+ // now.
+ // TODO(jar): If we don't see this error, use a CHECK() here instead.
+ UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", SET_TIMER_ERROR,
+ MESSAGE_LOOP_PROBLEM_MAX);
+}
+
+void MessagePumpForUI::PumpOutPendingPaintMessages() {
+ // If we are being called outside of the context of Run, then don't try to do
+ // any work.
+ if (!state_)
+ return;
+
+ // Create a mini-message-pump to force immediate processing of only Windows
+ // WM_PAINT messages. Don't provide an infinite loop, but do enough peeking
+ // to get the job done. Actual common max is 4 peeks, but we'll be a little
+ // safe here.
+ const int kMaxPeekCount = 20;
+ int peek_count;
+ for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) {
+ MSG msg;
+ if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT))
+ break;
+ ProcessMessageHelper(msg);
+ if (state_->should_quit) // Handle WM_QUIT.
+ break;
+ }
+ // Histogram what was really being used, to help to adjust kMaxPeekCount.
+ DHISTOGRAM_COUNTS("Loop.PumpOutPendingPaintMessages Peeks", peek_count);
+}
+
+//-----------------------------------------------------------------------------
+// MessagePumpForUI private:
+
+// static
+LRESULT CALLBACK MessagePumpForUI::WndProcThunk(
+ HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
+ switch (message) {
+ case kMsgHaveWork:
+ reinterpret_cast<MessagePumpForUI*>(wparam)->HandleWorkMessage();
+ break;
+ case WM_TIMER:
+ reinterpret_cast<MessagePumpForUI*>(wparam)->HandleTimerMessage();
+ break;
+ }
+ return DefWindowProc(hwnd, message, wparam, lparam);
+}
+
+void MessagePumpForUI::DoRunLoop() {
+ // IF this was just a simple PeekMessage() loop (servicing all possible work
+ // queues), then Windows would try to achieve the following order according
+ // to MSDN documentation about PeekMessage with no filter):
+ // * Sent messages
+ // * Posted messages
+ // * Sent messages (again)
+ // * WM_PAINT messages
+ // * WM_TIMER messages
+ //
+ // Summary: none of the above classes is starved, and sent messages has twice
+ // the chance of being processed (i.e., reduced service time).
+
+ for (;;) {
+ // If we do any work, we may create more messages etc., and more work may
+ // possibly be waiting in another task group. When we (for example)
+ // ProcessNextWindowsMessage(), there is a good chance there are still more
+ // messages waiting. On the other hand, when any of these methods return
+ // having done no work, then it is pretty unlikely that calling them again
+ // quickly will find any work to do. Finally, if they all say they had no
+ // work, then it is a good time to consider sleeping (waiting) for more
+ // work.
+
+ bool more_work_is_plausible = ProcessNextWindowsMessage();
+ if (state_->should_quit)
+ break;
+
+ more_work_is_plausible |= state_->delegate->DoWork();
+ if (state_->should_quit)
+ break;
+
+ more_work_is_plausible |=
+ state_->delegate->DoDelayedWork(&delayed_work_time_);
+ // If we did not process any delayed work, then we can assume that our
+ // existing WM_TIMER if any will fire when delayed work should run. We
+ // don't want to disturb that timer if it is already in flight. However,
+ // if we did do all remaining delayed work, then lets kill the WM_TIMER.
+ if (more_work_is_plausible && delayed_work_time_.is_null())
+ KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
+ if (state_->should_quit)
+ break;
+
+ if (more_work_is_plausible)
+ continue;
+
+ more_work_is_plausible = state_->delegate->DoIdleWork();
+ if (state_->should_quit)
+ break;
+
+ if (more_work_is_plausible)
+ continue;
+
+ WaitForWork(); // Wait (sleep) until we have work to do again.
+ }
+}
+
+void MessagePumpForUI::InitMessageWnd() {
+ WNDCLASSEX wc = {0};
+ wc.cbSize = sizeof(wc);
+ wc.lpfnWndProc = base::win::WrappedWindowProc<WndProcThunk>;
+ wc.hInstance = base::GetModuleFromAddress(wc.lpfnWndProc);
+ wc.lpszClassName = kWndClass;
+ instance_ = wc.hInstance;
+ RegisterClassEx(&wc);
+
+ message_hwnd_ =
+ CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, instance_, 0);
+ DCHECK(message_hwnd_);
+}
+
+void MessagePumpForUI::WaitForWork() {
+ // Wait until a message is available, up to the time needed by the timer
+ // manager to fire the next set of timers.
+ int delay = GetCurrentDelay();
+ if (delay < 0) // Negative value means no timers waiting.
+ delay = INFINITE;
+
+ DWORD result;
+ result = MsgWaitForMultipleObjectsEx(0, NULL, delay, QS_ALLINPUT,
+ MWMO_INPUTAVAILABLE);
+
+ if (WAIT_OBJECT_0 == result) {
+ // A WM_* message is available.
+ // If a parent child relationship exists between windows across threads
+ // then their thread inputs are implicitly attached.
+ // This causes the MsgWaitForMultipleObjectsEx API to return indicating
+ // that messages are ready for processing (Specifically, mouse messages
+ // intended for the child window may appear if the child window has
+ // capture).
+ // The subsequent PeekMessages call may fail to return any messages thus
+ // causing us to enter a tight loop at times.
+ // The WaitMessage call below is a workaround to give the child window
+ // some time to process its input messages.
+ MSG msg = {0};
+ DWORD queue_status = GetQueueStatus(QS_MOUSE);
+ if (HIWORD(queue_status) & QS_MOUSE &&
+ !PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE)) {
+ WaitMessage();
+ }
+ return;
+ }
+
+ DCHECK_NE(WAIT_FAILED, result) << GetLastError();
+}
+
+void MessagePumpForUI::HandleWorkMessage() {
+ // If we are being called outside of the context of Run, then don't try to do
+ // any work. This could correspond to a MessageBox call or something of that
+ // sort.
+ if (!state_) {
+ // Since we handled a kMsgHaveWork message, we must still update this flag.
+ InterlockedExchange(&have_work_, 0);
+ return;
+ }
+
+ // Let whatever would have run had we not been putting messages in the queue
+ // run now. This is an attempt to make our dummy message not starve other
+ // messages that may be in the Windows message queue.
+ ProcessPumpReplacementMessage();
+
+ // Now give the delegate a chance to do some work. He'll let us know if he
+ // needs to do more work.
+ if (state_->delegate->DoWork())
+ ScheduleWork();
+}
+
+void MessagePumpForUI::HandleTimerMessage() {
+ KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
+
+ // If we are being called outside of the context of Run, then don't do
+ // anything. This could correspond to a MessageBox call or something of
+ // that sort.
+ if (!state_)
+ return;
+
+ state_->delegate->DoDelayedWork(&delayed_work_time_);
+ if (!delayed_work_time_.is_null()) {
+ // A bit gratuitous to set delayed_work_time_ again, but oh well.
+ ScheduleDelayedWork(delayed_work_time_);
+ }
+}
+
+bool MessagePumpForUI::ProcessNextWindowsMessage() {
+ // If there are sent messages in the queue then PeekMessage internally
+ // dispatches the message and returns false. We return true in this
+ // case to ensure that the message loop peeks again instead of calling
+ // MsgWaitForMultipleObjectsEx again.
+ bool sent_messages_in_queue = false;
+ DWORD queue_status = GetQueueStatus(QS_SENDMESSAGE);
+ if (HIWORD(queue_status) & QS_SENDMESSAGE)
+ sent_messages_in_queue = true;
+
+ MSG msg;
+ if (message_filter_->DoPeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ return ProcessMessageHelper(msg);
+
+ return sent_messages_in_queue;
+}
+
+bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) {
+ TRACE_EVENT1("base", "MessagePumpForUI::ProcessMessageHelper",
+ "message", msg.message);
+ if (WM_QUIT == msg.message) {
+ // Repost the QUIT message so that it will be retrieved by the primary
+ // GetMessage() loop.
+ state_->should_quit = true;
+ PostQuitMessage(static_cast<int>(msg.wParam));
+ return false;
+ }
+
+ // While running our main message pump, we discard kMsgHaveWork messages.
+ if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_)
+ return ProcessPumpReplacementMessage();
+
+ if (CallMsgFilter(const_cast<MSG*>(&msg), kMessageFilterCode))
+ return true;
+
+ WillProcessMessage(msg);
+
+ if (!message_filter_->ProcessMessage(msg)) {
+ if (state_->dispatcher) {
+ if (!state_->dispatcher->Dispatch(msg))
+ state_->should_quit = true;
+ } else {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ DidProcessMessage(msg);
+ return true;
+}
+
+bool MessagePumpForUI::ProcessPumpReplacementMessage() {
+ // When we encounter a kMsgHaveWork message, this method is called to peek
+ // and process a replacement message, such as a WM_PAINT or WM_TIMER. The
+ // goal is to make the kMsgHaveWork as non-intrusive as possible, even though
+ // a continuous stream of such messages are posted. This method carefully
+ // peeks a message while there is no chance for a kMsgHaveWork to be pending,
+ // then resets the have_work_ flag (allowing a replacement kMsgHaveWork to
+ // possibly be posted), and finally dispatches that peeked replacement. Note
+ // that the re-post of kMsgHaveWork may be asynchronous to this thread!!
+
+ bool have_message = false;
+ MSG msg;
+ // We should not process all window messages if we are in the context of an
+ // OS modal loop, i.e. in the context of a windows API call like MessageBox.
+ // This is to ensure that these messages are peeked out by the OS modal loop.
+ if (MessageLoop::current()->os_modal_loop()) {
+ // We only peek out WM_PAINT and WM_TIMER here for reasons mentioned above.
+ have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) ||
+ PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE);
+ } else {
+ have_message = !!message_filter_->DoPeekMessage(&msg, NULL, 0, 0,
+ PM_REMOVE);
+ }
+
+ DCHECK(!have_message || kMsgHaveWork != msg.message ||
+ msg.hwnd != message_hwnd_);
+
+ // Since we discarded a kMsgHaveWork message, we must update the flag.
+ int old_have_work = InterlockedExchange(&have_work_, 0);
+ DCHECK(old_have_work);
+
+ // We don't need a special time slice if we didn't have_message to process.
+ if (!have_message)
+ return false;
+
+ // Guarantee we'll get another time slice in the case where we go into native
+ // windows code. This ScheduleWork() may hurt performance a tiny bit when
+ // tasks appear very infrequently, but when the event queue is busy, the
+ // kMsgHaveWork events get (percentage wise) rarer and rarer.
+ ScheduleWork();
+ return ProcessMessageHelper(msg);
+}
+
+void MessagePumpForUI::SetMessageFilter(
+ scoped_ptr<MessageFilter> message_filter) {
+ message_filter_ = message_filter.Pass();
+}
+
+//-----------------------------------------------------------------------------
+// MessagePumpForIO public:
+
+MessagePumpForIO::MessagePumpForIO() {
+ port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1));
+ DCHECK(port_.IsValid());
+}
+
+void MessagePumpForIO::ScheduleWork() {
+ if (InterlockedExchange(&have_work_, 1))
+ return; // Someone else continued the pumping.
+
+ // Make sure the MessagePump does some work for us.
+ BOOL ret = PostQueuedCompletionStatus(port_, 0,
+ reinterpret_cast<ULONG_PTR>(this),
+ reinterpret_cast<OVERLAPPED*>(this));
+ if (ret)
+ return; // Post worked perfectly.
+
+ // See comment in MessagePumpForUI::ScheduleWork() for this error recovery.
+ InterlockedExchange(&have_work_, 0); // Clarify that we didn't succeed.
+ UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", COMPLETION_POST_ERROR,
+ MESSAGE_LOOP_PROBLEM_MAX);
+}
+
+void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
+ // We know that we can't be blocked right now since this method can only be
+ // called on the same thread as Run, so we only need to update our record of
+ // how long to sleep when we do sleep.
+ delayed_work_time_ = delayed_work_time;
+}
+
+void MessagePumpForIO::RegisterIOHandler(HANDLE file_handle,
+ IOHandler* handler) {
+ ULONG_PTR key = HandlerToKey(handler, true);
+ HANDLE port = CreateIoCompletionPort(file_handle, port_, key, 1);
+ DPCHECK(port);
+}
+
+bool MessagePumpForIO::RegisterJobObject(HANDLE job_handle,
+ IOHandler* handler) {
+ // Job object notifications use the OVERLAPPED pointer to carry the message
+ // data. Mark the completion key correspondingly, so we will not try to
+ // convert OVERLAPPED* to IOContext*.
+ ULONG_PTR key = HandlerToKey(handler, false);
+ JOBOBJECT_ASSOCIATE_COMPLETION_PORT info;
+ info.CompletionKey = reinterpret_cast<void*>(key);
+ info.CompletionPort = port_;
+ return SetInformationJobObject(job_handle,
+ JobObjectAssociateCompletionPortInformation,
+ &info,
+ sizeof(info)) != FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// MessagePumpForIO private:
+
+void MessagePumpForIO::DoRunLoop() {
+ for (;;) {
+ // If we do any work, we may create more messages etc., and more work may
+ // possibly be waiting in another task group. When we (for example)
+ // WaitForIOCompletion(), there is a good chance there are still more
+ // messages waiting. On the other hand, when any of these methods return
+ // having done no work, then it is pretty unlikely that calling them
+ // again quickly will find any work to do. Finally, if they all say they
+ // had no work, then it is a good time to consider sleeping (waiting) for
+ // more work.
+
+ bool more_work_is_plausible = state_->delegate->DoWork();
+ if (state_->should_quit)
+ break;
+
+ more_work_is_plausible |= WaitForIOCompletion(0, NULL);
+ if (state_->should_quit)
+ break;
+
+ more_work_is_plausible |=
+ state_->delegate->DoDelayedWork(&delayed_work_time_);
+ if (state_->should_quit)
+ break;
+
+ if (more_work_is_plausible)
+ continue;
+
+ more_work_is_plausible = state_->delegate->DoIdleWork();
+ if (state_->should_quit)
+ break;
+
+ if (more_work_is_plausible)
+ continue;
+
+ WaitForWork(); // Wait (sleep) until we have work to do again.
+ }
+}
+
+// Wait until IO completes, up to the time needed by the timer manager to fire
+// the next set of timers.
+void MessagePumpForIO::WaitForWork() {
+ // We do not support nested IO message loops. This is to avoid messy
+ // recursion problems.
+ DCHECK_EQ(1, state_->run_depth) << "Cannot nest an IO message loop!";
+
+ int timeout = GetCurrentDelay();
+ if (timeout < 0) // Negative value means no timers waiting.
+ timeout = INFINITE;
+
+ WaitForIOCompletion(timeout, NULL);
+}
+
+bool MessagePumpForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) {
+ IOItem item;
+ if (completed_io_.empty() || !MatchCompletedIOItem(filter, &item)) {
+ // We have to ask the system for another IO completion.
+ if (!GetIOItem(timeout, &item))
+ return false;
+
+ if (ProcessInternalIOItem(item))
+ return true;
+ }
+
+ // If |item.has_valid_io_context| is false then |item.context| does not point
+ // to a context structure, and so should not be dereferenced, although it may
+ // still hold valid non-pointer data.
+ if (!item.has_valid_io_context || item.context->handler) {
+ if (filter && item.handler != filter) {
+ // Save this item for later
+ completed_io_.push_back(item);
+ } else {
+ DCHECK(!item.has_valid_io_context ||
+ (item.context->handler == item.handler));
+ WillProcessIOEvent();
+ item.handler->OnIOCompleted(item.context, item.bytes_transfered,
+ item.error);
+ DidProcessIOEvent();
+ }
+ } else {
+ // The handler must be gone by now, just cleanup the mess.
+ delete item.context;
+ }
+ return true;
+}
+
+// Asks the OS for another IO completion result.
+bool MessagePumpForIO::GetIOItem(DWORD timeout, IOItem* item) {
+ memset(item, 0, sizeof(*item));
+ ULONG_PTR key = NULL;
+ OVERLAPPED* overlapped = NULL;
+ if (!GetQueuedCompletionStatus(port_.Get(), &item->bytes_transfered, &key,
+ &overlapped, timeout)) {
+ if (!overlapped)
+ return false; // Nothing in the queue.
+ item->error = GetLastError();
+ item->bytes_transfered = 0;
+ }
+
+ item->handler = KeyToHandler(key, &item->has_valid_io_context);
+ item->context = reinterpret_cast<IOContext*>(overlapped);
+ return true;
+}
+
+bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) {
+ if (this == reinterpret_cast<MessagePumpForIO*>(item.context) &&
+ this == reinterpret_cast<MessagePumpForIO*>(item.handler)) {
+ // This is our internal completion.
+ DCHECK(!item.bytes_transfered);
+ InterlockedExchange(&have_work_, 0);
+ return true;
+ }
+ return false;
+}
+
+// Returns a completion item that was previously received.
+bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) {
+ DCHECK(!completed_io_.empty());
+ for (std::list<IOItem>::iterator it = completed_io_.begin();
+ it != completed_io_.end(); ++it) {
+ if (!filter || it->handler == filter) {
+ *item = *it;
+ completed_io_.erase(it);
+ return true;
+ }
+ }
+ return false;
+}
+
+void MessagePumpForIO::AddIOObserver(IOObserver *obs) {
+ io_observers_.AddObserver(obs);
+}
+
+void MessagePumpForIO::RemoveIOObserver(IOObserver *obs) {
+ io_observers_.RemoveObserver(obs);
+}
+
+void MessagePumpForIO::WillProcessIOEvent() {
+ FOR_EACH_OBSERVER(IOObserver, io_observers_, WillProcessIOEvent());
+}
+
+void MessagePumpForIO::DidProcessIOEvent() {
+ FOR_EACH_OBSERVER(IOObserver, io_observers_, DidProcessIOEvent());
+}
+
+// static
+ULONG_PTR MessagePumpForIO::HandlerToKey(IOHandler* handler,
+ bool has_valid_io_context) {
+ ULONG_PTR key = reinterpret_cast<ULONG_PTR>(handler);
+
+ // |IOHandler| is at least pointer-size aligned, so the lowest two bits are
+ // always cleared. We use the lowest bit to distinguish completion keys with
+ // and without the associated |IOContext|.
+ DCHECK((key & 1) == 0);
+
+ // Mark the completion key as context-less.
+ if (!has_valid_io_context)
+ key = key | 1;
+ return key;
+}
+
+// static
+MessagePumpForIO::IOHandler* MessagePumpForIO::KeyToHandler(
+ ULONG_PTR key,
+ bool* has_valid_io_context) {
+ *has_valid_io_context = ((key & 1) == 0);
+ return reinterpret_cast<IOHandler*>(key & ~static_cast<ULONG_PTR>(1));
+}
+
+} // namespace base
diff --git a/src/base/message_pump_win.h b/src/base/message_pump_win.h
new file mode 100644
index 0000000..5ea195f
--- /dev/null
+++ b/src/base/message_pump_win.h
@@ -0,0 +1,396 @@
+// Copyright (c) 2012 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_MESSAGE_PUMP_WIN_H_
+#define BASE_MESSAGE_PUMP_WIN_H_
+
+#include <windows.h>
+
+#include <list>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_pump.h"
+#include "base/message_pump_dispatcher.h"
+#include "base/message_pump_observer.h"
+#include "base/observer_list.h"
+#include "base/time.h"
+#include "base/win/scoped_handle.h"
+
+namespace base {
+
+// MessagePumpWin serves as the base for specialized versions of the MessagePump
+// for Windows. It provides basic functionality like handling of observers and
+// controlling the lifetime of the message pump.
+class BASE_EXPORT MessagePumpWin : public MessagePump {
+ public:
+ MessagePumpWin() : have_work_(0), state_(NULL) {}
+ virtual ~MessagePumpWin() {}
+
+ // Add an Observer, which will start receiving notifications immediately.
+ void AddObserver(MessagePumpObserver* observer);
+
+ // Remove an Observer. It is safe to call this method while an Observer is
+ // receiving a notification callback.
+ void RemoveObserver(MessagePumpObserver* observer);
+
+ // Give a chance to code processing additional messages to notify the
+ // message loop observers that another message has been processed.
+ void WillProcessMessage(const MSG& msg);
+ void DidProcessMessage(const MSG& msg);
+
+ // Like MessagePump::Run, but MSG objects are routed through dispatcher.
+ void RunWithDispatcher(Delegate* delegate, MessagePumpDispatcher* dispatcher);
+
+ // MessagePump methods:
+ virtual void Run(Delegate* delegate) { RunWithDispatcher(delegate, NULL); }
+ virtual void Quit();
+
+ protected:
+ struct RunState {
+ Delegate* delegate;
+ MessagePumpDispatcher* dispatcher;
+
+ // Used to flag that the current Run() invocation should return ASAP.
+ bool should_quit;
+
+ // Used to count how many Run() invocations are on the stack.
+ int run_depth;
+ };
+
+ virtual void DoRunLoop() = 0;
+ int GetCurrentDelay() const;
+
+ ObserverList<MessagePumpObserver> observers_;
+
+ // The time at which delayed work should run.
+ TimeTicks delayed_work_time_;
+
+ // A boolean value used to indicate if there is a kMsgDoWork message pending
+ // in the Windows Message queue. There is at most one such message, and it
+ // can drive execution of tasks when a native message pump is running.
+ LONG have_work_;
+
+ // State for the current invocation of Run.
+ RunState* state_;
+};
+
+//-----------------------------------------------------------------------------
+// MessagePumpForUI extends MessagePumpWin with methods that are particular to a
+// MessageLoop instantiated with TYPE_UI.
+//
+// MessagePumpForUI implements a "traditional" Windows message pump. It contains
+// a nearly infinite loop that peeks out messages, and then dispatches them.
+// Intermixed with those peeks are callouts to DoWork for pending tasks, and
+// DoDelayedWork for pending timers. When there are no events to be serviced,
+// this pump goes into a wait state. In most cases, this message pump handles
+// all processing.
+//
+// However, when a task, or windows event, invokes on the stack a native dialog
+// box or such, that window typically provides a bare bones (native?) message
+// pump. That bare-bones message pump generally supports little more than a
+// peek of the Windows message queue, followed by a dispatch of the peeked
+// message. MessageLoop extends that bare-bones message pump to also service
+// Tasks, at the cost of some complexity.
+//
+// The basic structure of the extension (refered to as a sub-pump) is that a
+// special message, kMsgHaveWork, is repeatedly injected into the Windows
+// Message queue. Each time the kMsgHaveWork message is peeked, checks are
+// made for an extended set of events, including the availability of Tasks to
+// run.
+//
+// After running a task, the special message kMsgHaveWork is again posted to
+// the Windows Message queue, ensuring a future time slice for processing a
+// future event. To prevent flooding the Windows Message queue, care is taken
+// to be sure that at most one kMsgHaveWork message is EVER pending in the
+// Window's Message queue.
+//
+// There are a few additional complexities in this system where, when there are
+// no Tasks to run, this otherwise infinite stream of messages which drives the
+// sub-pump is halted. The pump is automatically re-started when Tasks are
+// queued.
+//
+// A second complexity is that the presence of this stream of posted tasks may
+// prevent a bare-bones message pump from ever peeking a WM_PAINT or WM_TIMER.
+// Such paint and timer events always give priority to a posted message, such as
+// kMsgHaveWork messages. As a result, care is taken to do some peeking in
+// between the posting of each kMsgHaveWork message (i.e., after kMsgHaveWork
+// is peeked, and before a replacement kMsgHaveWork is posted).
+//
+// NOTE: Although it may seem odd that messages are used to start and stop this
+// flow (as opposed to signaling objects, etc.), it should be understood that
+// the native message pump will *only* respond to messages. As a result, it is
+// an excellent choice. It is also helpful that the starter messages that are
+// placed in the queue when new task arrive also awakens DoRunLoop.
+//
+class BASE_EXPORT MessagePumpForUI : public MessagePumpWin {
+ public:
+ // A MessageFilter implements the common Peek/Translate/Dispatch code to deal
+ // with windows messages.
+ // This abstraction is used to inject TSF message peeking. See
+ // TextServicesMessageFilter.
+ class BASE_EXPORT MessageFilter {
+ public:
+ virtual ~MessageFilter() {}
+ // Implements the functionality exposed by the OS through PeekMessage.
+ virtual BOOL DoPeekMessage(MSG* msg,
+ HWND window_handle,
+ UINT msg_filter_min,
+ UINT msg_filter_max,
+ UINT remove_msg) {
+ return PeekMessage(msg, window_handle, msg_filter_min, msg_filter_max,
+ remove_msg);
+ }
+ // Returns true if |message| was consumed by the filter and no extra
+ // processing is required. If this method returns false, it is the
+ // responsibility of the caller to ensure that normal processing takes
+ // place.
+ // The priority to consume messages is the following:
+ // - Native Windows' message filter (CallMsgFilter).
+ // - MessageFilter::ProcessMessage.
+ // - MessagePumpDispatcher.
+ // - TranslateMessage / DispatchMessage.
+ virtual bool ProcessMessage(const MSG& msg) { return false;}
+ };
+ // The application-defined code passed to the hook procedure.
+ static const int kMessageFilterCode = 0x5001;
+
+ MessagePumpForUI();
+ virtual ~MessagePumpForUI();
+
+ // Sets a new MessageFilter. MessagePumpForUI takes ownership of
+ // |message_filter|. When SetMessageFilter is called, old MessageFilter is
+ // deleted.
+ void SetMessageFilter(scoped_ptr<MessageFilter> message_filter);
+
+ // MessagePump methods:
+ virtual void ScheduleWork();
+ virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time);
+
+ // Applications can call this to encourage us to process all pending WM_PAINT
+ // messages. This method will process all paint messages the Windows Message
+ // queue can provide, up to some fixed number (to avoid any infinite loops).
+ void PumpOutPendingPaintMessages();
+
+ private:
+ static LRESULT CALLBACK WndProcThunk(HWND window_handle,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam);
+ virtual void DoRunLoop();
+ void InitMessageWnd();
+ void WaitForWork();
+ void HandleWorkMessage();
+ void HandleTimerMessage();
+ bool ProcessNextWindowsMessage();
+ bool ProcessMessageHelper(const MSG& msg);
+ bool ProcessPumpReplacementMessage();
+
+ // Instance of the module containing the window procedure.
+ HMODULE instance_;
+
+ // A hidden message-only window.
+ HWND message_hwnd_;
+
+ scoped_ptr<MessageFilter> message_filter_;
+};
+
+//-----------------------------------------------------------------------------
+// MessagePumpForIO extends MessagePumpWin with methods that are particular to a
+// MessageLoop instantiated with TYPE_IO. This version of MessagePump does not
+// deal with Windows mesagges, and instead has a Run loop based on Completion
+// Ports so it is better suited for IO operations.
+//
+class BASE_EXPORT MessagePumpForIO : public MessagePumpWin {
+ public:
+ struct IOContext;
+
+ // Clients interested in receiving OS notifications when asynchronous IO
+ // operations complete should implement this interface and register themselves
+ // with the message pump.
+ //
+ // Typical use #1:
+ // // Use only when there are no user's buffers involved on the actual IO,
+ // // so that all the cleanup can be done by the message pump.
+ // class MyFile : public IOHandler {
+ // MyFile() {
+ // ...
+ // context_ = new IOContext;
+ // context_->handler = this;
+ // message_pump->RegisterIOHandler(file_, this);
+ // }
+ // ~MyFile() {
+ // if (pending_) {
+ // // By setting the handler to NULL, we're asking for this context
+ // // to be deleted when received, without calling back to us.
+ // context_->handler = NULL;
+ // } else {
+ // delete context_;
+ // }
+ // }
+ // virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered,
+ // DWORD error) {
+ // pending_ = false;
+ // }
+ // void DoSomeIo() {
+ // ...
+ // // The only buffer required for this operation is the overlapped
+ // // structure.
+ // ConnectNamedPipe(file_, &context_->overlapped);
+ // pending_ = true;
+ // }
+ // bool pending_;
+ // IOContext* context_;
+ // HANDLE file_;
+ // };
+ //
+ // Typical use #2:
+ // class MyFile : public IOHandler {
+ // MyFile() {
+ // ...
+ // message_pump->RegisterIOHandler(file_, this);
+ // }
+ // // Plus some code to make sure that this destructor is not called
+ // // while there are pending IO operations.
+ // ~MyFile() {
+ // }
+ // virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered,
+ // DWORD error) {
+ // ...
+ // delete context;
+ // }
+ // void DoSomeIo() {
+ // ...
+ // IOContext* context = new IOContext;
+ // // This is not used for anything. It just prevents the context from
+ // // being considered "abandoned".
+ // context->handler = this;
+ // ReadFile(file_, buffer, num_bytes, &read, &context->overlapped);
+ // }
+ // HANDLE file_;
+ // };
+ //
+ // Typical use #3:
+ // Same as the previous example, except that in order to deal with the
+ // requirement stated for the destructor, the class calls WaitForIOCompletion
+ // from the destructor to block until all IO finishes.
+ // ~MyFile() {
+ // while(pending_)
+ // message_pump->WaitForIOCompletion(INFINITE, this);
+ // }
+ //
+ class IOHandler {
+ public:
+ virtual ~IOHandler() {}
+ // This will be called once the pending IO operation associated with
+ // |context| completes. |error| is the Win32 error code of the IO operation
+ // (ERROR_SUCCESS if there was no error). |bytes_transfered| will be zero
+ // on error.
+ virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered,
+ DWORD error) = 0;
+ };
+
+ // An IOObserver is an object that receives IO notifications from the
+ // MessagePump.
+ //
+ // NOTE: An IOObserver implementation should be extremely fast!
+ class IOObserver {
+ public:
+ IOObserver() {}
+
+ virtual void WillProcessIOEvent() = 0;
+ virtual void DidProcessIOEvent() = 0;
+
+ protected:
+ virtual ~IOObserver() {}
+ };
+
+ // The extended context that should be used as the base structure on every
+ // overlapped IO operation. |handler| must be set to the registered IOHandler
+ // for the given file when the operation is started, and it can be set to NULL
+ // before the operation completes to indicate that the handler should not be
+ // called anymore, and instead, the IOContext should be deleted when the OS
+ // notifies the completion of this operation. Please remember that any buffers
+ // involved with an IO operation should be around until the callback is
+ // received, so this technique can only be used for IO that do not involve
+ // additional buffers (other than the overlapped structure itself).
+ struct IOContext {
+ OVERLAPPED overlapped;
+ IOHandler* handler;
+ };
+
+ MessagePumpForIO();
+ virtual ~MessagePumpForIO() {}
+
+ // MessagePump methods:
+ virtual void ScheduleWork();
+ virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time);
+
+ // Register the handler to be used when asynchronous IO for the given file
+ // completes. The registration persists as long as |file_handle| is valid, so
+ // |handler| must be valid as long as there is pending IO for the given file.
+ void RegisterIOHandler(HANDLE file_handle, IOHandler* handler);
+
+ // Register the handler to be used to process job events. The registration
+ // persists as long as the job object is live, so |handler| must be valid
+ // until the job object is destroyed. Returns true if the registration
+ // succeeded, and false otherwise.
+ bool RegisterJobObject(HANDLE job_handle, IOHandler* handler);
+
+ // Waits for the next IO completion that should be processed by |filter|, for
+ // up to |timeout| milliseconds. Return true if any IO operation completed,
+ // regardless of the involved handler, and false if the timeout expired. If
+ // the completion port received any message and the involved IO handler
+ // matches |filter|, the callback is called before returning from this code;
+ // if the handler is not the one that we are looking for, the callback will
+ // be postponed for another time, so reentrancy problems can be avoided.
+ // External use of this method should be reserved for the rare case when the
+ // caller is willing to allow pausing regular task dispatching on this thread.
+ bool WaitForIOCompletion(DWORD timeout, IOHandler* filter);
+
+ void AddIOObserver(IOObserver* obs);
+ void RemoveIOObserver(IOObserver* obs);
+
+ private:
+ struct IOItem {
+ IOHandler* handler;
+ IOContext* context;
+ DWORD bytes_transfered;
+ DWORD error;
+
+ // In some cases |context| can be a non-pointer value casted to a pointer.
+ // |has_valid_io_context| is true if |context| is a valid IOContext
+ // pointer, and false otherwise.
+ bool has_valid_io_context;
+ };
+
+ virtual void DoRunLoop();
+ void WaitForWork();
+ bool MatchCompletedIOItem(IOHandler* filter, IOItem* item);
+ bool GetIOItem(DWORD timeout, IOItem* item);
+ bool ProcessInternalIOItem(const IOItem& item);
+ void WillProcessIOEvent();
+ void DidProcessIOEvent();
+
+ // Converts an IOHandler pointer to a completion port key.
+ // |has_valid_io_context| specifies whether completion packets posted to
+ // |handler| will have valid OVERLAPPED pointers.
+ static ULONG_PTR HandlerToKey(IOHandler* handler, bool has_valid_io_context);
+
+ // Converts a completion port key to an IOHandler pointer.
+ static IOHandler* KeyToHandler(ULONG_PTR key, bool* has_valid_io_context);
+
+ // The completion port associated with this thread.
+ win::ScopedHandle port_;
+ // This list will be empty almost always. It stores IO completions that have
+ // not been delivered yet because somebody was doing cleanup.
+ std::list<IOItem> completed_io_;
+
+ ObserverList<IOObserver> io_observers_;
+};
+
+} // namespace base
+
+#endif // BASE_MESSAGE_PUMP_WIN_H_
diff --git a/src/base/metrics/bucket_ranges.cc b/src/base/metrics/bucket_ranges.cc
new file mode 100644
index 0000000..01a5569
--- /dev/null
+++ b/src/base/metrics/bucket_ranges.cc
@@ -0,0 +1,143 @@
+// Copyright (c) 2012 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/metrics/bucket_ranges.h"
+
+#include <cmath>
+
+#include "base/logging.h"
+
+namespace base {
+
+// Static table of checksums for all possible 8 bit bytes.
+const uint32 kCrcTable[256] = { 0x0, 0x77073096L, 0xee0e612cL,
+0x990951baL, 0x76dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L,
+0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x9b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL,
+0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL,
+0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L,
+0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL,
+0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L,
+0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL,
+0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL,
+0xb6662d3dL, 0x76dc4190L, 0x1db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L,
+0x6b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0xf00f934L, 0x9609a88eL,
+0xe10e9818L, 0x7f6a0dbbL, 0x86d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L,
+0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L,
+0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL,
+0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L,
+0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL,
+0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L,
+0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L,
+0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L,
+0x9abfb3b6L, 0x3b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x4db2615L,
+0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0xd6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL,
+0x9309ff9dL, 0xa00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L,
+0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L,
+0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L,
+0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L,
+0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL,
+0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L,
+0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L,
+0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+0x26d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x5005713L, 0x95bf4a82L,
+0xe2b87a14L, 0x7bb12baeL, 0xcb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L,
+0xbdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL,
+0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL,
+0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL,
+0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L,
+0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L,
+0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL,
+0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+0x2d02ef8dL,
+};
+
+// We generate the CRC-32 using the low order bits to select whether to XOR in
+// the reversed polynomial 0xedb88320L. This is nice and simple, and allows us
+// to keep the quotient in a uint32. Since we're not concerned about the nature
+// of corruptions (i.e., we don't care about bit sequencing, since we are
+// handling memory changes, which are more grotesque) so we don't bother to
+// get the CRC correct for big-endian vs little-ending calculations. All we
+// need is a nice hash, that tends to depend on all the bits of the sample, with
+// very little chance of changes in one place impacting changes in another
+// place.
+static uint32 Crc32(uint32 sum, HistogramBase::Sample value) {
+ // TODO(jar): Switch to false and watch stats.
+ const bool kUseRealCrc = true;
+
+ if (kUseRealCrc) {
+ union {
+ HistogramBase::Sample range;
+ unsigned char bytes[sizeof(HistogramBase::Sample)];
+ } converter;
+ converter.range = value;
+ for (size_t i = 0; i < sizeof(converter); ++i)
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ sum = kCrcTable[(sum & 0xff) ^ converter.bytes[i]] ^ (sum >> 8);
+#else
+ sum = kCrcTable[(sum & 0xff) ^ converter.bytes[sizeof(converter) - i - 1]] ^ (sum >> 8);
+#endif
+ } else {
+ // Use hash techniques provided in ReallyFastHash, except we don't care
+ // about "avalanching" (which would worsten the hash, and add collisions),
+ // and we don't care about edge cases since we have an even number of bytes.
+ union {
+ HistogramBase::Sample range;
+ uint16 ints[sizeof(HistogramBase::Sample) / 2];
+ } converter;
+ DCHECK_EQ(sizeof(HistogramBase::Sample), sizeof(converter));
+ converter.range = value;
+ sum += converter.ints[0];
+ sum = (sum << 16) ^ sum ^ (static_cast<uint32>(converter.ints[1]) << 11);
+ sum += sum >> 11;
+ }
+ return sum;
+}
+
+BucketRanges::BucketRanges(size_t num_ranges)
+ : ranges_(num_ranges, 0),
+ checksum_(0) {}
+
+BucketRanges::~BucketRanges() {}
+
+void BucketRanges::set_range(size_t i, HistogramBase::Sample value) {
+ DCHECK_LT(i, ranges_.size());
+ CHECK_GE(value, 0);
+ ranges_[i] = value;
+}
+
+uint32 BucketRanges::CalculateChecksum() const {
+ // Seed checksum.
+ uint32 checksum = static_cast<uint32>(ranges_.size());
+
+ for (size_t index = 0; index < ranges_.size(); ++index)
+ checksum = Crc32(checksum, ranges_[index]);
+ return checksum;
+}
+
+bool BucketRanges::HasValidChecksum() const {
+ return CalculateChecksum() == checksum_;
+}
+
+void BucketRanges::ResetChecksum() {
+ checksum_ = CalculateChecksum();
+}
+
+bool BucketRanges::Equals(const BucketRanges* other) const {
+ if (checksum_ != other->checksum_)
+ return false;
+ if (ranges_.size() != other->ranges_.size())
+ return false;
+ for (size_t index = 0; index < ranges_.size(); ++index) {
+ if (ranges_[index] != other->ranges_[index])
+ return false;
+ }
+ return true;
+}
+
+} // namespace base
diff --git a/src/base/metrics/bucket_ranges.h b/src/base/metrics/bucket_ranges.h
new file mode 100644
index 0000000..90b9047
--- /dev/null
+++ b/src/base/metrics/bucket_ranges.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 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.
+//
+// BucketRanges stores the vector of ranges that delimit what samples are
+// tallied in the corresponding buckets of a histogram. Histograms that have
+// same ranges for all their corresponding buckets should share the same
+// BucketRanges object.
+//
+// E.g. A 5 buckets LinearHistogram with 1 as minimal value and 4 as maximal
+// value will need a BucketRanges with 6 ranges:
+// 0, 1, 2, 3, 4, INT_MAX
+//
+// TODO(kaiwang): Currently we keep all negative values in 0~1 bucket. Consider
+// changing 0 to INT_MIN.
+
+#ifndef BASE_METRICS_BUCKET_RANGES_H_
+#define BASE_METRICS_BUCKET_RANGES_H_
+
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/metrics/histogram_base.h"
+
+namespace base {
+
+class BASE_EXPORT BucketRanges {
+ public:
+ typedef std::vector<HistogramBase::Sample> Ranges;
+
+ BucketRanges(size_t num_ranges);
+ ~BucketRanges();
+
+ size_t size() const { return ranges_.size(); }
+ HistogramBase::Sample range(size_t i) const { return ranges_[i]; }
+ void set_range(size_t i, HistogramBase::Sample value);
+ uint32 checksum() const { return checksum_; }
+ void set_checksum(uint32 checksum) { checksum_ = checksum; }
+
+ // Checksum methods to verify whether the ranges are corrupted (e.g. bad
+ // memory access).
+ uint32 CalculateChecksum() const;
+ bool HasValidChecksum() const;
+ void ResetChecksum();
+
+ // Return true iff |other| object has same ranges_ as |this| object's ranges_.
+ bool Equals(const BucketRanges* other) const;
+
+ private:
+ // A monotonically increasing list of values which determine which bucket to
+ // put a sample into. For each index, show the smallest sample that can be
+ // added to the corresponding bucket.
+ Ranges ranges_;
+
+ // Checksum for the conntents of ranges_. Used to detect random over-writes
+ // of our data, and to quickly see if some other BucketRanges instance is
+ // possibly Equal() to this instance.
+ // TODO(kaiwang): Consider change this to uint64. Because we see a lot of
+ // noise on UMA dashboard.
+ uint32 checksum_;
+
+ DISALLOW_COPY_AND_ASSIGN(BucketRanges);
+};
+
+//////////////////////////////////////////////////////////////////////////////
+// Expose only for test.
+BASE_EXPORT_PRIVATE extern const uint32 kCrcTable[256];
+
+} // namespace base
+
+#endif // BASE_METRICS_BUCKET_RANGES_H_
diff --git a/src/base/metrics/bucket_ranges_unittest.cc b/src/base/metrics/bucket_ranges_unittest.cc
new file mode 100644
index 0000000..424ff63
--- /dev/null
+++ b/src/base/metrics/bucket_ranges_unittest.cc
@@ -0,0 +1,91 @@
+// Copyright (c) 2012 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/metrics/bucket_ranges.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+TEST(BucketRangesTest, NormalSetup) {
+ BucketRanges ranges(5);
+ ASSERT_EQ(5u, ranges.size());
+
+ for (int i = 0; i < 5; i++) {
+ EXPECT_EQ(0, ranges.range(i));
+ }
+ EXPECT_EQ(0u, ranges.checksum());
+
+ ranges.set_range(3, 100);
+ EXPECT_EQ(100, ranges.range(3));
+}
+
+TEST(BucketRangesTest, Equals) {
+ // Compare empty ranges.
+ BucketRanges ranges1(3);
+ BucketRanges ranges2(3);
+ BucketRanges ranges3(5);
+
+ EXPECT_TRUE(ranges1.Equals(&ranges2));
+ EXPECT_FALSE(ranges1.Equals(&ranges3));
+ EXPECT_FALSE(ranges2.Equals(&ranges3));
+
+ // Compare full filled ranges.
+ ranges1.set_range(0, 0);
+ ranges1.set_range(1, 1);
+ ranges1.set_range(2, 2);
+ ranges1.set_checksum(100);
+ ranges2.set_range(0, 0);
+ ranges2.set_range(1, 1);
+ ranges2.set_range(2, 2);
+ ranges2.set_checksum(100);
+
+ EXPECT_TRUE(ranges1.Equals(&ranges2));
+
+ // Checksum does not match.
+ ranges1.set_checksum(99);
+ EXPECT_FALSE(ranges1.Equals(&ranges2));
+ ranges1.set_checksum(100);
+
+ // Range does not match.
+ ranges1.set_range(1, 3);
+ EXPECT_FALSE(ranges1.Equals(&ranges2));
+}
+
+TEST(BucketRangesTest, Checksum) {
+ BucketRanges ranges(3);
+ ranges.set_range(0, 0);
+ ranges.set_range(1, 1);
+ ranges.set_range(2, 2);
+
+ ranges.ResetChecksum();
+ EXPECT_EQ(289217253u, ranges.checksum());
+
+ ranges.set_range(2, 3);
+ EXPECT_FALSE(ranges.HasValidChecksum());
+
+ ranges.ResetChecksum();
+ EXPECT_EQ(2843835776u, ranges.checksum());
+ EXPECT_TRUE(ranges.HasValidChecksum());
+}
+
+// Table was generated similarly to sample code for CRC-32 given on:
+// http://www.w3.org/TR/PNG/#D-CRCAppendix.
+TEST(BucketRangesTest, Crc32TableTest) {
+ for (int i = 0; i < 256; ++i) {
+ uint32 checksum = i;
+ for (int j = 0; j < 8; ++j) {
+ const uint32 kReversedPolynomial = 0xedb88320L;
+ if (checksum & 1)
+ checksum = kReversedPolynomial ^ (checksum >> 1);
+ else
+ checksum >>= 1;
+ }
+ EXPECT_EQ(kCrcTable[i], checksum);
+ }
+}
+
+} // namespace
+} // namespace base
diff --git a/src/base/metrics/field_trial.cc b/src/base/metrics/field_trial.cc
new file mode 100644
index 0000000..14e16ea
--- /dev/null
+++ b/src/base/metrics/field_trial.cc
@@ -0,0 +1,495 @@
+// Copyright (c) 2012 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/metrics/field_trial.h"
+
+#include "base/build_time.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/rand_util.h"
+#include "base/sha1.h"
+#include "base/stringprintf.h"
+#include "base/string_util.h"
+#include "base/sys_byteorder.h"
+#include "base/utf_string_conversions.h"
+
+namespace base {
+
+namespace {
+
+// Created a time value based on |year|, |month| and |day_of_month| parameters.
+Time CreateTimeFromParams(int year, int month, int day_of_month) {
+ DCHECK_GT(year, 1970);
+ DCHECK_GT(month, 0);
+ DCHECK_LT(month, 13);
+ DCHECK_GT(day_of_month, 0);
+ DCHECK_LT(day_of_month, 32);
+
+ Time::Exploded exploded;
+ exploded.year = year;
+ exploded.month = month;
+ exploded.day_of_week = 0; // Should be unused.
+ exploded.day_of_month = day_of_month;
+ exploded.hour = 0;
+ exploded.minute = 0;
+ exploded.second = 0;
+ exploded.millisecond = 0;
+
+ return Time::FromLocalExploded(exploded);
+}
+
+} // namespace
+
+static const char kHistogramFieldTrialSeparator('_');
+
+// statics
+const int FieldTrial::kNotFinalized = -1;
+const int FieldTrial::kDefaultGroupNumber = 0;
+bool FieldTrial::enable_benchmarking_ = false;
+
+const char FieldTrialList::kPersistentStringSeparator('/');
+int FieldTrialList::kExpirationYearInFuture = 0;
+
+//------------------------------------------------------------------------------
+// FieldTrial methods and members.
+
+FieldTrial::FieldTrial(const std::string& trial_name,
+ const Probability total_probability,
+ const std::string& default_group_name)
+ : trial_name_(trial_name),
+ divisor_(total_probability),
+ default_group_name_(default_group_name),
+ random_(static_cast<Probability>(divisor_ * RandDouble())),
+ accumulated_group_probability_(0),
+ next_group_number_(kDefaultGroupNumber + 1),
+ group_(kNotFinalized),
+ enable_field_trial_(true),
+ forced_(false),
+ group_reported_(false) {
+ DCHECK_GT(total_probability, 0);
+ DCHECK(!trial_name_.empty());
+ DCHECK(!default_group_name_.empty());
+}
+
+FieldTrial::EntropyProvider::~EntropyProvider() {
+}
+
+void FieldTrial::UseOneTimeRandomization() {
+ // No need to specify randomization when the group choice was forced.
+ if (forced_)
+ return;
+ DCHECK_EQ(group_, kNotFinalized);
+ DCHECK_EQ(kDefaultGroupNumber + 1, next_group_number_);
+ const EntropyProvider* entropy_provider =
+ FieldTrialList::GetEntropyProviderForOneTimeRandomization();
+ if (!entropy_provider) {
+ NOTREACHED();
+ // TODO(stevet): Remove this temporary histogram when logging
+ // investigations are complete.
+ UMA_HISTOGRAM_BOOLEAN("Variations.DisabledNoEntropyProvider", true);
+ Disable();
+ return;
+ }
+
+ random_ = static_cast<Probability>(
+ divisor_ * entropy_provider->GetEntropyForTrial(trial_name_));
+}
+
+void FieldTrial::Disable() {
+ DCHECK(!group_reported_);
+ enable_field_trial_ = false;
+
+ // In case we are disabled after initialization, we need to switch
+ // the trial to the default group.
+ if (group_ != kNotFinalized) {
+ // Only reset when not already the default group, because in case we were
+ // forced to the default group, the group number may not be
+ // kDefaultGroupNumber, so we should keep it as is.
+ if (group_name_ != default_group_name_)
+ SetGroupChoice(default_group_name_, kDefaultGroupNumber);
+ }
+}
+
+int FieldTrial::AppendGroup(const std::string& name,
+ Probability group_probability) {
+ // When the group choice was previously forced, we only need to return the
+ // the id of the chosen group, and anything can be returned for the others.
+ if (forced_) {
+ DCHECK(!group_name_.empty());
+ if (name == group_name_) {
+ return group_;
+ }
+ DCHECK_NE(next_group_number_, group_);
+ // We still return different numbers each time, in case some caller need
+ // them to be different.
+ return next_group_number_++;
+ }
+
+ DCHECK_LE(group_probability, divisor_);
+ DCHECK_GE(group_probability, 0);
+
+ if (enable_benchmarking_ || !enable_field_trial_)
+ group_probability = 0;
+
+ accumulated_group_probability_ += group_probability;
+
+ DCHECK_LE(accumulated_group_probability_, divisor_);
+ if (group_ == kNotFinalized && accumulated_group_probability_ > random_) {
+ // This is the group that crossed the random line, so we do the assignment.
+ SetGroupChoice(name, next_group_number_);
+ }
+ return next_group_number_++;
+}
+
+int FieldTrial::group() {
+ FinalizeGroupChoice();
+ FieldTrialList::NotifyFieldTrialGroupSelection(this);
+ return group_;
+}
+
+const std::string& FieldTrial::group_name() {
+ // Call |group()| to ensure group gets assigned and observers are notified.
+ group();
+ DCHECK(!group_name_.empty());
+ return group_name_;
+}
+
+// static
+std::string FieldTrial::MakeName(const std::string& name_prefix,
+ const std::string& trial_name) {
+ std::string big_string(name_prefix);
+ big_string.append(1, kHistogramFieldTrialSeparator);
+ return big_string.append(FieldTrialList::FindFullName(trial_name));
+}
+
+// static
+void FieldTrial::EnableBenchmarking() {
+ DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount());
+ enable_benchmarking_ = true;
+}
+
+void FieldTrial::SetForced() {
+ // We might have been forced before (e.g., by CreateFieldTrial) and it's
+ // first come first served, e.g., command line switch has precedence.
+ if (forced_)
+ return;
+
+ // And we must finalize the group choice before we mark ourselves as forced.
+ FinalizeGroupChoice();
+ forced_ = true;
+}
+
+FieldTrial::~FieldTrial() {}
+
+void FieldTrial::SetGroupChoice(const std::string& group_name, int number) {
+ group_ = number;
+ if (group_name.empty())
+ StringAppendF(&group_name_, "%d", group_);
+ else
+ group_name_ = group_name;
+ DVLOG(1) << "Field trial: " << trial_name_ << " Group choice:" << group_name_;
+}
+
+void FieldTrial::FinalizeGroupChoice() {
+ if (group_ != kNotFinalized)
+ return;
+ accumulated_group_probability_ = divisor_;
+ // Here it's OK to use |kDefaultGroupNumber| since we can't be forced and not
+ // finalized.
+ DCHECK(!forced_);
+ SetGroupChoice(default_group_name_, kDefaultGroupNumber);
+}
+
+bool FieldTrial::GetActiveGroup(ActiveGroup* active_group) const {
+ if (!group_reported_ || !enable_field_trial_) {
+ // TODO(asvitkine): Temporary histogram. Remove this once it is not needed.
+ if (trial_name_ == "UMA-Uniformity-Trial-1-Percent") {
+ const int kGroupNotReported = 1;
+ const int kTrialDisabled = 2;
+ int value = 0;
+ if (!group_reported_)
+ value |= kGroupNotReported;
+ if (!enable_field_trial_)
+ value |= kTrialDisabled;
+ UMA_HISTOGRAM_ENUMERATION("Variations.UniformityTrialGroupNotActive",
+ value, 4);
+ }
+ return false;
+ }
+ DCHECK_NE(group_, kNotFinalized);
+ active_group->trial_name = trial_name_;
+ active_group->group_name = group_name_;
+ return true;
+}
+
+//------------------------------------------------------------------------------
+// FieldTrialList methods and members.
+
+// static
+FieldTrialList* FieldTrialList::global_ = NULL;
+
+// static
+bool FieldTrialList::used_without_global_ = false;
+
+FieldTrialList::Observer::~Observer() {
+}
+
+FieldTrialList::FieldTrialList(
+ const FieldTrial::EntropyProvider* entropy_provider)
+ : entropy_provider_(entropy_provider),
+ observer_list_(new ObserverListThreadSafe<FieldTrialList::Observer>(
+ ObserverListBase<FieldTrialList::Observer>::NOTIFY_EXISTING_ONLY)) {
+ DCHECK(!global_);
+ DCHECK(!used_without_global_);
+ global_ = this;
+
+ Time::Exploded exploded;
+ Time two_years_from_now =
+ Time::NowFromSystemTime() + TimeDelta::FromDays(730);
+ two_years_from_now.LocalExplode(&exploded);
+ kExpirationYearInFuture = exploded.year;
+}
+
+FieldTrialList::~FieldTrialList() {
+ AutoLock auto_lock(lock_);
+ while (!registered_.empty()) {
+ RegistrationList::iterator it = registered_.begin();
+ it->second->Release();
+ registered_.erase(it->first);
+ }
+ DCHECK_EQ(this, global_);
+ global_ = NULL;
+}
+
+// static
+FieldTrial* FieldTrialList::FactoryGetFieldTrial(
+ const std::string& name,
+ FieldTrial::Probability total_probability,
+ const std::string& default_group_name,
+ const int year,
+ const int month,
+ const int day_of_month,
+ int* default_group_number) {
+ if (default_group_number)
+ *default_group_number = FieldTrial::kDefaultGroupNumber;
+ // Check if the field trial has already been created in some other way.
+ FieldTrial* existing_trial = Find(name);
+ if (existing_trial) {
+ CHECK(existing_trial->forced_);
+ // If the field trial has already been forced, check whether it was forced
+ // to the default group. Return the chosen group number, in that case..
+ if (default_group_number &&
+ default_group_name == existing_trial->default_group_name()) {
+ *default_group_number = existing_trial->group();
+ }
+ return existing_trial;
+ }
+
+ FieldTrial* field_trial =
+ new FieldTrial(name, total_probability, default_group_name);
+ if (GetBuildTime() > CreateTimeFromParams(year, month, day_of_month)) {
+ // TODO(asvitkine): Temporary histogram. Remove this once it is not needed.
+ if (name == "UMA-Uniformity-Trial-1-Percent")
+ UMA_HISTOGRAM_BOOLEAN("Variations.UniformityTrialExpired", true);
+ field_trial->Disable();
+ }
+ FieldTrialList::Register(field_trial);
+ return field_trial;
+}
+
+// static
+FieldTrial* FieldTrialList::Find(const std::string& name) {
+ if (!global_)
+ return NULL;
+ AutoLock auto_lock(global_->lock_);
+ return global_->PreLockedFind(name);
+}
+
+// static
+int FieldTrialList::FindValue(const std::string& name) {
+ FieldTrial* field_trial = Find(name);
+ if (field_trial)
+ return field_trial->group();
+ return FieldTrial::kNotFinalized;
+}
+
+// static
+std::string FieldTrialList::FindFullName(const std::string& name) {
+ FieldTrial* field_trial = Find(name);
+ if (field_trial)
+ return field_trial->group_name();
+ return "";
+}
+
+// static
+bool FieldTrialList::TrialExists(const std::string& name) {
+ return Find(name) != NULL;
+}
+
+// static
+void FieldTrialList::StatesToString(std::string* output) {
+ FieldTrial::ActiveGroups active_groups;
+ GetActiveFieldTrialGroups(&active_groups);
+ for (FieldTrial::ActiveGroups::const_iterator it = active_groups.begin();
+ it != active_groups.end(); ++it) {
+ DCHECK_EQ(std::string::npos,
+ it->trial_name.find(kPersistentStringSeparator));
+ DCHECK_EQ(std::string::npos,
+ it->group_name.find(kPersistentStringSeparator));
+ output->append(it->trial_name);
+ output->append(1, kPersistentStringSeparator);
+ output->append(it->group_name);
+ output->append(1, kPersistentStringSeparator);
+ }
+}
+
+// static
+void FieldTrialList::GetActiveFieldTrialGroups(
+ FieldTrial::ActiveGroups* active_groups) {
+ DCHECK(active_groups->empty());
+ if (!global_)
+ return;
+ AutoLock auto_lock(global_->lock_);
+
+ for (RegistrationList::iterator it = global_->registered_.begin();
+ it != global_->registered_.end(); ++it) {
+ FieldTrial::ActiveGroup active_group;
+ if (it->second->GetActiveGroup(&active_group))
+ active_groups->push_back(active_group);
+ }
+}
+
+// static
+bool FieldTrialList::CreateTrialsFromString(const std::string& trials_string) {
+ DCHECK(global_);
+ if (trials_string.empty() || !global_)
+ return true;
+
+ size_t next_item = 0;
+ while (next_item < trials_string.length()) {
+ size_t name_end = trials_string.find(kPersistentStringSeparator, next_item);
+ if (name_end == trials_string.npos || next_item == name_end)
+ return false;
+ size_t group_name_end = trials_string.find(kPersistentStringSeparator,
+ name_end + 1);
+ if (group_name_end == trials_string.npos || name_end + 1 == group_name_end)
+ return false;
+ std::string name(trials_string, next_item, name_end - next_item);
+ std::string group_name(trials_string, name_end + 1,
+ group_name_end - name_end - 1);
+ next_item = group_name_end + 1;
+
+ FieldTrial* trial = CreateFieldTrial(name, group_name);
+ if (!trial)
+ return false;
+ // Call |group()| to mark the trial as "used" and notify observers, if any.
+ // This is needed to ensure the trial is properly reported in child process
+ // crash reports.
+ trial->group();
+ }
+ return true;
+}
+
+// static
+FieldTrial* FieldTrialList::CreateFieldTrial(
+ const std::string& name,
+ const std::string& group_name) {
+ DCHECK(global_);
+ DCHECK_GE(name.size(), 0u);
+ DCHECK_GE(group_name.size(), 0u);
+ if (name.empty() || group_name.empty() || !global_)
+ return NULL;
+
+ FieldTrial* field_trial = FieldTrialList::Find(name);
+ if (field_trial) {
+ // In single process mode, or when we force them from the command line,
+ // we may have already created the field trial.
+ if (field_trial->group_name_internal() != group_name)
+ return NULL;
+ return field_trial;
+ }
+ const int kTotalProbability = 100;
+ field_trial = new FieldTrial(name, kTotalProbability, group_name);
+ // This is where we may assign a group number different from
+ // kDefaultGroupNumber to the default group.
+ field_trial->AppendGroup(group_name, kTotalProbability);
+ field_trial->forced_ = true;
+ FieldTrialList::Register(field_trial);
+ return field_trial;
+}
+
+// static
+void FieldTrialList::AddObserver(Observer* observer) {
+ if (!global_)
+ return;
+ global_->observer_list_->AddObserver(observer);
+}
+
+// static
+void FieldTrialList::RemoveObserver(Observer* observer) {
+ if (!global_)
+ return;
+ global_->observer_list_->RemoveObserver(observer);
+}
+
+// static
+void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) {
+ if (!global_)
+ return;
+
+ {
+ AutoLock auto_lock(global_->lock_);
+ if (field_trial->group_reported_)
+ return;
+ field_trial->group_reported_ = true;
+ }
+
+ if (!field_trial->enable_field_trial_)
+ return;
+
+ global_->observer_list_->Notify(
+ &FieldTrialList::Observer::OnFieldTrialGroupFinalized,
+ field_trial->trial_name(),
+ field_trial->group_name_internal());
+}
+
+// static
+size_t FieldTrialList::GetFieldTrialCount() {
+ if (!global_)
+ return 0;
+ AutoLock auto_lock(global_->lock_);
+ return global_->registered_.size();
+}
+
+// static
+const FieldTrial::EntropyProvider*
+ FieldTrialList::GetEntropyProviderForOneTimeRandomization() {
+ if (!global_) {
+ used_without_global_ = true;
+ return NULL;
+ }
+
+ return global_->entropy_provider_.get();
+}
+
+FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) {
+ RegistrationList::iterator it = registered_.find(name);
+ if (registered_.end() == it)
+ return NULL;
+ return it->second;
+}
+
+// static
+void FieldTrialList::Register(FieldTrial* trial) {
+ if (!global_) {
+ used_without_global_ = true;
+ return;
+ }
+ AutoLock auto_lock(global_->lock_);
+ DCHECK(!global_->PreLockedFind(trial->trial_name()));
+ trial->AddRef();
+ global_->registered_[trial->trial_name()] = trial;
+}
+
+} // namespace base
diff --git a/src/base/metrics/field_trial.h b/src/base/metrics/field_trial.h
new file mode 100644
index 0000000..007c1da
--- /dev/null
+++ b/src/base/metrics/field_trial.h
@@ -0,0 +1,444 @@
+// Copyright (c) 2012 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.
+
+// FieldTrial is a class for handling details of statistical experiments
+// performed by actual users in the field (i.e., in a shipped or beta product).
+// All code is called exclusively on the UI thread currently.
+//
+// The simplest example is an experiment to see whether one of two options
+// produces "better" results across our user population. In that scenario, UMA
+// data is uploaded to aggregate the test results, and this FieldTrial class
+// manages the state of each such experiment (state == which option was
+// pseudo-randomly selected).
+//
+// States are typically generated randomly, either based on a one time
+// randomization (which will yield the same results, in terms of selecting
+// the client for a field trial or not, for every run of the program on a
+// given machine), or by a startup randomization (generated each time the
+// application starts up, but held constant during the duration of the
+// process), or by continuous randomization across a run (where the state
+// can be recalculated again and again, many times during a process).
+// Continuous randomization is not yet implemented.
+
+//------------------------------------------------------------------------------
+// Example: Suppose we have an experiment involving memory, such as determining
+// the impact of some pruning algorithm.
+// We assume that we already have a histogram of memory usage, such as:
+
+// HISTOGRAM_COUNTS("Memory.RendererTotal", count);
+
+// Somewhere in main thread initialization code, we'd probably define an
+// instance of a FieldTrial, with code such as:
+
+// // FieldTrials are reference counted, and persist automagically until
+// // process teardown, courtesy of their automatic registration in
+// // FieldTrialList.
+// // Note: This field trial will run in Chrome instances compiled through
+// // 8 July, 2015, and after that all instances will be in "StandardMem".
+// scoped_refptr<base::FieldTrial> trial(
+// base::FieldTrialList::FactoryGetFieldTrial("MemoryExperiment", 1000,
+// "StandardMem", 2015, 7, 8,
+// NULL));
+// const int high_mem_group =
+// trial->AppendGroup("HighMem", 20); // 2% in HighMem group.
+// const int low_mem_group =
+// trial->AppendGroup("LowMem", 20); // 2% in LowMem group.
+// // Take action depending of which group we randomly land in.
+// if (trial->group() == high_mem_group)
+// SetPruningAlgorithm(kType1); // Sample setting of browser state.
+// else if (trial->group() == low_mem_group)
+// SetPruningAlgorithm(kType2); // Sample alternate setting.
+
+// We then, in addition to our original histogram, output histograms which have
+// slightly different names depending on what group the trial instance happened
+// to randomly be assigned:
+
+// HISTOGRAM_COUNTS("Memory.RendererTotal", count); // The original histogram.
+// static const bool memory_renderer_total_trial_exists =
+// FieldTrialList::TrialExists("MemoryExperiment");
+// if (memory_renderer_total_trial_exists) {
+// HISTOGRAM_COUNTS(FieldTrial::MakeName("Memory.RendererTotal",
+// "MemoryExperiment"), count);
+// }
+
+// The above code will create four distinct histograms, with each run of the
+// application being assigned to of of the three groups, and for each group, the
+// correspondingly named histogram will be populated:
+
+// Memory.RendererTotal // 100% of users still fill this histogram.
+// Memory.RendererTotal_HighMem // 2% of users will fill this histogram.
+// Memory.RendererTotal_LowMem // 2% of users will fill this histogram.
+// Memory.RendererTotal_StandardMem // 96% of users will fill this histogram.
+
+//------------------------------------------------------------------------------
+
+#ifndef BASE_METRICS_FIELD_TRIAL_H_
+#define BASE_METRICS_FIELD_TRIAL_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list_threadsafe.h"
+#include "base/synchronization/lock.h"
+#include "base/time.h"
+
+namespace base {
+
+class FieldTrialList;
+
+class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> {
+ public:
+ typedef int Probability; // Probability type for being selected in a trial.
+
+ // EntropyProvider is an interface for providing entropy for one-time
+ // randomized (persistent) field trials.
+ class BASE_EXPORT EntropyProvider {
+ public:
+ virtual ~EntropyProvider();
+
+ // Returns a double in the range of [0, 1) based on |trial_name| that will
+ // be used for the dice roll for the specified field trial. A given instance
+ // should always return the same value given the same input |trial_name|.
+ virtual double GetEntropyForTrial(const std::string& trial_name) const = 0;
+ };
+
+ // A pair representing a Field Trial and its selected group.
+ struct ActiveGroup {
+ std::string trial_name;
+ std::string group_name;
+ };
+
+ typedef std::vector<ActiveGroup> ActiveGroups;
+
+ // A return value to indicate that a given instance has not yet had a group
+ // assignment (and hence is not yet participating in the trial).
+ static const int kNotFinalized;
+
+ // Changes the field trial to use one-time randomization, i.e. produce the
+ // same result for the current trial on every run of this client. Must be
+ // called right after construction.
+ void UseOneTimeRandomization();
+
+ // Disables this trial, meaning it always determines the default group
+ // has been selected. May be called immediately after construction, or
+ // at any time after initialization (should not be interleaved with
+ // AppendGroup calls). Once disabled, there is no way to re-enable a
+ // trial.
+ // TODO(mad): http://code.google.com/p/chromium/issues/detail?id=121446
+ // This doesn't properly reset to Default when a group was forced.
+ void Disable();
+
+ // Establish the name and probability of the next group in this trial.
+ // Sometimes, based on construction randomization, this call may cause the
+ // provided group to be *THE* group selected for use in this instance.
+ // The return value is the group number of the new group.
+ int AppendGroup(const std::string& name, Probability group_probability);
+
+ // Return the name of the FieldTrial (excluding the group name).
+ const std::string& trial_name() const { return trial_name_; }
+
+ // Return the randomly selected group number that was assigned, and notify
+ // any/all observers that this finalized group number has presumably been used
+ // (queried), and will never change. Note that this will force an instance to
+ // participate, and make it illegal to attempt to probabilistically add any
+ // other groups to the trial.
+ int group();
+
+ // If the group's name is empty, a string version containing the group number
+ // is used as the group name. This causes a winner to be chosen if none was.
+ const std::string& group_name();
+
+ // Helper function for the most common use: as an argument to specify the
+ // name of a HISTOGRAM. Use the original histogram name as the name_prefix.
+ static std::string MakeName(const std::string& name_prefix,
+ const std::string& trial_name);
+
+ // Enable benchmarking sets field trials to a common setting.
+ static void EnableBenchmarking();
+
+ // Set the field trial as forced, meaning that it was setup earlier than
+ // the hard coded registration of the field trial to override it.
+ // This allows the code that was hard coded to register the field trial to
+ // still succeed even though the field trial has already been registered.
+ // This must be called after appending all the groups, since we will make
+ // the group choice here. Note that this is a NOOP for already forced trials.
+ // And, as the rest of the FieldTrial code, this is not thread safe and must
+ // be done from the UI thread.
+ void SetForced();
+
+ private:
+ // Allow tests to access our innards for testing purposes.
+ FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, Registration);
+ FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, AbsoluteProbabilities);
+ FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, RemainingProbability);
+ FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, FiftyFiftyProbability);
+ FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, MiddleProbabilities);
+ FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, OneWinner);
+ FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DisableProbability);
+ FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ActiveGroups);
+ FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ActiveGroupsNotFinalized);
+ FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, Save);
+ FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DuplicateRestore);
+ FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, MakeName);
+ FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, HashClientId);
+ FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, HashClientIdIsUniform);
+ FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, NameGroupIds);
+ FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, UseOneTimeRandomization);
+
+ friend class base::FieldTrialList;
+
+ friend class RefCounted<FieldTrial>;
+
+ // This is the group number of the 'default' group when a choice wasn't forced
+ // by a call to FieldTrialList::CreateFieldTrial. It is kept private so that
+ // consumers don't use it by mistake in cases where the group was forced.
+ static const int kDefaultGroupNumber;
+
+ FieldTrial(const std::string& name,
+ Probability total_probability,
+ const std::string& default_group_name);
+ virtual ~FieldTrial();
+
+ // Return the default group name of the FieldTrial.
+ std::string default_group_name() const { return default_group_name_; }
+
+ // Sets the chosen group name and number.
+ void SetGroupChoice(const std::string& group_name, int number);
+
+ // Ensures that a group is chosen, if it hasn't yet been. The field trial
+ // might yet be disabled, so this call will *not* notify observers of the
+ // status.
+ void FinalizeGroupChoice();
+
+ // Returns the trial name and selected group name for this field trial via
+ // the output parameter |active_group|, but only if the group has already
+ // been chosen and has been externally observed via |group()| and the trial
+ // has not been disabled. In that case, true is returned and |active_group|
+ // is filled in; otherwise, the result is false and |active_group| is left
+ // untouched.
+ bool GetActiveGroup(ActiveGroup* active_group) const;
+
+ // Returns the group_name. A winner need not have been chosen.
+ std::string group_name_internal() const { return group_name_; }
+
+ // The name of the field trial, as can be found via the FieldTrialList.
+ const std::string trial_name_;
+
+ // The maximum sum of all probabilities supplied, which corresponds to 100%.
+ // This is the scaling factor used to adjust supplied probabilities.
+ const Probability divisor_;
+
+ // The name of the default group.
+ const std::string default_group_name_;
+
+ // The randomly selected probability that is used to select a group (or have
+ // the instance not participate). It is the product of divisor_ and a random
+ // number between [0, 1).
+ Probability random_;
+
+ // Sum of the probabilities of all appended groups.
+ Probability accumulated_group_probability_;
+
+ int next_group_number_;
+
+ // The pseudo-randomly assigned group number.
+ // This is kNotFinalized if no group has been assigned.
+ int group_;
+
+ // A textual name for the randomly selected group. Valid after |group()|
+ // has been called.
+ std::string group_name_;
+
+ // When enable_field_trial_ is false, field trial reverts to the 'default'
+ // group.
+ bool enable_field_trial_;
+
+ // When forced_ is true, we return the chosen group from AppendGroup when
+ // appropriate.
+ bool forced_;
+
+ // Specifies whether the group choice has been reported to observers.
+ bool group_reported_;
+
+ // When benchmarking is enabled, field trials all revert to the 'default'
+ // group.
+ static bool enable_benchmarking_;
+
+ DISALLOW_COPY_AND_ASSIGN(FieldTrial);
+};
+
+//------------------------------------------------------------------------------
+// Class with a list of all active field trials. A trial is active if it has
+// been registered, which includes evaluating its state based on its probaility.
+// Only one instance of this class exists.
+class BASE_EXPORT FieldTrialList {
+ public:
+ // Define a separator character to use when creating a persistent form of an
+ // instance. This is intended for use as a command line argument, passed to a
+ // second process to mimic our state (i.e., provide the same group name).
+ static const char kPersistentStringSeparator; // Currently a slash.
+
+ // Define expiration year in future. It is initialized to two years from Now.
+ static int kExpirationYearInFuture;
+
+ // Observer is notified when a FieldTrial's group is selected.
+ class BASE_EXPORT Observer {
+ public:
+ // Notify observers when FieldTrials's group is selected.
+ virtual void OnFieldTrialGroupFinalized(const std::string& trial_name,
+ const std::string& group_name) = 0;
+
+ protected:
+ virtual ~Observer();
+ };
+
+ // This singleton holds the global list of registered FieldTrials.
+ //
+ // To support one-time randomized field trials, specify a non-NULL
+ // |entropy_provider| which should be a source of uniformly distributed
+ // entropy values. Takes ownership of |entropy_provider|. If one time
+ // randomization is not desired, pass in NULL for |entropy_provider|.
+ explicit FieldTrialList(const FieldTrial::EntropyProvider* entropy_provider);
+
+ // Destructor Release()'s references to all registered FieldTrial instances.
+ ~FieldTrialList();
+
+ // Get a FieldTrial instance from the factory.
+ //
+ // |name| is used to register the instance with the FieldTrialList class,
+ // and can be used to find the trial (only one trial can be present for each
+ // name). |default_group_name| is the name of the default group which will
+ // be chosen if none of the subsequent appended groups get to be chosen.
+ // |default_group_number| can receive the group number of the default group as
+ // AppendGroup returns the number of the subsequence groups. |trial_name| and
+ // |default_group_name| may not be empty but |default_group_number| can be
+ // NULL if the value is not needed.
+ //
+ // Group probabilities that are later supplied must sum to less than or equal
+ // to the |total_probability|. Arguments |year|, |month| and |day_of_month|
+ // specify the expiration time. If the build time is after the expiration time
+ // then the field trial reverts to the 'default' group.
+ //
+ // Use this static method to get a startup-randomized FieldTrial or a
+ // previously created forced FieldTrial. If you want a one-time randomized
+ // trial, call UseOneTimeRandomization() right after creation.
+ static FieldTrial* FactoryGetFieldTrial(
+ const std::string& trial_name,
+ FieldTrial::Probability total_probability,
+ const std::string& default_group_name,
+ const int year,
+ const int month,
+ const int day_of_month,
+ int* default_group_number);
+
+ // The Find() method can be used to test to see if a named Trial was already
+ // registered, or to retrieve a pointer to it from the global map.
+ static FieldTrial* Find(const std::string& name);
+
+ // Returns the group number chosen for the named trial, or
+ // FieldTrial::kNotFinalized if the trial does not exist.
+ static int FindValue(const std::string& name);
+
+ // Returns the group name chosen for the named trial, or the
+ // empty string if the trial does not exist.
+ static std::string FindFullName(const std::string& name);
+
+ // Returns true if the named trial has been registered.
+ static bool TrialExists(const std::string& name);
+
+ // Creates a persistent representation of active FieldTrial instances for
+ // resurrection in another process. This allows randomization to be done in
+ // one process, and secondary processes can be synchronized on the result.
+ // The resulting string contains the name and group name pairs of all
+ // registered FieldTrials for which the group has been chosen and externally
+ // observed (via |group()|) and which have not been disabled, with "/" used
+ // to separate all names and to terminate the string. This string is parsed
+ // by |CreateTrialsFromString()|.
+ static void StatesToString(std::string* output);
+
+ // Fills in the supplied vector |active_groups| (which must be empty when
+ // called) with a snapshot of all registered FieldTrials for which the group
+ // has been chosen and externally observed (via |group()|) and which have
+ // not been disabled.
+ static void GetActiveFieldTrialGroups(
+ FieldTrial::ActiveGroups* active_groups);
+
+ // Use a state string (re: StatesToString()) to augment the current list of
+ // field trials to include the supplied trials, and using a 100% probability
+ // for each trial, force them to have the same group string. This is commonly
+ // used in a non-browser process, to carry randomly selected state in a
+ // browser process into this non-browser process, but could also be invoked
+ // through a command line argument to the browser process. The created field
+ // trials are marked as "used" for the purposes of active trial reporting.
+ static bool CreateTrialsFromString(const std::string& prior_trials);
+
+ // Create a FieldTrial with the given |name| and using 100% probability for
+ // the FieldTrial, force FieldTrial to have the same group string as
+ // |group_name|. This is commonly used in a non-browser process, to carry
+ // randomly selected state in a browser process into this non-browser process.
+ // It returns NULL if there is a FieldTrial that is already registered with
+ // the same |name| but has different finalized group string (|group_name|).
+ static FieldTrial* CreateFieldTrial(const std::string& name,
+ const std::string& group_name);
+
+ // Add an observer to be notified when a field trial is irrevocably committed
+ // to being part of some specific field_group (and hence the group_name is
+ // also finalized for that field_trial).
+ static void AddObserver(Observer* observer);
+
+ // Remove an observer.
+ static void RemoveObserver(Observer* observer);
+
+ // Notify all observers that a group has been finalized for |field_trial|.
+ static void NotifyFieldTrialGroupSelection(FieldTrial* field_trial);
+
+ // Return the number of active field trials.
+ static size_t GetFieldTrialCount();
+
+ // If one-time randomization is enabled, returns a weak pointer to the
+ // corresponding EntropyProvider. Otherwise, returns NULL.
+ static const FieldTrial::EntropyProvider*
+ GetEntropyProviderForOneTimeRandomization();
+
+ private:
+ // A map from FieldTrial names to the actual instances.
+ typedef std::map<std::string, FieldTrial*> RegistrationList;
+
+ // Helper function should be called only while holding lock_.
+ FieldTrial* PreLockedFind(const std::string& name);
+
+ // Register() stores a pointer to the given trial in a global map.
+ // This method also AddRef's the indicated trial.
+ // This should always be called after creating a new FieldTrial instance.
+ static void Register(FieldTrial* trial);
+
+ static FieldTrialList* global_; // The singleton of this class.
+
+ // This will tell us if there is an attempt to register a field
+ // trial or check if one-time randomization is enabled without
+ // creating the FieldTrialList. This is not an error, unless a
+ // FieldTrialList is created after that.
+ static bool used_without_global_;
+
+ // Lock for access to registered_.
+ base::Lock lock_;
+ RegistrationList registered_;
+
+ // Entropy provider to be used for one-time randomized field trials. If NULL,
+ // one-time randomization is not supported.
+ scoped_ptr<const FieldTrial::EntropyProvider> entropy_provider_;
+
+ // List of observers to be notified when a group is selected for a FieldTrial.
+ scoped_refptr<ObserverListThreadSafe<Observer> > observer_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(FieldTrialList);
+};
+
+} // namespace base
+
+#endif // BASE_METRICS_FIELD_TRIAL_H_
diff --git a/src/base/metrics/field_trial_unittest.cc b/src/base/metrics/field_trial_unittest.cc
new file mode 100644
index 0000000..eed5f78
--- /dev/null
+++ b/src/base/metrics/field_trial_unittest.cc
@@ -0,0 +1,721 @@
+// Copyright (c) 2012 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/message_loop.h"
+#include "base/metrics/field_trial.h"
+#include "base/rand_util.h"
+#include "base/stringprintf.h"
+#include "base/string_number_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+// Default group name used by several tests.
+const char kDefaultGroupName[] = "DefaultGroup";
+
+// FieldTrialList::Observer implementation for testing.
+class TestFieldTrialObserver : public FieldTrialList::Observer {
+ public:
+ TestFieldTrialObserver() {
+ FieldTrialList::AddObserver(this);
+ }
+
+ virtual ~TestFieldTrialObserver() {
+ FieldTrialList::RemoveObserver(this);
+ }
+
+ virtual void OnFieldTrialGroupFinalized(const std::string& trial,
+ const std::string& group) OVERRIDE {
+ trial_name_ = trial;
+ group_name_ = group;
+ }
+
+ const std::string& trial_name() const { return trial_name_; }
+ const std::string& group_name() const { return group_name_; }
+
+ private:
+ std::string trial_name_;
+ std::string group_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestFieldTrialObserver);
+};
+
+} // namespace
+
+class FieldTrialTest : public testing::Test {
+ public:
+ FieldTrialTest() : trial_list_(NULL) {
+ Time now = Time::NowFromSystemTime();
+ TimeDelta one_year = TimeDelta::FromDays(365);
+ Time::Exploded exploded;
+
+ Time next_year_time = now + one_year;
+ next_year_time.LocalExplode(&exploded);
+ next_year_ = exploded.year;
+
+ Time last_year_time = now - one_year;
+ last_year_time.LocalExplode(&exploded);
+ last_year_ = exploded.year;
+ }
+
+ protected:
+ int next_year_;
+ int last_year_;
+ MessageLoop message_loop_;
+
+ private:
+ FieldTrialList trial_list_;
+};
+
+// Test registration, and also check that destructors are called for trials
+// (and that Valgrind doesn't catch us leaking).
+TEST_F(FieldTrialTest, Registration) {
+ const char* name1 = "name 1 test";
+ const char* name2 = "name 2 test";
+ EXPECT_FALSE(FieldTrialList::Find(name1));
+ EXPECT_FALSE(FieldTrialList::Find(name2));
+
+ FieldTrial* trial1 = FieldTrialList::FactoryGetFieldTrial(
+ name1, 10, "default name 1 test", next_year_, 12, 31, NULL);
+ EXPECT_EQ(FieldTrial::kNotFinalized, trial1->group_);
+ EXPECT_EQ(name1, trial1->trial_name());
+ EXPECT_EQ("", trial1->group_name_internal());
+
+ trial1->AppendGroup("", 7);
+
+ EXPECT_EQ(trial1, FieldTrialList::Find(name1));
+ EXPECT_FALSE(FieldTrialList::Find(name2));
+
+ FieldTrial* trial2 = FieldTrialList::FactoryGetFieldTrial(
+ name2, 10, "default name 2 test", next_year_, 12, 31, NULL);
+ EXPECT_EQ(FieldTrial::kNotFinalized, trial2->group_);
+ EXPECT_EQ(name2, trial2->trial_name());
+ EXPECT_EQ("", trial2->group_name_internal());
+
+ trial2->AppendGroup("a first group", 7);
+
+ EXPECT_EQ(trial1, FieldTrialList::Find(name1));
+ EXPECT_EQ(trial2, FieldTrialList::Find(name2));
+ // Note: FieldTrialList should delete the objects at shutdown.
+}
+
+TEST_F(FieldTrialTest, AbsoluteProbabilities) {
+ char always_true[] = " always true";
+ char default_always_true[] = " default always true";
+ char always_false[] = " always false";
+ char default_always_false[] = " default always false";
+ for (int i = 1; i < 250; ++i) {
+ // Try lots of names, by changing the first character of the name.
+ always_true[0] = i;
+ default_always_true[0] = i;
+ always_false[0] = i;
+ default_always_false[0] = i;
+
+ FieldTrial* trial_true = FieldTrialList::FactoryGetFieldTrial(
+ always_true, 10, default_always_true, next_year_, 12, 31, NULL);
+ const std::string winner = "TheWinner";
+ int winner_group = trial_true->AppendGroup(winner, 10);
+
+ EXPECT_EQ(winner_group, trial_true->group());
+ EXPECT_EQ(winner, trial_true->group_name());
+
+ FieldTrial* trial_false = FieldTrialList::FactoryGetFieldTrial(
+ always_false, 10, default_always_false, next_year_, 12, 31, NULL);
+ int loser_group = trial_false->AppendGroup("ALoser", 0);
+
+ EXPECT_NE(loser_group, trial_false->group());
+ }
+}
+
+TEST_F(FieldTrialTest, RemainingProbability) {
+ // First create a test that hasn't had a winner yet.
+ const std::string winner = "Winner";
+ const std::string loser = "Loser";
+ scoped_refptr<FieldTrial> trial;
+ int counter = 0;
+ int default_group_number = -1;
+ do {
+ std::string name = StringPrintf("trial%d", ++counter);
+ trial = FieldTrialList::FactoryGetFieldTrial(
+ name, 10, winner, next_year_, 12, 31, &default_group_number);
+ trial->AppendGroup(loser, 5); // 50% chance of not being chosen.
+ // If a group is not assigned, group_ will be kNotFinalized.
+ } while (trial->group_ != FieldTrial::kNotFinalized);
+
+ // And that 'default' group (winner) should always win.
+ EXPECT_EQ(default_group_number, trial->group());
+
+ // And that winner should ALWAYS win.
+ EXPECT_EQ(winner, trial->group_name());
+}
+
+TEST_F(FieldTrialTest, FiftyFiftyProbability) {
+ // Check that even with small divisors, we have the proper probabilities, and
+ // all outcomes are possible. Since this is a 50-50 test, it should get both
+ // outcomes in a few tries, but we'll try no more than 100 times (and be flaky
+ // with probability around 1 in 2^99).
+ bool first_winner = false;
+ bool second_winner = false;
+ int counter = 0;
+ do {
+ std::string name = base::StringPrintf("FiftyFifty%d", ++counter);
+ std::string default_group_name = base::StringPrintf("Default FiftyFifty%d",
+ ++counter);
+ scoped_refptr<FieldTrial> trial(FieldTrialList::FactoryGetFieldTrial(
+ name, 2, default_group_name, next_year_, 12, 31, NULL));
+ trial->AppendGroup("first", 1); // 50% chance of being chosen.
+ // If group_ is kNotFinalized, then a group assignement hasn't been done.
+ if (trial->group_ != FieldTrial::kNotFinalized) {
+ first_winner = true;
+ continue;
+ }
+ trial->AppendGroup("second", 1); // Always chosen at this point.
+ EXPECT_NE(FieldTrial::kNotFinalized, trial->group());
+ second_winner = true;
+ } while ((!second_winner || !first_winner) && counter < 100);
+ EXPECT_TRUE(second_winner);
+ EXPECT_TRUE(first_winner);
+}
+
+TEST_F(FieldTrialTest, MiddleProbabilities) {
+ char name[] = " same name";
+ char default_group_name[] = " default same name";
+ bool false_event_seen = false;
+ bool true_event_seen = false;
+ for (int i = 1; i < 250; ++i) {
+ name[0] = i;
+ default_group_name[0] = i;
+ FieldTrial* trial = FieldTrialList::FactoryGetFieldTrial(
+ name, 10, default_group_name, next_year_, 12, 31, NULL);
+ int might_win = trial->AppendGroup("MightWin", 5);
+
+ if (trial->group() == might_win) {
+ true_event_seen = true;
+ } else {
+ false_event_seen = true;
+ }
+ if (false_event_seen && true_event_seen)
+ return; // Successful test!!!
+ }
+ // Very surprising to get here. Probability should be around 1 in 2 ** 250.
+ // One of the following will fail.
+ EXPECT_TRUE(false_event_seen);
+ EXPECT_TRUE(true_event_seen);
+}
+
+TEST_F(FieldTrialTest, OneWinner) {
+ char name[] = "Some name";
+ char default_group_name[] = "Default some name";
+ int group_count(10);
+
+ int default_group_number = -1;
+ FieldTrial* trial = FieldTrialList::FactoryGetFieldTrial(
+ name, group_count, default_group_name, next_year_, 12, 31,
+ &default_group_number);
+ int winner_index(-2);
+ std::string winner_name;
+
+ for (int i = 1; i <= group_count; ++i) {
+ int might_win = trial->AppendGroup("", 1);
+
+ // Because we keep appending groups, we want to see if the last group that
+ // was added has been assigned or not.
+ if (trial->group_ == might_win) {
+ EXPECT_EQ(-2, winner_index);
+ winner_index = might_win;
+ StringAppendF(&winner_name, "%d", might_win);
+ EXPECT_EQ(winner_name, trial->group_name());
+ }
+ }
+ EXPECT_GE(winner_index, 0);
+ // Since all groups cover the total probability, we should not have
+ // chosen the default group.
+ EXPECT_NE(trial->group(), default_group_number);
+ EXPECT_EQ(trial->group(), winner_index);
+ EXPECT_EQ(trial->group_name(), winner_name);
+}
+
+TEST_F(FieldTrialTest, DisableProbability) {
+ const std::string default_group_name = "Default group";
+ const std::string loser = "Loser";
+ const std::string name = "Trial";
+
+ // Create a field trail that has expired.
+ int default_group_number = -1;
+ scoped_refptr<FieldTrial> trial;
+ trial = FieldTrialList::FactoryGetFieldTrial(
+ name, 1000000000, default_group_name, last_year_, 1, 1,
+ &default_group_number);
+ trial->AppendGroup(loser, 999999999); // 99.9999999% chance of being chosen.
+
+ // Because trial has expired, we should always be in the default group.
+ EXPECT_EQ(default_group_number, trial->group());
+
+ // And that default_group_name should ALWAYS win.
+ EXPECT_EQ(default_group_name, trial->group_name());
+}
+
+TEST_F(FieldTrialTest, ActiveGroups) {
+ std::string no_group("No Group");
+ scoped_refptr<FieldTrial> trial(FieldTrialList::FactoryGetFieldTrial(
+ no_group, 10, "Default", next_year_, 12, 31, NULL));
+
+ // There is no winner yet, so no NameGroupId should be returned.
+ FieldTrial::ActiveGroup active_group;
+ EXPECT_FALSE(trial->GetActiveGroup(&active_group));
+
+ // Create a single winning group.
+ std::string one_winner("One Winner");
+ trial = FieldTrialList::FactoryGetFieldTrial(
+ one_winner, 10, "Default", next_year_, 12, 31, NULL);
+ std::string winner("Winner");
+ trial->AppendGroup(winner, 10);
+ EXPECT_FALSE(trial->GetActiveGroup(&active_group));
+ // Finalize the group selection by accessing the selected group.
+ trial->group();
+ EXPECT_TRUE(trial->GetActiveGroup(&active_group));
+ EXPECT_EQ(one_winner, active_group.trial_name);
+ EXPECT_EQ(winner, active_group.group_name);
+
+ std::string multi_group("MultiGroup");
+ scoped_refptr<FieldTrial> multi_group_trial =
+ FieldTrialList::FactoryGetFieldTrial(multi_group, 9, "Default",
+ next_year_, 12, 31, NULL);
+
+ multi_group_trial->AppendGroup("Me", 3);
+ multi_group_trial->AppendGroup("You", 3);
+ multi_group_trial->AppendGroup("Them", 3);
+ EXPECT_FALSE(multi_group_trial->GetActiveGroup(&active_group));
+ // Finalize the group selection by accessing the selected group.
+ multi_group_trial->group();
+ EXPECT_TRUE(multi_group_trial->GetActiveGroup(&active_group));
+ EXPECT_EQ(multi_group, active_group.trial_name);
+ EXPECT_EQ(multi_group_trial->group_name(), active_group.group_name);
+
+ // Now check if the list is built properly...
+ FieldTrial::ActiveGroups active_groups;
+ FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
+ EXPECT_EQ(2U, active_groups.size());
+ for (size_t i = 0; i < active_groups.size(); ++i) {
+ // Order is not guaranteed, so check all values.
+ EXPECT_NE(no_group, active_groups[i].trial_name);
+ EXPECT_TRUE(one_winner != active_groups[i].trial_name ||
+ winner == active_groups[i].group_name);
+ EXPECT_TRUE(multi_group != active_groups[i].trial_name ||
+ multi_group_trial->group_name() == active_groups[i].group_name);
+ }
+}
+
+TEST_F(FieldTrialTest, ActiveGroupsNotFinalized) {
+ const char kTrialName[] = "TestTrial";
+ const char kSecondaryGroupName[] = "SecondaryGroup";
+
+ int default_group = -1;
+ FieldTrial* trial =
+ FieldTrialList::FactoryGetFieldTrial(kTrialName, 100, kDefaultGroupName,
+ next_year_, 12, 31, &default_group);
+ const int secondary_group = trial->AppendGroup(kSecondaryGroupName, 50);
+
+ // Before |group()| is called, |GetActiveGroup()| should return false.
+ FieldTrial::ActiveGroup active_group;
+ EXPECT_FALSE(trial->GetActiveGroup(&active_group));
+
+ // |GetActiveFieldTrialGroups()| should also not include the trial.
+ FieldTrial::ActiveGroups active_groups;
+ FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
+ EXPECT_TRUE(active_groups.empty());
+
+ // After |group()| has been called, both APIs should succeed.
+ const int chosen_group = trial->group();
+ EXPECT_TRUE(chosen_group == default_group || chosen_group == secondary_group);
+
+ EXPECT_TRUE(trial->GetActiveGroup(&active_group));
+ EXPECT_EQ(kTrialName, active_group.trial_name);
+ if (chosen_group == default_group)
+ EXPECT_EQ(kDefaultGroupName, active_group.group_name);
+ else
+ EXPECT_EQ(kSecondaryGroupName, active_group.group_name);
+
+ FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
+ ASSERT_EQ(1U, active_groups.size());
+ EXPECT_EQ(kTrialName, active_groups[0].trial_name);
+ EXPECT_EQ(active_group.group_name, active_groups[0].group_name);
+}
+
+TEST_F(FieldTrialTest, Save) {
+ std::string save_string;
+
+ FieldTrial* trial = FieldTrialList::FactoryGetFieldTrial(
+ "Some name", 10, "Default some name", next_year_, 12, 31, NULL);
+ // There is no winner yet, so no textual group name is associated with trial.
+ // In this case, the trial should not be included.
+ EXPECT_EQ("", trial->group_name_internal());
+ FieldTrialList::StatesToString(&save_string);
+ EXPECT_EQ("", save_string);
+ save_string.clear();
+
+ // Create a winning group.
+ trial->AppendGroup("Winner", 10);
+ // Finalize the group selection by accessing the selected group.
+ trial->group();
+ FieldTrialList::StatesToString(&save_string);
+ EXPECT_EQ("Some name/Winner/", save_string);
+ save_string.clear();
+
+ // Create a second trial and winning group.
+ FieldTrial* trial2 = FieldTrialList::FactoryGetFieldTrial(
+ "xxx", 10, "Default xxx", next_year_, 12, 31, NULL);
+ trial2->AppendGroup("yyyy", 10);
+ // Finalize the group selection by accessing the selected group.
+ trial2->group();
+
+ FieldTrialList::StatesToString(&save_string);
+ // We assume names are alphabetized... though this is not critical.
+ EXPECT_EQ("Some name/Winner/xxx/yyyy/", save_string);
+ save_string.clear();
+
+ // Create a third trial with only the default group.
+ FieldTrial* trial3 = FieldTrialList::FactoryGetFieldTrial(
+ "zzz", 10, "default", next_year_, 12, 31, NULL);
+ // Finalize the group selection by accessing the selected group.
+ trial3->group();
+
+ FieldTrialList::StatesToString(&save_string);
+ EXPECT_EQ("Some name/Winner/xxx/yyyy/zzz/default/", save_string);
+}
+
+TEST_F(FieldTrialTest, Restore) {
+ ASSERT_FALSE(FieldTrialList::TrialExists("Some_name"));
+ ASSERT_FALSE(FieldTrialList::TrialExists("xxx"));
+
+ FieldTrialList::CreateTrialsFromString("Some_name/Winner/xxx/yyyy/");
+
+ FieldTrial* trial = FieldTrialList::Find("Some_name");
+ ASSERT_NE(static_cast<FieldTrial*>(NULL), trial);
+ EXPECT_EQ("Winner", trial->group_name());
+ EXPECT_EQ("Some_name", trial->trial_name());
+
+ trial = FieldTrialList::Find("xxx");
+ ASSERT_NE(static_cast<FieldTrial*>(NULL), trial);
+ EXPECT_EQ("yyyy", trial->group_name());
+ EXPECT_EQ("xxx", trial->trial_name());
+}
+
+TEST_F(FieldTrialTest, BogusRestore) {
+ EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("MissingSlash"));
+ EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("MissingGroupName/"));
+ EXPECT_FALSE(FieldTrialList::CreateTrialsFromString(
+ "MissingFinalSlash/gname"));
+ EXPECT_FALSE(FieldTrialList::CreateTrialsFromString(
+ "noname, only group/"));
+}
+
+TEST_F(FieldTrialTest, DuplicateRestore) {
+ FieldTrial* trial = FieldTrialList::FactoryGetFieldTrial(
+ "Some name", 10, "Default some name", next_year_, 12, 31, NULL);
+ trial->AppendGroup("Winner", 10);
+ // Finalize the group selection by accessing the selected group.
+ trial->group();
+ std::string save_string;
+ FieldTrialList::StatesToString(&save_string);
+ EXPECT_EQ("Some name/Winner/", save_string);
+
+ // It is OK if we redundantly specify a winner.
+ EXPECT_TRUE(FieldTrialList::CreateTrialsFromString(save_string));
+
+ // But it is an error to try to change to a different winner.
+ EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("Some name/Loser/"));
+}
+
+TEST_F(FieldTrialTest, CreateTrialsFromStringAreActive) {
+ ASSERT_FALSE(FieldTrialList::TrialExists("Abc"));
+ ASSERT_FALSE(FieldTrialList::TrialExists("Xyz"));
+ ASSERT_TRUE(FieldTrialList::CreateTrialsFromString("Abc/def/Xyz/zyx/"));
+
+ FieldTrial::ActiveGroups active_groups;
+ FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
+ ASSERT_EQ(2U, active_groups.size());
+ EXPECT_EQ("Abc", active_groups[0].trial_name);
+ EXPECT_EQ("def", active_groups[0].group_name);
+ EXPECT_EQ("Xyz", active_groups[1].trial_name);
+ EXPECT_EQ("zyx", active_groups[1].group_name);
+}
+
+TEST_F(FieldTrialTest, CreateTrialsFromStringObserver) {
+ ASSERT_FALSE(FieldTrialList::TrialExists("Abc"));
+
+ TestFieldTrialObserver observer;
+ ASSERT_TRUE(FieldTrialList::CreateTrialsFromString("Abc/def/"));
+
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ("Abc", observer.trial_name());
+ EXPECT_EQ("def", observer.group_name());
+}
+
+TEST_F(FieldTrialTest, CreateFieldTrial) {
+ ASSERT_FALSE(FieldTrialList::TrialExists("Some_name"));
+
+ FieldTrialList::CreateFieldTrial("Some_name", "Winner");
+
+ FieldTrial* trial = FieldTrialList::Find("Some_name");
+ ASSERT_NE(static_cast<FieldTrial*>(NULL), trial);
+ EXPECT_EQ("Winner", trial->group_name());
+ EXPECT_EQ("Some_name", trial->trial_name());
+}
+
+TEST_F(FieldTrialTest, CreateFieldTrialIsNotActive) {
+ const char kTrialName[] = "CreateFieldTrialIsActiveTrial";
+ const char kWinnerGroup[] = "Winner";
+ ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName));
+ FieldTrialList::CreateFieldTrial(kTrialName, kWinnerGroup);
+
+ FieldTrial::ActiveGroups active_groups;
+ FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
+ EXPECT_TRUE(active_groups.empty());
+}
+
+TEST_F(FieldTrialTest, DuplicateFieldTrial) {
+ FieldTrial* trial = FieldTrialList::FactoryGetFieldTrial(
+ "Some_name", 10, "Default some name", next_year_, 12, 31, NULL);
+ trial->AppendGroup("Winner", 10);
+
+ // It is OK if we redundantly specify a winner.
+ FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("Some_name", "Winner");
+ EXPECT_TRUE(trial1 != NULL);
+
+ // But it is an error to try to change to a different winner.
+ FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("Some_name", "Loser");
+ EXPECT_TRUE(trial2 == NULL);
+}
+
+TEST_F(FieldTrialTest, MakeName) {
+ FieldTrial* trial = FieldTrialList::FactoryGetFieldTrial(
+ "Field Trial", 10, "Winner", next_year_, 12, 31, NULL);
+ trial->group();
+ EXPECT_EQ("Histogram_Winner",
+ FieldTrial::MakeName("Histogram", "Field Trial"));
+}
+
+TEST_F(FieldTrialTest, DisableImmediately) {
+ int default_group_number = -1;
+ FieldTrial* trial = FieldTrialList::FactoryGetFieldTrial(
+ "trial", 100, "default", next_year_, 12, 31, &default_group_number);
+ trial->Disable();
+ ASSERT_EQ("default", trial->group_name());
+ ASSERT_EQ(default_group_number, trial->group());
+}
+
+TEST_F(FieldTrialTest, DisableAfterInitialization) {
+ FieldTrial* trial =
+ FieldTrialList::FactoryGetFieldTrial("trial", 100, "default",
+ next_year_, 12, 31, NULL);
+ trial->AppendGroup("non_default", 100);
+ trial->Disable();
+ ASSERT_EQ("default", trial->group_name());
+}
+
+TEST_F(FieldTrialTest, ForcedFieldTrials) {
+ // Validate we keep the forced choice.
+ FieldTrial* forced_trial = FieldTrialList::CreateFieldTrial("Use the",
+ "Force");
+ EXPECT_STREQ("Force", forced_trial->group_name().c_str());
+
+ int default_group_number = -1;
+ FieldTrial* factory_trial = FieldTrialList::FactoryGetFieldTrial(
+ "Use the", 1000, "default", next_year_, 12, 31, &default_group_number);
+ EXPECT_EQ(factory_trial, forced_trial);
+
+ int chosen_group = factory_trial->AppendGroup("Force", 100);
+ EXPECT_EQ(chosen_group, factory_trial->group());
+ int not_chosen_group = factory_trial->AppendGroup("Dark Side", 100);
+ EXPECT_NE(chosen_group, not_chosen_group);
+
+ // Since we didn't force the default group, we should not be returned the
+ // chosen group as the default group.
+ EXPECT_NE(default_group_number, chosen_group);
+ int new_group = factory_trial->AppendGroup("Duck Tape", 800);
+ EXPECT_NE(chosen_group, new_group);
+ // The new group should not be the default group either.
+ EXPECT_NE(default_group_number, new_group);
+
+ // Forcing the default should use the proper group ID.
+ forced_trial = FieldTrialList::CreateFieldTrial("Trial Name", "Default");
+ factory_trial = FieldTrialList::FactoryGetFieldTrial(
+ "Trial Name", 1000, "Default", next_year_, 12, 31, &default_group_number);
+ EXPECT_EQ(forced_trial, factory_trial);
+
+ int other_group = factory_trial->AppendGroup("Not Default", 100);
+ EXPECT_STREQ("Default", factory_trial->group_name().c_str());
+ EXPECT_EQ(default_group_number, factory_trial->group());
+ EXPECT_NE(other_group, factory_trial->group());
+
+ int new_other_group = factory_trial->AppendGroup("Not Default Either", 800);
+ EXPECT_NE(new_other_group, factory_trial->group());
+}
+
+TEST_F(FieldTrialTest, SetForced) {
+ // Start by setting a trial for which we ensure a winner...
+ int default_group_number = -1;
+ FieldTrial* forced_trial = FieldTrialList::FactoryGetFieldTrial(
+ "Use the", 1, "default", next_year_, 12, 31, &default_group_number);
+ EXPECT_EQ(forced_trial, forced_trial);
+
+ int forced_group = forced_trial->AppendGroup("Force", 1);
+ EXPECT_EQ(forced_group, forced_trial->group());
+
+ // Now force it.
+ forced_trial->SetForced();
+
+ // Now try to set it up differently as a hard coded registration would.
+ FieldTrial* hard_coded_trial = FieldTrialList::FactoryGetFieldTrial(
+ "Use the", 1, "default", next_year_, 12, 31, &default_group_number);
+ EXPECT_EQ(hard_coded_trial, forced_trial);
+
+ int would_lose_group = hard_coded_trial->AppendGroup("Force", 0);
+ EXPECT_EQ(forced_group, hard_coded_trial->group());
+ EXPECT_EQ(forced_group, would_lose_group);
+
+ // Same thing if we would have done it to win again.
+ FieldTrial* other_hard_coded_trial = FieldTrialList::FactoryGetFieldTrial(
+ "Use the", 1, "default", next_year_, 12, 31, &default_group_number);
+ EXPECT_EQ(other_hard_coded_trial, forced_trial);
+
+ int would_win_group = other_hard_coded_trial->AppendGroup("Force", 1);
+ EXPECT_EQ(forced_group, other_hard_coded_trial->group());
+ EXPECT_EQ(forced_group, would_win_group);
+}
+
+TEST_F(FieldTrialTest, SetForcedDefaultOnly) {
+ const char kTrialName[] = "SetForcedDefaultOnly";
+ ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName));
+
+ int default_group = -1;
+ FieldTrial* trial =
+ FieldTrialList::FactoryGetFieldTrial(kTrialName, 100, kDefaultGroupName,
+ next_year_, 12, 31, &default_group);
+ trial->SetForced();
+
+ trial = FieldTrialList::FactoryGetFieldTrial(kTrialName, 100,
+ kDefaultGroupName, next_year_,
+ 12, 31, NULL);
+ EXPECT_EQ(default_group, trial->group());
+ EXPECT_EQ(kDefaultGroupName, trial->group_name());
+}
+
+TEST_F(FieldTrialTest, SetForcedDefaultWithExtraGroup) {
+ const char kTrialName[] = "SetForcedDefaultWithExtraGroup";
+ ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName));
+
+ int default_group = -1;
+ FieldTrial* trial =
+ FieldTrialList::FactoryGetFieldTrial(kTrialName, 100, kDefaultGroupName,
+ next_year_, 12, 31, &default_group);
+ trial->SetForced();
+
+ trial = FieldTrialList::FactoryGetFieldTrial(kTrialName, 100,
+ kDefaultGroupName, next_year_,
+ 12, 31, NULL);
+ const int extra_group = trial->AppendGroup("Extra", 100);
+ EXPECT_EQ(default_group, trial->group());
+ EXPECT_NE(extra_group, trial->group());
+ EXPECT_EQ(kDefaultGroupName, trial->group_name());
+}
+
+TEST_F(FieldTrialTest, Observe) {
+ const char kTrialName[] = "TrialToObserve1";
+ const char kSecondaryGroupName[] = "SecondaryGroup";
+
+ TestFieldTrialObserver observer;
+ int default_group = -1;
+ FieldTrial* trial =
+ FieldTrialList::FactoryGetFieldTrial(kTrialName, 100, kDefaultGroupName,
+ next_year_, 12, 31, &default_group);
+ const int secondary_group = trial->AppendGroup(kSecondaryGroupName, 50);
+ const int chosen_group = trial->group();
+ EXPECT_TRUE(chosen_group == default_group || chosen_group == secondary_group);
+
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(kTrialName, observer.trial_name());
+ if (chosen_group == default_group)
+ EXPECT_EQ(kDefaultGroupName, observer.group_name());
+ else
+ EXPECT_EQ(kSecondaryGroupName, observer.group_name());
+}
+
+TEST_F(FieldTrialTest, ObserveDisabled) {
+ const char kTrialName[] = "TrialToObserve2";
+
+ TestFieldTrialObserver observer;
+ int default_group = -1;
+ FieldTrial* trial =
+ FieldTrialList::FactoryGetFieldTrial(kTrialName, 100, kDefaultGroupName,
+ next_year_, 12, 31, &default_group);
+ trial->AppendGroup("A", 25);
+ trial->AppendGroup("B", 25);
+ trial->AppendGroup("C", 25);
+ trial->Disable();
+
+ // Observer shouldn't be notified of a disabled trial.
+ message_loop_.RunUntilIdle();
+ EXPECT_TRUE(observer.trial_name().empty());
+ EXPECT_TRUE(observer.group_name().empty());
+
+ // Observer shouldn't be notified even after a |group()| call.
+ EXPECT_EQ(default_group, trial->group());
+ message_loop_.RunUntilIdle();
+ EXPECT_TRUE(observer.trial_name().empty());
+ EXPECT_TRUE(observer.group_name().empty());
+}
+
+TEST_F(FieldTrialTest, ObserveForcedDisabled) {
+ const char kTrialName[] = "TrialToObserve3";
+
+ TestFieldTrialObserver observer;
+ int default_group = -1;
+ FieldTrial* trial =
+ FieldTrialList::FactoryGetFieldTrial(kTrialName, 100, kDefaultGroupName,
+ next_year_, 12, 31, &default_group);
+ trial->AppendGroup("A", 25);
+ trial->AppendGroup("B", 25);
+ trial->AppendGroup("C", 25);
+ trial->SetForced();
+ trial->Disable();
+
+ // Observer shouldn't be notified of a disabled trial, even when forced.
+ message_loop_.RunUntilIdle();
+ EXPECT_TRUE(observer.trial_name().empty());
+ EXPECT_TRUE(observer.group_name().empty());
+
+ // Observer shouldn't be notified even after a |group()| call.
+ EXPECT_EQ(default_group, trial->group());
+ message_loop_.RunUntilIdle();
+ EXPECT_TRUE(observer.trial_name().empty());
+ EXPECT_TRUE(observer.group_name().empty());
+}
+
+TEST_F(FieldTrialTest, DisabledTrialNotActive) {
+ const char kTrialName[] = "DisabledTrial";
+ ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName));
+
+ FieldTrial* trial =
+ FieldTrialList::FactoryGetFieldTrial(kTrialName, 100, kDefaultGroupName,
+ next_year_, 12, 31, NULL);
+ trial->AppendGroup("X", 50);
+ trial->Disable();
+
+ // Ensure the trial is not listed as active.
+ FieldTrial::ActiveGroups active_groups;
+ FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
+ EXPECT_TRUE(active_groups.empty());
+
+ // Ensure the trial is not listed in the |StatesToString()| result.
+ std::string states;
+ FieldTrialList::StatesToString(&states);
+ EXPECT_TRUE(states.empty());
+}
+
+
+} // namespace base
diff --git a/src/base/metrics/histogram.cc b/src/base/metrics/histogram.cc
new file mode 100644
index 0000000..795ea3a
--- /dev/null
+++ b/src/base/metrics/histogram.cc
@@ -0,0 +1,866 @@
+// Copyright (c) 2012 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.
+
+// Histogram is an object that aggregates statistics, and can summarize them in
+// various forms, including ASCII graphical, HTML, and numerically (as a
+// vector of numbers corresponding to each of the aggregating buckets).
+// See header file for details and examples.
+
+#include "base/metrics/histogram.h"
+
+#include <math.h>
+
+#include <algorithm>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/debug/alias.h"
+#include "base/logging.h"
+#include "base/metrics/sample_vector.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/pickle.h"
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "base/synchronization/lock.h"
+#include "base/values.h"
+
+using std::string;
+using std::vector;
+
+namespace base {
+
+typedef HistogramBase::Count Count;
+typedef HistogramBase::Sample Sample;
+
+// static
+const size_t Histogram::kBucketCount_MAX = 16384u;
+
+// TODO(rtenneti): delete this code after debugging.
+void CheckCorruption(const Histogram& histogram, bool new_histogram) {
+ const std::string& histogram_name = histogram.histogram_name();
+ char histogram_name_buf[128];
+ base::strlcpy(histogram_name_buf,
+ histogram_name.c_str(),
+ arraysize(histogram_name_buf));
+ base::debug::Alias(histogram_name_buf);
+
+ bool debug_new_histogram[1];
+ debug_new_histogram[0] = new_histogram;
+ base::debug::Alias(debug_new_histogram);
+
+ Sample previous_range = -1; // Bottom range is always 0.
+ for (size_t index = 0; index < histogram.bucket_count(); ++index) {
+ int new_range = histogram.ranges(index);
+ CHECK_LT(previous_range, new_range);
+ previous_range = new_range;
+ }
+
+ CHECK(histogram.bucket_ranges()->HasValidChecksum());
+}
+
+Histogram* Histogram::FactoryGet(const string& name,
+ Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ int32 flags) {
+ bool valid_arguments =
+ InspectConstructionArguments(name, &minimum, &maximum, &bucket_count);
+ DCHECK(valid_arguments);
+
+ Histogram* histogram = StatisticsRecorder::FindHistogram(name);
+ if (!histogram) {
+ // To avoid racy destruction at shutdown, the following will be leaked.
+ BucketRanges* ranges = new BucketRanges(bucket_count + 1);
+ InitializeBucketRanges(minimum, maximum, bucket_count, ranges);
+ const BucketRanges* registered_ranges =
+ StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
+
+ Histogram* tentative_histogram =
+ new Histogram(name, minimum, maximum, bucket_count, registered_ranges);
+ CheckCorruption(*tentative_histogram, true);
+
+ tentative_histogram->SetFlags(flags);
+ histogram =
+ StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
+ }
+ // TODO(rtenneti): delete this code after debugging.
+ CheckCorruption(*histogram, false);
+
+ CHECK_EQ(HISTOGRAM, histogram->GetHistogramType());
+ CHECK(histogram->HasConstructionArguments(minimum, maximum, bucket_count));
+ return histogram;
+}
+
+Histogram* Histogram::FactoryTimeGet(const string& name,
+ TimeDelta minimum,
+ TimeDelta maximum,
+ size_t bucket_count,
+ int32 flags) {
+ return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(),
+ bucket_count, flags);
+}
+
+TimeTicks Histogram::DebugNow() {
+#ifndef NDEBUG
+ return TimeTicks::Now();
+#else
+ return TimeTicks();
+#endif
+}
+
+// Calculate what range of values are held in each bucket.
+// We have to be careful that we don't pick a ratio between starting points in
+// consecutive buckets that is sooo small, that the integer bounds are the same
+// (effectively making one bucket get no values). We need to avoid:
+// ranges(i) == ranges(i + 1)
+// To avoid that, we just do a fine-grained bucket width as far as we need to
+// until we get a ratio that moves us along at least 2 units at a time. From
+// that bucket onward we do use the exponential growth of buckets.
+//
+// static
+void Histogram::InitializeBucketRanges(Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ BucketRanges* ranges) {
+ DCHECK_EQ(ranges->size(), bucket_count + 1);
+ double log_max = log(static_cast<double>(maximum));
+ double log_ratio;
+ double log_next;
+ size_t bucket_index = 1;
+ Sample current = minimum;
+ ranges->set_range(bucket_index, current);
+ while (bucket_count > ++bucket_index) {
+ double log_current;
+ log_current = log(static_cast<double>(current));
+ // Calculate the count'th root of the range.
+ log_ratio = (log_max - log_current) / (bucket_count - bucket_index);
+ // See where the next bucket would start.
+ log_next = log_current + log_ratio;
+ Sample next;
+ next = static_cast<int>(floor(exp(log_next) + 0.5));
+ if (next > current)
+ current = next;
+ else
+ ++current; // Just do a narrow bucket, and keep trying.
+ ranges->set_range(bucket_index, current);
+ }
+ ranges->set_range(ranges->size() - 1, HistogramBase::kSampleType_MAX);
+ ranges->ResetChecksum();
+}
+
+void Histogram::AddBoolean(bool value) {
+ DCHECK(false);
+}
+
+void Histogram::AddSamples(const HistogramSamples& samples) {
+ samples_->Add(samples);
+}
+
+bool Histogram::AddSamplesFromPickle(PickleIterator* iter) {
+ return samples_->AddFromPickle(iter);
+}
+
+// static
+string Histogram::SerializeHistogramInfo(const Histogram& histogram,
+ const HistogramSamples& snapshot) {
+ DCHECK(histogram.bucket_ranges()->HasValidChecksum());
+
+ Pickle pickle;
+ pickle.WriteString(histogram.histogram_name());
+ pickle.WriteInt(histogram.declared_min());
+ pickle.WriteInt(histogram.declared_max());
+ pickle.WriteUInt64(histogram.bucket_count());
+ pickle.WriteUInt32(histogram.bucket_ranges()->checksum());
+ pickle.WriteInt(histogram.GetHistogramType());
+ pickle.WriteInt(histogram.flags());
+
+ histogram.SerializeRanges(&pickle);
+
+ snapshot.Serialize(&pickle);
+
+ return string(static_cast<const char*>(pickle.data()), pickle.size());
+}
+
+// static
+bool Histogram::DeserializeHistogramInfo(const string& histogram_info) {
+ if (histogram_info.empty()) {
+ return false;
+ }
+
+ Pickle pickle(histogram_info.data(),
+ static_cast<int>(histogram_info.size()));
+ string histogram_name;
+ int declared_min;
+ int declared_max;
+ uint64 bucket_count;
+ uint32 range_checksum;
+ int histogram_type;
+ int pickle_flags;
+
+ PickleIterator iter(pickle);
+ if (!iter.ReadString(&histogram_name) ||
+ !iter.ReadInt(&declared_min) ||
+ !iter.ReadInt(&declared_max) ||
+ !iter.ReadUInt64(&bucket_count) ||
+ !iter.ReadUInt32(&range_checksum) ||
+ !iter.ReadInt(&histogram_type) ||
+ !iter.ReadInt(&pickle_flags)) {
+ DLOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name;
+ return false;
+ }
+
+ DCHECK(pickle_flags & kIPCSerializationSourceFlag);
+ // Since these fields may have come from an untrusted renderer, do additional
+ // checks above and beyond those in Histogram::Initialize()
+ if (declared_max <= 0 || declared_min <= 0 || declared_max < declared_min ||
+ INT_MAX / sizeof(Count) <= bucket_count || bucket_count < 2) {
+ DLOG(ERROR) << "Values error decoding Histogram: " << histogram_name;
+ return false;
+ }
+
+ Flags flags = static_cast<Flags>(pickle_flags & ~kIPCSerializationSourceFlag);
+
+ Histogram* render_histogram(NULL);
+
+ if (histogram_type == HISTOGRAM) {
+ render_histogram = Histogram::FactoryGet(
+ histogram_name, declared_min, declared_max, bucket_count, flags);
+ } else if (histogram_type == LINEAR_HISTOGRAM) {
+ render_histogram = LinearHistogram::FactoryGet(
+ histogram_name, declared_min, declared_max, bucket_count, flags);
+ } else if (histogram_type == BOOLEAN_HISTOGRAM) {
+ render_histogram = BooleanHistogram::FactoryGet(histogram_name, flags);
+ } else if (histogram_type == CUSTOM_HISTOGRAM) {
+ vector<Sample> sample_ranges(bucket_count);
+ if (!CustomHistogram::DeserializeRanges(&iter, &sample_ranges)) {
+ DLOG(ERROR) << "Pickle error decoding ranges: " << histogram_name;
+ return false;
+ }
+ render_histogram =
+ CustomHistogram::FactoryGet(histogram_name, sample_ranges, flags);
+ } else {
+ DLOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: "
+ << histogram_type;
+ return false;
+ }
+
+ DCHECK_EQ(render_histogram->declared_min(), declared_min);
+ DCHECK_EQ(render_histogram->declared_max(), declared_max);
+ DCHECK_EQ(render_histogram->bucket_count(), bucket_count);
+ DCHECK_EQ(render_histogram->GetHistogramType(), histogram_type);
+
+ if (render_histogram->bucket_ranges()->checksum() != range_checksum) {
+ return false;
+ }
+
+ if (render_histogram->flags() & kIPCSerializationSourceFlag) {
+ DVLOG(1) << "Single process mode, histogram observed and not copied: "
+ << histogram_name;
+ return true;
+ }
+
+ DCHECK_EQ(flags & render_histogram->flags(), flags);
+ return render_histogram->AddSamplesFromPickle(&iter);
+}
+
+// static
+const int Histogram::kCommonRaceBasedCountMismatch = 5;
+
+Histogram::Inconsistencies Histogram::FindCorruption(
+ const HistogramSamples& samples) const {
+ int inconsistencies = NO_INCONSISTENCIES;
+ Sample previous_range = -1; // Bottom range is always 0.
+ for (size_t index = 0; index < bucket_count(); ++index) {
+ int new_range = ranges(index);
+ if (previous_range >= new_range)
+ inconsistencies |= BUCKET_ORDER_ERROR;
+ previous_range = new_range;
+ }
+
+ if (!bucket_ranges()->HasValidChecksum())
+ inconsistencies |= RANGE_CHECKSUM_ERROR;
+
+ int64 delta64 = samples.redundant_count() - samples.TotalCount();
+ if (delta64 != 0) {
+ int delta = static_cast<int>(delta64);
+ if (delta != delta64)
+ delta = INT_MAX; // Flag all giant errors as INT_MAX.
+ if (delta > 0) {
+ UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountHigh", delta);
+ if (delta > kCommonRaceBasedCountMismatch)
+ inconsistencies |= COUNT_HIGH_ERROR;
+ } else {
+ DCHECK_GT(0, delta);
+ UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountLow", -delta);
+ if (-delta > kCommonRaceBasedCountMismatch)
+ inconsistencies |= COUNT_LOW_ERROR;
+ }
+ }
+ return static_cast<Inconsistencies>(inconsistencies);
+}
+
+Sample Histogram::ranges(size_t i) const {
+ return bucket_ranges_->range(i);
+}
+
+size_t Histogram::bucket_count() const {
+ return bucket_count_;
+}
+
+// static
+bool Histogram::InspectConstructionArguments(const string& name,
+ Sample* minimum,
+ Sample* maximum,
+ size_t* bucket_count) {
+ // Defensive code for backward compatibility.
+ if (*minimum < 1) {
+ DVLOG(1) << "Histogram: " << name << " has bad minimum: " << *minimum;
+ *minimum = 1;
+ }
+ if (*maximum >= kSampleType_MAX) {
+ DVLOG(1) << "Histogram: " << name << " has bad maximum: " << *maximum;
+ *maximum = kSampleType_MAX - 1;
+ }
+ if (*bucket_count >= kBucketCount_MAX) {
+ DVLOG(1) << "Histogram: " << name << " has bad bucket_count: "
+ << *bucket_count;
+ *bucket_count = kBucketCount_MAX - 1;
+ }
+
+ if (*minimum >= *maximum)
+ return false;
+ if (*bucket_count < 3)
+ return false;
+ if (*bucket_count > static_cast<size_t>(*maximum - *minimum + 2))
+ return false;
+ return true;
+}
+
+HistogramType Histogram::GetHistogramType() const {
+ return HISTOGRAM;
+}
+
+bool Histogram::HasConstructionArguments(Sample minimum,
+ Sample maximum,
+ size_t bucket_count) const {
+ return ((minimum == declared_min_) && (maximum == declared_max_) &&
+ (bucket_count == bucket_count_));
+}
+
+void Histogram::Add(int value) {
+ DCHECK_EQ(0, ranges(0));
+ DCHECK_EQ(kSampleType_MAX, ranges(bucket_count_));
+
+ if (value > kSampleType_MAX - 1)
+ value = kSampleType_MAX - 1;
+ if (value < 0)
+ value = 0;
+ samples_->Accumulate(value, 1);
+}
+
+scoped_ptr<HistogramSamples> Histogram::SnapshotSamples() const {
+ return SnapshotSampleVector().PassAs<HistogramSamples>();
+}
+
+// The following methods provide a graphical histogram display.
+void Histogram::WriteHTMLGraph(string* output) const {
+ // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc.
+ output->append("<PRE>");
+ WriteAsciiImpl(true, "<br>", output);
+ output->append("</PRE>");
+}
+
+void Histogram::WriteAscii(string* output) const {
+ WriteAsciiImpl(true, "\n", output);
+}
+
+Histogram::Histogram(const string& name,
+ Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ const BucketRanges* ranges)
+ : HistogramBase(name),
+ bucket_ranges_(ranges),
+ declared_min_(minimum),
+ declared_max_(maximum),
+ bucket_count_(bucket_count) {
+ if (ranges)
+ samples_.reset(new SampleVector(ranges));
+}
+
+Histogram::~Histogram() {
+ if (StatisticsRecorder::dump_on_exit()) {
+ string output;
+ WriteAsciiImpl(true, "\n", &output);
+ DLOG(INFO) << output;
+ }
+}
+
+bool Histogram::SerializeRanges(Pickle* pickle) const {
+ return true;
+}
+
+bool Histogram::PrintEmptyBucket(size_t index) const {
+ return true;
+}
+
+// Use the actual bucket widths (like a linear histogram) until the widths get
+// over some transition value, and then use that transition width. Exponentials
+// get so big so fast (and we don't expect to see a lot of entries in the large
+// buckets), so we need this to make it possible to see what is going on and
+// not have 0-graphical-height buckets.
+double Histogram::GetBucketSize(Count current, size_t i) const {
+ DCHECK_GT(ranges(i + 1), ranges(i));
+ static const double kTransitionWidth = 5;
+ double denominator = ranges(i + 1) - ranges(i);
+ if (denominator > kTransitionWidth)
+ denominator = kTransitionWidth; // Stop trying to normalize.
+ return current/denominator;
+}
+
+const string Histogram::GetAsciiBucketRange(size_t i) const {
+ string result;
+ if (kHexRangePrintingFlag & flags())
+ StringAppendF(&result, "%#x", ranges(i));
+ else
+ StringAppendF(&result, "%d", ranges(i));
+ return result;
+}
+
+//------------------------------------------------------------------------------
+// Private methods
+
+scoped_ptr<SampleVector> Histogram::SnapshotSampleVector() const {
+ scoped_ptr<SampleVector> samples(new SampleVector(bucket_ranges()));
+ samples->Add(*samples_);
+ return samples.Pass();
+}
+
+void Histogram::WriteAsciiImpl(bool graph_it,
+ const string& newline,
+ string* output) const {
+ // Get local (stack) copies of all effectively volatile class data so that we
+ // are consistent across our output activities.
+ scoped_ptr<SampleVector> snapshot = SnapshotSampleVector();
+ Count sample_count = snapshot->TotalCount();
+
+ WriteAsciiHeader(*snapshot, sample_count, output);
+ output->append(newline);
+
+ // Prepare to normalize graphical rendering of bucket contents.
+ double max_size = 0;
+ if (graph_it)
+ max_size = GetPeakBucketSize(*snapshot);
+
+ // Calculate space needed to print bucket range numbers. Leave room to print
+ // nearly the largest bucket range without sliding over the histogram.
+ size_t largest_non_empty_bucket = bucket_count() - 1;
+ while (0 == snapshot->GetCountAtIndex(largest_non_empty_bucket)) {
+ if (0 == largest_non_empty_bucket)
+ break; // All buckets are empty.
+ --largest_non_empty_bucket;
+ }
+
+ // Calculate largest print width needed for any of our bucket range displays.
+ size_t print_width = 1;
+ for (size_t i = 0; i < bucket_count(); ++i) {
+ if (snapshot->GetCountAtIndex(i)) {
+ size_t width = GetAsciiBucketRange(i).size() + 1;
+ if (width > print_width)
+ print_width = width;
+ }
+ }
+
+ int64 remaining = sample_count;
+ int64 past = 0;
+ // Output the actual histogram graph.
+ for (size_t i = 0; i < bucket_count(); ++i) {
+ Count current = snapshot->GetCountAtIndex(i);
+ if (!current && !PrintEmptyBucket(i))
+ continue;
+ remaining -= current;
+ string range = GetAsciiBucketRange(i);
+ output->append(range);
+ for (size_t j = 0; range.size() + j < print_width + 1; ++j)
+ output->push_back(' ');
+ if (0 == current && i < bucket_count() - 1 &&
+ 0 == snapshot->GetCountAtIndex(i + 1)) {
+ while (i < bucket_count() - 1 &&
+ 0 == snapshot->GetCountAtIndex(i + 1)) {
+ ++i;
+ }
+ output->append("... ");
+ output->append(newline);
+ continue; // No reason to plot emptiness.
+ }
+ double current_size = GetBucketSize(current, i);
+ if (graph_it)
+ WriteAsciiBucketGraph(current_size, max_size, output);
+ WriteAsciiBucketContext(past, current, remaining, i, output);
+ output->append(newline);
+ past += current;
+ }
+ DCHECK_EQ(sample_count, past);
+}
+
+double Histogram::GetPeakBucketSize(const SampleVector& samples) const {
+ double max = 0;
+ for (size_t i = 0; i < bucket_count() ; ++i) {
+ double current_size = GetBucketSize(samples.GetCountAtIndex(i), i);
+ if (current_size > max)
+ max = current_size;
+ }
+ return max;
+}
+
+void Histogram::WriteAsciiHeader(const SampleVector& samples,
+ Count sample_count,
+ string* output) const {
+ StringAppendF(output,
+ "Histogram: %s recorded %d samples",
+ histogram_name().c_str(),
+ sample_count);
+ if (0 == sample_count) {
+ DCHECK_EQ(samples.sum(), 0);
+ } else {
+ double average = static_cast<float>(samples.sum()) / sample_count;
+
+ StringAppendF(output, ", average = %.1f", average);
+ }
+ if (flags() & ~kHexRangePrintingFlag)
+ StringAppendF(output, " (flags = 0x%x)", flags() & ~kHexRangePrintingFlag);
+}
+
+void Histogram::WriteAsciiBucketContext(const int64 past,
+ const Count current,
+ const int64 remaining,
+ const size_t i,
+ string* output) const {
+ double scaled_sum = (past + current + remaining) / 100.0;
+ WriteAsciiBucketValue(current, scaled_sum, output);
+ if (0 < i) {
+ double percentage = past / scaled_sum;
+ StringAppendF(output, " {%3.1f%%}", percentage);
+ }
+}
+
+void Histogram::WriteAsciiBucketValue(Count current,
+ double scaled_sum,
+ string* output) const {
+ StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum);
+}
+
+void Histogram::WriteAsciiBucketGraph(double current_size,
+ double max_size,
+ string* output) const {
+ const int k_line_length = 72; // Maximal horizontal width of graph.
+ int x_count = static_cast<int>(k_line_length * (current_size / max_size)
+ + 0.5);
+ int x_remainder = k_line_length - x_count;
+
+ while (0 < x_count--)
+ output->append("-");
+ output->append("O");
+ while (0 < x_remainder--)
+ output->append(" ");
+}
+
+void Histogram::GetParameters(DictionaryValue* params) const {
+ params->SetString("type", HistogramTypeToString(GetHistogramType()));
+ params->SetInteger("min", declared_min());
+ params->SetInteger("max", declared_max());
+ params->SetInteger("bucket_count", static_cast<int>(bucket_count()));
+}
+
+void Histogram::GetCountAndBucketData(Count* count, ListValue* buckets) const {
+ scoped_ptr<SampleVector> snapshot = SnapshotSampleVector();
+ *count = snapshot->TotalCount();
+ size_t index = 0;
+ for (size_t i = 0; i < bucket_count(); ++i) {
+ Sample count = snapshot->GetCountAtIndex(i);
+ if (count > 0) {
+ scoped_ptr<DictionaryValue> bucket_value(new DictionaryValue());
+ bucket_value->SetInteger("low", ranges(i));
+ if (i != bucket_count() - 1)
+ bucket_value->SetInteger("high", ranges(i + 1));
+ bucket_value->SetInteger("count", count);
+ buckets->Set(index, bucket_value.release());
+ ++index;
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// LinearHistogram: This histogram uses a traditional set of evenly spaced
+// buckets.
+//------------------------------------------------------------------------------
+
+LinearHistogram::~LinearHistogram() {}
+
+Histogram* LinearHistogram::FactoryGet(const string& name,
+ Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ int32 flags) {
+ return FactoryGetWithRangeDescription(
+ name, minimum, maximum, bucket_count, flags, NULL);
+}
+
+Histogram* LinearHistogram::FactoryTimeGet(const string& name,
+ TimeDelta minimum,
+ TimeDelta maximum,
+ size_t bucket_count,
+ int32 flags) {
+ return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(),
+ bucket_count, flags);
+}
+
+Histogram* LinearHistogram::FactoryGetWithRangeDescription(
+ const std::string& name,
+ Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ int32 flags,
+ const DescriptionPair descriptions[]) {
+ bool valid_arguments = Histogram::InspectConstructionArguments(
+ name, &minimum, &maximum, &bucket_count);
+ DCHECK(valid_arguments);
+
+ Histogram* histogram = StatisticsRecorder::FindHistogram(name);
+ if (!histogram) {
+ // To avoid racy destruction at shutdown, the following will be leaked.
+ BucketRanges* ranges = new BucketRanges(bucket_count + 1);
+ InitializeBucketRanges(minimum, maximum, bucket_count, ranges);
+ const BucketRanges* registered_ranges =
+ StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
+
+ LinearHistogram* tentative_histogram =
+ new LinearHistogram(name, minimum, maximum, bucket_count,
+ registered_ranges);
+ CheckCorruption(*tentative_histogram, true);
+
+ // Set range descriptions.
+ if (descriptions) {
+ for (int i = 0; descriptions[i].description; ++i) {
+ tentative_histogram->bucket_description_[descriptions[i].sample] =
+ descriptions[i].description;
+ }
+ }
+
+ tentative_histogram->SetFlags(flags);
+ histogram =
+ StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
+ }
+ // TODO(rtenneti): delete this code after debugging.
+ CheckCorruption(*histogram, false);
+
+ CHECK_EQ(LINEAR_HISTOGRAM, histogram->GetHistogramType());
+ CHECK(histogram->HasConstructionArguments(minimum, maximum, bucket_count));
+ return histogram;
+}
+
+HistogramType LinearHistogram::GetHistogramType() const {
+ return LINEAR_HISTOGRAM;
+}
+
+LinearHistogram::LinearHistogram(const string& name,
+ Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ const BucketRanges* ranges)
+ : Histogram(name, minimum, maximum, bucket_count, ranges) {
+}
+
+double LinearHistogram::GetBucketSize(Count current, size_t i) const {
+ DCHECK_GT(ranges(i + 1), ranges(i));
+ // Adjacent buckets with different widths would have "surprisingly" many (few)
+ // samples in a histogram if we didn't normalize this way.
+ double denominator = ranges(i + 1) - ranges(i);
+ return current/denominator;
+}
+
+const string LinearHistogram::GetAsciiBucketRange(size_t i) const {
+ int range = ranges(i);
+ BucketDescriptionMap::const_iterator it = bucket_description_.find(range);
+ if (it == bucket_description_.end())
+ return Histogram::GetAsciiBucketRange(i);
+ return it->second;
+}
+
+bool LinearHistogram::PrintEmptyBucket(size_t index) const {
+ return bucket_description_.find(ranges(index)) == bucket_description_.end();
+}
+
+// static
+void LinearHistogram::InitializeBucketRanges(Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ BucketRanges* ranges) {
+ DCHECK_EQ(ranges->size(), bucket_count + 1);
+ double min = minimum;
+ double max = maximum;
+ size_t i;
+ for (i = 1; i < bucket_count; ++i) {
+ double linear_range =
+ (min * (bucket_count -1 - i) + max * (i - 1)) / (bucket_count - 2);
+ ranges->set_range(i, static_cast<Sample>(linear_range + 0.5));
+ }
+ ranges->set_range(ranges->size() - 1, HistogramBase::kSampleType_MAX);
+ ranges->ResetChecksum();
+}
+
+//------------------------------------------------------------------------------
+// This section provides implementation for BooleanHistogram.
+//------------------------------------------------------------------------------
+
+Histogram* BooleanHistogram::FactoryGet(const string& name, int32 flags) {
+ Histogram* histogram = StatisticsRecorder::FindHistogram(name);
+ if (!histogram) {
+ // To avoid racy destruction at shutdown, the following will be leaked.
+ BucketRanges* ranges = new BucketRanges(4);
+ LinearHistogram::InitializeBucketRanges(1, 2, 3, ranges);
+ const BucketRanges* registered_ranges =
+ StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
+
+ BooleanHistogram* tentative_histogram =
+ new BooleanHistogram(name, registered_ranges);
+ CheckCorruption(*tentative_histogram, true);
+
+ tentative_histogram->SetFlags(flags);
+ histogram =
+ StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
+ }
+ // TODO(rtenneti): delete this code after debugging.
+ CheckCorruption(*histogram, false);
+
+ CHECK_EQ(BOOLEAN_HISTOGRAM, histogram->GetHistogramType());
+ return histogram;
+}
+
+HistogramType BooleanHistogram::GetHistogramType() const {
+ return BOOLEAN_HISTOGRAM;
+}
+
+void BooleanHistogram::AddBoolean(bool value) {
+ Add(value ? 1 : 0);
+}
+
+BooleanHistogram::BooleanHistogram(const string& name,
+ const BucketRanges* ranges)
+ : LinearHistogram(name, 1, 2, 3, ranges) {}
+
+//------------------------------------------------------------------------------
+// CustomHistogram:
+//------------------------------------------------------------------------------
+
+Histogram* CustomHistogram::FactoryGet(const string& name,
+ const vector<Sample>& custom_ranges,
+ int32 flags) {
+ CHECK(ValidateCustomRanges(custom_ranges));
+
+ Histogram* histogram = StatisticsRecorder::FindHistogram(name);
+ if (!histogram) {
+ BucketRanges* ranges = CreateBucketRangesFromCustomRanges(custom_ranges);
+ const BucketRanges* registered_ranges =
+ StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
+
+ // To avoid racy destruction at shutdown, the following will be leaked.
+ CustomHistogram* tentative_histogram =
+ new CustomHistogram(name, registered_ranges);
+ CheckCorruption(*tentative_histogram, true);
+
+ tentative_histogram->SetFlags(flags);
+
+ histogram =
+ StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
+ }
+ // TODO(rtenneti): delete this code after debugging.
+ CheckCorruption(*histogram, false);
+
+ CHECK_EQ(histogram->GetHistogramType(), CUSTOM_HISTOGRAM);
+ return histogram;
+}
+
+HistogramType CustomHistogram::GetHistogramType() const {
+ return CUSTOM_HISTOGRAM;
+}
+
+// static
+vector<Sample> CustomHistogram::ArrayToCustomRanges(
+ const Sample* values, size_t num_values) {
+ vector<Sample> all_values;
+ for (size_t i = 0; i < num_values; ++i) {
+ Sample value = values[i];
+ all_values.push_back(value);
+
+ // Ensure that a guard bucket is added. If we end up with duplicate
+ // values, FactoryGet will take care of removing them.
+ all_values.push_back(value + 1);
+ }
+ return all_values;
+}
+
+CustomHistogram::CustomHistogram(const string& name,
+ const BucketRanges* ranges)
+ : Histogram(name,
+ ranges->range(1),
+ ranges->range(ranges->size() - 2),
+ ranges->size() - 1,
+ ranges) {}
+
+bool CustomHistogram::SerializeRanges(Pickle* pickle) const {
+ for (size_t i = 0; i < bucket_ranges()->size(); ++i) {
+ if (!pickle->WriteInt(bucket_ranges()->range(i)))
+ return false;
+ }
+ return true;
+}
+
+// static
+bool CustomHistogram::DeserializeRanges(
+ PickleIterator* iter, vector<Sample>* ranges) {
+ for (size_t i = 0; i < ranges->size(); ++i) {
+ if (!iter->ReadInt(&(*ranges)[i]))
+ return false;
+ }
+ return true;
+}
+
+double CustomHistogram::GetBucketSize(Count current, size_t i) const {
+ return 1;
+}
+
+// static
+bool CustomHistogram::ValidateCustomRanges(
+ const vector<Sample>& custom_ranges) {
+ bool has_valid_range = false;
+ for (size_t i = 0; i < custom_ranges.size(); i++) {
+ Sample sample = custom_ranges[i];
+ if (sample < 0 || sample > HistogramBase::kSampleType_MAX - 1)
+ return false;
+ if (sample != 0)
+ has_valid_range = true;
+ }
+ return has_valid_range;
+}
+
+// static
+BucketRanges* CustomHistogram::CreateBucketRangesFromCustomRanges(
+ const vector<Sample>& custom_ranges) {
+ // Remove the duplicates in the custom ranges array.
+ vector<int> ranges = custom_ranges;
+ ranges.push_back(0); // Ensure we have a zero value.
+ ranges.push_back(HistogramBase::kSampleType_MAX);
+ std::sort(ranges.begin(), ranges.end());
+ ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end());
+
+ BucketRanges* bucket_ranges = new BucketRanges(ranges.size());
+ for (size_t i = 0; i < ranges.size(); i++) {
+ bucket_ranges->set_range(i, ranges[i]);
+ }
+ bucket_ranges->ResetChecksum();
+ return bucket_ranges;
+}
+
+} // namespace base
diff --git a/src/base/metrics/histogram.h b/src/base/metrics/histogram.h
new file mode 100644
index 0000000..74474f3
--- /dev/null
+++ b/src/base/metrics/histogram.h
@@ -0,0 +1,737 @@
+// Copyright (c) 2012 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.
+
+// Histogram is an object that aggregates statistics, and can summarize them in
+// various forms, including ASCII graphical, HTML, and numerically (as a
+// vector of numbers corresponding to each of the aggregating buckets).
+
+// It supports calls to accumulate either time intervals (which are processed
+// as integral number of milliseconds), or arbitrary integral units.
+
+// For Histogram(exponential histogram), LinearHistogram and CustomHistogram,
+// the minimum for a declared range is 1 (instead of 0), while the maximum is
+// (HistogramBase::kSampleType_MAX - 1). Currently you can declare histograms
+// with ranges exceeding those limits (e.g. 0 as minimal or
+// HistogramBase::kSampleType_MAX as maximal), but those excesses will be
+// silently clamped to those limits (for backwards compatibility with existing
+// code). Best practice is to not exceed the limits.
+
+// Each use of a histogram with the same name will reference the same underlying
+// data, so it is safe to record to the same histogram from multiple locations
+// in the code. It is a runtime error if all uses of the same histogram do not
+// agree exactly in type, bucket size and range.
+
+// For Histogram and LinearHistogram, the maximum for a declared range should
+// always be larger (not equal) than minmal range. Zero and
+// HistogramBase::kSampleType_MAX are implicitly added as first and last ranges,
+// so the smallest legal bucket_count is 3. However CustomHistogram can have
+// bucket count as 2 (when you give a custom ranges vector containing only 1
+// range).
+// For these 3 kinds of histograms, the max bucket count is always
+// (Histogram::kBucketCount_MAX - 1).
+
+// The buckets layout of class Histogram is exponential. For example, buckets
+// might contain (sequentially) the count of values in the following intervals:
+// [0,1), [1,2), [2,4), [4,8), [8,16), [16,32), [32,64), [64,infinity)
+// That bucket allocation would actually result from construction of a histogram
+// for values between 1 and 64, with 8 buckets, such as:
+// Histogram count("some name", 1, 64, 8);
+// Note that the underflow bucket [0,1) and the overflow bucket [64,infinity)
+// are also counted by the constructor in the user supplied "bucket_count"
+// argument.
+// The above example has an exponential ratio of 2 (doubling the bucket width
+// in each consecutive bucket. The Histogram class automatically calculates
+// the smallest ratio that it can use to construct the number of buckets
+// selected in the constructor. An another example, if you had 50 buckets,
+// and millisecond time values from 1 to 10000, then the ratio between
+// consecutive bucket widths will be approximately somewhere around the 50th
+// root of 10000. This approach provides very fine grain (narrow) buckets
+// at the low end of the histogram scale, but allows the histogram to cover a
+// gigantic range with the addition of very few buckets.
+
+// Usually we use macros to define and use a histogram. These macros use a
+// pattern involving a function static variable, that is a pointer to a
+// histogram. This static is explicitly initialized on any thread
+// that detects a uninitialized (NULL) pointer. The potentially racy
+// initialization is not a problem as it is always set to point to the same
+// value (i.e., the FactoryGet always returns the same value). FactoryGet
+// is also completely thread safe, which results in a completely thread safe,
+// and relatively fast, set of counters. To avoid races at shutdown, the static
+// pointer is NOT deleted, and we leak the histograms at process termination.
+
+#ifndef BASE_METRICS_HISTOGRAM_H_
+#define BASE_METRICS_HISTOGRAM_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/atomicops.h"
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/bucket_ranges.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/time.h"
+
+class Pickle;
+class PickleIterator;
+
+namespace base {
+
+class Lock;
+//------------------------------------------------------------------------------
+// Histograms are often put in areas where they are called many many times, and
+// performance is critical. As a result, they are designed to have a very low
+// recurring cost of executing (adding additional samples). Toward that end,
+// the macros declare a static pointer to the histogram in question, and only
+// take a "slow path" to construct (or find) the histogram on the first run
+// through the macro. We leak the histograms at shutdown time so that we don't
+// have to validate using the pointers at any time during the running of the
+// process.
+
+// The following code is generally what a thread-safe static pointer
+// initializaion looks like for a histogram (after a macro is expanded). This
+// sample is an expansion (with comments) of the code for
+// HISTOGRAM_CUSTOM_COUNTS().
+
+/*
+ do {
+ // The pointer's presence indicates the initialization is complete.
+ // Initialization is idempotent, so it can safely be atomically repeated.
+ static base::subtle::AtomicWord atomic_histogram_pointer = 0;
+
+ // Acquire_Load() ensures that we acquire visibility to the pointed-to data
+ // in the histogrom.
+ base::Histogram* histogram_pointer(reinterpret_cast<base::Histogram*>(
+ base::subtle::Acquire_Load(&atomic_histogram_pointer)));
+
+ if (!histogram_pointer) {
+ // This is the slow path, which will construct OR find the matching
+ // histogram. FactoryGet includes locks on a global histogram name map
+ // and is completely thread safe.
+ histogram_pointer = base::Histogram::FactoryGet(
+ name, min, max, bucket_count, base::HistogramBase::kNoFlags);
+
+ // Use Release_Store to ensure that the histogram data is made available
+ // globally before we make the pointer visible.
+ // Several threads may perform this store, but the same value will be
+ // stored in all cases (for a given named/spec'ed histogram).
+ // We could do this without any barrier, since FactoryGet entered and
+ // exited a lock after construction, but this barrier makes things clear.
+ base::subtle::Release_Store(&atomic_histogram_pointer,
+ reinterpret_cast<base::subtle::AtomicWord>(histogram_pointer));
+ }
+
+ // Ensure calling contract is upheld, and the name does NOT vary.
+ DCHECK(histogram_pointer->histogram_name() == constant_histogram_name);
+
+ histogram_pointer->Add(sample);
+ } while (0);
+*/
+
+// The above pattern is repeated in several macros. The only elements that
+// vary are the invocation of the Add(sample) vs AddTime(sample), and the choice
+// of which FactoryGet method to use. The different FactoryGet methods have
+// various argument lists, so the function with its argument list is provided as
+// a macro argument here. The name is only used in a DCHECK, to assure that
+// callers don't try to vary the name of the histogram (which would tend to be
+// ignored by the one-time initialization of the histogtram_pointer).
+#define STATIC_HISTOGRAM_POINTER_BLOCK(constant_histogram_name, \
+ histogram_add_method_invocation, \
+ histogram_factory_get_invocation) \
+ do { \
+ static base::subtle::AtomicWord atomic_histogram_pointer = 0; \
+ base::Histogram* histogram_pointer(reinterpret_cast<base::Histogram*>( \
+ base::subtle::Acquire_Load(&atomic_histogram_pointer))); \
+ if (!histogram_pointer) { \
+ histogram_pointer = histogram_factory_get_invocation; \
+ base::subtle::Release_Store(&atomic_histogram_pointer, \
+ reinterpret_cast<base::subtle::AtomicWord>(histogram_pointer)); \
+ } \
+ DCHECK_EQ(histogram_pointer->histogram_name(), \
+ std::string(constant_histogram_name)); \
+ histogram_pointer->histogram_add_method_invocation; \
+ } while (0)
+
+
+//------------------------------------------------------------------------------
+// Provide easy general purpose histogram in a macro, just like stats counters.
+// The first four macros use 50 buckets.
+
+#define HISTOGRAM_TIMES(name, sample) HISTOGRAM_CUSTOM_TIMES( \
+ name, sample, base::TimeDelta::FromMilliseconds(1), \
+ base::TimeDelta::FromSeconds(10), 50)
+
+#define HISTOGRAM_COUNTS(name, sample) HISTOGRAM_CUSTOM_COUNTS( \
+ name, sample, 1, 1000000, 50)
+
+#define HISTOGRAM_COUNTS_100(name, sample) HISTOGRAM_CUSTOM_COUNTS( \
+ name, sample, 1, 100, 50)
+
+#define HISTOGRAM_COUNTS_10000(name, sample) HISTOGRAM_CUSTOM_COUNTS( \
+ name, sample, 1, 10000, 50)
+
+#define HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \
+ base::Histogram::FactoryGet(name, min, max, bucket_count, \
+ base::HistogramBase::kNoFlags))
+
+#define HISTOGRAM_PERCENTAGE(name, under_one_hundred) \
+ HISTOGRAM_ENUMERATION(name, under_one_hundred, 101)
+
+// For folks that need real specific times, use this to select a precise range
+// of times you want plotted, and the number of buckets you want used.
+#define HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, AddTime(sample), \
+ base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \
+ base::HistogramBase::kNoFlags))
+
+// Support histograming of an enumerated value. The samples should always be
+// strictly less than |boundary_value| -- this prevents you from running into
+// problems down the line if you add additional buckets to the histogram. Note
+// also that, despite explicitly setting the minimum bucket value to |1| below,
+// it is fine for enumerated histograms to be 0-indexed -- this is because
+// enumerated histograms should never have underflow.
+#define HISTOGRAM_ENUMERATION(name, sample, boundary_value) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \
+ base::LinearHistogram::FactoryGet(name, 1, boundary_value, \
+ boundary_value + 1, base::HistogramBase::kNoFlags))
+
+// Support histograming of an enumerated value. Samples should be one of the
+// std::vector<int> list provided via |custom_ranges|. See comments above
+// CustomRanges::FactoryGet about the requirement of |custom_ranges|.
+// You can use the helper function CustomHistogram::ArrayToCustomRanges to
+// transform a C-style array of valid sample values to a std::vector<int>.
+#define HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \
+ base::CustomHistogram::FactoryGet(name, custom_ranges, \
+ base::HistogramBase::kNoFlags))
+
+//------------------------------------------------------------------------------
+// Define Debug vs non-debug flavors of macros.
+#ifndef NDEBUG
+
+#define DHISTOGRAM_TIMES(name, sample) HISTOGRAM_TIMES(name, sample)
+#define DHISTOGRAM_COUNTS(name, sample) HISTOGRAM_COUNTS(name, sample)
+#define DHISTOGRAM_PERCENTAGE(name, under_one_hundred) HISTOGRAM_PERCENTAGE(\
+ name, under_one_hundred)
+#define DHISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \
+ HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count)
+#define DHISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) \
+ HISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count)
+#define DHISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \
+ HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count)
+#define DHISTOGRAM_ENUMERATION(name, sample, boundary_value) \
+ HISTOGRAM_ENUMERATION(name, sample, boundary_value)
+#define DHISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \
+ HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges)
+
+#else // NDEBUG
+// Keep a mention of passed variables to avoid unused variable warnings in
+// release build if these variables are only used in macros.
+#define DISCARD_2_ARGUMENTS(a, b) \
+ do { \
+ static_cast<void>(a); \
+ static_cast<void>(b); \
+ } while(false)
+#define DISCARD_3_ARGUMENTS(a, b, c) \
+ do { \
+ static_cast<void>(a); \
+ static_cast<void>(b); \
+ static_cast<void>(c); \
+ } while(false)
+#define DISCARD_5_ARGUMENTS(a, b, c, d, e) \
+ do { \
+ static_cast<void>(a); \
+ static_cast<void>(b); \
+ static_cast<void>(c); \
+ static_cast<void>(d); \
+ static_cast<void>(e); \
+ } while(false)
+#define DHISTOGRAM_TIMES(name, sample) \
+ DISCARD_2_ARGUMENTS(name, sample)
+
+#define DHISTOGRAM_COUNTS(name, sample) \
+ DISCARD_2_ARGUMENTS(name, sample)
+
+#define DHISTOGRAM_PERCENTAGE(name, under_one_hundred) \
+ DISCARD_2_ARGUMENTS(name, under_one_hundred)
+
+#define DHISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \
+ DISCARD_5_ARGUMENTS(name, sample, min, max, bucket_count)
+
+#define DHISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) \
+ DISCARD_5_ARGUMENTS(name, sample, min, max, bucket_count)
+
+#define DHISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \
+ DISCARD_5_ARGUMENTS(name, sample, min, max, bucket_count)
+
+#define DHISTOGRAM_ENUMERATION(name, sample, boundary_value) \
+ DISCARD_3_ARGUMENTS(name, sample, boundary_value)
+
+#define DHISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \
+ DISCARD_3_ARGUMENTS(name, sample, custom_ranges)
+
+#endif // NDEBUG
+
+//------------------------------------------------------------------------------
+// The following macros provide typical usage scenarios for callers that wish
+// to record histogram data, and have the data submitted/uploaded via UMA.
+// Not all systems support such UMA, but if they do, the following macros
+// should work with the service.
+#if defined(__LB_SHELL__FOR_RELEASE__)
+
+#define UMA_HISTOGRAM_TIMES(name, sample) DISCARD_2_ARGUMENTS(name, sample)
+#define UMA_HISTOGRAM_MEDIUM_TIMES(name, sample) \
+ DISCARD_2_ARGUMENTS(name, sample)
+
+// Use this macro when times can routinely be much longer than 10 seconds.
+#define UMA_HISTOGRAM_LONG_TIMES(name, sample) \
+ DISCARD_2_ARGUMENTS(name, sample)
+#define UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \
+ DISCARD_5_ARGUMENTS(name, sample, min, max, bucket_count)
+#define UMA_HISTOGRAM_COUNTS(name, sample) DISCARD_2_ARGUMENTS(name, sample)
+#define UMA_HISTOGRAM_COUNTS_100(name, sample) \
+ DISCARD_2_ARGUMENTS(name, sample)
+#define UMA_HISTOGRAM_COUNTS_10000(name, sample) \
+ DISCARD_2_ARGUMENTS(name, sample)
+#define UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \
+ DISCARD_5_ARGUMENTS(name, sample, min, max, bucket_count)
+#define UMA_HISTOGRAM_MEMORY_KB(name, sample) DISCARD_2_ARGUMENTS(name, sample)
+#define UMA_HISTOGRAM_MEMORY_MB(name, sample) DISCARD_2_ARGUMENTS(name, sample)
+#define UMA_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \
+ DISCARD_2_ARGUMENTS(name, under_one_hundred)
+#define UMA_HISTOGRAM_BOOLEAN(name, sample) DISCARD_2_ARGUMENTS(name, sample)
+#define UMA_HISTOGRAM_ENUMERATION(name, sample, boundary_value) \
+ DISCARD_3_ARGUMENTS(name, sample, boundary_value)
+#define UMA_HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \
+ DISCARD_3_ARGUMENTS(name, sample, custom_ranges)
+
+#else
+
+#define UMA_HISTOGRAM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
+ name, sample, base::TimeDelta::FromMilliseconds(1), \
+ base::TimeDelta::FromSeconds(10), 50)
+
+#define UMA_HISTOGRAM_MEDIUM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
+ name, sample, base::TimeDelta::FromMilliseconds(10), \
+ base::TimeDelta::FromMinutes(3), 50)
+
+// Use this macro when times can routinely be much longer than 10 seconds.
+#define UMA_HISTOGRAM_LONG_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
+ name, sample, base::TimeDelta::FromMilliseconds(1), \
+ base::TimeDelta::FromHours(1), 50)
+
+#define UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, AddTime(sample), \
+ base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \
+ base::Histogram::kUmaTargetedHistogramFlag))
+
+#define UMA_HISTOGRAM_COUNTS(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
+ name, sample, 1, 1000000, 50)
+
+#define UMA_HISTOGRAM_COUNTS_100(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
+ name, sample, 1, 100, 50)
+
+#define UMA_HISTOGRAM_COUNTS_10000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
+ name, sample, 1, 10000, 50)
+
+#define UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \
+ base::Histogram::FactoryGet(name, min, max, bucket_count, \
+ base::Histogram::kUmaTargetedHistogramFlag))
+
+#define UMA_HISTOGRAM_MEMORY_KB(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
+ name, sample, 1000, 500000, 50)
+
+#define UMA_HISTOGRAM_MEMORY_MB(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
+ name, sample, 1, 1000, 50)
+
+#define UMA_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \
+ UMA_HISTOGRAM_ENUMERATION(name, under_one_hundred, 101)
+
+#define UMA_HISTOGRAM_BOOLEAN(name, sample) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, AddBoolean(sample), \
+ base::BooleanHistogram::FactoryGet(name, \
+ base::Histogram::kUmaTargetedHistogramFlag))
+
+// The samples should always be strictly less than |boundary_value|. For more
+// details, see the comment for the |HISTOGRAM_ENUMERATION| macro, above.
+#define UMA_HISTOGRAM_ENUMERATION(name, sample, boundary_value) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \
+ base::LinearHistogram::FactoryGet(name, 1, boundary_value, \
+ boundary_value + 1, base::Histogram::kUmaTargetedHistogramFlag))
+
+#define UMA_HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \
+ base::CustomHistogram::FactoryGet(name, custom_ranges, \
+ base::Histogram::kUmaTargetedHistogramFlag))
+#endif // defined(__LB_SHELL__FOR_RELEASE__)
+
+//------------------------------------------------------------------------------
+
+class BucketRanges;
+class SampleVector;
+
+class BooleanHistogram;
+class CustomHistogram;
+class Histogram;
+class LinearHistogram;
+
+class BASE_EXPORT Histogram : public HistogramBase {
+ public:
+ // Initialize maximum number of buckets in histograms as 16,384.
+ static const size_t kBucketCount_MAX;
+
+ typedef std::vector<Count> Counts;
+
+ enum Inconsistencies {
+ NO_INCONSISTENCIES = 0x0,
+ RANGE_CHECKSUM_ERROR = 0x1,
+ BUCKET_ORDER_ERROR = 0x2,
+ COUNT_HIGH_ERROR = 0x4,
+ COUNT_LOW_ERROR = 0x8,
+
+ NEVER_EXCEEDED_VALUE = 0x10
+ };
+
+ struct DescriptionPair {
+ Sample sample;
+ const char* description; // Null means end of a list of pairs.
+ };
+
+ //----------------------------------------------------------------------------
+ // For a valid histogram, input should follow these restrictions:
+ // minimum > 0 (if a minimum below 1 is specified, it will implicitly be
+ // normalized up to 1)
+ // maximum > minimum
+ // buckets > 2 [minimum buckets needed: underflow, overflow and the range]
+ // Additionally,
+ // buckets <= (maximum - minimum + 2) - this is to ensure that we don't have
+ // more buckets than the range of numbers; having more buckets than 1 per
+ // value in the range would be nonsensical.
+ static Histogram* FactoryGet(const std::string& name,
+ Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ int32 flags);
+ static Histogram* FactoryTimeGet(const std::string& name,
+ base::TimeDelta minimum,
+ base::TimeDelta maximum,
+ size_t bucket_count,
+ int32 flags);
+
+ // Time call for use with DHISTOGRAM*.
+ // Returns TimeTicks::Now() in debug and TimeTicks() in release build.
+ static TimeTicks DebugNow();
+
+ static void InitializeBucketRanges(Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ BucketRanges* ranges);
+
+ // This method is an interface, used only by BooleanHistogram.
+ virtual void AddBoolean(bool value);
+
+ // Accept a TimeDelta to increment.
+ void AddTime(TimeDelta time) {
+ Add(static_cast<int>(time.InMilliseconds()));
+ }
+
+ void AddSamples(const HistogramSamples& samples);
+ bool AddSamplesFromPickle(PickleIterator* iter);
+
+ // Convenience methods for serializing/deserializing the histograms.
+ // Histograms from Renderer process are serialized and sent to the browser.
+ // Browser process reconstructs the histogram from the pickled version
+ // accumulates the browser-side shadow copy of histograms (that mirror
+ // histograms created in the renderer).
+
+ // Serialize the given snapshot of a Histogram into a String. Uses
+ // Pickle class to flatten the object.
+ static std::string SerializeHistogramInfo(const Histogram& histogram,
+ const HistogramSamples& snapshot);
+
+ // The following method accepts a list of pickled histograms and
+ // builds a histogram and updates shadow copy of histogram data in the
+ // browser process.
+ static bool DeserializeHistogramInfo(const std::string& histogram_info);
+
+ // This constant if for FindCorruption. Since snapshots of histograms are
+ // taken asynchronously relative to sampling, and our counting code currently
+ // does not prevent race conditions, it is pretty likely that we'll catch a
+ // redundant count that doesn't match the sample count. We allow for a
+ // certain amount of slop before flagging this as an inconsistency. Even with
+ // an inconsistency, we'll snapshot it again (for UMA in about a half hour),
+ // so we'll eventually get the data, if it was not the result of a corruption.
+ static const int kCommonRaceBasedCountMismatch;
+
+ // Check to see if bucket ranges, counts and tallies in the snapshot are
+ // consistent with the bucket ranges and checksums in our histogram. This can
+ // produce a false-alarm if a race occurred in the reading of the data during
+ // a SnapShot process, but should otherwise be false at all times (unless we
+ // have memory over-writes, or DRAM failures).
+ virtual Inconsistencies FindCorruption(const HistogramSamples& samples) const;
+
+ //----------------------------------------------------------------------------
+ // Accessors for factory constuction, serialization and testing.
+ //----------------------------------------------------------------------------
+ Sample declared_min() const { return declared_min_; }
+ Sample declared_max() const { return declared_max_; }
+ virtual Sample ranges(size_t i) const;
+ virtual size_t bucket_count() const;
+ const BucketRanges* bucket_ranges() const { return bucket_ranges_; }
+
+ // This function validates histogram construction arguments. It returns false
+ // if some of the arguments are totally bad.
+ // Note. Currently it allow some bad input, e.g. 0 as minimum, but silently
+ // converts it to good input: 1.
+ // TODO(kaiwang): Be more restrict and return false for any bad input, and
+ // make this a readonly validating function.
+ static bool InspectConstructionArguments(const std::string& name,
+ Sample* minimum,
+ Sample* maximum,
+ size_t* bucket_count);
+
+ // HistogramBase implementation:
+ virtual HistogramType GetHistogramType() const OVERRIDE;
+ virtual bool HasConstructionArguments(Sample minimum,
+ Sample maximum,
+ size_t bucket_count) const OVERRIDE;
+ virtual void Add(Sample value) OVERRIDE;
+ virtual scoped_ptr<HistogramSamples> SnapshotSamples() const OVERRIDE;
+ virtual void WriteHTMLGraph(std::string* output) const OVERRIDE;
+ virtual void WriteAscii(std::string* output) const OVERRIDE;
+
+ protected:
+ // |bucket_count| and |ranges| should contain the underflow and overflow
+ // buckets. See top comments for example.
+ Histogram(const std::string& name,
+ Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ const BucketRanges* ranges);
+
+ virtual ~Histogram();
+
+ // Serialize the histogram's ranges to |*pickle|, returning true on success.
+ // Most subclasses can leave this no-op implementation, but some will want to
+ // override it, especially if the ranges cannot be re-derived from other
+ // serialized parameters.
+ virtual bool SerializeRanges(Pickle* pickle) const;
+
+ // Method to override to skip the display of the i'th bucket if it's empty.
+ virtual bool PrintEmptyBucket(size_t index) const;
+
+ // Get normalized size, relative to the ranges(i).
+ virtual double GetBucketSize(Count current, size_t i) const;
+
+ // Return a string description of what goes in a given bucket.
+ // Most commonly this is the numeric value, but in derived classes it may
+ // be a name (or string description) given to the bucket.
+ virtual const std::string GetAsciiBucketRange(size_t it) const;
+
+ private:
+ // Allow tests to corrupt our innards for testing purposes.
+ FRIEND_TEST_ALL_PREFIXES(HistogramTest, BoundsTest);
+ FRIEND_TEST_ALL_PREFIXES(HistogramTest, BucketPlacementTest);
+ FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptBucketBounds);
+ FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts);
+ FRIEND_TEST_ALL_PREFIXES(HistogramTest, NameMatchTest);
+
+ friend class StatisticsRecorder; // To allow it to delete duplicates.
+ friend class StatisticsRecorderTest;
+
+ // Implementation of SnapshotSamples function.
+ scoped_ptr<SampleVector> SnapshotSampleVector() const;
+
+ //----------------------------------------------------------------------------
+ // Helpers for emitting Ascii graphic. Each method appends data to output.
+
+ void WriteAsciiImpl(bool graph_it,
+ const std::string& newline,
+ std::string* output) const;
+
+ // Find out how large (graphically) the largest bucket will appear to be.
+ double GetPeakBucketSize(const SampleVector& samples) const;
+
+ // Write a common header message describing this histogram.
+ void WriteAsciiHeader(const SampleVector& samples,
+ Count sample_count,
+ std::string* output) const;
+
+ // Write information about previous, current, and next buckets.
+ // Information such as cumulative percentage, etc.
+ void WriteAsciiBucketContext(const int64 past, const Count current,
+ const int64 remaining, const size_t i,
+ std::string* output) const;
+
+ // Write textual description of the bucket contents (relative to histogram).
+ // Output is the count in the buckets, as well as the percentage.
+ void WriteAsciiBucketValue(Count current, double scaled_sum,
+ std::string* output) const;
+
+ // Produce actual graph (set of blank vs non blank char's) for a bucket.
+ void WriteAsciiBucketGraph(double current_size, double max_size,
+ std::string* output) const;
+
+ // WriteJSON calls these.
+ virtual void GetParameters(DictionaryValue* params) const OVERRIDE;
+
+ virtual void GetCountAndBucketData(Count* count,
+ ListValue* buckets) const OVERRIDE;
+
+ // Does not own this object. Should get from StatisticsRecorder.
+ const BucketRanges* bucket_ranges_;
+
+ Sample declared_min_; // Less than this goes into counts_[0]
+ Sample declared_max_; // Over this goes into counts_[bucket_count_ - 1].
+ size_t bucket_count_; // Dimension of counts_[].
+
+ // Finally, provide the state that changes with the addition of each new
+ // sample.
+ scoped_ptr<SampleVector> samples_;
+
+ DISALLOW_COPY_AND_ASSIGN(Histogram);
+};
+
+//------------------------------------------------------------------------------
+
+// LinearHistogram is a more traditional histogram, with evenly spaced
+// buckets.
+class BASE_EXPORT LinearHistogram : public Histogram {
+ public:
+ virtual ~LinearHistogram();
+
+ /* minimum should start from 1. 0 is as minimum is invalid. 0 is an implicit
+ default underflow bucket. */
+ static Histogram* FactoryGet(const std::string& name,
+ Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ int32 flags);
+ static Histogram* FactoryTimeGet(const std::string& name,
+ TimeDelta minimum,
+ TimeDelta maximum,
+ size_t bucket_count,
+ int32 flags);
+
+ // Create a LinearHistogram and store a list of number/text values for use in
+ // writing the histogram graph.
+ // |descriptions| can be NULL, which means no special descriptions to set. If
+ // it's not NULL, the last element in the array must has a NULL in its
+ // "description" field.
+ static Histogram* FactoryGetWithRangeDescription(
+ const std::string& name,
+ Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ int32 flags,
+ const DescriptionPair descriptions[]);
+
+ static void InitializeBucketRanges(Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ BucketRanges* ranges);
+
+ // Overridden from Histogram:
+ virtual HistogramType GetHistogramType() const OVERRIDE;
+
+ protected:
+ LinearHistogram(const std::string& name,
+ Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ const BucketRanges* ranges);
+
+ virtual double GetBucketSize(Count current, size_t i) const OVERRIDE;
+
+ // If we have a description for a bucket, then return that. Otherwise
+ // let parent class provide a (numeric) description.
+ virtual const std::string GetAsciiBucketRange(size_t i) const OVERRIDE;
+
+ // Skip printing of name for numeric range if we have a name (and if this is
+ // an empty bucket).
+ virtual bool PrintEmptyBucket(size_t index) const OVERRIDE;
+
+ private:
+ // For some ranges, we store a printable description of a bucket range.
+ // If there is no desciption, then GetAsciiBucketRange() uses parent class
+ // to provide a description.
+ typedef std::map<Sample, std::string> BucketDescriptionMap;
+ BucketDescriptionMap bucket_description_;
+
+ DISALLOW_COPY_AND_ASSIGN(LinearHistogram);
+};
+
+//------------------------------------------------------------------------------
+
+// BooleanHistogram is a histogram for booleans.
+class BASE_EXPORT BooleanHistogram : public LinearHistogram {
+ public:
+ static Histogram* FactoryGet(const std::string& name, int32 flags);
+
+ virtual HistogramType GetHistogramType() const OVERRIDE;
+
+ virtual void AddBoolean(bool value) OVERRIDE;
+
+ private:
+ BooleanHistogram(const std::string& name, const BucketRanges* ranges);
+
+ DISALLOW_COPY_AND_ASSIGN(BooleanHistogram);
+};
+
+//------------------------------------------------------------------------------
+
+// CustomHistogram is a histogram for a set of custom integers.
+class BASE_EXPORT CustomHistogram : public Histogram {
+ public:
+ // |custom_ranges| contains a vector of limits on ranges. Each limit should be
+ // > 0 and < kSampleType_MAX. (Currently 0 is still accepted for backward
+ // compatibility). The limits can be unordered or contain duplication, but
+ // client should not depend on this.
+ static Histogram* FactoryGet(const std::string& name,
+ const std::vector<Sample>& custom_ranges,
+ int32 flags);
+
+ // Overridden from Histogram:
+ virtual HistogramType GetHistogramType() const OVERRIDE;
+
+ // Helper method for transforming an array of valid enumeration values
+ // to the std::vector<int> expected by HISTOGRAM_CUSTOM_ENUMERATION.
+ // This function ensures that a guard bucket exists right after any
+ // valid sample value (unless the next higher sample is also a valid value),
+ // so that invalid samples never fall into the same bucket as valid samples.
+ // TODO(kaiwang): Change name to ArrayToCustomEnumRanges.
+ static std::vector<Sample> ArrayToCustomRanges(const Sample* values,
+ size_t num_values);
+
+ // Helper for deserializing CustomHistograms. |*ranges| should already be
+ // correctly sized before this call. Return true on success.
+ static bool DeserializeRanges(PickleIterator* iter,
+ std::vector<Sample>* ranges);
+ protected:
+ CustomHistogram(const std::string& name,
+ const BucketRanges* ranges);
+
+ virtual bool SerializeRanges(Pickle* pickle) const OVERRIDE;
+
+ virtual double GetBucketSize(Count current, size_t i) const OVERRIDE;
+
+ private:
+ static bool ValidateCustomRanges(const std::vector<Sample>& custom_ranges);
+ static BucketRanges* CreateBucketRangesFromCustomRanges(
+ const std::vector<Sample>& custom_ranges);
+
+ DISALLOW_COPY_AND_ASSIGN(CustomHistogram);
+};
+
+} // namespace base
+
+#endif // BASE_METRICS_HISTOGRAM_H_
diff --git a/src/base/metrics/histogram_base.cc b/src/base/metrics/histogram_base.cc
new file mode 100644
index 0000000..bcfb57f
--- /dev/null
+++ b/src/base/metrics/histogram_base.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 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/metrics/histogram_base.h"
+
+#include <climits>
+
+#include "base/logging.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+
+namespace base {
+
+std::string HistogramTypeToString(HistogramType type) {
+ switch(type) {
+ case HISTOGRAM:
+ return "HISTOGRAM";
+ case LINEAR_HISTOGRAM:
+ return "LINEAR_HISTOGRAM";
+ case BOOLEAN_HISTOGRAM:
+ return "BOOLEAN_HISTOGRAM";
+ case CUSTOM_HISTOGRAM:
+ return "CUSTOM_HISTOGRAM";
+ case SPARSE_HISTOGRAM:
+ return "SPARSE_HISTOGRAM";
+ default:
+ NOTREACHED();
+ }
+ return "UNKNOWN";
+}
+
+const HistogramBase::Sample HistogramBase::kSampleType_MAX = INT_MAX;
+
+HistogramBase::HistogramBase(const std::string& name)
+ : histogram_name_(name),
+ flags_(kNoFlags) {}
+
+HistogramBase::~HistogramBase() {}
+
+void HistogramBase::SetFlags(int32 flags) {
+ flags_ |= flags;
+}
+
+void HistogramBase::ClearFlags(int32 flags) {
+ flags_ &= ~flags;
+}
+
+void HistogramBase::WriteJSON(std::string* output) const {
+ Count count;
+ scoped_ptr<ListValue> buckets(new ListValue());
+ GetCountAndBucketData(&count, buckets.get());
+ scoped_ptr<DictionaryValue> parameters(new DictionaryValue());
+ GetParameters(parameters.get());
+
+ JSONStringValueSerializer serializer(output);
+ DictionaryValue root;
+ root.SetString("name", histogram_name());
+ root.SetInteger("count", count);
+ root.SetInteger("flags", flags());
+ root.Set("params", parameters.release());
+ root.Set("buckets", buckets.release());
+ serializer.Serialize(root);
+}
+
+} // namespace base
diff --git a/src/base/metrics/histogram_base.h b/src/base/metrics/histogram_base.h
new file mode 100644
index 0000000..302795a
--- /dev/null
+++ b/src/base/metrics/histogram_base.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2012 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_METRICS_HISTOGRAM_BASE_H_
+#define BASE_METRICS_HISTOGRAM_BASE_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace base {
+
+class DictionaryValue;
+class HistogramSamples;
+class ListValue;
+
+////////////////////////////////////////////////////////////////////////////////
+// These enums are used to facilitate deserialization of histograms from other
+// processes into the browser. If you create another class that inherits from
+// HistogramBase, add new histogram types and names below.
+
+enum BASE_EXPORT HistogramType {
+ HISTOGRAM,
+ LINEAR_HISTOGRAM,
+ BOOLEAN_HISTOGRAM,
+ CUSTOM_HISTOGRAM,
+ SPARSE_HISTOGRAM,
+};
+
+std::string HistogramTypeToString(HistogramType type);
+
+////////////////////////////////////////////////////////////////////////////////
+
+class BASE_EXPORT HistogramBase {
+ public:
+ typedef int Sample; // Used for samples.
+ typedef int Count; // Used to count samples.
+
+ static const Sample kSampleType_MAX; // INT_MAX
+
+ enum Flags {
+ kNoFlags = 0,
+ kUmaTargetedHistogramFlag = 0x1, // Histogram should be UMA uploaded.
+
+ // Indicate that the histogram was pickled to be sent across an IPC Channel.
+ // If we observe this flag on a histogram being aggregated into after IPC,
+ // then we are running in a single process mode, and the aggregation should
+ // not take place (as we would be aggregating back into the source
+ // histogram!).
+ kIPCSerializationSourceFlag = 0x10,
+
+ // Only for Histogram and its sub classes: fancy bucket-naming support.
+ kHexRangePrintingFlag = 0x8000,
+ };
+
+ HistogramBase(const std::string& name);
+ virtual ~HistogramBase();
+
+ std::string histogram_name() const { return histogram_name_; }
+
+ // Operations with Flags enum.
+ int32 flags() const { return flags_; }
+ void SetFlags(int32 flags);
+ void ClearFlags(int32 flags);
+
+ virtual HistogramType GetHistogramType() const = 0;
+
+ // Whether the histogram has construction arguments as parameters specified.
+ // For histograms that don't have the concept of minimum, maximum or
+ // bucket_count, this function always returns false.
+ virtual bool HasConstructionArguments(Sample minimum,
+ Sample maximum,
+ size_t bucket_count) const = 0;
+
+ virtual void Add(Sample value) = 0;
+
+ // Snapshot the current complete set of sample data.
+ // Override with atomic/locked snapshot if needed.
+ virtual scoped_ptr<HistogramSamples> SnapshotSamples() const = 0;
+
+ // The following methods provide graphical histogram displays.
+ virtual void WriteHTMLGraph(std::string* output) const = 0;
+ virtual void WriteAscii(std::string* output) const = 0;
+
+ // Produce a JSON representation of the histogram. This is implemented with
+ // the help of GetParameters and GetCountAndBucketData; overwrite them to
+ // customize the output.
+ void WriteJSON(std::string* output) const;
+
+protected:
+ // Writes information about the construction parameters in |params|.
+ virtual void GetParameters(DictionaryValue* params) const = 0;
+
+ // Writes information about the current (non-empty) buckets and their sample
+ // counts to |buckets| and the total sample count to |count|.
+ virtual void GetCountAndBucketData(Count* count,
+ ListValue* buckets) const = 0;
+ private:
+ const std::string histogram_name_;
+ int32 flags_;
+
+ DISALLOW_COPY_AND_ASSIGN(HistogramBase);
+};
+
+} // namespace base
+
+#endif // BASE_METRICS_HISTOGRAM_BASE_H_
diff --git a/src/base/metrics/histogram_flattener.h b/src/base/metrics/histogram_flattener.h
new file mode 100644
index 0000000..6dd169e
--- /dev/null
+++ b/src/base/metrics/histogram_flattener.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2012 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_METRICS_HISTOGRAM_FLATTENER_H_
+#define BASE_METRICS_HISTOGRAM_FLATTENER_H_
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram.h"
+
+namespace base {
+
+class HistogramSamples;
+
+// HistogramFlattener is an interface used by HistogramSnapshotManager, which
+// handles the logistics of gathering up available histograms for recording.
+// The implementors handle the exact lower level recording mechanism, or
+// error report mechanism.
+class BASE_EXPORT HistogramFlattener {
+ public:
+ virtual void RecordDelta(const Histogram& histogram,
+ const HistogramSamples& snapshot) = 0;
+
+ // Will be called each time a type of Inconsistenies is seen on a histogram,
+ // during inspections done internally in HistogramSnapshotManager class.
+ virtual void InconsistencyDetected(Histogram::Inconsistencies problem) = 0;
+
+ // Will be called when a type of Inconsistenies is seen for the first time
+ // on a histogram.
+ virtual void UniqueInconsistencyDetected(
+ Histogram::Inconsistencies problem) = 0;
+
+ // Will be called when the total logged sample count of a histogram
+ // differs from the sum of logged sample count in all the buckets. The
+ // argument |amount| is the non-zero discrepancy.
+ virtual void InconsistencyDetectedInLoggedCount(int amount) = 0;
+
+ protected:
+ HistogramFlattener() {}
+ virtual ~HistogramFlattener() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HistogramFlattener);
+};
+
+} // namespace base
+
+#endif // BASE_METRICS_HISTOGRAM_FLATTENER_H_
diff --git a/src/base/metrics/histogram_samples.cc b/src/base/metrics/histogram_samples.cc
new file mode 100644
index 0000000..e375952
--- /dev/null
+++ b/src/base/metrics/histogram_samples.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2012 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/metrics/histogram_samples.h"
+
+#include "base/compiler_specific.h"
+#include "base/pickle.h"
+
+namespace base {
+
+namespace {
+
+class SampleCountPickleIterator : public SampleCountIterator {
+ public:
+ SampleCountPickleIterator(PickleIterator* iter);
+
+ virtual bool Done() const OVERRIDE;
+ virtual void Next() OVERRIDE;
+ virtual void Get(HistogramBase::Sample* min,
+ HistogramBase::Sample* max,
+ HistogramBase::Count* count) const OVERRIDE;
+ private:
+ PickleIterator* const iter_;
+
+ HistogramBase::Sample min_;
+ HistogramBase::Sample max_;
+ HistogramBase::Count count_;
+ bool is_done_;
+};
+
+SampleCountPickleIterator::SampleCountPickleIterator(PickleIterator* iter)
+ : iter_(iter),
+ is_done_(false) {
+ Next();
+}
+
+bool SampleCountPickleIterator::Done() const {
+ return is_done_;
+}
+
+void SampleCountPickleIterator::Next() {
+ DCHECK(!Done());
+ if (!iter_->ReadInt(&min_) ||
+ !iter_->ReadInt(&max_) ||
+ !iter_->ReadInt(&count_))
+ is_done_ = true;
+}
+
+void SampleCountPickleIterator::Get(HistogramBase::Sample* min,
+ HistogramBase::Sample* max,
+ HistogramBase::Count* count) const {
+ DCHECK(!Done());
+ *min = min_;
+ *max = max_;
+ *count = count_;
+}
+
+} // namespace
+
+HistogramSamples::HistogramSamples() : sum_(0), redundant_count_(0) {}
+
+HistogramSamples::~HistogramSamples() {}
+
+void HistogramSamples::Add(const HistogramSamples& other) {
+ sum_ += other.sum();
+ redundant_count_ += other.redundant_count();
+ bool success = AddSubtractImpl(other.Iterator().get(), ADD);
+ DCHECK(success);
+}
+
+bool HistogramSamples::AddFromPickle(PickleIterator* iter) {
+ int64 sum;
+ HistogramBase::Count redundant_count;
+
+ if (!iter->ReadInt64(&sum) || !iter->ReadInt(&redundant_count))
+ return false;
+ sum_ += sum;
+ redundant_count_ += redundant_count;
+
+ SampleCountPickleIterator pickle_iter(iter);
+ return AddSubtractImpl(&pickle_iter, ADD);
+}
+
+void HistogramSamples::Subtract(const HistogramSamples& other) {
+ sum_ -= other.sum();
+ redundant_count_ -= other.redundant_count();
+ bool success = AddSubtractImpl(other.Iterator().get(), SUBTRACT);
+ DCHECK(success);
+}
+
+bool HistogramSamples::Serialize(Pickle* pickle) const {
+ if (!pickle->WriteInt64(sum_) || !pickle->WriteInt(redundant_count_))
+ return false;
+
+ HistogramBase::Sample min;
+ HistogramBase::Sample max;
+ HistogramBase::Count count;
+ for (scoped_ptr<SampleCountIterator> it = Iterator();
+ !it->Done();
+ it->Next()) {
+ it->Get(&min, &max, &count);
+ if (!pickle->WriteInt(min) ||
+ !pickle->WriteInt(max) ||
+ !pickle->WriteInt(count))
+ return false;
+ }
+ return true;
+}
+
+void HistogramSamples::IncreaseSum(int64 diff) {
+ sum_ += diff;
+}
+
+void HistogramSamples::IncreaseRedundantCount(HistogramBase::Count diff) {
+ redundant_count_ += diff;
+}
+
+SampleCountIterator::~SampleCountIterator() {}
+
+bool SampleCountIterator::GetBucketIndex(size_t* index) const {
+ DCHECK(!Done());
+ return false;
+}
+
+} // namespace base
diff --git a/src/base/metrics/histogram_samples.h b/src/base/metrics/histogram_samples.h
new file mode 100644
index 0000000..c55b584
--- /dev/null
+++ b/src/base/metrics/histogram_samples.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2012 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_METRICS_HISTOGRAM_SAMPLES_H_
+#define BASE_METRICS_HISTOGRAM_SAMPLES_H_
+
+#include "base/basictypes.h"
+#include "base/metrics/histogram_base.h"
+#include "base/memory/scoped_ptr.h"
+
+class Pickle;
+class PickleIterator;
+
+namespace base {
+
+class SampleCountIterator;
+
+// HistogramSamples is a container storing all samples of a histogram.
+class BASE_EXPORT HistogramSamples {
+ public:
+ HistogramSamples();
+ virtual ~HistogramSamples();
+
+ virtual void Accumulate(HistogramBase::Sample value,
+ HistogramBase::Count count) = 0;
+ virtual HistogramBase::Count GetCount(HistogramBase::Sample value) const = 0;
+ virtual HistogramBase::Count TotalCount() const = 0;
+
+ virtual void Add(const HistogramSamples& other);
+
+ // Add from serialized samples.
+ virtual bool AddFromPickle(PickleIterator* iter);
+
+ virtual void Subtract(const HistogramSamples& other);
+
+ virtual scoped_ptr<SampleCountIterator> Iterator() const = 0;
+ virtual bool Serialize(Pickle* pickle) const;
+
+ // Accessor fuctions.
+ int64 sum() const { return sum_; }
+ HistogramBase::Count redundant_count() const { return redundant_count_; }
+
+ protected:
+ // Based on |op| type, add or subtract sample counts data from the iterator.
+ enum Operator { ADD, SUBTRACT };
+ virtual bool AddSubtractImpl(SampleCountIterator* iter, Operator op) = 0;
+
+ void IncreaseSum(int64 diff);
+ void IncreaseRedundantCount(HistogramBase::Count diff);
+
+ private:
+ int64 sum_;
+
+ // |redundant_count_| helps identify memory corruption. It redundantly stores
+ // the total number of samples accumulated in the histogram. We can compare
+ // this count to the sum of the counts (TotalCount() function), and detect
+ // problems. Note, depending on the implementation of different histogram
+ // types, there might be races during histogram accumulation and snapshotting
+ // that we choose to accept. In this case, the tallies might mismatch even
+ // when no memory corruption has happened.
+ HistogramBase::Count redundant_count_;
+};
+
+class BASE_EXPORT SampleCountIterator {
+ public:
+ virtual ~SampleCountIterator();
+
+ virtual bool Done() const = 0;
+ virtual void Next() = 0;
+
+ // Get the sample and count at current position.
+ // |min| |max| and |count| can be NULL if the value is not of interest.
+ // Requires: !Done();
+ virtual void Get(HistogramBase::Sample* min,
+ HistogramBase::Sample* max,
+ HistogramBase::Count* count) const = 0;
+
+ // Get the index of current histogram bucket.
+ // For histograms that don't use predefined buckets, it returns false.
+ // Requires: !Done();
+ virtual bool GetBucketIndex(size_t* index) const;
+};
+
+} // namespace base
+
+#endif // BASE_METRICS_HISTOGRAM_SAMPLES_H_
diff --git a/src/base/metrics/histogram_snapshot_manager.cc b/src/base/metrics/histogram_snapshot_manager.cc
new file mode 100644
index 0000000..ad054b1
--- /dev/null
+++ b/src/base/metrics/histogram_snapshot_manager.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2012 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/metrics/histogram_snapshot_manager.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram_flattener.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/stl_util.h"
+
+using std::map;
+using std::string;
+
+namespace base {
+
+HistogramSnapshotManager::HistogramSnapshotManager(
+ HistogramFlattener* histogram_flattener)
+ : histogram_flattener_(histogram_flattener) {
+ DCHECK(histogram_flattener_);
+}
+
+HistogramSnapshotManager::~HistogramSnapshotManager() {
+ STLDeleteValues(&logged_samples_);
+}
+
+void HistogramSnapshotManager::PrepareDeltas(Histogram::Flags flag_to_set,
+ bool record_only_uma) {
+ StatisticsRecorder::Histograms histograms;
+ StatisticsRecorder::GetHistograms(&histograms);
+ for (StatisticsRecorder::Histograms::const_iterator it = histograms.begin();
+ histograms.end() != it;
+ ++it) {
+ (*it)->SetFlags(flag_to_set);
+ if (record_only_uma &&
+ 0 == ((*it)->flags() & Histogram::kUmaTargetedHistogramFlag))
+ continue;
+ PrepareDelta(**it);
+ }
+}
+
+void HistogramSnapshotManager::PrepareDelta(const Histogram& histogram) {
+ DCHECK(histogram_flattener_);
+
+ // Get up-to-date snapshot of sample stats.
+ scoped_ptr<HistogramSamples> snapshot(histogram.SnapshotSamples());
+ const std::string& histogram_name = histogram.histogram_name();
+
+ int corruption = histogram.FindCorruption(*snapshot);
+
+ // Crash if we detect that our histograms have been overwritten. This may be
+ // a fair distance from the memory smasher, but we hope to correlate these
+ // crashes with other events, such as plugins, or usage patterns, etc.
+ if (Histogram::BUCKET_ORDER_ERROR & corruption) {
+ // The checksum should have caught this, so crash separately if it didn't.
+ CHECK_NE(0, Histogram::RANGE_CHECKSUM_ERROR & corruption);
+ CHECK(false); // Crash for the bucket order corruption.
+ }
+ // Checksum corruption might not have caused order corruption.
+ CHECK_EQ(0, Histogram::RANGE_CHECKSUM_ERROR & corruption);
+
+ // Note, at this point corruption can only be COUNT_HIGH_ERROR or
+ // COUNT_LOW_ERROR and they never arise together, so we don't need to extract
+ // bits from corruption.
+ if (corruption) {
+ DLOG(ERROR) << "Histogram: " << histogram_name
+ << " has data corruption: " << corruption;
+ histogram_flattener_->InconsistencyDetected(
+ static_cast<Histogram::Inconsistencies>(corruption));
+ // Don't record corrupt data to metrics services.
+ int old_corruption = inconsistencies_[histogram_name];
+ if (old_corruption == (corruption | old_corruption))
+ return; // We've already seen this corruption for this histogram.
+ inconsistencies_[histogram_name] |= corruption;
+ histogram_flattener_->UniqueInconsistencyDetected(
+ static_cast<Histogram::Inconsistencies>(corruption));
+ return;
+ }
+
+ HistogramSamples* to_log;
+ map<string, HistogramSamples*>::iterator it =
+ logged_samples_.find(histogram_name);
+ if (it == logged_samples_.end()) {
+ to_log = snapshot.release();
+
+ // This histogram has not been logged before, add a new entry.
+ logged_samples_[histogram_name] = to_log;
+ } else {
+ HistogramSamples* already_logged = it->second;
+ InspectLoggedSamplesInconsistency(*snapshot, already_logged);
+ snapshot->Subtract(*already_logged);
+ already_logged->Add(*snapshot);
+ to_log = snapshot.get();
+ }
+
+ if (to_log->redundant_count() > 0)
+ histogram_flattener_->RecordDelta(histogram, *to_log);
+}
+
+void HistogramSnapshotManager::InspectLoggedSamplesInconsistency(
+ const HistogramSamples& new_snapshot,
+ HistogramSamples* logged_samples) {
+ HistogramBase::Count discrepancy =
+ logged_samples->TotalCount() - logged_samples->redundant_count();
+ if (!discrepancy)
+ return;
+
+ histogram_flattener_->InconsistencyDetectedInLoggedCount(discrepancy);
+ if (discrepancy > Histogram::kCommonRaceBasedCountMismatch) {
+ // Fix logged_samples.
+ logged_samples->Subtract(*logged_samples);
+ logged_samples->Add(new_snapshot);
+ }
+}
+
+} // namespace base
diff --git a/src/base/metrics/histogram_snapshot_manager.h b/src/base/metrics/histogram_snapshot_manager.h
new file mode 100644
index 0000000..0ec1ac3
--- /dev/null
+++ b/src/base/metrics/histogram_snapshot_manager.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 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_METRICS_HISTOGRAM_SNAPSHOT_MANAGER_H_
+#define BASE_METRICS_HISTOGRAM_SNAPSHOT_MANAGER_H_
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/metrics/histogram.h"
+
+namespace base {
+
+class HistogramSamples;
+class HistogramFlattener;
+
+// HistogramSnapshotManager handles the logistics of gathering up available
+// histograms for recording either to disk or for transmission (such as from
+// renderer to browser, or from browser to UMA upload). Since histograms can sit
+// in memory for an extended period of time, and are vulnerable to memory
+// corruption, this class also validates as much rendundancy as it can before
+// calling for the marginal change (a.k.a., delta) in a histogram to be
+// recorded.
+class BASE_EXPORT HistogramSnapshotManager {
+ public:
+ explicit HistogramSnapshotManager(HistogramFlattener* histogram_flattener);
+ virtual ~HistogramSnapshotManager();
+
+ // Snapshot all histograms, and ask |histogram_flattener_| to record the
+ // delta. The arguments allow selecting only a subset of histograms for
+ // recording, or to set a flag in each recorded histogram.
+ void PrepareDeltas(Histogram::Flags flags_to_set, bool record_only_uma);
+
+ private:
+ // Snapshot this histogram, and record the delta.
+ void PrepareDelta(const Histogram& histogram);
+
+ // Try to detect and fix count inconsistency of logged samples.
+ void InspectLoggedSamplesInconsistency(
+ const HistogramSamples& new_snapshot,
+ HistogramSamples* logged_samples);
+
+ // For histograms, track what we've already recorded (as a sample for
+ // each histogram) so that we can record only the delta with the next log.
+ std::map<std::string, HistogramSamples*> logged_samples_;
+
+ // List of histograms found to be corrupt, and their problems.
+ std::map<std::string, int> inconsistencies_;
+
+ // |histogram_flattener_| handles the logistics of recording the histogram
+ // deltas.
+ HistogramFlattener* histogram_flattener_; // Weak.
+
+ DISALLOW_COPY_AND_ASSIGN(HistogramSnapshotManager);
+};
+
+} // namespace base
+
+#endif // BASE_METRICS_HISTOGRAM_SNAPSHOT_MANAGER_H_
diff --git a/src/base/metrics/histogram_unittest.cc b/src/base/metrics/histogram_unittest.cc
new file mode 100644
index 0000000..9fb9422
--- /dev/null
+++ b/src/base/metrics/histogram_unittest.cc
@@ -0,0 +1,404 @@
+// Copyright (c) 2012 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.
+
+// Test of Histogram class
+
+#include <climits>
+#include <algorithm>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/bucket_ranges.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/sample_vector.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::vector;
+
+namespace base {
+
+class HistogramTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Each test will have a clean state (no Histogram / BucketRanges
+ // registered).
+ InitializeStatisticsRecorder();
+ }
+
+ virtual void TearDown() {
+ UninitializeStatisticsRecorder();
+ }
+
+ void InitializeStatisticsRecorder() {
+ statistics_recorder_ = new StatisticsRecorder();
+ }
+
+ void UninitializeStatisticsRecorder() {
+ delete statistics_recorder_;
+ statistics_recorder_ = NULL;
+ }
+
+ StatisticsRecorder* statistics_recorder_;
+};
+
+// Check for basic syntax and use.
+TEST_F(HistogramTest, BasicTest) {
+ // Try basic construction
+ Histogram* histogram(Histogram::FactoryGet(
+ "TestHistogram", 1, 1000, 10, Histogram::kNoFlags));
+ EXPECT_NE(reinterpret_cast<Histogram*>(NULL), histogram);
+
+ Histogram* linear_histogram(LinearHistogram::FactoryGet(
+ "TestLinearHistogram", 1, 1000, 10, Histogram::kNoFlags));
+ EXPECT_NE(reinterpret_cast<Histogram*>(NULL), linear_histogram);
+
+ vector<int> custom_ranges;
+ custom_ranges.push_back(1);
+ custom_ranges.push_back(5);
+ Histogram* custom_histogram(CustomHistogram::FactoryGet(
+ "TestCustomHistogram", custom_ranges, Histogram::kNoFlags));
+ EXPECT_NE(reinterpret_cast<Histogram*>(NULL), custom_histogram);
+
+ // Use standard macros (but with fixed samples)
+ HISTOGRAM_TIMES("Test2Histogram", TimeDelta::FromDays(1));
+ HISTOGRAM_COUNTS("Test3Histogram", 30);
+
+ DHISTOGRAM_TIMES("Test4Histogram", TimeDelta::FromDays(1));
+ DHISTOGRAM_COUNTS("Test5Histogram", 30);
+
+ HISTOGRAM_ENUMERATION("Test6Histogram", 129, 130);
+}
+
+// Check that the macro correctly matches histograms by name and records their
+// data together.
+TEST_F(HistogramTest, NameMatchTest) {
+ HISTOGRAM_PERCENTAGE("DuplicatedHistogram", 10);
+ HISTOGRAM_PERCENTAGE("DuplicatedHistogram", 10);
+ Histogram* histogram(LinearHistogram::FactoryGet(
+ "DuplicatedHistogram", 1, 101, 102, Histogram::kNoFlags));
+ scoped_ptr<SampleVector> samples = histogram->SnapshotSampleVector();
+ EXPECT_EQ(2, samples->TotalCount());
+ EXPECT_EQ(2, samples->GetCountAtIndex(10));
+}
+
+TEST_F(HistogramTest, ExponentialRangesTest) {
+ // Check that we got a nice exponential when there was enough rooom.
+ BucketRanges ranges(9);
+ Histogram::InitializeBucketRanges(1, 64, 8, &ranges);
+ EXPECT_EQ(0, ranges.range(0));
+ int power_of_2 = 1;
+ for (int i = 1; i < 8; i++) {
+ EXPECT_EQ(power_of_2, ranges.range(i));
+ power_of_2 *= 2;
+ }
+ EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges.range(8));
+
+ // Check the corresponding Histogram will use the correct ranges.
+ Histogram* histogram(Histogram::FactoryGet(
+ "Histogram", 1, 64, 8, Histogram::kNoFlags));
+ EXPECT_TRUE(ranges.Equals(histogram->bucket_ranges()));
+
+ // When bucket count is limited, exponential ranges will partially look like
+ // linear.
+ BucketRanges ranges2(16);
+ Histogram::InitializeBucketRanges(1, 32, 15, &ranges2);
+
+ EXPECT_EQ(0, ranges2.range(0));
+ EXPECT_EQ(1, ranges2.range(1));
+ EXPECT_EQ(2, ranges2.range(2));
+ EXPECT_EQ(3, ranges2.range(3));
+ EXPECT_EQ(4, ranges2.range(4));
+ EXPECT_EQ(5, ranges2.range(5));
+ EXPECT_EQ(6, ranges2.range(6));
+ EXPECT_EQ(7, ranges2.range(7));
+ EXPECT_EQ(9, ranges2.range(8));
+ EXPECT_EQ(11, ranges2.range(9));
+ EXPECT_EQ(14, ranges2.range(10));
+ EXPECT_EQ(17, ranges2.range(11));
+ EXPECT_EQ(21, ranges2.range(12));
+ EXPECT_EQ(26, ranges2.range(13));
+ EXPECT_EQ(32, ranges2.range(14));
+ EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges2.range(15));
+
+ // Check the corresponding Histogram will use the correct ranges.
+ Histogram* histogram2(Histogram::FactoryGet(
+ "Histogram2", 1, 32, 15, Histogram::kNoFlags));
+ EXPECT_TRUE(ranges2.Equals(histogram2->bucket_ranges()));
+}
+
+TEST_F(HistogramTest, LinearRangesTest) {
+ BucketRanges ranges(9);
+ LinearHistogram::InitializeBucketRanges(1, 7, 8, &ranges);
+ // Gets a nice linear set of bucket ranges.
+ for (int i = 0; i < 8; i++)
+ EXPECT_EQ(i, ranges.range(i));
+ EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges.range(8));
+ // The correspoding LinearHistogram should use the correct ranges.
+ Histogram* histogram(
+ LinearHistogram::FactoryGet("Linear", 1, 7, 8, Histogram::kNoFlags));
+ EXPECT_TRUE(ranges.Equals(histogram->bucket_ranges()));
+
+ // Linear ranges are not divisible.
+ BucketRanges ranges2(6);
+ LinearHistogram::InitializeBucketRanges(1, 6, 5, &ranges2);
+ EXPECT_EQ(0, ranges2.range(0));
+ EXPECT_EQ(1, ranges2.range(1));
+ EXPECT_EQ(3, ranges2.range(2));
+ EXPECT_EQ(4, ranges2.range(3));
+ EXPECT_EQ(6, ranges2.range(4));
+ EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges2.range(5));
+ // The correspoding LinearHistogram should use the correct ranges.
+ Histogram* histogram2(
+ LinearHistogram::FactoryGet("Linear2", 1, 6, 5, Histogram::kNoFlags));
+ EXPECT_TRUE(ranges2.Equals(histogram2->bucket_ranges()));
+}
+
+TEST_F(HistogramTest, ArrayToCustomRangesTest) {
+ const HistogramBase::Sample ranges[3] = {5, 10, 20};
+ vector<HistogramBase::Sample> ranges_vec =
+ CustomHistogram::ArrayToCustomRanges(ranges, 3);
+ ASSERT_EQ(6u, ranges_vec.size());
+ EXPECT_EQ(5, ranges_vec[0]);
+ EXPECT_EQ(6, ranges_vec[1]);
+ EXPECT_EQ(10, ranges_vec[2]);
+ EXPECT_EQ(11, ranges_vec[3]);
+ EXPECT_EQ(20, ranges_vec[4]);
+ EXPECT_EQ(21, ranges_vec[5]);
+}
+
+TEST_F(HistogramTest, CustomHistogramTest) {
+ // A well prepared custom ranges.
+ vector<HistogramBase::Sample> custom_ranges;
+ custom_ranges.push_back(1);
+ custom_ranges.push_back(2);
+ Histogram* histogram = CustomHistogram::FactoryGet(
+ "TestCustomHistogram1", custom_ranges, Histogram::kNoFlags);
+ const BucketRanges* ranges = histogram->bucket_ranges();
+ ASSERT_EQ(4u, ranges->size());
+ EXPECT_EQ(0, ranges->range(0)); // Auto added.
+ EXPECT_EQ(1, ranges->range(1));
+ EXPECT_EQ(2, ranges->range(2));
+ EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(3)); // Auto added.
+
+ // A unordered custom ranges.
+ custom_ranges.clear();
+ custom_ranges.push_back(2);
+ custom_ranges.push_back(1);
+ histogram = CustomHistogram::FactoryGet(
+ "TestCustomHistogram2", custom_ranges, Histogram::kNoFlags);
+ ranges = histogram->bucket_ranges();
+ ASSERT_EQ(4u, ranges->size());
+ EXPECT_EQ(0, ranges->range(0));
+ EXPECT_EQ(1, ranges->range(1));
+ EXPECT_EQ(2, ranges->range(2));
+ EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(3));
+
+ // A custom ranges with duplicated values.
+ custom_ranges.clear();
+ custom_ranges.push_back(4);
+ custom_ranges.push_back(1);
+ custom_ranges.push_back(4);
+ histogram = CustomHistogram::FactoryGet(
+ "TestCustomHistogram3", custom_ranges, Histogram::kNoFlags);
+ ranges = histogram->bucket_ranges();
+ ASSERT_EQ(4u, ranges->size());
+ EXPECT_EQ(0, ranges->range(0));
+ EXPECT_EQ(1, ranges->range(1));
+ EXPECT_EQ(4, ranges->range(2));
+ EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(3));
+}
+
+TEST_F(HistogramTest, CustomHistogramWithOnly2Buckets) {
+ // This test exploits the fact that the CustomHistogram can have 2 buckets,
+ // while the base class Histogram is *supposed* to have at least 3 buckets.
+ // We should probably change the restriction on the base class (or not inherit
+ // the base class!).
+
+ vector<HistogramBase::Sample> custom_ranges;
+ custom_ranges.push_back(4);
+
+ Histogram* histogram = CustomHistogram::FactoryGet(
+ "2BucketsCustomHistogram", custom_ranges, Histogram::kNoFlags);
+ const BucketRanges* ranges = histogram->bucket_ranges();
+ ASSERT_EQ(3u, ranges->size());
+ EXPECT_EQ(0, ranges->range(0));
+ EXPECT_EQ(4, ranges->range(1));
+ EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(2));
+}
+
+// Make sure histogram handles out-of-bounds data gracefully.
+TEST_F(HistogramTest, BoundsTest) {
+ const size_t kBucketCount = 50;
+ Histogram* histogram(Histogram::FactoryGet(
+ "Bounded", 10, 100, kBucketCount, Histogram::kNoFlags));
+
+ // Put two samples "out of bounds" above and below.
+ histogram->Add(5);
+ histogram->Add(-50);
+
+ histogram->Add(100);
+ histogram->Add(10000);
+
+ // Verify they landed in the underflow, and overflow buckets.
+ scoped_ptr<SampleVector> samples = histogram->SnapshotSampleVector();
+ EXPECT_EQ(2, samples->GetCountAtIndex(0));
+ EXPECT_EQ(0, samples->GetCountAtIndex(1));
+ size_t array_size = histogram->bucket_count();
+ EXPECT_EQ(kBucketCount, array_size);
+ EXPECT_EQ(0, samples->GetCountAtIndex(array_size - 2));
+ EXPECT_EQ(2, samples->GetCountAtIndex(array_size - 1));
+
+ vector<int> custom_ranges;
+ custom_ranges.push_back(10);
+ custom_ranges.push_back(50);
+ custom_ranges.push_back(100);
+ Histogram* test_custom_histogram(CustomHistogram::FactoryGet(
+ "TestCustomRangeBoundedHistogram", custom_ranges, Histogram::kNoFlags));
+
+ // Put two samples "out of bounds" above and below.
+ test_custom_histogram->Add(5);
+ test_custom_histogram->Add(-50);
+ test_custom_histogram->Add(100);
+ test_custom_histogram->Add(1000);
+ test_custom_histogram->Add(INT_MAX);
+
+ // Verify they landed in the underflow, and overflow buckets.
+ scoped_ptr<SampleVector> custom_samples =
+ test_custom_histogram->SnapshotSampleVector();
+ EXPECT_EQ(2, custom_samples->GetCountAtIndex(0));
+ EXPECT_EQ(0, custom_samples->GetCountAtIndex(1));
+ size_t bucket_count = test_custom_histogram->bucket_count();
+ EXPECT_EQ(0, custom_samples->GetCountAtIndex(bucket_count - 2));
+ EXPECT_EQ(3, custom_samples->GetCountAtIndex(bucket_count - 1));
+}
+
+// Check to be sure samples land as expected is "correct" buckets.
+TEST_F(HistogramTest, BucketPlacementTest) {
+ Histogram* histogram(Histogram::FactoryGet(
+ "Histogram", 1, 64, 8, Histogram::kNoFlags));
+
+ // Add i+1 samples to the i'th bucket.
+ histogram->Add(0);
+ int power_of_2 = 1;
+ for (int i = 1; i < 8; i++) {
+ for (int j = 0; j <= i; j++)
+ histogram->Add(power_of_2);
+ power_of_2 *= 2;
+ }
+
+ // Check to see that the bucket counts reflect our additions.
+ scoped_ptr<SampleVector> samples = histogram->SnapshotSampleVector();
+ for (int i = 0; i < 8; i++)
+ EXPECT_EQ(i + 1, samples->GetCountAtIndex(i));
+}
+
+TEST_F(HistogramTest, CorruptSampleCounts) {
+ Histogram* histogram(Histogram::FactoryGet(
+ "Histogram", 1, 64, 8, Histogram::kNoFlags)); // As per header file.
+
+ // Add some samples.
+ histogram->Add(20);
+ histogram->Add(40);
+
+ scoped_ptr<SampleVector> snapshot = histogram->SnapshotSampleVector();
+ EXPECT_EQ(Histogram::NO_INCONSISTENCIES,
+ histogram->FindCorruption(*snapshot));
+ EXPECT_EQ(2, snapshot->redundant_count());
+ EXPECT_EQ(2, snapshot->TotalCount());
+
+ snapshot->counts_[3] += 100; // Sample count won't match redundant count.
+ EXPECT_EQ(Histogram::COUNT_LOW_ERROR,
+ histogram->FindCorruption(*snapshot));
+ snapshot->counts_[2] -= 200;
+ EXPECT_EQ(Histogram::COUNT_HIGH_ERROR,
+ histogram->FindCorruption(*snapshot));
+
+ // But we can't spot a corruption if it is compensated for.
+ snapshot->counts_[1] += 100;
+ EXPECT_EQ(Histogram::NO_INCONSISTENCIES,
+ histogram->FindCorruption(*snapshot));
+}
+
+TEST_F(HistogramTest, CorruptBucketBounds) {
+ Histogram* histogram(Histogram::FactoryGet(
+ "Histogram", 1, 64, 8, Histogram::kNoFlags)); // As per header file.
+
+ scoped_ptr<SampleVector> snapshot = histogram->SnapshotSampleVector();
+ EXPECT_EQ(Histogram::NO_INCONSISTENCIES,
+ histogram->FindCorruption(*snapshot));
+
+ BucketRanges* bucket_ranges =
+ const_cast<BucketRanges*>(histogram->bucket_ranges());
+ HistogramBase::Sample tmp = bucket_ranges->range(1);
+ bucket_ranges->set_range(1, bucket_ranges->range(2));
+ bucket_ranges->set_range(2, tmp);
+ EXPECT_EQ(Histogram::BUCKET_ORDER_ERROR | Histogram::RANGE_CHECKSUM_ERROR,
+ histogram->FindCorruption(*snapshot));
+
+ bucket_ranges->set_range(2, bucket_ranges->range(1));
+ bucket_ranges->set_range(1, tmp);
+ EXPECT_EQ(0, histogram->FindCorruption(*snapshot));
+
+ // Show that two simple changes don't offset each other
+ bucket_ranges->set_range(3, bucket_ranges->range(3) + 1);
+ EXPECT_EQ(Histogram::RANGE_CHECKSUM_ERROR,
+ histogram->FindCorruption(*snapshot));
+
+ bucket_ranges->set_range(4, bucket_ranges->range(4) - 1);
+ EXPECT_EQ(Histogram::RANGE_CHECKSUM_ERROR,
+ histogram->FindCorruption(*snapshot));
+
+ // Repair histogram so that destructor won't DCHECK().
+ bucket_ranges->set_range(3, bucket_ranges->range(3) - 1);
+ bucket_ranges->set_range(4, bucket_ranges->range(4) + 1);
+}
+
+#if GTEST_HAS_DEATH_TEST
+// For Histogram, LinearHistogram and CustomHistogram, the minimum for a
+// declared range is 1, while the maximum is (HistogramBase::kSampleType_MAX -
+// 1). But we accept ranges exceeding those limits, and silently clamped to
+// those limits. This is for backwards compatibility.
+TEST(HistogramDeathTest, BadRangesTest) {
+ Histogram* histogram = Histogram::FactoryGet(
+ "BadRanges", 0, HistogramBase::kSampleType_MAX, 8, Histogram::kNoFlags);
+ EXPECT_EQ(1, histogram->declared_min());
+ EXPECT_EQ(HistogramBase::kSampleType_MAX - 1, histogram->declared_max());
+
+ Histogram* linear_histogram = LinearHistogram::FactoryGet(
+ "BadRangesLinear", 0, HistogramBase::kSampleType_MAX, 8,
+ Histogram::kNoFlags);
+ EXPECT_EQ(1, linear_histogram->declared_min());
+ EXPECT_EQ(HistogramBase::kSampleType_MAX - 1,
+ linear_histogram->declared_max());
+
+ vector<int> custom_ranges;
+ custom_ranges.push_back(0);
+ custom_ranges.push_back(5);
+ Histogram* custom_histogram1 = CustomHistogram::FactoryGet(
+ "BadRangesCustom", custom_ranges, Histogram::kNoFlags);
+ const BucketRanges* ranges = custom_histogram1->bucket_ranges();
+ ASSERT_EQ(3u, ranges->size());
+ EXPECT_EQ(0, ranges->range(0));
+ EXPECT_EQ(5, ranges->range(1));
+ EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(2));
+
+ // CustomHistogram does not accepts kSampleType_MAX as range.
+ custom_ranges.push_back(HistogramBase::kSampleType_MAX);
+ EXPECT_DEATH(CustomHistogram::FactoryGet("BadRangesCustom2", custom_ranges,
+ Histogram::kNoFlags),
+ "");
+
+ // CustomHistogram needs at least 1 valid range.
+ custom_ranges.clear();
+ custom_ranges.push_back(0);
+ EXPECT_DEATH(CustomHistogram::FactoryGet("BadRangesCustom3", custom_ranges,
+ Histogram::kNoFlags),
+ "");
+}
+#endif
+
+} // namespace base
diff --git a/src/base/metrics/sample_map.cc b/src/base/metrics/sample_map.cc
new file mode 100644
index 0000000..9bd78e7
--- /dev/null
+++ b/src/base/metrics/sample_map.cc
@@ -0,0 +1,91 @@
+// Copyright (c) 2012 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/metrics/sample_map.h"
+
+#include "base/logging.h"
+
+using std::map;
+
+namespace base {
+
+typedef HistogramBase::Count Count;
+typedef HistogramBase::Sample Sample;
+
+SampleMap::SampleMap() {}
+
+SampleMap::~SampleMap() {}
+
+void SampleMap::Accumulate(Sample value, Count count) {
+ sample_counts_[value] += count;
+ IncreaseSum(count * value);
+ IncreaseRedundantCount(count);
+}
+
+Count SampleMap::GetCount(Sample value) const {
+ map<Sample, Count>::const_iterator it = sample_counts_.find(value);
+ if (it == sample_counts_.end())
+ return 0;
+ return it->second;
+}
+
+Count SampleMap::TotalCount() const {
+ Count count = 0;
+ for (map<Sample, Count>::const_iterator it = sample_counts_.begin();
+ it != sample_counts_.end();
+ ++it) {
+ count += it->second;
+ }
+ return count;
+}
+
+scoped_ptr<SampleCountIterator> SampleMap::Iterator() const {
+ return scoped_ptr<SampleCountIterator>(new SampleMapIterator(sample_counts_));
+}
+
+void SampleMap::ResetRedundantCount(Count count) {
+ IncreaseRedundantCount(-redundant_count());
+ IncreaseRedundantCount(count);
+}
+
+bool SampleMap::AddSubtractImpl(SampleCountIterator* iter,
+ HistogramSamples::Operator op) {
+ Sample min;
+ Sample max;
+ Count count;
+ for (; !iter->Done(); iter->Next()) {
+ iter->Get(&min, &max, &count);
+ if (min + 1 != max)
+ return false; // SparseHistogram only supports bucket with size 1.
+ sample_counts_[min] += (op == HistogramSamples::ADD) ? count : -count;
+ }
+ return true;
+}
+
+SampleMapIterator::SampleMapIterator(const SampleToCountMap& sample_counts)
+ : iter_(sample_counts.begin()),
+ end_(sample_counts.end()) {}
+
+SampleMapIterator::~SampleMapIterator() {}
+
+bool SampleMapIterator::Done() const {
+ return iter_ == end_;
+}
+
+void SampleMapIterator::Next() {
+ DCHECK(!Done());
+ iter_++;
+}
+
+void SampleMapIterator::Get(Sample* min, Sample* max, Count* count) const {
+ DCHECK(!Done());
+ if (min != NULL)
+ *min = iter_->first;
+ if (max != NULL)
+ *max = iter_->first + 1;
+ if (count != NULL)
+ *count = iter_->second;
+}
+
+} // namespace base
diff --git a/src/base/metrics/sample_map.h b/src/base/metrics/sample_map.h
new file mode 100644
index 0000000..65a31f1
--- /dev/null
+++ b/src/base/metrics/sample_map.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 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.
+
+// SampleMap implements HistogramSamples interface. It is used by the
+// SparseHistogram class to store samples.
+
+#ifndef BASE_METRICS_SAMPLE_MAP_H_
+#define BASE_METRICS_SAMPLE_MAP_H_
+
+#include <map>
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_samples.h"
+
+namespace base {
+
+class BASE_EXPORT_PRIVATE SampleMap : public HistogramSamples {
+ public:
+ SampleMap();
+ virtual ~SampleMap();
+
+ // HistogramSamples implementation:
+ virtual void Accumulate(HistogramBase::Sample value,
+ HistogramBase::Count count) OVERRIDE;
+ virtual HistogramBase::Count GetCount(
+ HistogramBase::Sample value) const OVERRIDE;
+ virtual HistogramBase::Count TotalCount() const OVERRIDE;
+ virtual scoped_ptr<SampleCountIterator> Iterator() const OVERRIDE;
+
+ void ResetRedundantCount(HistogramBase::Count count);
+
+ protected:
+ virtual bool AddSubtractImpl(
+ SampleCountIterator* iter,
+ HistogramSamples::Operator op) OVERRIDE; // |op| is ADD or SUBTRACT.
+
+ private:
+ std::map<HistogramBase::Sample, HistogramBase::Count> sample_counts_;
+
+ DISALLOW_COPY_AND_ASSIGN(SampleMap);
+};
+
+class BASE_EXPORT_PRIVATE SampleMapIterator : public SampleCountIterator {
+ public:
+ typedef std::map<HistogramBase::Sample, HistogramBase::Count>
+ SampleToCountMap;
+
+ SampleMapIterator(const SampleToCountMap& sample_counts);
+ virtual ~SampleMapIterator();
+
+ // SampleCountIterator implementation:
+ virtual bool Done() const OVERRIDE;
+ virtual void Next() OVERRIDE;
+ virtual void Get(HistogramBase::Sample* min,
+ HistogramBase::Sample* max,
+ HistogramBase::Count* count) const OVERRIDE;
+ private:
+ SampleToCountMap::const_iterator iter_;
+ const SampleToCountMap::const_iterator end_;
+};
+
+} // namespace base
+
+#endif // BASE_METRICS_SAMPLE_MAP_H_
diff --git a/src/base/metrics/sample_map_unittest.cc b/src/base/metrics/sample_map_unittest.cc
new file mode 100644
index 0000000..1a53ee7
--- /dev/null
+++ b/src/base/metrics/sample_map_unittest.cc
@@ -0,0 +1,123 @@
+// Copyright (c) 2012 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/memory/scoped_ptr.h"
+#include "base/metrics/sample_map.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+TEST(SampleMapTest, AccumulateTest) {
+ SampleMap samples;
+
+ samples.Accumulate(1, 100);
+ samples.Accumulate(2, 200);
+ samples.Accumulate(1, -200);
+ EXPECT_EQ(-100, samples.GetCount(1));
+ EXPECT_EQ(200, samples.GetCount(2));
+
+ EXPECT_EQ(300, samples.sum());
+ EXPECT_EQ(100, samples.TotalCount());
+ EXPECT_EQ(samples.redundant_count(), samples.TotalCount());
+}
+
+TEST(SampleMapTest, AddSubtractTest) {
+ SampleMap samples1;
+ SampleMap samples2;
+
+ samples1.Accumulate(1, 100);
+ samples1.Accumulate(2, 100);
+ samples1.Accumulate(3, 100);
+
+ samples2.Accumulate(1, 200);
+ samples2.Accumulate(2, 200);
+ samples2.Accumulate(4, 200);
+
+ samples1.Add(samples2);
+ EXPECT_EQ(300, samples1.GetCount(1));
+ EXPECT_EQ(300, samples1.GetCount(2));
+ EXPECT_EQ(100, samples1.GetCount(3));
+ EXPECT_EQ(200, samples1.GetCount(4));
+ EXPECT_EQ(2000, samples1.sum());
+ EXPECT_EQ(900, samples1.TotalCount());
+ EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount());
+
+ samples1.Subtract(samples2);
+ EXPECT_EQ(100, samples1.GetCount(1));
+ EXPECT_EQ(100, samples1.GetCount(2));
+ EXPECT_EQ(100, samples1.GetCount(3));
+ EXPECT_EQ(0, samples1.GetCount(4));
+ EXPECT_EQ(600, samples1.sum());
+ EXPECT_EQ(300, samples1.TotalCount());
+ EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount());
+}
+
+TEST(SampleMapIteratorTest, IterateTest) {
+ SampleMap samples;
+ samples.Accumulate(1, 100);
+ samples.Accumulate(2, 200);
+ samples.Accumulate(4, -300);
+ samples.Accumulate(5, 0);
+
+ scoped_ptr<SampleCountIterator> it = samples.Iterator();
+
+ HistogramBase::Sample min;
+ HistogramBase::Sample max;
+ HistogramBase::Count count;
+
+ it->Get(&min, &max, &count);
+ EXPECT_EQ(1, min);
+ EXPECT_EQ(2, max);
+ EXPECT_EQ(100, count);
+ EXPECT_FALSE(it->GetBucketIndex(NULL));
+
+ it->Next();
+ it->Get(&min, &max, &count);
+ EXPECT_EQ(2, min);
+ EXPECT_EQ(3, max);
+ EXPECT_EQ(200, count);
+
+ it->Next();
+ it->Get(&min, &max, &count);
+ EXPECT_EQ(4, min);
+ EXPECT_EQ(5, max);
+ EXPECT_EQ(-300, count);
+
+ it->Next();
+ it->Get(&min, &max, &count);
+ EXPECT_EQ(5, min);
+ EXPECT_EQ(6, max);
+ EXPECT_EQ(0, count);
+
+ it->Next();
+ EXPECT_TRUE(it->Done());
+}
+
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST
+
+TEST(SampleMapIteratorDeathTest, IterateDoneTest) {
+ SampleMap samples;
+
+ scoped_ptr<SampleCountIterator> it = samples.Iterator();
+
+ EXPECT_TRUE(it->Done());
+
+ HistogramBase::Sample min;
+ HistogramBase::Sample max;
+ HistogramBase::Count count;
+ EXPECT_DEATH(it->Get(&min, &max, &count), "");
+
+ EXPECT_DEATH(it->Next(), "");
+
+ samples.Accumulate(1, 100);
+ it = samples.Iterator();
+ EXPECT_FALSE(it->Done());
+}
+
+#endif
+// (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST
+
+} // namespace
+} // namespace base
diff --git a/src/base/metrics/sample_vector.cc b/src/base/metrics/sample_vector.cc
new file mode 100644
index 0000000..8f14510
--- /dev/null
+++ b/src/base/metrics/sample_vector.cc
@@ -0,0 +1,161 @@
+// Copyright (c) 2012 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/metrics/sample_vector.h"
+
+#include "base/logging.h"
+#include "base/metrics/bucket_ranges.h"
+
+using std::vector;
+
+namespace base {
+
+typedef HistogramBase::Count Count;
+typedef HistogramBase::Sample Sample;
+
+SampleVector::SampleVector(const BucketRanges* bucket_ranges)
+ : counts_(bucket_ranges->size() - 1),
+ bucket_ranges_(bucket_ranges) {
+ CHECK_GE(bucket_ranges_->size(), 2u);
+}
+
+SampleVector::~SampleVector() {}
+
+void SampleVector::Accumulate(Sample value, Count count) {
+ size_t bucket_index = GetBucketIndex(value);
+ counts_[bucket_index] += count;
+ IncreaseSum(count * value);
+ IncreaseRedundantCount(count);
+}
+
+Count SampleVector::GetCount(Sample value) const {
+ size_t bucket_index = GetBucketIndex(value);
+ return counts_[bucket_index];
+}
+
+Count SampleVector::TotalCount() const {
+ Count count = 0;
+ for (size_t i = 0; i < counts_.size(); i++) {
+ count += counts_[i];
+ }
+ return count;
+}
+
+Count SampleVector::GetCountAtIndex(size_t bucket_index) const {
+ DCHECK(bucket_index < counts_.size());
+ return counts_[bucket_index];
+}
+
+scoped_ptr<SampleCountIterator> SampleVector::Iterator() const {
+ return scoped_ptr<SampleCountIterator>(
+ new SampleVectorIterator(&counts_, bucket_ranges_));
+}
+
+bool SampleVector::AddSubtractImpl(SampleCountIterator* iter,
+ HistogramSamples::Operator op) {
+ HistogramBase::Sample min;
+ HistogramBase::Sample max;
+ HistogramBase::Count count;
+
+ // Go through the iterator and add the counts into correct bucket.
+ size_t index = 0;
+ while (index < counts_.size() && !iter->Done()) {
+ iter->Get(&min, &max, &count);
+ if (min == bucket_ranges_->range(index) &&
+ max == bucket_ranges_->range(index + 1)) {
+ // Sample matches this bucket!
+ counts_[index] += (op == HistogramSamples::ADD) ? count : -count;
+ iter->Next();
+ } else if (min > bucket_ranges_->range(index)) {
+ // Sample is larger than current bucket range. Try next.
+ index++;
+ } else {
+ // Sample is smaller than current bucket range. We scan buckets from
+ // smallest to largest, so the sample value must be invalid.
+ return false;
+ }
+ }
+
+ return iter->Done();
+}
+
+// Use simple binary search. This is very general, but there are better
+// approaches if we knew that the buckets were linearly distributed.
+size_t SampleVector::GetBucketIndex(Sample value) const {
+ size_t bucket_count = bucket_ranges_->size() - 1;
+ CHECK_GE(bucket_count, 1u);
+ CHECK_GE(value, bucket_ranges_->range(0));
+ CHECK_LT(value, bucket_ranges_->range(bucket_count));
+
+ size_t under = 0;
+ size_t over = bucket_count;
+ size_t mid;
+ do {
+ DCHECK_GE(over, under);
+ mid = under + (over - under)/2;
+ if (mid == under)
+ break;
+ if (bucket_ranges_->range(mid) <= value)
+ under = mid;
+ else
+ over = mid;
+ } while (true);
+
+ DCHECK_LE(bucket_ranges_->range(mid), value);
+ CHECK_GT(bucket_ranges_->range(mid + 1), value);
+ return mid;
+}
+
+SampleVectorIterator::SampleVectorIterator(const vector<Count>* counts,
+ const BucketRanges* bucket_ranges)
+ : counts_(counts),
+ bucket_ranges_(bucket_ranges),
+ index_(0) {
+ CHECK_GT(bucket_ranges_->size(), counts_->size());
+ SkipEmptyBuckets();
+}
+
+SampleVectorIterator::~SampleVectorIterator() {}
+
+bool SampleVectorIterator::Done() const {
+ return index_ >= counts_->size();
+}
+
+void SampleVectorIterator::Next() {
+ DCHECK(!Done());
+ index_++;
+ SkipEmptyBuckets();
+}
+
+void SampleVectorIterator::Get(HistogramBase::Sample* min,
+ HistogramBase::Sample* max,
+ HistogramBase::Count* count) const {
+ DCHECK(!Done());
+ if (min != NULL)
+ *min = bucket_ranges_->range(index_);
+ if (max != NULL)
+ *max = bucket_ranges_->range(index_ + 1);
+ if (count != NULL)
+ *count = (*counts_)[index_];
+}
+
+bool SampleVectorIterator::GetBucketIndex(size_t* index) const {
+ DCHECK(!Done());
+ if (index != NULL)
+ *index = index_;
+ return true;
+}
+
+void SampleVectorIterator::SkipEmptyBuckets() {
+ if (Done())
+ return;
+
+ while (index_ < counts_->size()) {
+ if ((*counts_)[index_] != 0)
+ return;
+ index_++;
+ }
+}
+
+} // namespace base
diff --git a/src/base/metrics/sample_vector.h b/src/base/metrics/sample_vector.h
new file mode 100644
index 0000000..67c344a
--- /dev/null
+++ b/src/base/metrics/sample_vector.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2012 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.
+
+// SampleVector implements HistogramSamples interface. It is used by all
+// Histogram based classes to store samples.
+
+#ifndef BASE_METRICS_SAMPLE_VECTOR_H_
+#define BASE_METRICS_SAMPLE_VECTOR_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_samples.h"
+
+namespace base {
+
+class BucketRanges;
+
+class BASE_EXPORT_PRIVATE SampleVector : public HistogramSamples {
+ public:
+ explicit SampleVector(const BucketRanges* bucket_ranges);
+ virtual ~SampleVector();
+
+ // HistogramSamples implementation:
+ virtual void Accumulate(HistogramBase::Sample value,
+ HistogramBase::Count count) OVERRIDE;
+ virtual HistogramBase::Count GetCount(
+ HistogramBase::Sample value) const OVERRIDE;
+ virtual HistogramBase::Count TotalCount() const OVERRIDE;
+ virtual scoped_ptr<SampleCountIterator> Iterator() const OVERRIDE;
+
+ // Get count of a specific bucket.
+ HistogramBase::Count GetCountAtIndex(size_t bucket_index) const;
+
+ protected:
+ virtual bool AddSubtractImpl(
+ SampleCountIterator* iter,
+ HistogramSamples::Operator op) OVERRIDE; // |op| is ADD or SUBTRACT.
+
+ virtual size_t GetBucketIndex(HistogramBase::Sample value) const;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts);
+
+ std::vector<HistogramBase::Count> counts_;
+
+ // Shares the same BucketRanges with Histogram object.
+ const BucketRanges* const bucket_ranges_;
+
+ DISALLOW_COPY_AND_ASSIGN(SampleVector);
+};
+
+class BASE_EXPORT_PRIVATE SampleVectorIterator : public SampleCountIterator {
+ public:
+ SampleVectorIterator(const std::vector<HistogramBase::Count>* counts,
+ const BucketRanges* bucket_ranges);
+ virtual ~SampleVectorIterator();
+
+ // SampleCountIterator implementation:
+ virtual bool Done() const OVERRIDE;
+ virtual void Next() OVERRIDE;
+ virtual void Get(HistogramBase::Sample* min,
+ HistogramBase::Sample* max,
+ HistogramBase::Count* count) const OVERRIDE;
+
+ // SampleVector uses predefined buckets, so iterator can return bucket index.
+ virtual bool GetBucketIndex(size_t* index) const OVERRIDE;
+
+ private:
+ void SkipEmptyBuckets();
+
+ const std::vector<HistogramBase::Count>* counts_;
+ const BucketRanges* bucket_ranges_;
+
+ size_t index_;
+};
+
+} // namespace base
+
+#endif // BASE_METRICS_SAMPLE_VECTOR_H_
diff --git a/src/base/metrics/sample_vector_unittest.cc b/src/base/metrics/sample_vector_unittest.cc
new file mode 100644
index 0000000..dbbbd15
--- /dev/null
+++ b/src/base/metrics/sample_vector_unittest.cc
@@ -0,0 +1,265 @@
+// Copyright (c) 2012 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 <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/bucket_ranges.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/sample_vector.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::vector;
+
+namespace base {
+namespace {
+
+TEST(SampleVectorTest, AccumulateTest) {
+ // Custom buckets: [1, 5) [5, 10)
+ BucketRanges ranges(3);
+ ranges.set_range(0, 1);
+ ranges.set_range(1, 5);
+ ranges.set_range(2, 10);
+ SampleVector samples(&ranges);
+
+ samples.Accumulate(1, 200);
+ samples.Accumulate(2, -300);
+ EXPECT_EQ(-100, samples.GetCountAtIndex(0));
+
+ samples.Accumulate(5, 200);
+ EXPECT_EQ(200, samples.GetCountAtIndex(1));
+
+ EXPECT_EQ(600, samples.sum());
+ EXPECT_EQ(100, samples.redundant_count());
+ EXPECT_EQ(samples.TotalCount(), samples.redundant_count());
+
+ samples.Accumulate(5, -100);
+ EXPECT_EQ(100, samples.GetCountAtIndex(1));
+
+ EXPECT_EQ(100, samples.sum());
+ EXPECT_EQ(0, samples.redundant_count());
+ EXPECT_EQ(samples.TotalCount(), samples.redundant_count());
+}
+
+TEST(SampleVectorTest, AddSubtractTest) {
+ // Custom buckets: [0, 1) [1, 2) [2, 3) [3, INT_MAX)
+ BucketRanges ranges(5);
+ ranges.set_range(0, 0);
+ ranges.set_range(1, 1);
+ ranges.set_range(2, 2);
+ ranges.set_range(3, 3);
+ ranges.set_range(4, INT_MAX);
+
+ SampleVector samples1(&ranges);
+ samples1.Accumulate(0, 100);
+ samples1.Accumulate(2, 100);
+ samples1.Accumulate(4, 100);
+ EXPECT_EQ(600, samples1.sum());
+ EXPECT_EQ(300, samples1.TotalCount());
+ EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount());
+
+ SampleVector samples2(&ranges);
+ samples2.Accumulate(1, 200);
+ samples2.Accumulate(2, 200);
+ samples2.Accumulate(4, 200);
+ EXPECT_EQ(1400, samples2.sum());
+ EXPECT_EQ(600, samples2.TotalCount());
+ EXPECT_EQ(samples2.redundant_count(), samples2.TotalCount());
+
+ samples1.Add(samples2);
+ EXPECT_EQ(100, samples1.GetCountAtIndex(0));
+ EXPECT_EQ(200, samples1.GetCountAtIndex(1));
+ EXPECT_EQ(300, samples1.GetCountAtIndex(2));
+ EXPECT_EQ(300, samples1.GetCountAtIndex(3));
+ EXPECT_EQ(2000, samples1.sum());
+ EXPECT_EQ(900, samples1.TotalCount());
+ EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount());
+
+ samples1.Subtract(samples2);
+ EXPECT_EQ(100, samples1.GetCountAtIndex(0));
+ EXPECT_EQ(0, samples1.GetCountAtIndex(1));
+ EXPECT_EQ(100, samples1.GetCountAtIndex(2));
+ EXPECT_EQ(100, samples1.GetCountAtIndex(3));
+ EXPECT_EQ(600, samples1.sum());
+ EXPECT_EQ(300, samples1.TotalCount());
+ EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount());
+}
+
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST
+TEST(SampleVectorDeathTest, BucketIndexTest) {
+ // 8 buckets with exponential layout:
+ // [0, 1) [1, 2) [2, 4) [4, 8) [8, 16) [16, 32) [32, 64) [64, INT_MAX)
+ BucketRanges ranges(9);
+ Histogram::InitializeBucketRanges(1, 64, 8, &ranges);
+ SampleVector samples(&ranges);
+
+ // Normal case
+ samples.Accumulate(0, 1);
+ samples.Accumulate(3, 2);
+ samples.Accumulate(64, 3);
+ EXPECT_EQ(1, samples.GetCount(0));
+ EXPECT_EQ(2, samples.GetCount(2));
+ EXPECT_EQ(3, samples.GetCount(65));
+
+ // Extreme case.
+ EXPECT_DEATH(samples.Accumulate(INT_MIN, 100), "");
+ EXPECT_DEATH(samples.Accumulate(-1, 100), "");
+ EXPECT_DEATH(samples.Accumulate(INT_MAX, 100), "");
+
+ // Custom buckets: [1, 5) [5, 10)
+ // Note, this is not a valid BucketRanges for Histogram because it does not
+ // have overflow buckets.
+ BucketRanges ranges2(3);
+ ranges2.set_range(0, 1);
+ ranges2.set_range(1, 5);
+ ranges2.set_range(2, 10);
+ SampleVector samples2(&ranges2);
+
+ // Normal case.
+ samples2.Accumulate(1, 1);
+ samples2.Accumulate(4, 1);
+ samples2.Accumulate(5, 2);
+ samples2.Accumulate(9, 2);
+ EXPECT_EQ(2, samples2.GetCount(1));
+ EXPECT_EQ(4, samples2.GetCount(5));
+
+ // Extreme case.
+ EXPECT_DEATH(samples2.Accumulate(0, 100), "");
+ EXPECT_DEATH(samples2.Accumulate(10, 100), "");
+}
+
+TEST(SampleVectorDeathTest, AddSubtractBucketNotMatchTest) {
+ // Custom buckets 1: [1, 3) [3, 5)
+ BucketRanges ranges1(3);
+ ranges1.set_range(0, 1);
+ ranges1.set_range(1, 3);
+ ranges1.set_range(2, 5);
+ SampleVector samples1(&ranges1);
+
+ // Custom buckets 2: [0, 1) [1, 3) [3, 6) [6, 7)
+ BucketRanges ranges2(5);
+ ranges2.set_range(0, 0);
+ ranges2.set_range(1, 1);
+ ranges2.set_range(2, 3);
+ ranges2.set_range(3, 6);
+ ranges2.set_range(4, 7);
+ SampleVector samples2(&ranges2);
+
+ samples2.Accumulate(1, 100);
+ samples1.Add(samples2);
+ EXPECT_EQ(100, samples1.GetCountAtIndex(0));
+
+ // Extra bucket in the beginning.
+ samples2.Accumulate(0, 100);
+ EXPECT_DEATH(samples1.Add(samples2), "");
+ EXPECT_DEATH(samples1.Subtract(samples2), "");
+
+ // Extra bucket in the end.
+ samples2.Accumulate(0, -100);
+ samples2.Accumulate(6, 100);
+ EXPECT_DEATH(samples1.Add(samples2), "");
+ EXPECT_DEATH(samples1.Subtract(samples2), "");
+
+ // Bucket not match: [3, 5) VS [3, 6)
+ samples2.Accumulate(6, -100);
+ samples2.Accumulate(3, 100);
+ EXPECT_DEATH(samples1.Add(samples2), "");
+ EXPECT_DEATH(samples1.Subtract(samples2), "");
+}
+
+#endif
+// (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST
+
+TEST(SampleVectorIteratorTest, IterateTest) {
+ BucketRanges ranges(5);
+ ranges.set_range(0, 0);
+ ranges.set_range(1, 1);
+ ranges.set_range(2, 2);
+ ranges.set_range(3, 3);
+ ranges.set_range(4, 4);
+
+ vector<HistogramBase::Count> counts(3);
+ counts[0] = 1;
+ counts[1] = 0; // Iterator will bypass this empty bucket.
+ counts[2] = 2;
+
+ // BucketRanges can have larger size than counts.
+ SampleVectorIterator it(&counts, &ranges);
+ size_t index;
+
+ HistogramBase::Sample min;
+ HistogramBase::Sample max;
+ HistogramBase::Count count;
+ it.Get(&min, &max, &count);
+ EXPECT_EQ(0, min);
+ EXPECT_EQ(1, max);
+ EXPECT_EQ(1, count);
+ EXPECT_TRUE(it.GetBucketIndex(&index));
+ EXPECT_EQ(0u, index);
+
+ it.Next();
+ it.Get(&min, &max, &count);
+ EXPECT_EQ(2, min);
+ EXPECT_EQ(3, max);
+ EXPECT_EQ(2, count);
+ EXPECT_TRUE(it.GetBucketIndex(&index));
+ EXPECT_EQ(2u, index);
+
+ it.Next();
+ EXPECT_TRUE(it.Done());
+
+ // Create iterator from SampleVector.
+ SampleVector samples(&ranges);
+ samples.Accumulate(0, 0);
+ samples.Accumulate(1, 1);
+ samples.Accumulate(2, 2);
+ samples.Accumulate(3, 3);
+ scoped_ptr<SampleCountIterator> it2 = samples.Iterator();
+
+ int i;
+ for (i = 1; !it2->Done(); i++, it2->Next()) {
+ it2->Get(&min, &max, &count);
+ EXPECT_EQ(i, min);
+ EXPECT_EQ(i + 1, max);
+ EXPECT_EQ(i, count);
+
+ size_t index;
+ EXPECT_TRUE(it2->GetBucketIndex(&index));
+ EXPECT_EQ(static_cast<size_t>(i), index);
+ }
+ EXPECT_EQ(4, i);
+}
+
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST
+
+TEST(SampleVectorIteratorDeathTest, IterateDoneTest) {
+ BucketRanges ranges(5);
+ ranges.set_range(0, 0);
+ ranges.set_range(1, 1);
+ ranges.set_range(2, 2);
+ ranges.set_range(3, 3);
+ ranges.set_range(4, INT_MAX);
+ SampleVector samples(&ranges);
+
+ scoped_ptr<SampleCountIterator> it = samples.Iterator();
+
+ EXPECT_TRUE(it->Done());
+
+ HistogramBase::Sample min;
+ HistogramBase::Sample max;
+ HistogramBase::Count count;
+ EXPECT_DEATH(it->Get(&min, &max, &count), "");
+
+ EXPECT_DEATH(it->Next(), "");
+
+ samples.Accumulate(2, 100);
+ it = samples.Iterator();
+ EXPECT_FALSE(it->Done());
+}
+
+#endif
+// (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST
+
+} // namespace
+} // namespace base
diff --git a/src/base/metrics/sparse_histogram.cc b/src/base/metrics/sparse_histogram.cc
new file mode 100644
index 0000000..169037d
--- /dev/null
+++ b/src/base/metrics/sparse_histogram.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2012 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/metrics/sparse_histogram.h"
+
+#include "base/metrics/sample_map.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/synchronization/lock.h"
+
+using std::map;
+using std::string;
+
+namespace base {
+
+typedef HistogramBase::Count Count;
+typedef HistogramBase::Sample Sample;
+
+// static
+HistogramBase* SparseHistogram::FactoryGet(const string& name, int32 flags) {
+ // TODO(kaiwang): Register and get SparseHistogram with StatisticsRecorder.
+ HistogramBase* histogram = new SparseHistogram(name);
+ histogram->SetFlags(flags);
+ return histogram;
+}
+
+SparseHistogram::~SparseHistogram() {}
+
+HistogramType SparseHistogram::GetHistogramType() const {
+ return SPARSE_HISTOGRAM;
+}
+
+bool SparseHistogram::HasConstructionArguments(Sample minimum,
+ Sample maximum,
+ size_t bucket_count) const {
+ // SparseHistogram never has min/max/bucket_count limit.
+ return false;
+}
+
+void SparseHistogram::Add(Sample value) {
+ base::AutoLock auto_lock(lock_);
+ sample_counts_[value]++;
+ redundant_count_ += 1;
+}
+
+scoped_ptr<HistogramSamples> SparseHistogram::SnapshotSamples() const {
+ scoped_ptr<SampleMap> snapshot(new SampleMap());
+
+ base::AutoLock auto_lock(lock_);
+ for(map<Sample, Count>::const_iterator it = sample_counts_.begin();
+ it != sample_counts_.end();
+ ++it) {
+ snapshot->Accumulate(it->first, it->second);
+ }
+ snapshot->ResetRedundantCount(redundant_count_);
+ return snapshot.PassAs<HistogramSamples>();
+}
+
+void SparseHistogram::WriteHTMLGraph(string* output) const {
+ // TODO(kaiwang): Implement.
+}
+
+void SparseHistogram::WriteAscii(string* output) const {
+ // TODO(kaiwang): Implement.
+}
+
+SparseHistogram::SparseHistogram(const string& name)
+ : HistogramBase(name),
+ redundant_count_(0) {}
+
+void SparseHistogram::GetParameters(DictionaryValue* params) const {
+ // TODO(kaiwang): Implement. (See HistogramBase::WriteJSON.)
+}
+
+void SparseHistogram::GetCountAndBucketData(Count* count,
+ ListValue* buckets) const {
+ // TODO(kaiwang): Implement. (See HistogramBase::WriteJSON.)
+}
+
+} // namespace base
diff --git a/src/base/metrics/sparse_histogram.h b/src/base/metrics/sparse_histogram.h
new file mode 100644
index 0000000..a7c5695
--- /dev/null
+++ b/src/base/metrics/sparse_histogram.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 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_METRICS_SPARSE_HISTOGRAM_H_
+#define BASE_METRICS_SPARSE_HISTOGRAM_H_
+
+#include <map>
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram_base.h"
+#include "base/synchronization/lock.h"
+
+namespace base {
+
+class HistogramSamples;
+
+class BASE_EXPORT_PRIVATE SparseHistogram : public HistogramBase {
+ public:
+ // If there's one with same name, return the existing one. If not, create a
+ // new one.
+ static HistogramBase* FactoryGet(const std::string& name, int32 flags);
+
+ virtual ~SparseHistogram();
+
+ // HistogramBase implementation:
+ virtual HistogramType GetHistogramType() const OVERRIDE;
+ virtual bool HasConstructionArguments(Sample minimum,
+ Sample maximum,
+ size_t bucket_count) const OVERRIDE;
+ virtual void Add(Sample value) OVERRIDE;
+ virtual void WriteHTMLGraph(std::string* output) const OVERRIDE;
+ virtual void WriteAscii(std::string* output) const OVERRIDE;
+ virtual scoped_ptr<HistogramSamples> SnapshotSamples() const OVERRIDE;
+
+ private:
+ // Clients should always use FactoryGet to create SparseHistogram.
+ SparseHistogram(const std::string& name);
+
+ virtual void GetParameters(DictionaryValue* params) const OVERRIDE;
+ virtual void GetCountAndBucketData(Count* count,
+ ListValue* buckets) const OVERRIDE;
+
+ friend class SparseHistogramTest; // For constuctor calling.
+
+ // Protects access to |sample_counts_| and |redundant_count_|.
+ mutable base::Lock lock_;
+ std::map<HistogramBase::Sample, HistogramBase::Count> sample_counts_;
+ HistogramBase::Count redundant_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(SparseHistogram);
+};
+
+} // namespace base
+
+#endif // BASE_METRICS_SPARSE_HISTOGRAM_H_
diff --git a/src/base/metrics/sparse_histogram_unittest.cc b/src/base/metrics/sparse_histogram_unittest.cc
new file mode 100644
index 0000000..b4eb8fd
--- /dev/null
+++ b/src/base/metrics/sparse_histogram_unittest.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 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/memory/scoped_ptr.h"
+#include "base/metrics/sample_map.h"
+#include "base/metrics/sparse_histogram.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+class SparseHistogramTest : public testing::Test {
+ protected:
+ scoped_ptr<SparseHistogram> NewSparseHistogram(const std::string& name) {
+ return scoped_ptr<SparseHistogram>(new SparseHistogram(name));
+ }
+};
+
+TEST_F(SparseHistogramTest, BasicTest) {
+ scoped_ptr<SparseHistogram> histogram(NewSparseHistogram("Sparse1"));
+ scoped_ptr<HistogramSamples> snapshot(histogram->SnapshotSamples());
+ EXPECT_EQ(0, snapshot->TotalCount());
+ EXPECT_EQ(0, snapshot->sum());
+
+ histogram->Add(100);
+ scoped_ptr<HistogramSamples> snapshot1(histogram->SnapshotSamples());
+ EXPECT_EQ(1, snapshot1->TotalCount());
+ EXPECT_EQ(1, snapshot1->GetCount(100));
+
+ histogram->Add(100);
+ histogram->Add(101);
+ scoped_ptr<HistogramSamples> snapshot2(histogram->SnapshotSamples());
+ EXPECT_EQ(3, snapshot2->TotalCount());
+ EXPECT_EQ(2, snapshot2->GetCount(100));
+ EXPECT_EQ(1, snapshot2->GetCount(101));
+}
+
+} // namespace base
diff --git a/src/base/metrics/statistics_recorder.cc b/src/base/metrics/statistics_recorder.cc
new file mode 100644
index 0000000..c1111c3
--- /dev/null
+++ b/src/base/metrics/statistics_recorder.cc
@@ -0,0 +1,340 @@
+// Copyright (c) 2012 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/metrics/statistics_recorder.h"
+
+#include "base/debug/leak_annotations.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram.h"
+#include "base/stringprintf.h"
+#include "base/synchronization/lock.h"
+
+using std::list;
+using std::string;
+
+namespace {
+// Initialize histogram statistics gathering system.
+base::LazyInstance<base::StatisticsRecorder>::Leaky g_statistics_recorder_ =
+ LAZY_INSTANCE_INITIALIZER;
+} // namespace
+
+namespace base {
+
+// Collect the number of histograms created.
+static uint32 number_of_histograms_ = 0;
+// Collect the number of vectors saved because of caching ranges.
+static uint32 number_of_vectors_saved_ = 0;
+// Collect the number of ranges_ elements saved because of caching ranges.
+static size_t saved_ranges_size_ = 0;
+
+// static
+void StatisticsRecorder::Initialize() {
+ // Ensure that an instance of the StatisticsRecorder object is created.
+ g_statistics_recorder_.Get();
+}
+
+
+// static
+bool StatisticsRecorder::IsActive() {
+ if (lock_ == NULL)
+ return false;
+ base::AutoLock auto_lock(*lock_);
+ return NULL != histograms_;
+}
+
+// static
+Histogram* StatisticsRecorder::RegisterOrDeleteDuplicate(Histogram* histogram) {
+ // As per crbug.com/79322 the histograms are intentionally leaked, so we need
+ // to annotate them. Because ANNOTATE_LEAKING_OBJECT_PTR may be used only once
+ // for an object, the duplicates should not be annotated.
+ // Callers are responsible for not calling RegisterOrDeleteDuplicate(ptr)
+ // twice if (lock_ == NULL) || (!histograms_).
+ if (lock_ == NULL) {
+ ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322
+ return histogram;
+ }
+
+ Histogram* histogram_to_delete = NULL;
+ Histogram* histogram_to_return = NULL;
+ {
+ base::AutoLock auto_lock(*lock_);
+ if (histograms_ == NULL) {
+ ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322
+ histogram_to_return = histogram;
+ } else {
+ const string& name = histogram->histogram_name();
+ HistogramMap::iterator it = histograms_->find(name);
+ if (histograms_->end() == it) {
+ (*histograms_)[name] = histogram;
+ ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322
+ ++number_of_histograms_;
+ histogram_to_return = histogram;
+ } else if (histogram == it->second) {
+ // The histogram was registered before.
+ histogram_to_return = histogram;
+ } else {
+ // We already have one histogram with this name.
+ histogram_to_return = it->second;
+ histogram_to_delete = histogram;
+ }
+ }
+ }
+ delete histogram_to_delete;
+ return histogram_to_return;
+}
+
+// static
+const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges(
+ const BucketRanges* ranges) {
+ DCHECK(ranges->HasValidChecksum());
+ scoped_ptr<const BucketRanges> ranges_deleter;
+
+ if (lock_ == NULL) {
+ ANNOTATE_LEAKING_OBJECT_PTR(ranges);
+ return ranges;
+ }
+
+ base::AutoLock auto_lock(*lock_);
+ if (ranges_ == NULL) {
+ ANNOTATE_LEAKING_OBJECT_PTR(ranges);
+ return ranges;
+ }
+
+ list<const BucketRanges*>* checksum_matching_list;
+ RangesMap::iterator ranges_it = ranges_->find(ranges->checksum());
+ if (ranges_->end() == ranges_it) {
+ // Add a new matching list to map.
+ checksum_matching_list = new list<const BucketRanges*>();
+ ANNOTATE_LEAKING_OBJECT_PTR(checksum_matching_list);
+ (*ranges_)[ranges->checksum()] = checksum_matching_list;
+ } else {
+ checksum_matching_list = ranges_it->second;
+ }
+
+ list<const BucketRanges*>::iterator checksum_matching_list_it;
+ for (checksum_matching_list_it = checksum_matching_list->begin();
+ checksum_matching_list_it != checksum_matching_list->end();
+ ++checksum_matching_list_it) {
+ const BucketRanges* existing_ranges = *checksum_matching_list_it;
+ if (existing_ranges->Equals(ranges)) {
+ if (existing_ranges == ranges) {
+ return ranges;
+ } else {
+ ++number_of_vectors_saved_;
+ saved_ranges_size_ += ranges->size();
+ ranges_deleter.reset(ranges);
+ return existing_ranges;
+ }
+ }
+ }
+ // We haven't found a BucketRanges which has the same ranges. Register the
+ // new BucketRanges.
+ checksum_matching_list->push_front(ranges);
+ return ranges;
+}
+
+// static
+void StatisticsRecorder::CollectHistogramStats(const std::string& suffix) {
+ static int uma_upload_attempt = 0;
+ ++uma_upload_attempt;
+ if (uma_upload_attempt == 1) {
+ UMA_HISTOGRAM_COUNTS_10000(
+ "Histogram.SharedRange.Count.FirstUpload." + suffix,
+ number_of_histograms_);
+ UMA_HISTOGRAM_COUNTS_10000(
+ "Histogram.SharedRange.RangesSaved.FirstUpload." + suffix,
+ number_of_vectors_saved_);
+ UMA_HISTOGRAM_COUNTS(
+ "Histogram.SharedRange.ElementsSaved.FirstUpload." + suffix,
+ static_cast<int>(saved_ranges_size_));
+ number_of_histograms_ = 0;
+ number_of_vectors_saved_ = 0;
+ saved_ranges_size_ = 0;
+ return;
+ }
+ if (uma_upload_attempt == 2) {
+ UMA_HISTOGRAM_COUNTS_10000(
+ "Histogram.SharedRange.Count.SecondUpload." + suffix,
+ number_of_histograms_);
+ UMA_HISTOGRAM_COUNTS_10000(
+ "Histogram.SharedRange.RangesSaved.SecondUpload." + suffix,
+ number_of_vectors_saved_);
+ UMA_HISTOGRAM_COUNTS(
+ "Histogram.SharedRange.ElementsSaved.SecondUpload." + suffix,
+ static_cast<int>(saved_ranges_size_));
+ number_of_histograms_ = 0;
+ number_of_vectors_saved_ = 0;
+ saved_ranges_size_ = 0;
+ return;
+ }
+ UMA_HISTOGRAM_COUNTS_10000(
+ "Histogram.SharedRange.Count.RestOfUploads." + suffix,
+ number_of_histograms_);
+ UMA_HISTOGRAM_COUNTS_10000(
+ "Histogram.SharedRange.RangesSaved.RestOfUploads." + suffix,
+ number_of_vectors_saved_);
+ UMA_HISTOGRAM_COUNTS(
+ "Histogram.SharedRange.ElementsSaved.RestOfUploads." + suffix,
+ static_cast<int>(saved_ranges_size_));
+}
+
+// static
+void StatisticsRecorder::WriteHTMLGraph(const std::string& query,
+ std::string* output) {
+ if (!IsActive())
+ return;
+
+ Histograms snapshot;
+ GetSnapshot(query, &snapshot);
+ for (Histograms::iterator it = snapshot.begin();
+ it != snapshot.end();
+ ++it) {
+ (*it)->WriteHTMLGraph(output);
+ output->append("<br><hr><br>");
+ }
+}
+
+// static
+void StatisticsRecorder::WriteGraph(const std::string& query,
+ std::string* output) {
+ if (!IsActive())
+ return;
+ if (query.length())
+ StringAppendF(output, "Collections of histograms for %s\n", query.c_str());
+ else
+ output->append("Collections of all histograms\n");
+
+ Histograms snapshot;
+ GetSnapshot(query, &snapshot);
+ for (Histograms::iterator it = snapshot.begin();
+ it != snapshot.end();
+ ++it) {
+ (*it)->WriteAscii(output);
+ output->append("\n");
+ }
+}
+
+// static
+void StatisticsRecorder::GetHistograms(Histograms* output) {
+ if (lock_ == NULL)
+ return;
+ base::AutoLock auto_lock(*lock_);
+ if (histograms_ == NULL)
+ return;
+
+ for (HistogramMap::iterator it = histograms_->begin();
+ histograms_->end() != it;
+ ++it) {
+ DCHECK_EQ(it->first, it->second->histogram_name());
+ output->push_back(it->second);
+ }
+}
+
+// static
+void StatisticsRecorder::GetBucketRanges(
+ std::vector<const BucketRanges*>* output) {
+ if (lock_ == NULL)
+ return;
+ base::AutoLock auto_lock(*lock_);
+ if (ranges_ == NULL)
+ return;
+
+ for (RangesMap::iterator it = ranges_->begin();
+ ranges_->end() != it;
+ ++it) {
+ list<const BucketRanges*>* ranges_list = it->second;
+ list<const BucketRanges*>::iterator ranges_list_it;
+ for (ranges_list_it = ranges_list->begin();
+ ranges_list_it != ranges_list->end();
+ ++ranges_list_it) {
+ output->push_back(*ranges_list_it);
+ }
+ }
+}
+
+// static
+Histogram* StatisticsRecorder::FindHistogram(const std::string& name) {
+ if (lock_ == NULL)
+ return NULL;
+ base::AutoLock auto_lock(*lock_);
+ if (histograms_ == NULL)
+ return NULL;
+
+ HistogramMap::iterator it = histograms_->find(name);
+ if (histograms_->end() == it)
+ return NULL;
+ return it->second;
+}
+
+// private static
+void StatisticsRecorder::GetSnapshot(const std::string& query,
+ Histograms* snapshot) {
+ if (lock_ == NULL)
+ return;
+ base::AutoLock auto_lock(*lock_);
+ if (histograms_ == NULL)
+ return;
+
+ for (HistogramMap::iterator it = histograms_->begin();
+ histograms_->end() != it;
+ ++it) {
+ if (it->first.find(query) != std::string::npos)
+ snapshot->push_back(it->second);
+ }
+}
+
+// This singleton instance should be started during the single threaded portion
+// of main(), and hence it is not thread safe. It initializes globals to
+// provide support for all future calls.
+StatisticsRecorder::StatisticsRecorder() {
+ DCHECK(!histograms_);
+ if (lock_ == NULL) {
+ // This will leak on purpose. It's the only way to make sure we won't race
+ // against the static uninitialization of the module while one of our
+ // static methods relying on the lock get called at an inappropriate time
+ // during the termination phase. Since it's a static data member, we will
+ // leak one per process, which would be similar to the instance allocated
+ // during static initialization and released only on process termination.
+ lock_ = new base::Lock;
+ }
+ base::AutoLock auto_lock(*lock_);
+ histograms_ = new HistogramMap;
+ ranges_ = new RangesMap;
+}
+
+StatisticsRecorder::~StatisticsRecorder() {
+ DCHECK(histograms_ && ranges_ && lock_);
+ if (dump_on_exit_) {
+ string output;
+ WriteGraph("", &output);
+ DLOG(INFO) << output;
+ }
+
+ // Clean up.
+ scoped_ptr<HistogramMap> histograms_deleter;
+ scoped_ptr<RangesMap> ranges_deleter;
+ // We don't delete lock_ on purpose to avoid having to properly protect
+ // against it going away after we checked for NULL in the static methods.
+ {
+ base::AutoLock auto_lock(*lock_);
+ histograms_deleter.reset(histograms_);
+ ranges_deleter.reset(ranges_);
+ histograms_ = NULL;
+ ranges_ = NULL;
+ }
+ // We are going to leak the histograms and the ranges.
+}
+
+
+// static
+StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL;
+// static
+StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = NULL;
+// static
+base::Lock* StatisticsRecorder::lock_ = NULL;
+// static
+bool StatisticsRecorder::dump_on_exit_ = false;
+
+} // namespace base
diff --git a/src/base/metrics/statistics_recorder.h b/src/base/metrics/statistics_recorder.h
new file mode 100644
index 0000000..5345735
--- /dev/null
+++ b/src/base/metrics/statistics_recorder.h
@@ -0,0 +1,116 @@
+// Copyright (c) 2012 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.
+
+// StatisticsRecorder holds all Histograms and BucketRanges that are used by
+// Histograms in the system. It provides a general place for
+// Histograms/BucketRanges to register, and supports a global API for accessing
+// (i.e., dumping, or graphing) the data.
+
+#ifndef BASE_METRICS_STATISTICS_RECORDER_H_
+#define BASE_METRICS_STATISTICS_RECORDER_H_
+
+#include <list>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/lazy_instance.h"
+
+namespace base {
+
+class BucketRanges;
+class Histogram;
+class Lock;
+
+class BASE_EXPORT StatisticsRecorder {
+ public:
+ typedef std::vector<Histogram*> Histograms;
+
+ // Initializes the StatisticsRecorder system.
+ static void Initialize();
+
+ // Find out if histograms can now be registered into our list.
+ static bool IsActive();
+
+ // Register, or add a new histogram to the collection of statistics. If an
+ // identically named histogram is already registered, then the argument
+ // |histogram| will deleted. The returned value is always the registered
+ // histogram (either the argument, or the pre-existing registered histogram).
+ static Histogram* RegisterOrDeleteDuplicate(Histogram* histogram);
+
+ // Register, or add a new BucketRanges. If an identically BucketRanges is
+ // already registered, then the argument |ranges| will deleted. The returned
+ // value is always the registered BucketRanges (either the argument, or the
+ // pre-existing one).
+ static const BucketRanges* RegisterOrDeleteDuplicateRanges(
+ const BucketRanges* ranges);
+
+ // Method for collecting stats about histograms created in browser and
+ // renderer processes. |suffix| is appended to histogram names. |suffix| could
+ // be either browser or renderer.
+ static void CollectHistogramStats(const std::string& suffix);
+
+ // Methods for printing histograms. Only histograms which have query as
+ // a substring are written to output (an empty string will process all
+ // registered histograms).
+ static void WriteHTMLGraph(const std::string& query, std::string* output);
+ static void WriteGraph(const std::string& query, std::string* output);
+
+ // Method for extracting histograms which were marked for use by UMA.
+ static void GetHistograms(Histograms* output);
+
+ // Method for extracting BucketRanges used by all histograms registered.
+ static void GetBucketRanges(std::vector<const BucketRanges*>* output);
+
+ // Find a histogram by name. It matches the exact name. This method is thread
+ // safe. It returns NULL if a matching histogram is not found.
+ static Histogram* FindHistogram(const std::string& name);
+
+ static bool dump_on_exit() { return dump_on_exit_; }
+
+ static void set_dump_on_exit(bool enable) { dump_on_exit_ = enable; }
+
+ // GetSnapshot copies some of the pointers to registered histograms into the
+ // caller supplied vector (Histograms). Only histograms with names matching
+ // query are returned. The query must be a substring of histogram name for its
+ // pointer to be copied.
+ static void GetSnapshot(const std::string& query, Histograms* snapshot);
+
+ private:
+ // We keep all registered histograms in a map, from name to histogram.
+ typedef std::map<std::string, Histogram*> HistogramMap;
+
+ // We keep all |bucket_ranges_| in a map, from checksum to a list of
+ // |bucket_ranges_|. Checksum is calculated from the |ranges_| in
+ // |bucket_ranges_|.
+ typedef std::map<uint32, std::list<const BucketRanges*>*> RangesMap;
+
+ friend struct DefaultLazyInstanceTraits<StatisticsRecorder>;
+ friend class HistogramTest;
+ friend class StatisticsRecorderTest;
+
+ // The constructor just initializes static members. Usually client code should
+ // use Initialize to do this. But in test code, you can friend this class and
+ // call destructor/constructor to get a clean StatisticsRecorder.
+ StatisticsRecorder();
+ ~StatisticsRecorder();
+
+ static HistogramMap* histograms_;
+ static RangesMap* ranges_;
+
+ // Lock protects access to above maps.
+ static base::Lock* lock_;
+
+ // Dump all known histograms to log.
+ static bool dump_on_exit_;
+
+ DISALLOW_COPY_AND_ASSIGN(StatisticsRecorder);
+};
+
+} // namespace base
+
+#endif // BASE_METRICS_STATISTICS_RECORDER_H_
diff --git a/src/base/metrics/statistics_recorder_unittest.cc b/src/base/metrics/statistics_recorder_unittest.cc
new file mode 100644
index 0000000..95759c1
--- /dev/null
+++ b/src/base/metrics/statistics_recorder_unittest.cc
@@ -0,0 +1,256 @@
+// Copyright (c) 2012 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 <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/statistics_recorder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+class StatisticsRecorderTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Each test will have a clean state (no Histogram / BucketRanges
+ // registered).
+ InitializeStatisticsRecorder();
+ }
+
+ virtual void TearDown() {
+ UninitializeStatisticsRecorder();
+ }
+
+ void InitializeStatisticsRecorder() {
+ statistics_recorder_ = new StatisticsRecorder();
+ }
+
+ void UninitializeStatisticsRecorder() {
+ delete statistics_recorder_;
+ statistics_recorder_ = NULL;
+ }
+
+ void DeleteHistogram(Histogram* histogram) {
+ delete histogram;
+ }
+
+ StatisticsRecorder* statistics_recorder_;
+};
+
+TEST_F(StatisticsRecorderTest, NotInitialized) {
+ UninitializeStatisticsRecorder();
+
+ ASSERT_FALSE(StatisticsRecorder::IsActive());
+
+ StatisticsRecorder::Histograms registered_histograms;
+ std::vector<const BucketRanges*> registered_ranges;
+
+ // We can still create histograms, but it's not registered.
+ // TODO(kaiwang): Do not depend on Histogram FactoryGet implementation.
+ Histogram* histogram(
+ Histogram::FactoryGet("StatisticsRecorderTest_NotInitialized",
+ 1, 1000, 10, Histogram::kNoFlags));
+ StatisticsRecorder::GetHistograms(®istered_histograms);
+ EXPECT_EQ(0u, registered_histograms.size());
+
+ // RegisterOrDeleteDuplicateRanges is a no-op.
+ BucketRanges* ranges = new BucketRanges(3);;
+ ranges->ResetChecksum();
+ EXPECT_EQ(ranges,
+ StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges));
+ StatisticsRecorder::GetBucketRanges(®istered_ranges);
+ EXPECT_EQ(0u, registered_ranges.size());
+
+ DeleteHistogram(histogram);
+}
+
+TEST_F(StatisticsRecorderTest, RegisterBucketRanges) {
+ std::vector<const BucketRanges*> registered_ranges;
+
+ BucketRanges* ranges1 = new BucketRanges(3);;
+ ranges1->ResetChecksum();
+ BucketRanges* ranges2 = new BucketRanges(4);;
+ ranges2->ResetChecksum();
+
+ // Register new ranges.
+ EXPECT_EQ(ranges1,
+ StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges1));
+ EXPECT_EQ(ranges2,
+ StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges2));
+ StatisticsRecorder::GetBucketRanges(®istered_ranges);
+ ASSERT_EQ(2u, registered_ranges.size());
+
+ // Register some ranges again.
+ EXPECT_EQ(ranges1,
+ StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges1));
+ registered_ranges.clear();
+ StatisticsRecorder::GetBucketRanges(®istered_ranges);
+ ASSERT_EQ(2u, registered_ranges.size());
+ // Make sure the ranges is still the one we know.
+ ASSERT_EQ(3u, ranges1->size());
+ EXPECT_EQ(0, ranges1->range(0));
+ EXPECT_EQ(0, ranges1->range(1));
+ EXPECT_EQ(0, ranges1->range(2));
+
+ // Register ranges with same values.
+ BucketRanges* ranges3 = new BucketRanges(3);;
+ ranges3->ResetChecksum();
+ EXPECT_EQ(ranges1, // returning ranges1
+ StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges3));
+ registered_ranges.clear();
+ StatisticsRecorder::GetBucketRanges(®istered_ranges);
+ ASSERT_EQ(2u, registered_ranges.size());
+}
+
+TEST_F(StatisticsRecorderTest, RegisterHistogram) {
+ // Create a Histogram that was not registered.
+ // TODO(kaiwang): Do not depend on Histogram FactoryGet implementation.
+ UninitializeStatisticsRecorder();
+ Histogram* histogram = Histogram::FactoryGet(
+ "TestHistogram", 1, 1000, 10, Histogram::kNoFlags);
+
+ // Clean StatisticsRecorder.
+ InitializeStatisticsRecorder();
+ StatisticsRecorder::Histograms registered_histograms;
+ StatisticsRecorder::GetHistograms(®istered_histograms);
+ EXPECT_EQ(0u, registered_histograms.size());
+
+ // Register the Histogram.
+ EXPECT_EQ(histogram,
+ StatisticsRecorder::RegisterOrDeleteDuplicate(histogram));
+ StatisticsRecorder::GetHistograms(®istered_histograms);
+ EXPECT_EQ(1u, registered_histograms.size());
+
+ // Register the same Histogram again.
+ EXPECT_EQ(histogram,
+ StatisticsRecorder::RegisterOrDeleteDuplicate(histogram));
+ registered_histograms.clear();
+ StatisticsRecorder::GetHistograms(®istered_histograms);
+ EXPECT_EQ(1u, registered_histograms.size());
+}
+
+TEST_F(StatisticsRecorderTest, FindHistogram) {
+ Histogram* histogram1 = Histogram::FactoryGet(
+ "TestHistogram1", 1, 1000, 10, Histogram::kNoFlags);
+ Histogram* histogram2 = Histogram::FactoryGet(
+ "TestHistogram2", 1, 1000, 10, Histogram::kNoFlags);
+
+ EXPECT_EQ(histogram1, StatisticsRecorder::FindHistogram("TestHistogram1"));
+ EXPECT_EQ(histogram2, StatisticsRecorder::FindHistogram("TestHistogram2"));
+ EXPECT_TRUE(StatisticsRecorder::FindHistogram("TestHistogram") == NULL);
+}
+
+TEST_F(StatisticsRecorderTest, GetSnapshot) {
+ Histogram::FactoryGet("TestHistogram1", 1, 1000, 10, Histogram::kNoFlags);
+ Histogram::FactoryGet("TestHistogram2", 1, 1000, 10, Histogram::kNoFlags);
+ Histogram::FactoryGet("TestHistogram3", 1, 1000, 10, Histogram::kNoFlags);
+
+ StatisticsRecorder::Histograms snapshot;
+ StatisticsRecorder::GetSnapshot("Test", &snapshot);
+ EXPECT_EQ(3u, snapshot.size());
+
+ snapshot.clear();
+ StatisticsRecorder::GetSnapshot("1", &snapshot);
+ EXPECT_EQ(1u, snapshot.size());
+
+ snapshot.clear();
+ StatisticsRecorder::GetSnapshot("hello", &snapshot);
+ EXPECT_EQ(0u, snapshot.size());
+}
+
+TEST_F(StatisticsRecorderTest, RegisterHistogramWithFactoryGet) {
+ StatisticsRecorder::Histograms registered_histograms;
+
+ StatisticsRecorder::GetHistograms(®istered_histograms);
+ ASSERT_EQ(0u, registered_histograms.size());
+
+ // Create a Histogram.
+ Histogram* histogram = Histogram::FactoryGet(
+ "TestHistogram", 1, 1000, 10, Histogram::kNoFlags);
+ registered_histograms.clear();
+ StatisticsRecorder::GetHistograms(®istered_histograms);
+ EXPECT_EQ(1u, registered_histograms.size());
+
+ // Get an existing histogram.
+ Histogram* histogram2 = Histogram::FactoryGet(
+ "TestHistogram", 1, 1000, 10, Histogram::kNoFlags);
+ registered_histograms.clear();
+ StatisticsRecorder::GetHistograms(®istered_histograms);
+ EXPECT_EQ(1u, registered_histograms.size());
+ EXPECT_EQ(histogram, histogram2);
+
+ // Create a LinearHistogram.
+ histogram = LinearHistogram::FactoryGet(
+ "TestLinearHistogram", 1, 1000, 10, Histogram::kNoFlags);
+ registered_histograms.clear();
+ StatisticsRecorder::GetHistograms(®istered_histograms);
+ EXPECT_EQ(2u, registered_histograms.size());
+
+ // Create a BooleanHistogram.
+ histogram = BooleanHistogram::FactoryGet(
+ "TestBooleanHistogram", Histogram::kNoFlags);
+ registered_histograms.clear();
+ StatisticsRecorder::GetHistograms(®istered_histograms);
+ EXPECT_EQ(3u, registered_histograms.size());
+
+ // Create a CustomHistogram.
+ std::vector<int> custom_ranges;
+ custom_ranges.push_back(1);
+ custom_ranges.push_back(5);
+ histogram = CustomHistogram::FactoryGet(
+ "TestCustomHistogram", custom_ranges, Histogram::kNoFlags);
+ registered_histograms.clear();
+ StatisticsRecorder::GetHistograms(®istered_histograms);
+ EXPECT_EQ(4u, registered_histograms.size());
+}
+
+TEST_F(StatisticsRecorderTest, RegisterHistogramWithMacros) {
+ StatisticsRecorder::Histograms registered_histograms;
+
+ Histogram* histogram = Histogram::FactoryGet(
+ "TestHistogramCounts", 1, 1000000, 50, Histogram::kNoFlags);
+
+ // The histogram we got from macro is the same as from FactoryGet.
+ HISTOGRAM_COUNTS("TestHistogramCounts", 30);
+ registered_histograms.clear();
+ StatisticsRecorder::GetHistograms(®istered_histograms);
+ ASSERT_EQ(1u, registered_histograms.size());
+ EXPECT_EQ(histogram, registered_histograms[0]);
+
+ HISTOGRAM_TIMES("TestHistogramTimes", TimeDelta::FromDays(1));
+ HISTOGRAM_ENUMERATION("TestHistogramEnumeration", 20, 200);
+
+ registered_histograms.clear();
+ StatisticsRecorder::GetHistograms(®istered_histograms);
+ EXPECT_EQ(3u, registered_histograms.size());
+
+ // Debugging only macros.
+ DHISTOGRAM_TIMES("TestHistogramDebugTimes", TimeDelta::FromDays(1));
+ DHISTOGRAM_COUNTS("TestHistogramDebugCounts", 30);
+ registered_histograms.clear();
+ StatisticsRecorder::GetHistograms(®istered_histograms);
+#ifndef NDEBUG
+ EXPECT_EQ(5u, registered_histograms.size());
+#else
+ EXPECT_EQ(3u, registered_histograms.size());
+#endif
+}
+
+TEST_F(StatisticsRecorderTest, BucketRangesSharing) {
+ Histogram* histogram1(Histogram::FactoryGet(
+ "Histogram", 1, 64, 8, Histogram::kNoFlags));
+ Histogram* histogram2(Histogram::FactoryGet(
+ "Histogram2", 1, 64, 8, Histogram::kNoFlags));
+ Histogram* histogram3(Histogram::FactoryGet(
+ "Histogram3", 1, 64, 16, Histogram::kNoFlags));
+
+ const BucketRanges* bucket_ranges1 = histogram1->bucket_ranges();
+ const BucketRanges* bucket_ranges2 = histogram2->bucket_ranges();
+ const BucketRanges* bucket_ranges3 = histogram3->bucket_ranges();
+ EXPECT_EQ(bucket_ranges1, bucket_ranges2);
+ EXPECT_FALSE(bucket_ranges1->Equals(bucket_ranges3));
+}
+
+} // namespace base
diff --git a/src/base/metrics/stats_counters.cc b/src/base/metrics/stats_counters.cc
new file mode 100644
index 0000000..0f0a5ef
--- /dev/null
+++ b/src/base/metrics/stats_counters.cc
@@ -0,0 +1,165 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/metrics/stats_counters.h"
+
+#include "base/logging.h"
+
+namespace base {
+#if defined(__LB_SHELL__) || defined(COBALT)
+// Intentionally no-ops.
+StatsCounter::StatsCounter() {
+}
+StatsCounter::StatsCounter(const std::string& name) {
+}
+StatsCounter::~StatsCounter() {
+}
+void StatsCounter::Set(int value) {
+}
+void StatsCounter::Add(int value) {
+}
+int* StatsCounter::GetPtr() {
+ return NULL;
+}
+StatsCounterTimer::StatsCounterTimer(const std::string& name) {
+}
+StatsCounterTimer::~StatsCounterTimer() {
+}
+void StatsCounterTimer::Start() {
+}
+void StatsCounterTimer::Stop() {
+}
+bool StatsCounterTimer::Running() {
+ return false;
+}
+void StatsCounterTimer::AddTime(TimeDelta time) {
+}
+void StatsCounterTimer::Record() {
+}
+StatsRate::StatsRate(const std::string& name) :
+ StatsCounterTimer(name), counter_(name), largest_add_("") {
+}
+StatsRate::~StatsRate() {
+}
+void StatsRate::Add(int value) {
+}
+#else
+
+StatsCounter::StatsCounter(const std::string& name)
+ : counter_id_(-1) {
+ // We prepend the name with 'c:' to indicate that it is a counter.
+ if (StatsTable::current()) {
+ // TODO(mbelshe): name_ construction is racy and it may corrupt memory for
+ // static.
+ name_ = "c:";
+ name_.append(name);
+ }
+}
+
+StatsCounter::~StatsCounter() {
+}
+
+void StatsCounter::Set(int value) {
+ int* loc = GetPtr();
+ if (loc)
+ *loc = value;
+}
+
+void StatsCounter::Add(int value) {
+ int* loc = GetPtr();
+ if (loc)
+ (*loc) += value;
+}
+
+StatsCounter::StatsCounter()
+ : counter_id_(-1) {
+}
+
+int* StatsCounter::GetPtr() {
+ StatsTable* table = StatsTable::current();
+ if (!table)
+ return NULL;
+
+ // If counter_id_ is -1, then we haven't looked it up yet.
+ if (counter_id_ == -1) {
+ counter_id_ = table->FindCounter(name_);
+ if (table->GetSlot() == 0) {
+ if (!table->RegisterThread("")) {
+ // There is no room for this thread. This thread
+ // cannot use counters.
+ counter_id_ = 0;
+ return NULL;
+ }
+ }
+ }
+
+ // If counter_id_ is > 0, then we have a valid counter.
+ if (counter_id_ > 0)
+ return table->GetLocation(counter_id_, table->GetSlot());
+
+ // counter_id_ was zero, which means the table is full.
+ return NULL;
+}
+
+
+StatsCounterTimer::StatsCounterTimer(const std::string& name) {
+ // we prepend the name with 't:' to indicate that it is a timer.
+ if (StatsTable::current()) {
+ // TODO(mbelshe): name_ construction is racy and it may corrupt memory for
+ // static.
+ name_ = "t:";
+ name_.append(name);
+ }
+}
+
+StatsCounterTimer::~StatsCounterTimer() {
+}
+
+void StatsCounterTimer::Start() {
+ if (!Enabled())
+ return;
+ start_time_ = TimeTicks::Now();
+ stop_time_ = TimeTicks();
+}
+
+// Stop the timer and record the results.
+void StatsCounterTimer::Stop() {
+ if (!Enabled() || !Running())
+ return;
+ stop_time_ = TimeTicks::Now();
+ Record();
+}
+
+// Returns true if the timer is running.
+bool StatsCounterTimer::Running() {
+ return Enabled() && !start_time_.is_null() && stop_time_.is_null();
+}
+
+// Accept a TimeDelta to increment.
+void StatsCounterTimer::AddTime(TimeDelta time) {
+ Add(static_cast<int>(time.InMilliseconds()));
+}
+
+void StatsCounterTimer::Record() {
+ AddTime(stop_time_ - start_time_);
+}
+
+
+StatsRate::StatsRate(const std::string& name)
+ : StatsCounterTimer(name),
+ counter_(name),
+ largest_add_(std::string(" ").append(name).append("MAX")) {
+}
+
+StatsRate::~StatsRate() {
+}
+
+void StatsRate::Add(int value) {
+ counter_.Increment();
+ StatsCounterTimer::Add(value);
+ if (value > largest_add_.value())
+ largest_add_.Set(value);
+}
+#endif
+} // namespace base
diff --git a/src/base/metrics/stats_counters.h b/src/base/metrics/stats_counters.h
new file mode 100644
index 0000000..bc2016c
--- /dev/null
+++ b/src/base/metrics/stats_counters.h
@@ -0,0 +1,205 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_METRICS_STATS_COUNTERS_H_
+#define BASE_METRICS_STATS_COUNTERS_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#if !defined(__LB_SHELL__) && !defined(COBALT)
+#include "base/metrics/stats_table.h"
+#endif
+#include "base/time.h"
+
+namespace base {
+
+// StatsCounters are dynamically created values which can be tracked in
+// the StatsTable. They are designed to be lightweight to create and
+// easy to use.
+//
+// Since StatsCounters can be created dynamically by name, there is
+// a hash table lookup to find the counter in the table. A StatsCounter
+// object can be created once and used across multiple threads safely.
+//
+// Example usage:
+// {
+// StatsCounter request_count("RequestCount");
+// request_count.Increment();
+// }
+//
+// Note that creating counters on the stack does work, however creating
+// the counter object requires a hash table lookup. For inner loops, it
+// may be better to create the counter either as a member of another object
+// (or otherwise outside of the loop) for maximum performance.
+//
+// Internally, a counter represents a value in a row of a StatsTable.
+// The row has a 32bit value for each process/thread in the table and also
+// a name (stored in the table metadata).
+//
+// NOTE: In order to make stats_counters usable in lots of different code,
+// avoid any dependencies inside this header file.
+//
+
+//------------------------------------------------------------------------------
+// Define macros for ease of use. They also allow us to change definitions
+// as the implementation varies, or depending on compile options.
+//------------------------------------------------------------------------------
+// First provide generic macros, which exist in production as well as debug.
+#if defined(__LB_SHELL__) || defined(COBALT)
+#define STATS_COUNTER(name, delta)
+#define SIMPLE_STATS_COUNTER(name)
+#define RATE_COUNTER(name, duration)
+#else
+#define STATS_COUNTER(name, delta) do { \
+ base::StatsCounter counter(name); \
+ counter.Add(delta); \
+} while (0)
+
+#define SIMPLE_STATS_COUNTER(name) STATS_COUNTER(name, 1)
+
+#define RATE_COUNTER(name, duration) do { \
+ base::StatsRate hit_count(name); \
+ hit_count.AddTime(duration); \
+} while (0)
+#endif
+
+// Define Debug vs non-debug flavors of macros.
+#ifndef NDEBUG
+
+#define DSTATS_COUNTER(name, delta) STATS_COUNTER(name, delta)
+#define DSIMPLE_STATS_COUNTER(name) SIMPLE_STATS_COUNTER(name)
+#define DRATE_COUNTER(name, duration) RATE_COUNTER(name, duration)
+
+#else // NDEBUG
+
+#define DSTATS_COUNTER(name, delta) do {} while (0)
+#define DSIMPLE_STATS_COUNTER(name) do {} while (0)
+#define DRATE_COUNTER(name, duration) do {} while (0)
+
+#endif // NDEBUG
+
+//------------------------------------------------------------------------------
+// StatsCounter represents a counter in the StatsTable class.
+class BASE_EXPORT StatsCounter {
+ public:
+ // Create a StatsCounter object.
+ explicit StatsCounter(const std::string& name);
+ virtual ~StatsCounter();
+
+ // Sets the counter to a specific value.
+ void Set(int value);
+
+ // Increments the counter.
+ void Increment() {
+ Add(1);
+ }
+
+ virtual void Add(int value);
+
+ // Decrements the counter.
+ void Decrement() {
+ Add(-1);
+ }
+
+ void Subtract(int value) {
+ Add(-value);
+ }
+
+ // Is this counter enabled?
+ // Returns false if table is full.
+ bool Enabled() {
+ return GetPtr() != NULL;
+ }
+
+ int value() {
+ int* loc = GetPtr();
+ if (loc) return *loc;
+ return 0;
+ }
+
+ protected:
+ StatsCounter();
+
+ // Returns the cached address of this counter location.
+ int* GetPtr();
+
+ std::string name_;
+ // The counter id in the table. We initialize to -1 (an invalid value)
+ // and then cache it once it has been looked up. The counter_id is
+ // valid across all threads and processes.
+ int32 counter_id_;
+};
+
+
+// A StatsCounterTimer is a StatsCounter which keeps a timer during
+// the scope of the StatsCounterTimer. On destruction, it will record
+// its time measurement.
+class BASE_EXPORT StatsCounterTimer : protected StatsCounter {
+ public:
+ // Constructs and starts the timer.
+ explicit StatsCounterTimer(const std::string& name);
+ virtual ~StatsCounterTimer();
+
+ // Start the timer.
+ void Start();
+
+ // Stop the timer and record the results.
+ void Stop();
+
+ // Returns true if the timer is running.
+ bool Running();
+
+ // Accept a TimeDelta to increment.
+ virtual void AddTime(TimeDelta time);
+
+ protected:
+ // Compute the delta between start and stop, in milliseconds.
+ void Record();
+
+ TimeTicks start_time_;
+ TimeTicks stop_time_;
+};
+
+// A StatsRate is a timer that keeps a count of the number of intervals added so
+// that several statistics can be produced:
+// min, max, avg, count, total
+class BASE_EXPORT StatsRate : public StatsCounterTimer {
+ public:
+ // Constructs and starts the timer.
+ explicit StatsRate(const std::string& name);
+ virtual ~StatsRate();
+
+ virtual void Add(int value) OVERRIDE;
+
+ private:
+ StatsCounter counter_;
+ StatsCounter largest_add_;
+};
+
+
+// Helper class for scoping a timer or rate.
+template<class T> class StatsScope {
+ public:
+ explicit StatsScope<T>(T& timer)
+ : timer_(timer) {
+ timer_.Start();
+ }
+
+ ~StatsScope() {
+ timer_.Stop();
+ }
+
+ void Stop() {
+ timer_.Stop();
+ }
+
+ private:
+ T& timer_;
+};
+
+} // namespace base
+
+#endif // BASE_METRICS_STATS_COUNTERS_H_
diff --git a/src/base/metrics/stats_table.cc b/src/base/metrics/stats_table.cc
new file mode 100644
index 0000000..2bccc90
--- /dev/null
+++ b/src/base/metrics/stats_table.cc
@@ -0,0 +1,560 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/metrics/stats_table.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/process_util.h"
+#include "base/shared_memory.h"
+#include "base/string_piece.h"
+#include "base/string_util.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread_local_storage.h"
+#include "base/utf_string_conversions.h"
+
+#if defined(OS_POSIX)
+#include "errno.h"
+#endif
+
+namespace base {
+
+// The StatsTable uses a shared memory segment that is laid out as follows
+//
+// +-------------------------------------------+
+// | Version | Size | MaxCounters | MaxThreads |
+// +-------------------------------------------+
+// | Thread names table |
+// +-------------------------------------------+
+// | Thread TID table |
+// +-------------------------------------------+
+// | Thread PID table |
+// +-------------------------------------------+
+// | Counter names table |
+// +-------------------------------------------+
+// | Data |
+// +-------------------------------------------+
+//
+// The data layout is a grid, where the columns are the thread_ids and the
+// rows are the counter_ids.
+//
+// If the first character of the thread_name is '\0', then that column is
+// empty.
+// If the first character of the counter_name is '\0', then that row is
+// empty.
+//
+// About Locking:
+// This class is designed to be both multi-thread and multi-process safe.
+// Aside from initialization, this is done by partitioning the data which
+// each thread uses so that no locking is required. However, to allocate
+// the rows and columns of the table to particular threads, locking is
+// required.
+//
+// At the shared-memory level, we have a lock. This lock protects the
+// shared-memory table only, and is used when we create new counters (e.g.
+// use rows) or when we register new threads (e.g. use columns). Reading
+// data from the table does not require any locking at the shared memory
+// level.
+//
+// Each process which accesses the table will create a StatsTable object.
+// The StatsTable maintains a hash table of the existing counters in the
+// table for faster lookup. Since the hash table is process specific,
+// each process maintains its own cache. We avoid complexity here by never
+// de-allocating from the hash table. (Counters are dynamically added,
+// but not dynamically removed).
+
+// In order for external viewers to be able to read our shared memory,
+// we all need to use the same size ints.
+COMPILE_ASSERT(sizeof(int)==4, expect_4_byte_ints);
+
+namespace {
+
+// An internal version in case we ever change the format of this
+// file, and so that we can identify our table.
+const int kTableVersion = 0x13131313;
+
+// The name for un-named counters and threads in the table.
+const char kUnknownName[] = "<unknown>";
+
+// Calculates delta to align an offset to the size of an int
+inline int AlignOffset(int offset) {
+ return (sizeof(int) - (offset % sizeof(int))) % sizeof(int);
+}
+
+inline int AlignedSize(int size) {
+ return size + AlignOffset(size);
+}
+
+} // namespace
+
+// The StatsTable::Private maintains convenience pointers into the
+// shared memory segment. Use this class to keep the data structure
+// clean and accessible.
+class StatsTable::Private {
+ public:
+ // Various header information contained in the memory mapped segment.
+ struct TableHeader {
+ int version;
+ int size;
+ int max_counters;
+ int max_threads;
+ };
+
+ // Construct a new Private based on expected size parameters, or
+ // return NULL on failure.
+ static Private* New(const std::string& name, int size,
+ int max_threads, int max_counters);
+
+ SharedMemory* shared_memory() { return &shared_memory_; }
+
+ // Accessors for our header pointers
+ TableHeader* table_header() const { return table_header_; }
+ int version() const { return table_header_->version; }
+ int size() const { return table_header_->size; }
+ int max_counters() const { return table_header_->max_counters; }
+ int max_threads() const { return table_header_->max_threads; }
+
+ // Accessors for our tables
+ char* thread_name(int slot_id) const {
+ return &thread_names_table_[
+ (slot_id-1) * (StatsTable::kMaxThreadNameLength)];
+ }
+ PlatformThreadId* thread_tid(int slot_id) const {
+ return &(thread_tid_table_[slot_id-1]);
+ }
+ int* thread_pid(int slot_id) const {
+ return &(thread_pid_table_[slot_id-1]);
+ }
+ char* counter_name(int counter_id) const {
+ return &counter_names_table_[
+ (counter_id-1) * (StatsTable::kMaxCounterNameLength)];
+ }
+ int* row(int counter_id) const {
+ return &data_table_[(counter_id-1) * max_threads()];
+ }
+
+ private:
+ // Constructor is private because you should use New() instead.
+ Private()
+ : table_header_(NULL),
+ thread_names_table_(NULL),
+ thread_tid_table_(NULL),
+ thread_pid_table_(NULL),
+ counter_names_table_(NULL),
+ data_table_(NULL) {
+ }
+
+ // Initializes the table on first access. Sets header values
+ // appropriately and zeroes all counters.
+ void InitializeTable(void* memory, int size, int max_counters,
+ int max_threads);
+
+ // Initializes our in-memory pointers into a pre-created StatsTable.
+ void ComputeMappedPointers(void* memory);
+
+ SharedMemory shared_memory_;
+ TableHeader* table_header_;
+ char* thread_names_table_;
+ PlatformThreadId* thread_tid_table_;
+ int* thread_pid_table_;
+ char* counter_names_table_;
+ int* data_table_;
+};
+
+// static
+StatsTable::Private* StatsTable::Private::New(const std::string& name,
+ int size,
+ int max_threads,
+ int max_counters) {
+ scoped_ptr<Private> priv(new Private());
+ if (!priv->shared_memory_.CreateNamed(name, true, size))
+ return NULL;
+ if (!priv->shared_memory_.Map(size))
+ return NULL;
+ void* memory = priv->shared_memory_.memory();
+
+ TableHeader* header = static_cast<TableHeader*>(memory);
+
+ // If the version does not match, then assume the table needs
+ // to be initialized.
+ if (header->version != kTableVersion)
+ priv->InitializeTable(memory, size, max_counters, max_threads);
+
+ // We have a valid table, so compute our pointers.
+ priv->ComputeMappedPointers(memory);
+
+ return priv.release();
+}
+
+void StatsTable::Private::InitializeTable(void* memory, int size,
+ int max_counters,
+ int max_threads) {
+ // Zero everything.
+ memset(memory, 0, size);
+
+ // Initialize the header.
+ TableHeader* header = static_cast<TableHeader*>(memory);
+ header->version = kTableVersion;
+ header->size = size;
+ header->max_counters = max_counters;
+ header->max_threads = max_threads;
+}
+
+void StatsTable::Private::ComputeMappedPointers(void* memory) {
+ char* data = static_cast<char*>(memory);
+ int offset = 0;
+
+ table_header_ = reinterpret_cast<TableHeader*>(data);
+ offset += sizeof(*table_header_);
+ offset += AlignOffset(offset);
+
+ // Verify we're looking at a valid StatsTable.
+ DCHECK_EQ(table_header_->version, kTableVersion);
+
+ thread_names_table_ = reinterpret_cast<char*>(data + offset);
+ offset += sizeof(char) *
+ max_threads() * StatsTable::kMaxThreadNameLength;
+ offset += AlignOffset(offset);
+
+ thread_tid_table_ = reinterpret_cast<PlatformThreadId*>(data + offset);
+ offset += sizeof(int) * max_threads();
+ offset += AlignOffset(offset);
+
+ thread_pid_table_ = reinterpret_cast<int*>(data + offset);
+ offset += sizeof(int) * max_threads();
+ offset += AlignOffset(offset);
+
+ counter_names_table_ = reinterpret_cast<char*>(data + offset);
+ offset += sizeof(char) *
+ max_counters() * StatsTable::kMaxCounterNameLength;
+ offset += AlignOffset(offset);
+
+ data_table_ = reinterpret_cast<int*>(data + offset);
+ offset += sizeof(int) * max_threads() * max_counters();
+
+ DCHECK_EQ(offset, size());
+}
+
+// TLSData carries the data stored in the TLS slots for the
+// StatsTable. This is used so that we can properly cleanup when the
+// thread exits and return the table slot.
+//
+// Each thread that calls RegisterThread in the StatsTable will have
+// a TLSData stored in its TLS.
+struct StatsTable::TLSData {
+ StatsTable* table;
+ int slot;
+};
+
+// We keep a singleton table which can be easily accessed.
+StatsTable* StatsTable::global_table_ = NULL;
+
+StatsTable::StatsTable(const std::string& name, int max_threads,
+ int max_counters)
+ : impl_(NULL),
+ tls_index_(SlotReturnFunction) {
+ int table_size =
+ AlignedSize(sizeof(Private::TableHeader)) +
+ AlignedSize((max_counters * sizeof(char) * kMaxCounterNameLength)) +
+ AlignedSize((max_threads * sizeof(char) * kMaxThreadNameLength)) +
+ AlignedSize(max_threads * sizeof(int)) +
+ AlignedSize(max_threads * sizeof(int)) +
+ AlignedSize((sizeof(int) * (max_counters * max_threads)));
+
+ impl_ = Private::New(name, table_size, max_threads, max_counters);
+
+ if (!impl_)
+ DPLOG(ERROR) << "StatsTable did not initialize";
+}
+
+StatsTable::~StatsTable() {
+ // Before we tear down our copy of the table, be sure to
+ // unregister our thread.
+ UnregisterThread();
+
+ // Return ThreadLocalStorage. At this point, if any registered threads
+ // still exist, they cannot Unregister.
+ tls_index_.Free();
+
+ // Cleanup our shared memory.
+ delete impl_;
+
+ // If we are the global table, unregister ourselves.
+ if (global_table_ == this)
+ global_table_ = NULL;
+}
+
+int StatsTable::GetSlot() const {
+ TLSData* data = GetTLSData();
+ if (!data)
+ return 0;
+ return data->slot;
+}
+
+int StatsTable::RegisterThread(const std::string& name) {
+ int slot = 0;
+ if (!impl_)
+ return 0;
+
+ // Registering a thread requires that we lock the shared memory
+ // so that two threads don't grab the same slot. Fortunately,
+ // thread creation shouldn't happen in inner loops.
+ {
+ SharedMemoryAutoLock lock(impl_->shared_memory());
+ slot = FindEmptyThread();
+ if (!slot) {
+ return 0;
+ }
+
+ // We have space, so consume a column in the table.
+ std::string thread_name = name;
+ if (name.empty())
+ thread_name = kUnknownName;
+ strlcpy(impl_->thread_name(slot), thread_name.c_str(),
+ kMaxThreadNameLength);
+ *(impl_->thread_tid(slot)) = PlatformThread::CurrentId();
+ *(impl_->thread_pid(slot)) = GetCurrentProcId();
+ }
+
+ // Set our thread local storage.
+ TLSData* data = new TLSData;
+ data->table = this;
+ data->slot = slot;
+ tls_index_.Set(data);
+ return slot;
+}
+
+int StatsTable::CountThreadsRegistered() const {
+ if (!impl_)
+ return 0;
+
+ // Loop through the shared memory and count the threads that are active.
+ // We intentionally do not lock the table during the operation.
+ int count = 0;
+ for (int index = 1; index <= impl_->max_threads(); index++) {
+ char* name = impl_->thread_name(index);
+ if (*name != '\0')
+ count++;
+ }
+ return count;
+}
+
+int StatsTable::FindCounter(const std::string& name) {
+ // Note: the API returns counters numbered from 1..N, although
+ // internally, the array is 0..N-1. This is so that we can return
+ // zero as "not found".
+ if (!impl_)
+ return 0;
+
+ // Create a scope for our auto-lock.
+ {
+ AutoLock scoped_lock(counters_lock_);
+
+ // Attempt to find the counter.
+ CountersMap::const_iterator iter;
+ iter = counters_.find(name);
+ if (iter != counters_.end())
+ return iter->second;
+ }
+
+ // Counter does not exist, so add it.
+ return AddCounter(name);
+}
+
+int* StatsTable::GetLocation(int counter_id, int slot_id) const {
+ if (!impl_)
+ return NULL;
+ if (slot_id > impl_->max_threads())
+ return NULL;
+
+ int* row = impl_->row(counter_id);
+ return &(row[slot_id-1]);
+}
+
+const char* StatsTable::GetRowName(int index) const {
+ if (!impl_)
+ return NULL;
+
+ return impl_->counter_name(index);
+}
+
+int StatsTable::GetRowValue(int index) const {
+ return GetRowValue(index, 0);
+}
+
+int StatsTable::GetRowValue(int index, int pid) const {
+ if (!impl_)
+ return 0;
+
+ int rv = 0;
+ int* row = impl_->row(index);
+ for (int slot_id = 0; slot_id < impl_->max_threads(); slot_id++) {
+ if (pid == 0 || *impl_->thread_pid(slot_id) == pid)
+ rv += row[slot_id];
+ }
+ return rv;
+}
+
+int StatsTable::GetCounterValue(const std::string& name) {
+ return GetCounterValue(name, 0);
+}
+
+int StatsTable::GetCounterValue(const std::string& name, int pid) {
+ if (!impl_)
+ return 0;
+
+ int row = FindCounter(name);
+ if (!row)
+ return 0;
+ return GetRowValue(row, pid);
+}
+
+int StatsTable::GetMaxCounters() const {
+ if (!impl_)
+ return 0;
+ return impl_->max_counters();
+}
+
+int StatsTable::GetMaxThreads() const {
+ if (!impl_)
+ return 0;
+ return impl_->max_threads();
+}
+
+int* StatsTable::FindLocation(const char* name) {
+ // Get the static StatsTable
+ StatsTable *table = StatsTable::current();
+ if (!table)
+ return NULL;
+
+ // Get the slot for this thread. Try to register
+ // it if none exists.
+ int slot = table->GetSlot();
+ if (!slot && !(slot = table->RegisterThread("")))
+ return NULL;
+
+ // Find the counter id for the counter.
+ std::string str_name(name);
+ int counter = table->FindCounter(str_name);
+
+ // Now we can find the location in the table.
+ return table->GetLocation(counter, slot);
+}
+
+void StatsTable::UnregisterThread() {
+ UnregisterThread(GetTLSData());
+}
+
+void StatsTable::UnregisterThread(TLSData* data) {
+ if (!data)
+ return;
+ DCHECK(impl_);
+
+ // Mark the slot free by zeroing out the thread name.
+ char* name = impl_->thread_name(data->slot);
+ *name = '\0';
+
+ // Remove the calling thread's TLS so that it cannot use the slot.
+ tls_index_.Set(NULL);
+ delete data;
+}
+
+void StatsTable::SlotReturnFunction(void* data) {
+ // This is called by the TLS destructor, which on some platforms has
+ // already cleared the TLS info, so use the tls_data argument
+ // rather than trying to fetch it ourselves.
+ TLSData* tls_data = static_cast<TLSData*>(data);
+ if (tls_data) {
+ DCHECK(tls_data->table);
+ tls_data->table->UnregisterThread(tls_data);
+ }
+}
+
+int StatsTable::FindEmptyThread() const {
+ // Note: the API returns slots numbered from 1..N, although
+ // internally, the array is 0..N-1. This is so that we can return
+ // zero as "not found".
+ //
+ // The reason for doing this is because the thread 'slot' is stored
+ // in TLS, which is always initialized to zero, not -1. If 0 were
+ // returned as a valid slot number, it would be confused with the
+ // uninitialized state.
+ if (!impl_)
+ return 0;
+
+ int index = 1;
+ for (; index <= impl_->max_threads(); index++) {
+ char* name = impl_->thread_name(index);
+ if (!*name)
+ break;
+ }
+ if (index > impl_->max_threads())
+ return 0; // The table is full.
+ return index;
+}
+
+int StatsTable::FindCounterOrEmptyRow(const std::string& name) const {
+ // Note: the API returns slots numbered from 1..N, although
+ // internally, the array is 0..N-1. This is so that we can return
+ // zero as "not found".
+ //
+ // There isn't much reason for this other than to be consistent
+ // with the way we track columns for thread slots. (See comments
+ // in FindEmptyThread for why it is done this way).
+ if (!impl_)
+ return 0;
+
+ int free_slot = 0;
+ for (int index = 1; index <= impl_->max_counters(); index++) {
+ char* row_name = impl_->counter_name(index);
+ if (!*row_name && !free_slot)
+ free_slot = index; // save that we found a free slot
+ else if (!strncmp(row_name, name.c_str(), kMaxCounterNameLength))
+ return index;
+ }
+ return free_slot;
+}
+
+int StatsTable::AddCounter(const std::string& name) {
+ if (!impl_)
+ return 0;
+
+ int counter_id = 0;
+ {
+ // To add a counter to the shared memory, we need the
+ // shared memory lock.
+ SharedMemoryAutoLock lock(impl_->shared_memory());
+
+ // We have space, so create a new counter.
+ counter_id = FindCounterOrEmptyRow(name);
+ if (!counter_id)
+ return 0;
+
+ std::string counter_name = name;
+ if (name.empty())
+ counter_name = kUnknownName;
+ strlcpy(impl_->counter_name(counter_id), counter_name.c_str(),
+ kMaxCounterNameLength);
+ }
+
+ // now add to our in-memory cache
+ {
+ AutoLock lock(counters_lock_);
+ counters_[name] = counter_id;
+ }
+ return counter_id;
+}
+
+StatsTable::TLSData* StatsTable::GetTLSData() const {
+ TLSData* data =
+ static_cast<TLSData*>(tls_index_.Get());
+ if (!data)
+ return NULL;
+
+ DCHECK(data->slot);
+ DCHECK_EQ(data->table, this);
+ return data;
+}
+
+} // namespace base
diff --git a/src/base/metrics/stats_table.h b/src/base/metrics/stats_table.h
new file mode 100644
index 0000000..d972e7c
--- /dev/null
+++ b/src/base/metrics/stats_table.h
@@ -0,0 +1,195 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A StatsTable is a table of statistics. It can be used across multiple
+// processes and threads, maintaining cheap statistics counters without
+// locking.
+//
+// The goal is to make it very cheap and easy for developers to add
+// counters to code, without having to build one-off utilities or mechanisms
+// to track the counters, and also to allow a single "view" to display
+// the contents of all counters.
+//
+// To achieve this, StatsTable creates a shared memory segment to store
+// the data for the counters. Upon creation, it has a specific size
+// which governs the maximum number of counters and concurrent
+// threads/processes which can use it.
+//
+
+#ifndef BASE_METRICS_STATS_TABLE_H_
+#define BASE_METRICS_STATS_TABLE_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/hash_tables.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_local_storage.h"
+
+namespace base {
+
+class BASE_EXPORT StatsTable {
+ public:
+ // Create a new StatsTable.
+ // If a StatsTable already exists with the specified name, this StatsTable
+ // will use the same shared memory segment as the original. Otherwise,
+ // a new StatsTable is created and all counters are zeroed.
+ //
+ // name is the name of the StatsTable to use.
+ //
+ // max_threads is the maximum number of threads the table will support.
+ // If the StatsTable already exists, this number is ignored.
+ //
+ // max_counters is the maximum number of counters the table will support.
+ // If the StatsTable already exists, this number is ignored.
+ StatsTable(const std::string& name, int max_threads, int max_counters);
+
+ // Destroys the StatsTable. When the last StatsTable is destroyed
+ // (across all processes), the StatsTable is removed from disk.
+ ~StatsTable();
+
+ // For convenience, we create a static table. This is generally
+ // used automatically by the counters.
+ static StatsTable* current() { return global_table_; }
+
+ // Set the global table for use in this process.
+ static void set_current(StatsTable* value) { global_table_ = value; }
+
+ // Get the slot id for the calling thread. Returns 0 if no
+ // slot is assigned.
+ int GetSlot() const;
+
+ // All threads that contribute data to the table must register with the
+ // table first. This function will set thread local storage for the
+ // thread containing the location in the table where this thread will
+ // write its counter data.
+ //
+ // name is just a debugging tag to label the thread, and it does not
+ // need to be unique. It will be truncated to kMaxThreadNameLength-1
+ // characters.
+ //
+ // On success, returns the slot id for this thread. On failure,
+ // returns 0.
+ int RegisterThread(const std::string& name);
+
+ // Returns the number of threads currently registered. This is really not
+ // useful except for diagnostics and debugging.
+ int CountThreadsRegistered() const;
+
+ // Find a counter in the StatsTable.
+ //
+ // Returns an id for the counter which can be used to call GetLocation().
+ // If the counter does not exist, attempts to create a row for the new
+ // counter. If there is no space in the table for the new counter,
+ // returns 0.
+ int FindCounter(const std::string& name);
+
+ // TODO(mbelshe): implement RemoveCounter.
+
+ // Gets the location of a particular value in the table based on
+ // the counter id and slot id.
+ int* GetLocation(int counter_id, int slot_id) const;
+
+ // Gets the counter name at a particular row. If the row is empty,
+ // returns NULL.
+ const char* GetRowName(int index) const;
+
+ // Gets the sum of the values for a particular row.
+ int GetRowValue(int index) const;
+
+ // Gets the sum of the values for a particular row for a given pid.
+ int GetRowValue(int index, int pid) const;
+
+ // Gets the sum of the values for a particular counter. If the counter
+ // does not exist, creates the counter.
+ int GetCounterValue(const std::string& name);
+
+ // Gets the sum of the values for a particular counter for a given pid.
+ // If the counter does not exist, creates the counter.
+ int GetCounterValue(const std::string& name, int pid);
+
+ // The maxinum number of counters/rows in the table.
+ int GetMaxCounters() const;
+
+ // The maxinum number of threads/columns in the table.
+ int GetMaxThreads() const;
+
+ // The maximum length (in characters) of a Thread's name including
+ // null terminator, as stored in the shared memory.
+ static const int kMaxThreadNameLength = 32;
+
+ // The maximum length (in characters) of a Counter's name including
+ // null terminator, as stored in the shared memory.
+ static const int kMaxCounterNameLength = 64;
+
+ // Convenience function to lookup a counter location for a
+ // counter by name for the calling thread. Will register
+ // the thread if it is not already registered.
+ static int* FindLocation(const char *name);
+
+ private:
+ class Private;
+ struct TLSData;
+ typedef hash_map<std::string, int> CountersMap;
+
+ // Returns the space occupied by a thread in the table. Generally used
+ // if a thread terminates but the process continues. This function
+ // does not zero out the thread's counters.
+ // Cannot be used inside a posix tls destructor.
+ void UnregisterThread();
+
+ // This variant expects the tls data to be passed in, so it is safe to
+ // call from inside a posix tls destructor (see doc for pthread_key_create).
+ void UnregisterThread(TLSData* tls_data);
+
+ // The SlotReturnFunction is called at thread exit for each thread
+ // which used the StatsTable.
+ static void SlotReturnFunction(void* data);
+
+ // Locates a free slot in the table. Returns a number > 0 on success,
+ // or 0 on failure. The caller must hold the shared_memory lock when
+ // calling this function.
+ int FindEmptyThread() const;
+
+ // Locates a counter in the table or finds an empty row. Returns a
+ // number > 0 on success, or 0 on failure. The caller must hold the
+ // shared_memory_lock when calling this function.
+ int FindCounterOrEmptyRow(const std::string& name) const;
+
+ // Internal function to add a counter to the StatsTable. Assumes that
+ // the counter does not already exist in the table.
+ //
+ // name is a unique identifier for this counter, and will be truncated
+ // to kMaxCounterNameLength-1 characters.
+ //
+ // On success, returns the counter_id for the newly added counter.
+ // On failure, returns 0.
+ int AddCounter(const std::string& name);
+
+ // Get the TLS data for the calling thread. Returns NULL if none is
+ // initialized.
+ TLSData* GetTLSData() const;
+
+ Private* impl_;
+
+ // The counters_lock_ protects the counters_ hash table.
+ base::Lock counters_lock_;
+
+ // The counters_ hash map is an in-memory hash of the counters.
+ // It is used for quick lookup of counters, but is cannot be used
+ // as a substitute for what is in the shared memory. Even though
+ // we don't have a counter in our hash table, another process may
+ // have created it.
+ CountersMap counters_;
+ ThreadLocalStorage::Slot tls_index_;
+
+ static StatsTable* global_table_;
+
+ DISALLOW_COPY_AND_ASSIGN(StatsTable);
+};
+
+} // namespace base
+
+#endif // BASE_METRICS_STATS_TABLE_H_
diff --git a/src/base/metrics/stats_table_unittest.cc b/src/base/metrics/stats_table_unittest.cc
new file mode 100644
index 0000000..579bab7
--- /dev/null
+++ b/src/base/metrics/stats_table_unittest.cc
@@ -0,0 +1,423 @@
+// Copyright (c) 2012 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/metrics/stats_counters.h"
+#include "base/metrics/stats_table.h"
+#include "base/shared_memory.h"
+#include "base/stringprintf.h"
+#include "base/string_piece.h"
+#include "base/test/multiprocess_test.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/simple_thread.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace base {
+
+class StatsTableTest : public MultiProcessTest {
+ public:
+ void DeleteShmem(const std::string& name) {
+ SharedMemory mem;
+ mem.Delete(name);
+ }
+};
+
+// Open a StatsTable and verify that we can write to each of the
+// locations in the table.
+TEST_F(StatsTableTest, VerifySlots) {
+ const std::string kTableName = "VerifySlotsStatTable";
+ const int kMaxThreads = 1;
+ const int kMaxCounter = 5;
+ DeleteShmem(kTableName);
+ StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+
+ // Register a single thread.
+ std::string thread_name = "mainThread";
+ int slot_id = table.RegisterThread(thread_name);
+ EXPECT_NE(slot_id, 0);
+
+ // Fill up the table with counters.
+ std::string counter_base_name = "counter";
+ for (int index = 0; index < kMaxCounter; index++) {
+ std::string counter_name = counter_base_name;
+ base::StringAppendF(&counter_name, "counter.ctr%d", index);
+ int counter_id = table.FindCounter(counter_name);
+ EXPECT_GT(counter_id, 0);
+ }
+
+ // Try to allocate an additional thread. Verify it fails.
+ slot_id = table.RegisterThread("too many threads");
+ EXPECT_EQ(slot_id, 0);
+
+ // Try to allocate an additional counter. Verify it fails.
+ int counter_id = table.FindCounter(counter_base_name);
+ EXPECT_EQ(counter_id, 0);
+
+ DeleteShmem(kTableName);
+}
+
+// CounterZero will continually be set to 0.
+const std::string kCounterZero = "CounterZero";
+// Counter1313 will continually be set to 1313.
+const std::string kCounter1313 = "Counter1313";
+// CounterIncrement will be incremented each time.
+const std::string kCounterIncrement = "CounterIncrement";
+// CounterDecrement will be decremented each time.
+const std::string kCounterDecrement = "CounterDecrement";
+// CounterMixed will be incremented by odd numbered threads and
+// decremented by even threads.
+const std::string kCounterMixed = "CounterMixed";
+// The number of thread loops that we will do.
+const int kThreadLoops = 100;
+
+class StatsTableThread : public SimpleThread {
+ public:
+ StatsTableThread(std::string name, int id)
+ : SimpleThread(name),
+ id_(id) {}
+
+ virtual void Run() OVERRIDE;
+
+ private:
+ int id_;
+};
+
+void StatsTableThread::Run() {
+ // Each thread will open the shared memory and set counters
+ // concurrently in a loop. We'll use some pauses to
+ // mixup the thread scheduling.
+
+ StatsCounter zero_counter(kCounterZero);
+ StatsCounter lucky13_counter(kCounter1313);
+ StatsCounter increment_counter(kCounterIncrement);
+ StatsCounter decrement_counter(kCounterDecrement);
+ for (int index = 0; index < kThreadLoops; index++) {
+ StatsCounter mixed_counter(kCounterMixed); // create this one in the loop
+ zero_counter.Set(0);
+ lucky13_counter.Set(1313);
+ increment_counter.Increment();
+ decrement_counter.Decrement();
+ if (id_ % 2)
+ mixed_counter.Decrement();
+ else
+ mixed_counter.Increment();
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(index % 10));
+ }
+}
+
+// Create a few threads and have them poke on their counters.
+// See http://crbug.com/10611 for more information.
+#if defined(OS_MACOSX)
+#define MAYBE_MultipleThreads DISABLED_MultipleThreads
+#else
+#define MAYBE_MultipleThreads MultipleThreads
+#endif
+TEST_F(StatsTableTest, MAYBE_MultipleThreads) {
+ // Create a stats table.
+ const std::string kTableName = "MultipleThreadStatTable";
+ const int kMaxThreads = 20;
+ const int kMaxCounter = 5;
+ DeleteShmem(kTableName);
+ StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+ StatsTable::set_current(&table);
+
+ EXPECT_EQ(0, table.CountThreadsRegistered());
+
+ // Spin up a set of threads to go bang on the various counters.
+ // After we join the threads, we'll make sure the counters
+ // contain the values we expected.
+ StatsTableThread* threads[kMaxThreads];
+
+ // Spawn the threads.
+ for (int index = 0; index < kMaxThreads; index++) {
+ threads[index] = new StatsTableThread("MultipleThreadsTest", index);
+ threads[index]->Start();
+ }
+
+ // Wait for the threads to finish.
+ for (int index = 0; index < kMaxThreads; index++) {
+ threads[index]->Join();
+ delete threads[index];
+ }
+
+ StatsCounter zero_counter(kCounterZero);
+ StatsCounter lucky13_counter(kCounter1313);
+ StatsCounter increment_counter(kCounterIncrement);
+ StatsCounter decrement_counter(kCounterDecrement);
+ StatsCounter mixed_counter(kCounterMixed);
+
+ // Verify the various counters are correct.
+ std::string name;
+ name = "c:" + kCounterZero;
+ EXPECT_EQ(0, table.GetCounterValue(name));
+ name = "c:" + kCounter1313;
+ EXPECT_EQ(1313 * kMaxThreads,
+ table.GetCounterValue(name));
+ name = "c:" + kCounterIncrement;
+ EXPECT_EQ(kMaxThreads * kThreadLoops,
+ table.GetCounterValue(name));
+ name = "c:" + kCounterDecrement;
+ EXPECT_EQ(-kMaxThreads * kThreadLoops,
+ table.GetCounterValue(name));
+ name = "c:" + kCounterMixed;
+ EXPECT_EQ((kMaxThreads % 2) * kThreadLoops,
+ table.GetCounterValue(name));
+ EXPECT_EQ(0, table.CountThreadsRegistered());
+
+ DeleteShmem(kTableName);
+}
+
+const std::string kMPTableName = "MultipleProcessStatTable";
+
+MULTIPROCESS_TEST_MAIN(StatsTableMultipleProcessMain) {
+ // Each process will open the shared memory and set counters
+ // concurrently in a loop. We'll use some pauses to
+ // mixup the scheduling.
+
+ StatsTable table(kMPTableName, 0, 0);
+ StatsTable::set_current(&table);
+ StatsCounter zero_counter(kCounterZero);
+ StatsCounter lucky13_counter(kCounter1313);
+ StatsCounter increment_counter(kCounterIncrement);
+ StatsCounter decrement_counter(kCounterDecrement);
+ for (int index = 0; index < kThreadLoops; index++) {
+ zero_counter.Set(0);
+ lucky13_counter.Set(1313);
+ increment_counter.Increment();
+ decrement_counter.Decrement();
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(index % 10));
+ }
+ return 0;
+}
+
+// Create a few processes and have them poke on their counters.
+// This test is slow and flaky http://crbug.com/10611
+TEST_F(StatsTableTest, DISABLED_MultipleProcesses) {
+ // Create a stats table.
+ const int kMaxProcs = 20;
+ const int kMaxCounter = 5;
+ DeleteShmem(kMPTableName);
+ StatsTable table(kMPTableName, kMaxProcs, kMaxCounter);
+ StatsTable::set_current(&table);
+ EXPECT_EQ(0, table.CountThreadsRegistered());
+
+ // Spin up a set of processes to go bang on the various counters.
+ // After we join the processes, we'll make sure the counters
+ // contain the values we expected.
+ ProcessHandle procs[kMaxProcs];
+
+ // Spawn the processes.
+ for (int16 index = 0; index < kMaxProcs; index++) {
+ procs[index] = this->SpawnChild("StatsTableMultipleProcessMain", false);
+ EXPECT_NE(kNullProcessHandle, procs[index]);
+ }
+
+ // Wait for the processes to finish.
+ for (int index = 0; index < kMaxProcs; index++) {
+ EXPECT_TRUE(WaitForSingleProcess(
+ procs[index], base::TimeDelta::FromMinutes(1)));
+ CloseProcessHandle(procs[index]);
+ }
+
+ StatsCounter zero_counter(kCounterZero);
+ StatsCounter lucky13_counter(kCounter1313);
+ StatsCounter increment_counter(kCounterIncrement);
+ StatsCounter decrement_counter(kCounterDecrement);
+
+ // Verify the various counters are correct.
+ std::string name;
+ name = "c:" + kCounterZero;
+ EXPECT_EQ(0, table.GetCounterValue(name));
+ name = "c:" + kCounter1313;
+ EXPECT_EQ(1313 * kMaxProcs,
+ table.GetCounterValue(name));
+ name = "c:" + kCounterIncrement;
+ EXPECT_EQ(kMaxProcs * kThreadLoops,
+ table.GetCounterValue(name));
+ name = "c:" + kCounterDecrement;
+ EXPECT_EQ(-kMaxProcs * kThreadLoops,
+ table.GetCounterValue(name));
+ EXPECT_EQ(0, table.CountThreadsRegistered());
+
+ DeleteShmem(kMPTableName);
+}
+
+class MockStatsCounter : public StatsCounter {
+ public:
+ explicit MockStatsCounter(const std::string& name)
+ : StatsCounter(name) {}
+ int* Pointer() { return GetPtr(); }
+};
+
+// Test some basic StatsCounter operations
+TEST_F(StatsTableTest, StatsCounter) {
+ // Create a stats table.
+ const std::string kTableName = "StatTable";
+ const int kMaxThreads = 20;
+ const int kMaxCounter = 5;
+ DeleteShmem(kTableName);
+ StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+ StatsTable::set_current(&table);
+
+ MockStatsCounter foo("foo");
+
+ // Test initial state.
+ EXPECT_TRUE(foo.Enabled());
+ ASSERT_NE(foo.Pointer(), static_cast<int*>(0));
+ EXPECT_EQ(0, *(foo.Pointer()));
+ EXPECT_EQ(0, table.GetCounterValue("c:foo"));
+
+ // Test Increment.
+ while (*(foo.Pointer()) < 123) foo.Increment();
+ EXPECT_EQ(123, table.GetCounterValue("c:foo"));
+ foo.Add(0);
+ EXPECT_EQ(123, table.GetCounterValue("c:foo"));
+ foo.Add(-1);
+ EXPECT_EQ(122, table.GetCounterValue("c:foo"));
+
+ // Test Set.
+ foo.Set(0);
+ EXPECT_EQ(0, table.GetCounterValue("c:foo"));
+ foo.Set(100);
+ EXPECT_EQ(100, table.GetCounterValue("c:foo"));
+ foo.Set(-1);
+ EXPECT_EQ(-1, table.GetCounterValue("c:foo"));
+ foo.Set(0);
+ EXPECT_EQ(0, table.GetCounterValue("c:foo"));
+
+ // Test Decrement.
+ foo.Subtract(1);
+ EXPECT_EQ(-1, table.GetCounterValue("c:foo"));
+ foo.Subtract(0);
+ EXPECT_EQ(-1, table.GetCounterValue("c:foo"));
+ foo.Subtract(-1);
+ EXPECT_EQ(0, table.GetCounterValue("c:foo"));
+
+ DeleteShmem(kTableName);
+}
+
+class MockStatsCounterTimer : public StatsCounterTimer {
+ public:
+ explicit MockStatsCounterTimer(const std::string& name)
+ : StatsCounterTimer(name) {}
+
+ TimeTicks start_time() { return start_time_; }
+ TimeTicks stop_time() { return stop_time_; }
+};
+
+// Test some basic StatsCounterTimer operations
+TEST_F(StatsTableTest, StatsCounterTimer) {
+ // Create a stats table.
+ const std::string kTableName = "StatTable";
+ const int kMaxThreads = 20;
+ const int kMaxCounter = 5;
+ DeleteShmem(kTableName);
+ StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+ StatsTable::set_current(&table);
+
+ MockStatsCounterTimer bar("bar");
+
+ // Test initial state.
+ EXPECT_FALSE(bar.Running());
+ EXPECT_TRUE(bar.start_time().is_null());
+ EXPECT_TRUE(bar.stop_time().is_null());
+
+ const TimeDelta kDuration = TimeDelta::FromMilliseconds(100);
+
+ // Do some timing.
+ bar.Start();
+ PlatformThread::Sleep(kDuration);
+ bar.Stop();
+ EXPECT_GT(table.GetCounterValue("t:bar"), 0);
+ EXPECT_LE(kDuration.InMilliseconds(), table.GetCounterValue("t:bar"));
+
+ // Verify that timing again is additive.
+ bar.Start();
+ PlatformThread::Sleep(kDuration);
+ bar.Stop();
+ EXPECT_GT(table.GetCounterValue("t:bar"), 0);
+ EXPECT_LE(kDuration.InMilliseconds() * 2, table.GetCounterValue("t:bar"));
+ DeleteShmem(kTableName);
+}
+
+// Test some basic StatsRate operations
+TEST_F(StatsTableTest, StatsRate) {
+ // Create a stats table.
+ const std::string kTableName = "StatTable";
+ const int kMaxThreads = 20;
+ const int kMaxCounter = 5;
+ DeleteShmem(kTableName);
+ StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+ StatsTable::set_current(&table);
+
+ StatsRate baz("baz");
+
+ // Test initial state.
+ EXPECT_FALSE(baz.Running());
+ EXPECT_EQ(0, table.GetCounterValue("c:baz"));
+ EXPECT_EQ(0, table.GetCounterValue("t:baz"));
+
+ const TimeDelta kDuration = TimeDelta::FromMilliseconds(100);
+
+ // Do some timing.
+ baz.Start();
+ PlatformThread::Sleep(kDuration);
+ baz.Stop();
+ EXPECT_EQ(1, table.GetCounterValue("c:baz"));
+ EXPECT_LE(kDuration.InMilliseconds(), table.GetCounterValue("t:baz"));
+
+ // Verify that timing again is additive.
+ baz.Start();
+ PlatformThread::Sleep(kDuration);
+ baz.Stop();
+ EXPECT_EQ(2, table.GetCounterValue("c:baz"));
+ EXPECT_LE(kDuration.InMilliseconds() * 2, table.GetCounterValue("t:baz"));
+ DeleteShmem(kTableName);
+}
+
+// Test some basic StatsScope operations
+TEST_F(StatsTableTest, StatsScope) {
+ // Create a stats table.
+ const std::string kTableName = "StatTable";
+ const int kMaxThreads = 20;
+ const int kMaxCounter = 5;
+ DeleteShmem(kTableName);
+ StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+ StatsTable::set_current(&table);
+
+ StatsCounterTimer foo("foo");
+ StatsRate bar("bar");
+
+ // Test initial state.
+ EXPECT_EQ(0, table.GetCounterValue("t:foo"));
+ EXPECT_EQ(0, table.GetCounterValue("t:bar"));
+ EXPECT_EQ(0, table.GetCounterValue("c:bar"));
+
+ const TimeDelta kDuration = TimeDelta::FromMilliseconds(100);
+
+ // Try a scope.
+ {
+ StatsScope<StatsCounterTimer> timer(foo);
+ StatsScope<StatsRate> timer2(bar);
+ PlatformThread::Sleep(kDuration);
+ }
+ EXPECT_LE(kDuration.InMilliseconds(), table.GetCounterValue("t:foo"));
+ EXPECT_LE(kDuration.InMilliseconds(), table.GetCounterValue("t:bar"));
+ EXPECT_EQ(1, table.GetCounterValue("c:bar"));
+
+ // Try a second scope.
+ {
+ StatsScope<StatsCounterTimer> timer(foo);
+ StatsScope<StatsRate> timer2(bar);
+ PlatformThread::Sleep(kDuration);
+ }
+ EXPECT_LE(kDuration.InMilliseconds() * 2, table.GetCounterValue("t:foo"));
+ EXPECT_LE(kDuration.InMilliseconds() * 2, table.GetCounterValue("t:bar"));
+ EXPECT_EQ(2, table.GetCounterValue("c:bar"));
+
+ DeleteShmem(kTableName);
+}
+
+} // namespace base
diff --git a/src/base/move.h b/src/base/move.h
new file mode 100644
index 0000000..b92f22e
--- /dev/null
+++ b/src/base/move.h
@@ -0,0 +1,207 @@
+// Copyright (c) 2012 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_MOVE_H_
+#define BASE_MOVE_H_
+
+// Macro with the boilerplate that makes a type move-only in C++03.
+//
+// USAGE
+//
+// This macro should be used instead of DISALLOW_COPY_AND_ASSIGN to create
+// a "move-only" type. Unlike DISALLOW_COPY_AND_ASSIGN, this macro should be
+// the first line in a class declaration.
+//
+// A class using this macro must call .Pass() (or somehow be an r-value already)
+// before it can be:
+//
+// * Passed as a function argument
+// * Used as the right-hand side of an assignment
+// * Returned from a function
+//
+// Each class will still need to define their own "move constructor" and "move
+// operator=" to make this useful. Here's an example of the macro, the move
+// constructor, and the move operator= from the scoped_ptr class:
+//
+// template <typename T>
+// class scoped_ptr {
+// MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue)
+// public:
+// scoped_ptr(RValue& other) : ptr_(other.release()) { }
+// scoped_ptr& operator=(RValue& other) {
+// swap(other);
+// return *this;
+// }
+// };
+//
+// Note that the constructor must NOT be marked explicit.
+//
+// For consistency, the second parameter to the macro should always be RValue
+// unless you have a strong reason to do otherwise. It is only exposed as a
+// macro parameter so that the move constructor and move operator= don't look
+// like they're using a phantom type.
+//
+//
+// HOW THIS WORKS
+//
+// For a thorough explanation of this technique, see:
+//
+// http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Move_Constructor
+//
+// The summary is that we take advantage of 2 properties:
+//
+// 1) non-const references will not bind to r-values.
+// 2) C++ can apply one user-defined conversion when initializing a
+// variable.
+//
+// The first lets us disable the copy constructor and assignment operator
+// by declaring private version of them with a non-const reference parameter.
+//
+// For l-values, direct initialization still fails like in
+// DISALLOW_COPY_AND_ASSIGN because the copy constructor and assignment
+// operators are private.
+//
+// For r-values, the situation is different. The copy constructor and
+// assignment operator are not viable due to (1), so we are trying to call
+// a non-existent constructor and non-existing operator= rather than a private
+// one. Since we have not committed an error quite yet, we can provide an
+// alternate conversion sequence and a constructor. We add
+//
+// * a private struct named "RValue"
+// * a user-defined conversion "operator RValue()"
+// * a "move constructor" and "move operator=" that take the RValue& as
+// their sole parameter.
+//
+// Only r-values will trigger this sequence and execute our "move constructor"
+// or "move operator=." L-values will match the private copy constructor and
+// operator= first giving a "private in this context" error. This combination
+// gives us a move-only type.
+//
+// For signaling a destructive transfer of data from an l-value, we provide a
+// method named Pass() which creates an r-value for the current instance
+// triggering the move constructor or move operator=.
+//
+// Other ways to get r-values is to use the result of an expression like a
+// function call.
+//
+// Here's an example with comments explaining what gets triggered where:
+//
+// class Foo {
+// MOVE_ONLY_TYPE_FOR_CPP_03(Foo, RValue);
+//
+// public:
+// ... API ...
+// Foo(RValue other); // Move constructor.
+// Foo& operator=(RValue rhs); // Move operator=
+// };
+//
+// Foo MakeFoo(); // Function that returns a Foo.
+//
+// Foo f;
+// Foo f_copy(f); // ERROR: Foo(Foo&) is private in this context.
+// Foo f_assign;
+// f_assign = f; // ERROR: operator=(Foo&) is private in this context.
+//
+//
+// Foo f(MakeFoo()); // R-value so alternate conversion executed.
+// Foo f_copy(f.Pass()); // R-value so alternate conversion executed.
+// f = f_copy.Pass(); // R-value so alternate conversion executed.
+//
+//
+// IMPLEMENTATION SUBTLETIES WITH RValue
+//
+// The RValue struct is just a container for a pointer back to the original
+// object. It should only ever be created as a temporary, and no external
+// class should ever declare it or use it in a parameter.
+//
+// It is tempting to want to use the RValue type in function parameters, but
+// excluding the limited usage here for the move constructor and move
+// operator=, doing so would mean that the function could take both r-values
+// and l-values equially which is unexpected. See COMPARED To Boost.Move for
+// more details.
+//
+// An alternate, and incorrect, implementation of the RValue class used by
+// Boost.Move makes RValue a fieldless child of the move-only type. RValue&
+// is then used in place of RValue in the various operators. The RValue& is
+// "created" by doing *reinterpret_cast<RValue*>(this). This has the appeal
+// of never creating a temproary RValue struct even with optimizations
+// disabled. Also, by virtue of inheritance you can treat the RValue
+// reference as if it were the move-only type itself. Unfortuantely,
+// using the result of this reinterpret_cast<> is actually undefined behavior
+// due to C++98 5.2.10.7. In certain compilers (eg., NaCl) the optimizer
+// will generate non-working code.
+//
+// In optimized builds, both implementations generate the same assembly so we
+// choose the one that adheres to the standard.
+//
+//
+// COMPARED TO C++11
+//
+// In C++11, you would implement this functionality using an r-value reference
+// and our .Pass() method would be replaced with a call to std::move().
+//
+// This emulation also has a deficiency where it uses up the single
+// user-defined conversion allowed by C++ during initialization. This can
+// cause problems in some API edge cases. For instance, in scoped_ptr, it is
+// impossible to make an function "void Foo(scoped_ptr<Parent> p)" accept a
+// value of type scoped_ptr<Child> even if you add a constructor to
+// scoped_ptr<> that would make it look like it should work. C++11 does not
+// have this deficiency.
+//
+//
+// COMPARED TO Boost.Move
+//
+// Our implementation similar to Boost.Move, but we keep the RValue struct
+// private to the move-only type, and we don't use the reinterpret_cast<> hack.
+//
+// In Boost.Move, RValue is the boost::rv<> template. This type can be used
+// when writing APIs like:
+//
+// void MyFunc(boost::rv<Foo>& f)
+//
+// that can take advantage of rv<> to avoid extra copies of a type. However you
+// would still be able to call this version of MyFunc with an l-value:
+//
+// Foo f;
+// MyFunc(f); // Uh oh, we probably just destroyed |f| w/o calling Pass().
+//
+// unless someone is very careful to also declare a parallel override like:
+//
+// void MyFunc(const Foo& f)
+//
+// that would catch the l-values first. This was declared unsafe in C++11 and
+// a C++11 compiler will explicitly fail MyFunc(f). Unfortunately, we cannot
+// ensure this in C++03.
+//
+// Since we have no need for writing such APIs yet, our implementation keeps
+// RValue private and uses a .Pass() method to do the conversion instead of
+// trying to write a version of "std::move()." Writing an API like std::move()
+// would require the RValue structs to be public.
+//
+//
+// CAVEATS
+//
+// If you include a move-only type as a field inside a class that does not
+// explicitly declare a copy constructor, the containing class's implicit
+// copy constructor will change from Containing(const Containing&) to
+// Containing(Containing&). This can cause some unexpected errors.
+//
+// http://llvm.org/bugs/show_bug.cgi?id=11528
+//
+// The workaround is to explicitly declare your copy constructor.
+//
+#define MOVE_ONLY_TYPE_FOR_CPP_03(type, rvalue_type) \
+ private: \
+ struct rvalue_type { \
+ rvalue_type(type* object) : object(object) {} \
+ type* object; \
+ }; \
+ type(type&); \
+ void operator=(type&); \
+ public: \
+ operator rvalue_type() { return rvalue_type(this); } \
+ type Pass() { return type(rvalue_type(this)); } \
+ private:
+
+#endif // BASE_MOVE_H_
diff --git a/src/base/native_library.h b/src/base/native_library.h
new file mode 100644
index 0000000..845989d
--- /dev/null
+++ b/src/base/native_library.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_NATIVE_LIBRARY_H_
+#define BASE_NATIVE_LIBRARY_H_
+
+// This file defines a cross-platform "NativeLibrary" type which represents
+// a loadable module.
+
+#include "base/base_export.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_MACOSX)
+#import <CoreFoundation/CoreFoundation.h>
+#endif // OS_*
+
+#include "base/string16.h"
+
+// Macro useful for writing cross-platform function pointers.
+#if defined(OS_WIN) && !defined(CDECL)
+#define CDECL __cdecl
+#else
+#define CDECL
+#endif
+
+class FilePath;
+
+namespace base {
+
+#if defined(OS_WIN)
+typedef HMODULE NativeLibrary;
+#elif defined(OS_MACOSX)
+enum NativeLibraryType {
+ BUNDLE,
+ DYNAMIC_LIB
+};
+struct NativeLibraryStruct {
+ NativeLibraryType type;
+ CFBundleRefNum bundle_resource_ref;
+ union {
+ CFBundleRef bundle;
+ void* dylib;
+ };
+};
+typedef NativeLibraryStruct* NativeLibrary;
+#elif defined(OS_POSIX)
+typedef void* NativeLibrary;
+#endif // OS_*
+
+// Loads a native library from disk. Release it with UnloadNativeLibrary when
+// you're done. Returns NULL on failure.
+// If |err| is not NULL, it may be filled in with an error message on
+// error.
+BASE_EXPORT NativeLibrary LoadNativeLibrary(const FilePath& library_path,
+ std::string* error);
+
+#if defined(OS_WIN)
+// Loads a native library from disk. Release it with UnloadNativeLibrary when
+// you're done.
+// This function retrieves the LoadLibrary function exported from kernel32.dll
+// and calls it instead of directly calling the LoadLibrary function via the
+// import table.
+BASE_EXPORT NativeLibrary LoadNativeLibraryDynamically(
+ const FilePath& library_path);
+#endif // OS_WIN
+
+// Unloads a native library.
+BASE_EXPORT void UnloadNativeLibrary(NativeLibrary library);
+
+// Gets a function pointer from a native library.
+BASE_EXPORT void* GetFunctionPointerFromNativeLibrary(NativeLibrary library,
+ const char* name);
+
+// Returns the full platform specific name for a native library.
+// For example:
+// "mylib" returns "mylib.dll" on Windows, "libmylib.so" on Linux,
+// "mylib.dylib" on Mac.
+BASE_EXPORT string16 GetNativeLibraryName(const string16& name);
+
+} // namespace base
+
+#endif // BASE_NATIVE_LIBRARY_H_
diff --git a/src/base/native_library_mac.mm b/src/base/native_library_mac.mm
new file mode 100644
index 0000000..9f7a967
--- /dev/null
+++ b/src/base/native_library_mac.mm
@@ -0,0 +1,80 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/native_library.h"
+
+#include <dlfcn.h>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/string_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/utf_string_conversions.h"
+
+namespace base {
+
+// static
+NativeLibrary LoadNativeLibrary(const FilePath& library_path,
+ std::string* error) {
+ // dlopen() etc. open the file off disk.
+ if (library_path.Extension() == "dylib" ||
+ !file_util::DirectoryExists(library_path)) {
+ void* dylib = dlopen(library_path.value().c_str(), RTLD_LAZY);
+ if (!dylib)
+ return NULL;
+ NativeLibrary native_lib = new NativeLibraryStruct();
+ native_lib->type = DYNAMIC_LIB;
+ native_lib->dylib = dylib;
+ return native_lib;
+ }
+ base::mac::ScopedCFTypeRef<CFURLRef> url(
+ CFURLCreateFromFileSystemRepresentation(
+ kCFAllocatorDefault,
+ (const UInt8*)library_path.value().c_str(),
+ library_path.value().length(),
+ true));
+ if (!url)
+ return NULL;
+ CFBundleRef bundle = CFBundleCreate(kCFAllocatorDefault, url.get());
+ if (!bundle)
+ return NULL;
+
+ NativeLibrary native_lib = new NativeLibraryStruct();
+ native_lib->type = BUNDLE;
+ native_lib->bundle = bundle;
+ native_lib->bundle_resource_ref = CFBundleOpenBundleResourceMap(bundle);
+ return native_lib;
+}
+
+// static
+void UnloadNativeLibrary(NativeLibrary library) {
+ if (library->type == BUNDLE) {
+ CFBundleCloseBundleResourceMap(library->bundle,
+ library->bundle_resource_ref);
+ CFRelease(library->bundle);
+ } else {
+ dlclose(library->dylib);
+ }
+ delete library;
+}
+
+// static
+void* GetFunctionPointerFromNativeLibrary(NativeLibrary library,
+ const char* name) {
+ if (library->type == BUNDLE) {
+ base::mac::ScopedCFTypeRef<CFStringRef> symbol_name(
+ CFStringCreateWithCString(kCFAllocatorDefault, name,
+ kCFStringEncodingUTF8));
+ return CFBundleGetFunctionPointerForName(library->bundle, symbol_name);
+ }
+ return dlsym(library->dylib, name);
+}
+
+// static
+string16 GetNativeLibraryName(const string16& name) {
+ return name + ASCIIToUTF16(".dylib");
+}
+
+} // namespace base
diff --git a/src/base/native_library_posix.cc b/src/base/native_library_posix.cc
new file mode 100644
index 0000000..4b82ff4
--- /dev/null
+++ b/src/base/native_library_posix.cc
@@ -0,0 +1,53 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/native_library.h"
+
+#include <dlfcn.h>
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/utf_string_conversions.h"
+
+namespace base {
+
+// static
+NativeLibrary LoadNativeLibrary(const FilePath& library_path,
+ std::string* error) {
+ // dlopen() opens the file off disk.
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ // We deliberately do not use RTLD_DEEPBIND. For the history why, please
+ // refer to the bug tracker. Some useful bug reports to read include:
+ // http://crbug.com/17943, http://crbug.com/17557, http://crbug.com/36892,
+ // and http://crbug.com/40794.
+ void* dl = dlopen(library_path.value().c_str(), RTLD_LAZY);
+ if (!dl && error)
+ *error = dlerror();
+
+ return dl;
+}
+
+// static
+void UnloadNativeLibrary(NativeLibrary library) {
+ int ret = dlclose(library);
+ if (ret < 0) {
+ DLOG(ERROR) << "dlclose failed: " << dlerror();
+ NOTREACHED();
+ }
+}
+
+// static
+void* GetFunctionPointerFromNativeLibrary(NativeLibrary library,
+ const char* name) {
+ return dlsym(library, name);
+}
+
+// static
+string16 GetNativeLibraryName(const string16& name) {
+ return ASCIIToUTF16("lib") + name + ASCIIToUTF16(".so");
+}
+
+} // namespace base
diff --git a/src/base/native_library_shell.cc b/src/base/native_library_shell.cc
new file mode 100644
index 0000000..40cf8b2
--- /dev/null
+++ b/src/base/native_library_shell.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/native_library.h"
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/utf_string_conversions.h"
+
+namespace base {
+
+// static
+NativeLibrary LoadNativeLibrary(const FilePath& library_path,
+ std::string* error) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+// static
+void UnloadNativeLibrary(NativeLibrary library) {
+ NOTIMPLEMENTED();
+}
+
+// static
+void* GetFunctionPointerFromNativeLibrary(NativeLibrary library,
+ const char* name) {
+ NOTIMPLEMENTED();
+ return NULL;
+}
+
+// static
+string16 GetNativeLibraryName(const string16& name) {
+ NOTIMPLEMENTED();
+ return string16();
+}
+
+} // namespace base
diff --git a/src/base/native_library_win.cc b/src/base/native_library_win.cc
new file mode 100644
index 0000000..af00cd1
--- /dev/null
+++ b/src/base/native_library_win.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/native_library.h"
+
+#include <windows.h>
+
+#include "base/file_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/utf_string_conversions.h"
+
+namespace base {
+
+typedef HMODULE (WINAPI* LoadLibraryFunction)(const wchar_t* file_name);
+
+NativeLibrary LoadNativeLibraryHelper(const FilePath& library_path,
+ LoadLibraryFunction load_library_api) {
+ // LoadLibrary() opens the file off disk.
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ // Switch the current directory to the library directory as the library
+ // may have dependencies on DLLs in this directory.
+ bool restore_directory = false;
+ FilePath current_directory;
+ if (file_util::GetCurrentDirectory(¤t_directory)) {
+ FilePath plugin_path = library_path.DirName();
+ if (!plugin_path.empty()) {
+ file_util::SetCurrentDirectory(plugin_path);
+ restore_directory = true;
+ }
+ }
+
+ HMODULE module = (*load_library_api)(library_path.value().c_str());
+ if (restore_directory)
+ file_util::SetCurrentDirectory(current_directory);
+
+ return module;
+}
+
+// static
+NativeLibrary LoadNativeLibrary(const FilePath& library_path,
+ std::string* error) {
+ return LoadNativeLibraryHelper(library_path, LoadLibraryW);
+}
+
+NativeLibrary LoadNativeLibraryDynamically(const FilePath& library_path) {
+ typedef HMODULE (WINAPI* LoadLibraryFunction)(const wchar_t* file_name);
+
+ LoadLibraryFunction load_library;
+ load_library = reinterpret_cast<LoadLibraryFunction>(
+ GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW"));
+
+ return LoadNativeLibraryHelper(library_path, load_library);
+}
+
+// static
+void UnloadNativeLibrary(NativeLibrary library) {
+ FreeLibrary(library);
+}
+
+// static
+void* GetFunctionPointerFromNativeLibrary(NativeLibrary library,
+ const char* name) {
+ return GetProcAddress(library, name);
+}
+
+// static
+string16 GetNativeLibraryName(const string16& name) {
+ return name + ASCIIToUTF16(".dll");
+}
+
+} // namespace base
diff --git a/src/base/nix/mime_util_xdg.cc b/src/base/nix/mime_util_xdg.cc
new file mode 100644
index 0000000..e58e03d
--- /dev/null
+++ b/src/base/nix/mime_util_xdg.cc
@@ -0,0 +1,678 @@
+// Copyright (c) 2012 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/nix/mime_util_xdg.h"
+
+#include <cstdlib>
+#include <list>
+#include <map>
+#include <vector>
+
+#include "base/environment.h"
+#include "base/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/nix/xdg_util.h"
+#include "base/string_split.h"
+#include "base/string_util.h"
+#include "base/synchronization/lock.h"
+#include "base/third_party/xdg_mime/xdgmime.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time.h"
+
+#if defined(TOOLKIT_GTK)
+#include <gtk/gtk.h> // NOLINT
+
+#include "base/message_loop.h"
+#endif
+
+namespace {
+
+class IconTheme;
+
+// None of the XDG stuff is thread-safe, so serialize all access under
+// this lock.
+base::LazyInstance<base::Lock>::Leaky
+ g_mime_util_xdg_lock = LAZY_INSTANCE_INITIALIZER;
+
+class MimeUtilConstants {
+ public:
+ typedef std::map<std::string, IconTheme*> IconThemeMap;
+ typedef std::map<FilePath, base::Time> IconDirMtimeMap;
+ typedef std::vector<std::string> IconFormats;
+
+ // Specified by XDG icon theme specs.
+ static const int kUpdateIntervalInSeconds = 5;
+
+ static const size_t kDefaultThemeNum = 4;
+
+ static MimeUtilConstants* GetInstance() {
+ return Singleton<MimeUtilConstants>::get();
+ }
+
+ // Store icon directories and their mtimes.
+ IconDirMtimeMap icon_dirs_;
+
+ // Store icon formats.
+ IconFormats icon_formats_;
+
+ // Store loaded icon_theme.
+ IconThemeMap icon_themes_;
+
+ // The default theme.
+ IconTheme* default_themes_[kDefaultThemeNum];
+
+ base::TimeTicks last_check_time_;
+
+#if defined(TOOLKIT_GTK)
+ // This is set by DetectGtkTheme(). We cache it so that we can access the
+ // theme name from threads that aren't allowed to call
+ // gtk_settings_get_default().
+ std::string gtk_theme_name_;
+#endif
+
+ private:
+ MimeUtilConstants() {
+ icon_formats_.push_back(".png");
+ icon_formats_.push_back(".svg");
+ icon_formats_.push_back(".xpm");
+
+ for (size_t i = 0; i < kDefaultThemeNum; ++i)
+ default_themes_[i] = NULL;
+ }
+ ~MimeUtilConstants();
+
+ friend struct DefaultSingletonTraits<MimeUtilConstants>;
+
+ DISALLOW_COPY_AND_ASSIGN(MimeUtilConstants);
+};
+
+// IconTheme represents an icon theme as defined by the xdg icon theme spec.
+// Example themes on GNOME include 'Human' and 'Mist'.
+// Example themes on KDE include 'crystalsvg' and 'kdeclassic'.
+class IconTheme {
+ public:
+ // A theme consists of multiple sub-directories, like '32x32' and 'scalable'.
+ class SubDirInfo {
+ public:
+ // See spec for details.
+ enum Type {
+ Fixed,
+ Scalable,
+ Threshold
+ };
+ SubDirInfo()
+ : size(0),
+ type(Threshold),
+ max_size(0),
+ min_size(0),
+ threshold(2) {
+ }
+ size_t size; // Nominal size of the icons in this directory.
+ Type type; // Type of the icon size.
+ size_t max_size; // Maximum size that the icons can be scaled to.
+ size_t min_size; // Minimum size that the icons can be scaled to.
+ size_t threshold; // Maximum difference from desired size. 2 by default.
+ };
+
+ explicit IconTheme(const std::string& name);
+
+ ~IconTheme() {}
+
+ // Returns the path to an icon with the name |icon_name| and a size of |size|
+ // pixels. If the icon does not exist, but |inherits| is true, then look for
+ // the icon in the parent theme.
+ FilePath GetIconPath(const std::string& icon_name, int size, bool inherits);
+
+ // Load a theme with the name |theme_name| into memory. Returns null if theme
+ // is invalid.
+ static IconTheme* LoadTheme(const std::string& theme_name);
+
+ private:
+ // Returns the path to an icon with the name |icon_name| in |subdir|.
+ FilePath GetIconPathUnderSubdir(const std::string& icon_name,
+ const std::string& subdir);
+
+ // Whether the theme loaded properly.
+ bool IsValid() {
+ return index_theme_loaded_;
+ }
+
+ // Read and parse |file| which is usually named 'index.theme' per theme spec.
+ bool LoadIndexTheme(const FilePath& file);
+
+ // Checks to see if the icons in |info| matches |size| (in pixels). Returns
+ // 0 if they match, or the size difference in pixels.
+ size_t MatchesSize(SubDirInfo* info, size_t size);
+
+ // Yet another function to read a line.
+ std::string ReadLine(FILE* fp);
+
+ // Set directories to search for icons to the comma-separated list |dirs|.
+ bool SetDirectories(const std::string& dirs);
+
+ bool index_theme_loaded_; // True if an instance is properly loaded.
+ // store the scattered directories of this theme.
+ std::list<FilePath> dirs_;
+
+ // store the subdirs of this theme and array index of |info_array_|.
+ std::map<std::string, int> subdirs_;
+ scoped_array<SubDirInfo> info_array_; // List of sub-directories.
+ std::string inherits_; // Name of the theme this one inherits from.
+};
+
+IconTheme::IconTheme(const std::string& name)
+ : index_theme_loaded_(false),
+ info_array_(NULL) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ // Iterate on all icon directories to find directories of the specified
+ // theme and load the first encountered index.theme.
+ MimeUtilConstants::IconDirMtimeMap::iterator iter;
+ FilePath theme_path;
+ MimeUtilConstants::IconDirMtimeMap* icon_dirs =
+ &MimeUtilConstants::GetInstance()->icon_dirs_;
+ for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) {
+ theme_path = iter->first.Append(name);
+ if (!file_util::DirectoryExists(theme_path))
+ continue;
+ FilePath theme_index = theme_path.Append("index.theme");
+ if (!index_theme_loaded_ && file_util::PathExists(theme_index)) {
+ if (!LoadIndexTheme(theme_index))
+ return;
+ index_theme_loaded_ = true;
+ }
+ dirs_.push_back(theme_path);
+ }
+}
+
+FilePath IconTheme::GetIconPath(const std::string& icon_name, int size,
+ bool inherits) {
+ std::map<std::string, int>::iterator subdir_iter;
+ FilePath icon_path;
+
+ for (subdir_iter = subdirs_.begin();
+ subdir_iter != subdirs_.end();
+ ++subdir_iter) {
+ SubDirInfo* info = &info_array_[subdir_iter->second];
+ if (MatchesSize(info, size) == 0) {
+ icon_path = GetIconPathUnderSubdir(icon_name, subdir_iter->first);
+ if (!icon_path.empty())
+ return icon_path;
+ }
+ }
+ // Now looking for the mostly matched.
+ size_t min_delta_seen = 9999;
+
+ for (subdir_iter = subdirs_.begin();
+ subdir_iter != subdirs_.end();
+ ++subdir_iter) {
+ SubDirInfo* info = &info_array_[subdir_iter->second];
+ size_t delta = MatchesSize(info, size);
+ if (delta < min_delta_seen) {
+ FilePath path = GetIconPathUnderSubdir(icon_name, subdir_iter->first);
+ if (!path.empty()) {
+ min_delta_seen = delta;
+ icon_path = path;
+ }
+ }
+ }
+
+ if (!icon_path.empty() || !inherits || inherits_ == "")
+ return icon_path;
+
+ IconTheme* theme = LoadTheme(inherits_);
+ // Inheriting from itself means the theme is buggy but we shouldn't crash.
+ if (theme && theme != this)
+ return theme->GetIconPath(icon_name, size, inherits);
+ else
+ return FilePath();
+}
+
+IconTheme* IconTheme::LoadTheme(const std::string& theme_name) {
+ scoped_ptr<IconTheme> theme;
+ MimeUtilConstants::IconThemeMap* icon_themes =
+ &MimeUtilConstants::GetInstance()->icon_themes_;
+ if (icon_themes->find(theme_name) != icon_themes->end()) {
+ theme.reset((*icon_themes)[theme_name]);
+ } else {
+ theme.reset(new IconTheme(theme_name));
+ if (!theme->IsValid())
+ theme.reset();
+ (*icon_themes)[theme_name] = theme.get();
+ }
+ return theme.release();
+}
+
+FilePath IconTheme::GetIconPathUnderSubdir(const std::string& icon_name,
+ const std::string& subdir) {
+ FilePath icon_path;
+ std::list<FilePath>::iterator dir_iter;
+ MimeUtilConstants::IconFormats* icon_formats =
+ &MimeUtilConstants::GetInstance()->icon_formats_;
+ for (dir_iter = dirs_.begin(); dir_iter != dirs_.end(); ++dir_iter) {
+ for (size_t i = 0; i < icon_formats->size(); ++i) {
+ icon_path = dir_iter->Append(subdir);
+ icon_path = icon_path.Append(icon_name + (*icon_formats)[i]);
+ if (file_util::PathExists(icon_path))
+ return icon_path;
+ }
+ }
+ return FilePath();
+}
+
+bool IconTheme::LoadIndexTheme(const FilePath& file) {
+ FILE* fp = file_util::OpenFile(file, "r");
+ SubDirInfo* current_info = NULL;
+ if (!fp)
+ return false;
+
+ // Read entries.
+ while (!feof(fp) && !ferror(fp)) {
+ std::string buf = ReadLine(fp);
+ if (buf == "")
+ break;
+
+ std::string entry;
+ TrimWhitespaceASCII(buf, TRIM_ALL, &entry);
+ if (entry.length() == 0 || entry[0] == '#') {
+ // Blank line or Comment.
+ continue;
+ } else if (entry[0] == '[' && info_array_.get()) {
+ current_info = NULL;
+ std::string subdir = entry.substr(1, entry.length() - 2);
+ if (subdirs_.find(subdir) != subdirs_.end())
+ current_info = &info_array_[subdirs_[subdir]];
+ }
+
+ std::string key, value;
+ std::vector<std::string> r;
+ base::SplitStringDontTrim(entry, '=', &r);
+ if (r.size() < 2)
+ continue;
+
+ TrimWhitespaceASCII(r[0], TRIM_ALL, &key);
+ for (size_t i = 1; i < r.size(); i++)
+ value.append(r[i]);
+ TrimWhitespaceASCII(value, TRIM_ALL, &value);
+
+ if (current_info) {
+ if (key == "Size") {
+ current_info->size = atoi(value.c_str());
+ } else if (key == "Type") {
+ if (value == "Fixed")
+ current_info->type = SubDirInfo::Fixed;
+ else if (value == "Scalable")
+ current_info->type = SubDirInfo::Scalable;
+ else if (value == "Threshold")
+ current_info->type = SubDirInfo::Threshold;
+ } else if (key == "MaxSize") {
+ current_info->max_size = atoi(value.c_str());
+ } else if (key == "MinSize") {
+ current_info->min_size = atoi(value.c_str());
+ } else if (key == "Threshold") {
+ current_info->threshold = atoi(value.c_str());
+ }
+ } else {
+ if (key.compare("Directories") == 0 && !info_array_.get()) {
+ if (!SetDirectories(value)) break;
+ } else if (key.compare("Inherits") == 0) {
+ if (value != "hicolor")
+ inherits_ = value;
+ }
+ }
+ }
+
+ file_util::CloseFile(fp);
+ return info_array_.get() != NULL;
+}
+
+size_t IconTheme::MatchesSize(SubDirInfo* info, size_t size) {
+ if (info->type == SubDirInfo::Fixed) {
+ if (size > info->size)
+ return size - info->size;
+ else
+ return info->size - size;
+ } else if (info->type == SubDirInfo::Scalable) {
+ if (size < info->min_size)
+ return info->min_size - size;
+ if (size > info->max_size)
+ return size - info->max_size;
+ return 0;
+ } else {
+ if (size + info->threshold < info->size)
+ return info->size - size - info->threshold;
+ if (size > info->size + info->threshold)
+ return size - info->size - info->threshold;
+ return 0;
+ }
+}
+
+std::string IconTheme::ReadLine(FILE* fp) {
+ if (!fp)
+ return "";
+
+ std::string result = "";
+ const size_t kBufferSize = 100;
+ char buffer[kBufferSize];
+ while ((fgets(buffer, kBufferSize - 1, fp)) != NULL) {
+ result += buffer;
+ size_t len = result.length();
+ if (len == 0)
+ break;
+ char end = result[len - 1];
+ if (end == '\n' || end == '\0')
+ break;
+ }
+
+ return result;
+}
+
+bool IconTheme::SetDirectories(const std::string& dirs) {
+ int num = 0;
+ std::string::size_type pos = 0, epos;
+ std::string dir;
+ while ((epos = dirs.find(',', pos)) != std::string::npos) {
+ TrimWhitespaceASCII(dirs.substr(pos, epos - pos), TRIM_ALL, &dir);
+ if (dir.length() == 0) {
+ DLOG(WARNING) << "Invalid index.theme: blank subdir";
+ return false;
+ }
+ subdirs_[dir] = num++;
+ pos = epos + 1;
+ }
+ TrimWhitespaceASCII(dirs.substr(pos), TRIM_ALL, &dir);
+ if (dir.length() == 0) {
+ DLOG(WARNING) << "Invalid index.theme: blank subdir";
+ return false;
+ }
+ subdirs_[dir] = num++;
+ info_array_.reset(new SubDirInfo[num]);
+ return true;
+}
+
+bool CheckDirExistsAndGetMtime(const FilePath& dir,
+ base::Time* last_modified) {
+ if (!file_util::DirectoryExists(dir))
+ return false;
+ base::PlatformFileInfo file_info;
+ if (!file_util::GetFileInfo(dir, &file_info))
+ return false;
+ *last_modified = file_info.last_modified;
+ return true;
+}
+
+// Make sure |dir| exists and add it to the list of icon directories.
+void TryAddIconDir(const FilePath& dir) {
+ base::Time last_modified;
+ if (!CheckDirExistsAndGetMtime(dir, &last_modified))
+ return;
+ MimeUtilConstants::GetInstance()->icon_dirs_[dir] = last_modified;
+}
+
+// For a xdg directory |dir|, add the appropriate icon sub-directories.
+void AddXDGDataDir(const FilePath& dir) {
+ if (!file_util::DirectoryExists(dir))
+ return;
+ TryAddIconDir(dir.Append("icons"));
+ TryAddIconDir(dir.Append("pixmaps"));
+}
+
+// Add all the xdg icon directories.
+void InitIconDir() {
+ FilePath home = file_util::GetHomeDir();
+ if (!home.empty()) {
+ FilePath legacy_data_dir(home);
+ legacy_data_dir = legacy_data_dir.AppendASCII(".icons");
+ if (file_util::DirectoryExists(legacy_data_dir))
+ TryAddIconDir(legacy_data_dir);
+ }
+ const char* env = getenv("XDG_DATA_HOME");
+ if (env) {
+ AddXDGDataDir(FilePath(env));
+ } else if (!home.empty()) {
+ FilePath local_data_dir(home);
+ local_data_dir = local_data_dir.AppendASCII(".local");
+ local_data_dir = local_data_dir.AppendASCII("share");
+ AddXDGDataDir(local_data_dir);
+ }
+
+ env = getenv("XDG_DATA_DIRS");
+ if (!env) {
+ AddXDGDataDir(FilePath("/usr/local/share"));
+ AddXDGDataDir(FilePath("/usr/share"));
+ } else {
+ std::string xdg_data_dirs = env;
+ std::string::size_type pos = 0, epos;
+ while ((epos = xdg_data_dirs.find(':', pos)) != std::string::npos) {
+ AddXDGDataDir(FilePath(xdg_data_dirs.substr(pos, epos - pos)));
+ pos = epos + 1;
+ }
+ AddXDGDataDir(FilePath(xdg_data_dirs.substr(pos)));
+ }
+}
+
+void EnsureUpdated() {
+ MimeUtilConstants* constants = MimeUtilConstants::GetInstance();
+ if (constants->last_check_time_.is_null()) {
+ constants->last_check_time_ = base::TimeTicks::Now();
+ InitIconDir();
+ return;
+ }
+
+ // Per xdg theme spec, we should check the icon directories every so often
+ // for newly added icons.
+ base::TimeDelta time_since_last_check =
+ base::TimeTicks::Now() - constants->last_check_time_;
+ if (time_since_last_check.InSeconds() > constants->kUpdateIntervalInSeconds) {
+ constants->last_check_time_ += time_since_last_check;
+
+ bool rescan_icon_dirs = false;
+ MimeUtilConstants::IconDirMtimeMap* icon_dirs = &constants->icon_dirs_;
+ MimeUtilConstants::IconDirMtimeMap::iterator iter;
+ for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) {
+ base::Time last_modified;
+ if (!CheckDirExistsAndGetMtime(iter->first, &last_modified) ||
+ last_modified != iter->second) {
+ rescan_icon_dirs = true;
+ break;
+ }
+ }
+
+ if (rescan_icon_dirs) {
+ constants->icon_dirs_.clear();
+ constants->icon_themes_.clear();
+ InitIconDir();
+ }
+ }
+}
+
+// Find a fallback icon if we cannot find it in the default theme.
+FilePath LookupFallbackIcon(const std::string& icon_name) {
+ MimeUtilConstants* constants = MimeUtilConstants::GetInstance();
+ MimeUtilConstants::IconDirMtimeMap::iterator iter;
+ MimeUtilConstants::IconDirMtimeMap* icon_dirs = &constants->icon_dirs_;
+ MimeUtilConstants::IconFormats* icon_formats = &constants->icon_formats_;
+ for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) {
+ for (size_t i = 0; i < icon_formats->size(); ++i) {
+ FilePath icon = iter->first.Append(icon_name + (*icon_formats)[i]);
+ if (file_util::PathExists(icon))
+ return icon;
+ }
+ }
+ return FilePath();
+}
+
+// Initialize the list of default themes.
+void InitDefaultThemes() {
+ IconTheme** default_themes =
+ MimeUtilConstants::GetInstance()->default_themes_;
+
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ base::nix::DesktopEnvironment desktop_env =
+ base::nix::GetDesktopEnvironment(env.get());
+ if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE3 ||
+ desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE4) {
+ // KDE
+ std::string kde_default_theme;
+ std::string kde_fallback_theme;
+
+ // TODO(thestig): Figure out how to get the current icon theme on KDE.
+ // Setting stored in ~/.kde/share/config/kdeglobals under Icons -> Theme.
+ default_themes[0] = NULL;
+
+ // Try some reasonable defaults for KDE.
+ if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE3) {
+ // KDE 3
+ kde_default_theme = "default.kde";
+ kde_fallback_theme = "crystalsvg";
+ } else {
+ // KDE 4
+ kde_default_theme = "default.kde4";
+ kde_fallback_theme = "oxygen";
+ }
+ default_themes[1] = IconTheme::LoadTheme(kde_default_theme);
+ default_themes[2] = IconTheme::LoadTheme(kde_fallback_theme);
+ } else {
+#if defined(TOOLKIT_GTK)
+ // Assume it's Gnome and use GTK to figure out the theme.
+ default_themes[1] = IconTheme::LoadTheme(
+ MimeUtilConstants::GetInstance()->gtk_theme_name_);
+ default_themes[2] = IconTheme::LoadTheme("gnome");
+#endif
+ }
+ // hicolor needs to be last per icon theme spec.
+ default_themes[3] = IconTheme::LoadTheme("hicolor");
+
+ for (size_t i = 0; i < MimeUtilConstants::kDefaultThemeNum; i++) {
+ if (default_themes[i] == NULL)
+ continue;
+ // NULL out duplicate pointers.
+ for (size_t j = i + 1; j < MimeUtilConstants::kDefaultThemeNum; j++) {
+ if (default_themes[j] == default_themes[i])
+ default_themes[j] = NULL;
+ }
+ }
+}
+
+// Try to find an icon with the name |icon_name| that's |size| pixels.
+FilePath LookupIconInDefaultTheme(const std::string& icon_name, int size) {
+ EnsureUpdated();
+ MimeUtilConstants* constants = MimeUtilConstants::GetInstance();
+ MimeUtilConstants::IconThemeMap* icon_themes = &constants->icon_themes_;
+ if (icon_themes->empty())
+ InitDefaultThemes();
+
+ FilePath icon_path;
+ IconTheme** default_themes = constants->default_themes_;
+ for (size_t i = 0; i < MimeUtilConstants::kDefaultThemeNum; i++) {
+ if (default_themes[i]) {
+ icon_path = default_themes[i]->GetIconPath(icon_name, size, true);
+ if (!icon_path.empty())
+ return icon_path;
+ }
+ }
+ return LookupFallbackIcon(icon_name);
+}
+
+MimeUtilConstants::~MimeUtilConstants() {
+ for (size_t i = 0; i < kDefaultThemeNum; i++)
+ delete default_themes_[i];
+}
+
+} // namespace
+
+namespace base {
+namespace nix {
+
+std::string GetFileMimeType(const FilePath& filepath) {
+ if (filepath.empty())
+ return std::string();
+ base::ThreadRestrictions::AssertIOAllowed();
+ base::AutoLock scoped_lock(g_mime_util_xdg_lock.Get());
+ return xdg_mime_get_mime_type_from_file_name(filepath.value().c_str());
+}
+
+std::string GetDataMimeType(const std::string& data) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ base::AutoLock scoped_lock(g_mime_util_xdg_lock.Get());
+ return xdg_mime_get_mime_type_for_data(data.data(), data.length(), NULL);
+}
+
+#if defined(TOOLKIT_GTK)
+void DetectGtkTheme() {
+ // If the theme name is already loaded, do nothing. Chrome doesn't respond
+ // to changes in the system theme, so we never need to set this more than
+ // once.
+ if (!MimeUtilConstants::GetInstance()->gtk_theme_name_.empty())
+ return;
+
+ // We should only be called on the UI thread.
+ DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type());
+
+ gchar* gtk_theme_name;
+ g_object_get(gtk_settings_get_default(),
+ "gtk-icon-theme-name",
+ >k_theme_name, NULL);
+ MimeUtilConstants::GetInstance()->gtk_theme_name_.assign(gtk_theme_name);
+ g_free(gtk_theme_name);
+}
+#endif
+
+FilePath GetMimeIcon(const std::string& mime_type, size_t size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ std::vector<std::string> icon_names;
+ std::string icon_name;
+ FilePath icon_file;
+
+ if (!mime_type.empty()) {
+ base::AutoLock scoped_lock(g_mime_util_xdg_lock.Get());
+ const char *icon = xdg_mime_get_icon(mime_type.c_str());
+ icon_name = std::string(icon ? icon : "");
+ }
+
+ if (icon_name.length())
+ icon_names.push_back(icon_name);
+
+ // For text/plain, try text-plain.
+ icon_name = mime_type;
+ for (size_t i = icon_name.find('/', 0); i != std::string::npos;
+ i = icon_name.find('/', i + 1)) {
+ icon_name[i] = '-';
+ }
+ icon_names.push_back(icon_name);
+ // Also try gnome-mime-text-plain.
+ icon_names.push_back("gnome-mime-" + icon_name);
+
+ // Try "deb" for "application/x-deb" in KDE 3.
+ size_t x_substr_pos = mime_type.find("/x-");
+ if (x_substr_pos != std::string::npos) {
+ icon_name = mime_type.substr(x_substr_pos + 3);
+ icon_names.push_back(icon_name);
+ }
+
+ // Try generic name like text-x-generic.
+ icon_name = mime_type.substr(0, mime_type.find('/')) + "-x-generic";
+ icon_names.push_back(icon_name);
+
+ // Last resort
+ icon_names.push_back("unknown");
+
+ for (size_t i = 0; i < icon_names.size(); i++) {
+ if (icon_names[i][0] == '/') {
+ icon_file = FilePath(icon_names[i]);
+ if (file_util::PathExists(icon_file))
+ return icon_file;
+ } else {
+ icon_file = LookupIconInDefaultTheme(icon_names[i], size);
+ if (!icon_file.empty())
+ return icon_file;
+ }
+ }
+ return FilePath();
+}
+
+} // namespace nix
+} // namespace base
diff --git a/src/base/nix/mime_util_xdg.h b/src/base/nix/mime_util_xdg.h
new file mode 100644
index 0000000..444ce4f
--- /dev/null
+++ b/src/base/nix/mime_util_xdg.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_NIX_MIME_UTIL_XDG_H_
+#define BASE_NIX_MIME_UTIL_XDG_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "build/build_config.h"
+
+class FilePath;
+
+namespace base {
+namespace nix {
+
+// Gets the mime type for a file based on its filename. The file path does not
+// have to exist. Please note because it doesn't touch the disk, this does not
+// work for directories.
+// If the mime type is unknown, this will return application/octet-stream.
+BASE_EXPORT std::string GetFileMimeType(const FilePath& filepath);
+
+// Get the mime type for a byte vector.
+BASE_EXPORT std::string GetDataMimeType(const std::string& data);
+
+#if defined(TOOLKIT_GTK)
+// This detects the current GTK theme by calling gtk_settings_get_default().
+// It should only be executed on the UI thread and must be called before
+// GetMimeIcon().
+BASE_EXPORT void DetectGtkTheme();
+#endif
+
+// Gets the file name for an icon given the mime type and icon pixel size.
+// Where an icon is a square image of |size| x |size|.
+// This will try to find the closest matching icon. If that's not available,
+// then a generic icon, and finally an empty FilePath if all else fails.
+BASE_EXPORT FilePath GetMimeIcon(const std::string& mime_type, size_t size);
+
+} // namespace nix
+} // namespace base
+
+#endif // BASE_NIX_MIME_UTIL_XDG_H_
diff --git a/src/base/nix/xdg_util.cc b/src/base/nix/xdg_util.cc
new file mode 100644
index 0000000..10cbcaa
--- /dev/null
+++ b/src/base/nix/xdg_util.cc
@@ -0,0 +1,115 @@
+// Copyright (c) 2012 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/nix/xdg_util.h"
+
+#include <string>
+
+#include "base/environment.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/third_party/xdg_user_dirs/xdg_user_dir_lookup.h"
+
+namespace {
+
+// The KDE session version environment variable used in KDE 4.
+const char kKDE4SessionEnvVar[] = "KDE_SESSION_VERSION";
+
+} // namespace
+
+namespace base {
+namespace nix {
+
+const char kDotConfigDir[] = ".config";
+const char kXdgConfigHomeEnvVar[] = "XDG_CONFIG_HOME";
+
+FilePath GetXDGDirectory(Environment* env, const char* env_name,
+ const char* fallback_dir) {
+ FilePath path;
+ std::string env_value;
+ if (env->GetVar(env_name, &env_value) && !env_value.empty())
+ path = FilePath(env_value);
+ else
+ path = file_util::GetHomeDir().Append(fallback_dir);
+ return path.StripTrailingSeparators();
+}
+
+FilePath GetXDGUserDirectory(const char* dir_name, const char* fallback_dir) {
+ FilePath path;
+ char* xdg_dir = xdg_user_dir_lookup(dir_name);
+ if (xdg_dir) {
+ path = FilePath(xdg_dir);
+ free(xdg_dir);
+ } else {
+ path = file_util::GetHomeDir().Append(fallback_dir);
+ }
+ return path.StripTrailingSeparators();
+}
+
+DesktopEnvironment GetDesktopEnvironment(Environment* env) {
+ // XDG_CURRENT_DESKTOP is the newest standard circa 2012.
+ std::string xdg_current_desktop;
+ if (env->GetVar("XDG_CURRENT_DESKTOP", &xdg_current_desktop)) {
+ // Not all desktop environments set this env var as of this writing.
+ if (xdg_current_desktop == "Unity")
+ return DESKTOP_ENVIRONMENT_UNITY;
+ else if (xdg_current_desktop == "GNOME")
+ return DESKTOP_ENVIRONMENT_GNOME;
+ }
+
+ // DESKTOP_SESSION was what everyone used in 2010.
+ std::string desktop_session;
+ if (env->GetVar("DESKTOP_SESSION", &desktop_session)) {
+ if (desktop_session == "gnome") {
+ return DESKTOP_ENVIRONMENT_GNOME;
+ } else if (desktop_session == "kde4") {
+ return DESKTOP_ENVIRONMENT_KDE4;
+ } else if (desktop_session == "kde") {
+ // This may mean KDE4 on newer systems, so we have to check.
+ if (env->HasVar(kKDE4SessionEnvVar))
+ return DESKTOP_ENVIRONMENT_KDE4;
+ return DESKTOP_ENVIRONMENT_KDE3;
+ } else if (desktop_session.find("xfce") != std::string::npos ||
+ desktop_session == "xubuntu") {
+ return DESKTOP_ENVIRONMENT_XFCE;
+ }
+ }
+
+ // Fall back on some older environment variables.
+ // Useful particularly in the DESKTOP_SESSION=default case.
+ if (env->HasVar("GNOME_DESKTOP_SESSION_ID")) {
+ return DESKTOP_ENVIRONMENT_GNOME;
+ } else if (env->HasVar("KDE_FULL_SESSION")) {
+ if (env->HasVar(kKDE4SessionEnvVar))
+ return DESKTOP_ENVIRONMENT_KDE4;
+ return DESKTOP_ENVIRONMENT_KDE3;
+ }
+
+ return DESKTOP_ENVIRONMENT_OTHER;
+}
+
+const char* GetDesktopEnvironmentName(DesktopEnvironment env) {
+ switch (env) {
+ case DESKTOP_ENVIRONMENT_OTHER:
+ return NULL;
+ case DESKTOP_ENVIRONMENT_GNOME:
+ return "GNOME";
+ case DESKTOP_ENVIRONMENT_KDE3:
+ return "KDE3";
+ case DESKTOP_ENVIRONMENT_KDE4:
+ return "KDE4";
+ case DESKTOP_ENVIRONMENT_UNITY:
+ return "UNITY";
+ case DESKTOP_ENVIRONMENT_XFCE:
+ return "XFCE";
+ }
+ return NULL;
+}
+
+const char* GetDesktopEnvironmentName(Environment* env) {
+ return GetDesktopEnvironmentName(GetDesktopEnvironment(env));
+}
+
+} // namespace nix
+} // namespace base
diff --git a/src/base/nix/xdg_util.h b/src/base/nix/xdg_util.h
new file mode 100644
index 0000000..fc72663
--- /dev/null
+++ b/src/base/nix/xdg_util.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 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_NIX_XDG_UTIL_H_
+#define BASE_NIX_XDG_UTIL_H_
+
+// XDG refers to http://en.wikipedia.org/wiki/Freedesktop.org .
+// This file contains utilities found across free desktop environments.
+//
+// TODO(brettw) this file should be in app/x11, but is currently used by
+// net. We should have a net API to allow the embedder to specify the behavior
+// that it uses XDG for, and then move this file.
+
+#include "base/base_export.h"
+
+#ifdef nix
+#error asdf
+#endif
+
+class FilePath;
+
+namespace base {
+
+class Environment;
+
+namespace nix {
+
+// The default XDG config directory name.
+BASE_EXPORT extern const char kDotConfigDir[];
+
+// The XDG config directory environment variable.
+BASE_EXPORT extern const char kXdgConfigHomeEnvVar[];
+
+// Utility function for getting XDG directories.
+// |env_name| is the name of an environment variable that we want to use to get
+// a directory path. |fallback_dir| is the directory relative to $HOME that we
+// use if |env_name| cannot be found or is empty. |fallback_dir| may be NULL.
+// Examples of |env_name| are XDG_CONFIG_HOME and XDG_DATA_HOME.
+BASE_EXPORT FilePath GetXDGDirectory(Environment* env, const char* env_name,
+ const char* fallback_dir);
+
+// Wrapper around xdg_user_dir_lookup() from src/base/third_party/xdg-user-dirs
+// This looks up "well known" user directories like the desktop and music
+// folder. Examples of |dir_name| are DESKTOP and MUSIC.
+BASE_EXPORT FilePath GetXDGUserDirectory(const char* dir_name,
+ const char* fallback_dir);
+
+enum DesktopEnvironment {
+ DESKTOP_ENVIRONMENT_OTHER,
+ DESKTOP_ENVIRONMENT_GNOME,
+ // KDE3 and KDE4 are sufficiently different that we count
+ // them as two different desktop environments here.
+ DESKTOP_ENVIRONMENT_KDE3,
+ DESKTOP_ENVIRONMENT_KDE4,
+ DESKTOP_ENVIRONMENT_UNITY,
+ DESKTOP_ENVIRONMENT_XFCE,
+};
+
+// Return an entry from the DesktopEnvironment enum with a best guess
+// of which desktop environment we're using. We use this to know when
+// to attempt to use preferences from the desktop environment --
+// proxy settings, password manager, etc.
+BASE_EXPORT DesktopEnvironment GetDesktopEnvironment(Environment* env);
+
+// Return a string representation of the given desktop environment.
+// May return NULL in the case of DESKTOP_ENVIRONMENT_OTHER.
+BASE_EXPORT const char* GetDesktopEnvironmentName(DesktopEnvironment env);
+// Convenience wrapper that calls GetDesktopEnvironment() first.
+BASE_EXPORT const char* GetDesktopEnvironmentName(Environment* env);
+
+} // namespace nix
+} // namespace base
+
+#endif // BASE_NIX_XDG_UTIL_H_
diff --git a/src/base/nix/xdg_util_unittest.cc b/src/base/nix/xdg_util_unittest.cc
new file mode 100644
index 0000000..2fc9d4c
--- /dev/null
+++ b/src/base/nix/xdg_util_unittest.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/nix/xdg_util.h"
+
+#include "base/environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+using ::testing::StrEq;
+
+namespace base {
+namespace nix {
+
+namespace {
+
+class MockEnvironment : public Environment {
+ public:
+ MOCK_METHOD2(GetVar, bool(const char*, std::string* result));
+ MOCK_METHOD2(SetVar, bool(const char*, const std::string& new_value));
+ MOCK_METHOD1(UnSetVar, bool(const char*));
+};
+
+const char* kGnome = "gnome";
+const char* kKDE4 = "kde4";
+const char* kKDE = "kde";
+const char* kXFCE = "xfce";
+
+} // namespace
+
+TEST(XDGUtilTest, GetDesktopEnvironmentGnome) {
+ MockEnvironment getter;
+ EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false));
+ EXPECT_CALL(getter, GetVar(StrEq("DESKTOP_SESSION"), _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(kGnome), Return(true)));
+
+ EXPECT_EQ(DESKTOP_ENVIRONMENT_GNOME,
+ GetDesktopEnvironment(&getter));
+}
+
+TEST(XDGUtilTest, GetDesktopEnvironmentKDE4) {
+ MockEnvironment getter;
+ EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false));
+ EXPECT_CALL(getter, GetVar(StrEq("DESKTOP_SESSION"), _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(kKDE4), Return(true)));
+
+ EXPECT_EQ(DESKTOP_ENVIRONMENT_KDE4,
+ GetDesktopEnvironment(&getter));
+}
+
+TEST(XDGUtilTest, GetDesktopEnvironmentKDE3) {
+ MockEnvironment getter;
+ EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false));
+ EXPECT_CALL(getter, GetVar(StrEq("DESKTOP_SESSION"), _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(kKDE), Return(true)));
+
+ EXPECT_EQ(DESKTOP_ENVIRONMENT_KDE3,
+ GetDesktopEnvironment(&getter));
+}
+
+TEST(XDGUtilTest, GetDesktopEnvironmentXFCE) {
+ MockEnvironment getter;
+ EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false));
+ EXPECT_CALL(getter, GetVar(StrEq("DESKTOP_SESSION"), _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(kXFCE), Return(true)));
+
+ EXPECT_EQ(DESKTOP_ENVIRONMENT_XFCE,
+ GetDesktopEnvironment(&getter));
+}
+
+} // namespace nix
+} // namespace base
diff --git a/src/base/nullable_string16.h b/src/base/nullable_string16.h
new file mode 100644
index 0000000..6f07183
--- /dev/null
+++ b/src/base/nullable_string16.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_NULLABLE_STRING16_H_
+#define BASE_NULLABLE_STRING16_H_
+
+#include "base/string16.h"
+
+// This class is a simple wrapper for string16 which also contains a null
+// state. This should be used only where the difference between null and
+// empty is meaningful.
+class NullableString16 {
+ public:
+ NullableString16() : is_null_(false) { }
+ explicit NullableString16(bool is_null) : is_null_(is_null) { }
+ NullableString16(const string16& string, bool is_null)
+ : string_(string), is_null_(is_null) {
+ }
+
+ const string16& string() const { return string_; }
+ bool is_null() const { return is_null_; }
+
+ private:
+ string16 string_;
+ bool is_null_;
+};
+
+#endif // BASE_NULLABLE_STRING16_H_
diff --git a/src/base/object_tracker.h b/src/base/object_tracker.h
new file mode 100644
index 0000000..f3aca25
--- /dev/null
+++ b/src/base/object_tracker.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BASE_OBJECT_TRACKER_H_
+#define BASE_OBJECT_TRACKER_H_
+
+#include <assert.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/stringprintf.h"
+#include "lb_memory_debug.h"
+#include "lb_memory_manager.h"
+
+#if defined(__LB_SHELL__)
+#include "lb_mutex.h"
+#else // defined(__LB_SHELL__)
+#error "Have to include platform header for mutex"
+#endif // defined(__LB_SHELL__)
+
+namespace base {
+
+// This class can be used to track and dump all objects belongs to a certain
+// type. For example, we can use the following steps to dump all Nodes:
+// 1. Make Node inherit from ObjectTracker<Node>.
+// 2. Call "Node::Dump(log_cb);" on a particular event (like a key press).
+// We can also return false in ShouldDump() to skip certain objects and
+// implement DumpSpecificInfo() to dump information specific to the object like
+// ref count, etc..
+// This class is meant to be used for debugging only.
+template <typename Object, uint32 BacktraceLevel = 10>
+class ObjectTracker {
+ public:
+#if defined(__LB_SHELL__)
+ // We implement our own Lock class to remove the dependency on base::Lock, so
+ // we can track base::Lock.
+ class Lock {
+ public:
+ Lock() {
+ int rv = lb_shell_mutex_init(&mutex_);
+ assert(rv == 0);
+ UNREFERENCED_PARAMETER(rv);
+ }
+ ~Lock() {
+ int rv = lb_shell_mutex_destroy(&mutex_);
+ assert(rv == 0);
+ UNREFERENCED_PARAMETER(rv);
+ }
+ void Acquire() {
+ int rv = lb_shell_mutex_lock(&mutex_);
+ assert(rv == 0);
+ UNREFERENCED_PARAMETER(rv);
+ }
+ void Release() {
+ int rv = lb_shell_mutex_unlock(&mutex_);
+ assert(rv == 0);
+ UNREFERENCED_PARAMETER(rv);
+ }
+
+ private:
+ lb_shell_mutex_t mutex_;
+ };
+#endif // defined(__LB_SHELL__)
+
+ typedef void LogCB(const std::string&);
+
+ static void Dump(LogCB log_cb) {
+ lock().Acquire();
+ log_cb("============================ start ============================\n");
+ // Sort the objects based on their addresses.
+ std::vector<ObjectTracker*> trackers;
+ trackers.reserve(16 * 1024);
+ ObjectTracker* current = head_;
+ while (current) {
+ trackers.push_back(current);
+ current = current->next_;
+ }
+ std::sort(trackers.begin(), trackers.end());
+
+ int skipped = 0;
+
+ for (size_t i = 0; i < trackers.size(); ++i) {
+ std::string line;
+ current = trackers[i];
+ if ((static_cast<Object*>(current))->ShouldDump()) {
+ line = base::StringPrintf("%" PRIxPTR " ",
+ reinterpret_cast<intptr_t>(current));
+ line += (static_cast<Object*>(current))->DumpSpecificInfo();
+ for (uint32 j = 0; j < BacktraceLevel; ++j) {
+ base::StringAppendF(&line, " %" PRIxPTR, current->back_trace_[j]);
+ }
+ line += "\n";
+ log_cb(line);
+ } else {
+ ++skipped;
+ }
+ }
+ if (skipped != 0) {
+ log_cb(base::StringPrintf("Dumped %d items, skipped %d items\n",
+ static_cast<int>(trackers.size()), skipped));
+ } else {
+ log_cb(base::StringPrintf("Dumped %d items\n",
+ static_cast<int>(trackers.size())));
+ }
+ log_cb("============================= end =============================\n");
+ lock().Release();
+ }
+
+ protected:
+ ObjectTracker() : next_(NULL), prev_(NULL) {
+ int valid_trace_count = 0;
+ if (LB::Memory::GetBacktraceEnabled()) {
+ valid_trace_count =
+ LB::Memory::Backtrace(0, BacktraceLevel, back_trace_, NULL);
+ }
+ if (valid_trace_count != BacktraceLevel) {
+ memset(back_trace_ + valid_trace_count, 0,
+ (BacktraceLevel - valid_trace_count) * sizeof(back_trace_[0]));
+ }
+
+ lock().Acquire();
+ next_ = head_;
+ if (next_) {
+ next_->prev_ = this;
+ }
+ head_ = this;
+ lock().Release();
+ }
+
+ ~ObjectTracker() {
+ lock().Acquire();
+ if (next_) {
+ next_->prev_ = prev_;
+ }
+ if (prev_) {
+ prev_->next_ = next_;
+ } else {
+ head_ = next_;
+ }
+ lock().Release();
+ }
+
+ bool ShouldDump() { return true; }
+ std::string DumpSpecificInfo() { return ""; }
+
+ private:
+ static ObjectTracker* head_;
+ static Lock& lock() {
+ static Lock* lock;
+ // This isn't thread safe. But it should be fine as ObjectTracker is
+ // supposed to be used only for debugging and this removed its dependency
+ // on LazyInstance which is in turn depended on AtExitManager.
+ if (lock == NULL) {
+ lock = new Lock;
+ }
+ return *lock;
+ }
+
+ ObjectTracker* prev_;
+ ObjectTracker* next_;
+ uintptr_t back_trace_[BacktraceLevel];
+};
+
+// static
+template <typename Object, uint32 BacktraceLevel>
+ObjectTracker<Object, BacktraceLevel>*
+ ObjectTracker<Object, BacktraceLevel>::head_;
+
+} // namespace base
+
+#endif // BASE_OBJECT_TRACKER_H_
diff --git a/src/base/object_watcher_shell.cc b/src/base/object_watcher_shell.cc
new file mode 100644
index 0000000..811b9b9
--- /dev/null
+++ b/src/base/object_watcher_shell.cc
@@ -0,0 +1,364 @@
+/*
+ * Copyright 2012 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "base/object_watcher_shell.h"
+
+#include <stack>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+
+namespace base {
+
+struct Watch {
+ ObjectWatcher* watcher; // associated ObjectWatcher instance
+ int object; // the file descriptor being watched
+ int watch_handle; // used for uniquely identifying watches
+ MessageLoop* origin_loop; // the origin thread's message loop
+ ObjectWatcher::Delegate* delegate; // delegate to notify when signaled
+ bool did_signal; // set when DoneWaiting is called
+ MessagePumpShell::Mode mode; // callback on read, write or both?
+
+ void Run() {
+ // The watcher may have already been torn down, in which case we need to
+ // just get out of dodge.
+ if (!watcher)
+ return;
+
+ DCHECK(did_signal);
+ watcher->StopWatching();
+
+ delegate->OnObjectSignaled(object);
+ }
+};
+
+// To emulate the behavior of the former Task system:
+static void WatchTask(Watch* watch) {
+ watch->Run();
+ delete watch;
+}
+
+ObjectWatchMultiplexer* ObjectWatchMultiplexer::s_instance = NULL;
+
+ObjectWatchMultiplexer::ObjectWatchMultiplexer()
+ : base::SimpleThread(
+ "ObjectWatchMultiplexer Thread",
+ base::SimpleThread::Options(
+ // TODO: Pass these into ObjectWatchMultiplexer.
+ 0 /* kObjectWatcherThreadStackSize */,
+ kThreadPriority_Default /* kObjectWatcherThreadPriority */,
+ kNoThreadAffinity /* kNetworkIOThreadAffinity */)),
+ non_empty_list_event_(true, false),
+ max_watch_handle_(0),
+ should_recompose_pollfd_array_(true),
+ exit_(false) {
+ DCHECK(!s_instance);
+ s_instance = this;
+ Start();
+}
+
+ObjectWatchMultiplexer::~ObjectWatchMultiplexer() {
+ DCHECK(s_instance);
+ Join();
+ s_instance = NULL;
+}
+
+// blocking call to exit the internal thread
+void ObjectWatchMultiplexer::Join() {
+ {
+ // RecomposePollFDArray may reset the event, but can only be called
+ // while this lock is held. To break the race between these two, grab
+ // the lock before setting exit_ and signalling the event, and make sure
+ // that exit_ is checked before resetting the event.
+ base::AutoLock lock(add_watch_list_lock_);
+ exit_ = true;
+ // wake up the thread in case it's waiting on the empty list event
+ non_empty_list_event_.Signal();
+ }
+ // in any event it should timeout eventually and see the exit flag..
+ base::SimpleThread::Join();
+}
+
+void ObjectWatchMultiplexer::AddWatch(Watch* watch) {
+ DCHECK(watch);
+ DCHECK_EQ(watch->watch_handle, 0);
+ // grab the mutex to modify the watch map et al
+ base::AutoLock lock(add_watch_list_lock_);
+ should_recompose_pollfd_array_ = true;
+ watch->watch_handle = ++max_watch_handle_;
+ new_watch_map_.insert(WatchMap::value_type(watch->object, watch));
+ non_empty_list_event_.Signal();
+}
+
+void ObjectWatchMultiplexer::RemoveWatch(Watch* watch) {
+ DCHECK(watch);
+ base::AutoLock lock(remove_watch_list_lock_);
+ WatchMap::iterator start = watch_map_.lower_bound(watch->object);
+ WatchMap::iterator end = watch_map_.upper_bound(watch->object);
+ WatchMap::iterator it;
+ // search within those elements having the same file descriptor
+ for (it = start; it != end; it++)
+ if (it->second->watch_handle == watch->watch_handle)
+ break;
+ if (it != end) {
+ watch_map_.erase(it);
+ should_recompose_pollfd_array_ = true;
+ } else {
+ base::AutoLock lock(add_watch_list_lock_);
+ // not found, search new_watch_map_
+ WatchMap::iterator start = new_watch_map_.lower_bound(watch->object);
+ WatchMap::iterator end = new_watch_map_.upper_bound(watch->object);
+ for (it = start; it != end; it++)
+ if (it->second->watch_handle == watch->watch_handle)
+ break;
+ if (it != end) {
+ new_watch_map_.erase(it);
+ should_recompose_pollfd_array_ = true;
+ }
+ }
+}
+
+void ObjectWatchMultiplexer::Run() {
+ // we keep a stack of pointers to watches needing a callback and
+ // subsequent removal from the map
+ std::stack<WatchMap::iterator> watch_removal_stack;
+ // separate stack of callbacks to process after releasing the
+ // WatchMap mutex
+ std::stack<Watch*> watch_callback_stack;
+
+ while (!exit_) {
+ // We need both locks here. Order is important
+ remove_watch_list_lock_.Acquire();
+ add_watch_list_lock_.Acquire();
+ if (should_recompose_pollfd_array_)
+ RecomposePollFDArray();
+ add_watch_list_lock_.Release();
+ remove_watch_list_lock_.Release();
+
+ if (!pollfd_array_.empty()) {
+ // timeout on a 100ms to wake up and see if we need
+ // to modify the array of fds or exit..
+ int event_count =
+ poll(&pollfd_array_.front(), pollfd_array_.size(), kPollTimeout);
+ // negative value returned means error...
+ if (event_count < 0) {
+ // Sleep for kPollTimeout, otherwise this thread could busy-wait until
+ // poll returns a non-error value
+ base::PlatformThread::Sleep(
+ base::TimeDelta::FromMilliseconds(kPollTimeout));
+ }
+
+ // we need the mutex to access the map. AddWatch() is on a different
+ // map object so we dont need the add_watch_list_lock_ here.
+ remove_watch_list_lock_.Acquire();
+ DCHECK_LE(event_count, static_cast<int>(pollfd_array_.size()));
+ for (int j = 0; j < pollfd_array_.size() && event_count > 0; ++j) {
+ if (pollfd_array_[j].revents != 0) {
+ event_count--;
+ WatchMap::iterator start =
+ watch_map_.lower_bound(pollfd_array_[j].fd);
+ WatchMap::iterator stop =
+ watch_map_.upper_bound(pollfd_array_[j].fd);
+ for (WatchMap::iterator it = start; it != stop; it++) {
+ // oh STL, your capacity for obfuscation never ceases to amaze.
+ // this if/else block tries to match interest for each fd event
+ // to the associated watchers. If a watcher was watching for
+ // reads and a read happened, then we add it to the stack of
+ // callbacks to process. Else if it was watching for writes and
+ // a write happened then we do the same.
+ // note that signaled watches are going to become the property
+ // of their respectively owned MessageLoops and will be deleted
+ // once executed, and so will be removed from the hash_map
+ if ((pollfd_array_[j].revents & POLLIN) &&
+ (it->second->mode & MessagePumpShell::WATCH_READ)) {
+ watch_removal_stack.push(it);
+ watch_callback_stack.push(it->second);
+ } else if ((pollfd_array_[j].revents & POLLOUT) &&
+ (it->second->mode & MessagePumpShell::WATCH_WRITE)) {
+ watch_removal_stack.push(it);
+ watch_callback_stack.push(it->second);
+ }
+ }
+ }
+ } // for(int j = 0; j < pollfd_array_.size() && event_count > 0; ++j)
+
+ // process all callbacks. this must unfortunately be done inside
+ // of the mutex to prevent the contention on watch objects being
+ // deleted due to a call to ObjectWatcher::StopWatching that could
+ // occur while the watch is on this callback stack
+ while (!watch_callback_stack.empty()) {
+ Watch* watch = watch_callback_stack.top();
+ watch_callback_stack.pop();
+ // signal this watch to run in the original thread context
+ // of the calling thread message loop. The watch will be
+ // posted as a task to the MessageLoop which will take
+ // ownership of the object and delete it
+ watch->did_signal = true;
+ watch->origin_loop->PostTask(FROM_HERE, base::Bind(WatchTask, watch));
+ }
+ // if we're going to signal something we should recompose
+ // the array as we are going to be removing watches
+ if (!watch_removal_stack.empty())
+ should_recompose_pollfd_array_ = true;
+ // now remove the callback elements from the current hash_map
+ while (!watch_removal_stack.empty()) {
+ watch_map_.erase(watch_removal_stack.top());
+ watch_removal_stack.pop();
+ }
+
+ remove_watch_list_lock_.Release();
+ } else { // if (!pollfd_array_.empty())
+ // sleep until flagged that something has been added to the list
+ DCHECK(watch_map_.empty());
+ // NOTE: You can't DCHECK(new_watch_map_.empty()) because we let go
+ // of the lock already and new stuff could have been added before we
+ // get here. If that happens, the event will have already been
+ // signaled and we won't have to wait.
+ non_empty_list_event_.Wait();
+ } // if (!pollfd_array_.empty())
+ } // while (!exit_)
+}
+
+void ObjectWatchMultiplexer::RecomposePollFDArray() {
+ remove_watch_list_lock_.AssertAcquired();
+ add_watch_list_lock_.AssertAcquired();
+ // process new requests and move them into working map
+ WatchMap::iterator it;
+ for (it = new_watch_map_.begin(); it != new_watch_map_.end(); it++) {
+ watch_map_.insert(*it);
+ }
+ new_watch_map_.clear();
+ // count the number of unique file descriptors in the map
+ int count = 0;
+ int last_fd = -1;
+ for (it = watch_map_.begin(); it != watch_map_.end(); it++) {
+ if (it->first != last_fd) {
+ last_fd = it->first;
+ count++;
+ }
+ }
+ pollfd_array_.resize(count);
+
+ // init pollfd_array_
+ if (!pollfd_array_.empty()) {
+ int j = -1;
+ last_fd = -1;
+ for (it = watch_map_.begin(); it != watch_map_.end(); it++) {
+ if (it->first != last_fd) {
+ // we keep j pointing at this current element for ORing in the watch
+ // flags
+ j++;
+ last_fd = it->first;
+ pollfd_array_[j].fd = last_fd;
+ // assume we care about both, we will filter based on mode on callback
+ pollfd_array_[j].events = 0;
+ if (it->second->mode & MessagePumpShell::WATCH_READ)
+ pollfd_array_[j].events |= POLLIN;
+ if (it->second->mode & MessagePumpShell::WATCH_WRITE)
+ pollfd_array_[j].events |= POLLOUT;
+ pollfd_array_[j].revents = 0;
+ } else { // if (it->first != last_fd)
+ // OR in the watch flags to the already initialized pollfd struct
+ if (it->second->mode & MessagePumpShell::WATCH_READ)
+ pollfd_array_[j].events |= POLLIN;
+ if (it->second->mode & MessagePumpShell::WATCH_WRITE)
+ pollfd_array_[j].events |= POLLOUT;
+ } // if (it->first != last_fd)
+ } // for (it = watch_map_.begin(); it != watch_map_.end(); it++)
+ } else { // if (!pollfd_array_.empty())
+ // reset the empty event if we've completely emptied the watch list
+ DCHECK(watch_map_.empty());
+ DCHECK(new_watch_map_.empty());
+ // Join may have signalled the event because we are exitting. If so,
+ // don't reset the event. That would lead to an infinite wait on
+ // shutdown.
+ if (!exit_)
+ non_empty_list_event_.Reset();
+ } // if (!pollfd_array_.empty())
+ // reset flag
+ should_recompose_pollfd_array_ = false;
+}
+
+ObjectWatcher::ObjectWatcher() : watch_(NULL) {
+}
+
+ObjectWatcher::~ObjectWatcher() {
+ StopWatching();
+}
+
+bool ObjectWatcher::StartWatching(int object,
+ MessagePumpShell::Mode mode,
+ Delegate* delegate) {
+ DCHECK(ObjectWatchMultiplexer::GetInstance() != NULL);
+ if (watch_) {
+ NOTREACHED() << "Already watching an object";
+ return false;
+ }
+
+ watch_ = new Watch;
+ watch_->watcher = this;
+ watch_->object = object;
+ watch_->mode = mode;
+ watch_->origin_loop = MessageLoop::current();
+ watch_->delegate = delegate;
+ watch_->did_signal = false;
+ watch_->watch_handle = 0;
+
+ ObjectWatchMultiplexer::GetInstance()->AddWatch(watch_);
+
+ // We need to know if the current message loop is going away so we can
+ // prevent the wait thread from trying to access a dead message loop.
+ MessageLoop::current()->AddDestructionObserver(this);
+ return true;
+}
+
+bool ObjectWatcher::StopWatching() {
+ if (!watch_)
+ return false;
+
+ DCHECK(ObjectWatchMultiplexer::GetInstance() != NULL);
+
+ // make sure stop call happens on same thread as start call
+ DCHECK(watch_->origin_loop == MessageLoop::current());
+
+ // this will block until this watch has been removed from the mux
+ ObjectWatchMultiplexer::GetInstance()->RemoveWatch(watch_);
+ // let the watch know that the watcher has died, in case the watch is
+ // still sitting in a message loop. See Watch::Run() to see that it
+ // will bug out in that event.
+ watch_->watcher = NULL;
+
+ // if the watch was signaled, then it is now property of the MessageLoop
+ // and will be deleted there. It has also been safely removed from the
+ // internal list of watches in the ObjectWatchMux. Therefore if it hasn't
+ // been signaled we delete it here to avoid leaking the watch
+ if (!watch_->did_signal)
+ delete watch_;
+
+ watch_ = NULL;
+
+ MessageLoop::current()->RemoveDestructionObserver(this);
+ return true;
+}
+
+void ObjectWatcher::WillDestroyCurrentMessageLoop() {
+ // Need to shutdown the watch so that we don't try to access the MessageLoop
+ // after this point.
+ StopWatching();
+}
+
+} // namespace base
diff --git a/src/base/object_watcher_shell.h b/src/base/object_watcher_shell.h
new file mode 100644
index 0000000..10519bc
--- /dev/null
+++ b/src/base/object_watcher_shell.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef BASE_OBJECT_WATCHER_SHELL_H_
+#define BASE_OBJECT_WATCHER_SHELL_H_
+
+#if defined(__LB_SHELL__)
+
+#include <sys/poll.h>
+
+#include <map>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/message_loop.h"
+#include "base/message_pump_shell.h"
+#include "base/threading/simple_thread.h"
+
+// overall interface inspired by base/win/object_watcher.h
+// however internal implementation is driven by platform necessity
+namespace base {
+
+// A class that provides a means to asynchronously wait for a file descriptor to
+// become signaled. It uses an internal thread on a singleton object that
+// blocks on poll() calls to an internal list of fds, each represented by an
+// instance of the ObjectWatcher class.
+// It provides a notification callback, OnObjectSignaled, that runs back on
+// the origin thread (i.e., the thread that called StartWatching).
+//
+//
+// Typical usage:
+//
+// class MyClass : public base::ObjectWatcher::Delegate {
+// public:
+// void DoStuffWhenSignaled(int object) {
+// watcher_.StartWatching(object, this);
+// }
+// virtual void OnObjectSignaled(int object) {
+// // OK, time to do stuff!
+// }
+// private:
+// base::ObjectWatcher watcher_;
+// };
+//
+// In the above example, MyClass wants to "do stuff" when object becomes
+// signaled. ObjectWatcher makes this task easy. When MyClass goes out of
+// scope, the watcher_ will be destroyed, and there is no need to worry about
+// OnObjectSignaled being called on a deleted MyClass pointer. Easy!
+//
+
+struct Watch;
+
+// -----------------------------------------------------------------------------
+// ObjectWatchMultiplexer
+// this object runs an internal thread to block on the aggregate of all watched
+// objects and poll them for activity. the best emulation of callback-driven
+// io I can muster on the PS3.
+class ObjectWatchMultiplexer : public SimpleThread {
+ public:
+ ObjectWatchMultiplexer();
+ ~ObjectWatchMultiplexer();
+
+ // blocking call to exit the internal thread
+ void Join();
+ // although you can add different watches with the same file descriptor all
+ // day long, please don't add the same watch twice it will create an error.
+ void AddWatch(Watch* watch);
+ void RemoveWatch(Watch* watch);
+ static ObjectWatchMultiplexer* GetInstance() {return s_instance;}
+
+ private:
+ // the internal thread runloop
+ void Run();
+ void RecomposePollFDArray();
+
+ // used to let the thread block until it actually has _something_ to poll
+ base::WaitableEvent non_empty_list_event_;
+
+#if defined(__LB_WIIU__)
+ // 33ms is a good balance between quickly servicing new
+ // sockets to poll, and not overloading the IO pipeline.
+ static const int kPollTimeout = 33;
+#else
+ static const int kPollTimeout = 100;
+#endif
+
+ // this lock protects watch_map_, should_recompose_pollfd_array_, and
+ // max_watch_handle_ which is used to generate unique watch handles to
+ // disambiguate watches on the same file descriptor
+ base::Lock add_watch_list_lock_;
+ base::Lock remove_watch_list_lock_;
+ typedef std::multimap<int, Watch*> WatchMap;
+ WatchMap watch_map_; // current working watches
+ WatchMap new_watch_map_; // new watches
+ int max_watch_handle_;
+ std::vector<pollfd> pollfd_array_;
+ bool should_recompose_pollfd_array_;
+ bool exit_;
+ static ObjectWatchMultiplexer* s_instance;
+ DISALLOW_COPY_AND_ASSIGN(ObjectWatchMultiplexer);
+};
+
+class BASE_EXPORT ObjectWatcher : public MessageLoop::DestructionObserver {
+ public:
+ class BASE_EXPORT Delegate {
+ public:
+ virtual ~Delegate() {}
+ virtual void OnObjectSignaled(int object) = 0;
+ };
+
+ ObjectWatcher();
+ ~ObjectWatcher();
+
+ // When the object is signaled with mode, the given delegate is notified on
+ // the thread where StartWatching is called. The ObjectWatcher is not
+ // responsible for deleting the delegate.
+ //
+ // Returns true if the watch was started. Otherwise, false is returned.
+ //
+ bool StartWatching(int object,
+ MessagePumpShell::Mode mode,
+ Delegate* delegate);
+
+ // Stops watching. Does nothing if the watch has already completed. If the
+ // watch is still active, then it is canceled, and the associated delegate is
+ // not notified.
+ //
+ // Returns true if the watch was canceled. Otherwise, false is returned.
+ //
+ bool StopWatching();
+
+ // Returns the fd of the object being watched, or -1 if the object
+ // watcher is stopped.
+ int GetWatchedObject();
+
+ private:
+ // MessageLoop::DestructionObserver implementation:
+ virtual void WillDestroyCurrentMessageLoop();
+
+ struct Watch* watch_;
+
+ DISALLOW_COPY_AND_ASSIGN(ObjectWatcher);
+};
+
+} // namespace base
+
+#endif // defined(__LB_SHELL__)
+#endif // BASE_OBJECT_WATCHER_SHELL_H_
diff --git a/src/base/observer_list.h b/src/base/observer_list.h
new file mode 100644
index 0000000..ca33bff
--- /dev/null
+++ b/src/base/observer_list.h
@@ -0,0 +1,219 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_OBSERVER_LIST_H__
+#define BASE_OBSERVER_LIST_H__
+
+#include <algorithm>
+#include <limits>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/weak_ptr.h"
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// OVERVIEW:
+//
+// A container for a list of observers. Unlike a normal STL vector or list,
+// this container can be modified during iteration without invalidating the
+// iterator. So, it safely handles the case of an observer removing itself
+// or other observers from the list while observers are being notified.
+//
+// TYPICAL USAGE:
+//
+// class MyWidget {
+// public:
+// ...
+//
+// class Observer {
+// public:
+// virtual void OnFoo(MyWidget* w) = 0;
+// virtual void OnBar(MyWidget* w, int x, int y) = 0;
+// };
+//
+// void AddObserver(Observer* obs) {
+// observer_list_.AddObserver(obs);
+// }
+//
+// void RemoveObserver(Observer* obs) {
+// observer_list_.RemoveObserver(obs);
+// }
+//
+// void NotifyFoo() {
+// FOR_EACH_OBSERVER(Observer, observer_list_, OnFoo(this));
+// }
+//
+// void NotifyBar(int x, int y) {
+// FOR_EACH_OBSERVER(Observer, observer_list_, OnBar(this, x, y));
+// }
+//
+// private:
+// ObserverList<Observer> observer_list_;
+// };
+//
+//
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename ObserverType>
+class ObserverListThreadSafe;
+
+template <class ObserverType>
+class ObserverListBase
+ : public base::SupportsWeakPtr<ObserverListBase<ObserverType> > {
+ public:
+ // Enumeration of which observers are notified.
+ enum NotificationType {
+ // Specifies that any observers added during notification are notified.
+ // This is the default type if non type is provided to the constructor.
+ NOTIFY_ALL,
+
+ // Specifies that observers added while sending out notification are not
+ // notified.
+ NOTIFY_EXISTING_ONLY
+ };
+
+ // An iterator class that can be used to access the list of observers. See
+ // also the FOR_EACH_OBSERVER macro defined below.
+ class Iterator {
+ public:
+ Iterator(ObserverListBase<ObserverType>& list)
+ : list_(list.AsWeakPtr()),
+ index_(0),
+ max_index_(list.type_ == NOTIFY_ALL ?
+ std::numeric_limits<size_t>::max() :
+ list.observers_.size()) {
+ ++list_->notify_depth_;
+ }
+
+ ~Iterator() {
+ if (list_ && --list_->notify_depth_ == 0)
+ list_->Compact();
+ }
+
+ ObserverType* GetNext() {
+ if (!list_)
+ return NULL;
+ ListType& observers = list_->observers_;
+ // Advance if the current element is null
+ size_t max_index = std::min(max_index_, observers.size());
+ while (index_ < max_index && !observers[index_])
+ ++index_;
+ return index_ < max_index ? observers[index_++] : NULL;
+ }
+
+ private:
+ base::WeakPtr<ObserverListBase<ObserverType> > list_;
+ size_t index_;
+ size_t max_index_;
+ };
+
+ ObserverListBase() : notify_depth_(0), type_(NOTIFY_ALL) {}
+ explicit ObserverListBase(NotificationType type)
+ : notify_depth_(0), type_(type) {}
+
+ // Add an observer to the list. An observer should not be added to
+ // the same list more than once.
+ void AddObserver(ObserverType* obs) {
+ if (std::find(observers_.begin(), observers_.end(), obs)
+ != observers_.end()) {
+ NOTREACHED() << "Observers can only be added once!";
+ return;
+ }
+ observers_.push_back(obs);
+ }
+
+ // Remove an observer from the list if it is in the list.
+ void RemoveObserver(ObserverType* obs) {
+ typename ListType::iterator it =
+ std::find(observers_.begin(), observers_.end(), obs);
+ if (it != observers_.end()) {
+ if (notify_depth_) {
+ *it = 0;
+ } else {
+ observers_.erase(it);
+ }
+ }
+ }
+
+ bool HasObserver(ObserverType* observer) const {
+ for (size_t i = 0; i < observers_.size(); ++i) {
+ if (observers_[i] == observer)
+ return true;
+ }
+ return false;
+ }
+
+ void Clear() {
+ if (notify_depth_) {
+ for (typename ListType::iterator it = observers_.begin();
+ it != observers_.end(); ++it) {
+ *it = 0;
+ }
+ } else {
+ observers_.clear();
+ }
+ }
+
+ size_t size() const { return observers_.size(); }
+
+ protected:
+ void Compact() {
+ observers_.erase(
+ std::remove(observers_.begin(), observers_.end(),
+ static_cast<ObserverType*>(NULL)), observers_.end());
+ }
+
+ private:
+ friend class ObserverListThreadSafe<ObserverType>;
+
+ typedef std::vector<ObserverType*> ListType;
+
+ ListType observers_;
+ int notify_depth_;
+ NotificationType type_;
+
+#if !defined(__LB_SHELL__) && !defined(OS_STARBOARD)
+ // friend is not needed for nested classes in modern compilers.
+ friend class ObserverListBase::Iterator;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ObserverListBase);
+};
+
+template <class ObserverType, bool check_empty = false>
+class ObserverList : public ObserverListBase<ObserverType> {
+ public:
+ typedef typename ObserverListBase<ObserverType>::NotificationType
+ NotificationType;
+
+ ObserverList() {}
+ explicit ObserverList(NotificationType type)
+ : ObserverListBase<ObserverType>(type) {}
+
+ ~ObserverList() {
+ // When check_empty is true, assert that the list is empty on destruction.
+ if (check_empty) {
+ ObserverListBase<ObserverType>::Compact();
+ DCHECK_EQ(ObserverListBase<ObserverType>::size(), 0U);
+ }
+ }
+
+ bool might_have_observers() const {
+ return ObserverListBase<ObserverType>::size() != 0;
+ }
+};
+
+#define FOR_EACH_OBSERVER(ObserverType, observer_list, func) \
+ do { \
+ if ((observer_list).might_have_observers()) { \
+ ObserverListBase<ObserverType>::Iterator it(observer_list); \
+ ObserverType* obs; \
+ while ((obs = it.GetNext()) != NULL) \
+ obs->func; \
+ } \
+ } while (0)
+
+#endif // BASE_OBSERVER_LIST_H__
diff --git a/src/base/observer_list_threadsafe.h b/src/base/observer_list_threadsafe.h
new file mode 100644
index 0000000..7322a1b
--- /dev/null
+++ b/src/base/observer_list_threadsafe.h
@@ -0,0 +1,297 @@
+// Copyright (c) 2012 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_OBSERVER_LIST_THREADSAFE_H_
+#define BASE_OBSERVER_LIST_THREADSAFE_H_
+
+#include <algorithm>
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/observer_list.h"
+#include "base/threading/platform_thread.h"
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// OVERVIEW:
+//
+// A thread-safe container for a list of observers.
+// This is similar to the observer_list (see observer_list.h), but it
+// is more robust for multi-threaded situations.
+//
+// The following use cases are supported:
+// * Observers can register for notifications from any thread.
+// Callbacks to the observer will occur on the same thread where
+// the observer initially called AddObserver() from.
+// * Any thread may trigger a notification via Notify().
+// * Observers can remove themselves from the observer list inside
+// of a callback.
+// * If one thread is notifying observers concurrently with an observer
+// removing itself from the observer list, the notifications will
+// be silently dropped.
+//
+// The drawback of the threadsafe observer list is that notifications
+// are not as real-time as the non-threadsafe version of this class.
+// Notifications will always be done via PostTask() to another thread,
+// whereas with the non-thread-safe observer_list, notifications happen
+// synchronously and immediately.
+//
+// IMPLEMENTATION NOTES
+// The ObserverListThreadSafe maintains an ObserverList for each thread
+// which uses the ThreadSafeObserver. When Notifying the observers,
+// we simply call PostTask to each registered thread, and then each thread
+// will notify its regular ObserverList.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+// Forward declaration for ObserverListThreadSafeTraits.
+template <class ObserverType>
+class ObserverListThreadSafe;
+
+// An UnboundMethod is a wrapper for a method where the actual object is
+// provided at Run dispatch time.
+template <class T, class Method, class Params>
+class UnboundMethod {
+ public:
+ UnboundMethod(Method m, const Params& p) : m_(m), p_(p) {
+ COMPILE_ASSERT(
+ (base::internal::ParamsUseScopedRefptrCorrectly<Params>::value),
+ badunboundmethodparams);
+ }
+ void Run(T* obj) const {
+ DispatchToMethod(obj, m_, p_);
+ }
+ private:
+ Method m_;
+ Params p_;
+};
+
+// This class is used to work around VS2005 not accepting:
+//
+// friend class
+// base::RefCountedThreadSafe<ObserverListThreadSafe<ObserverType> >;
+//
+// Instead of friending the class, we could friend the actual function
+// which calls delete. However, this ends up being
+// RefCountedThreadSafe::DeleteInternal(), which is private. So we
+// define our own templated traits class so we can friend it.
+template <class T>
+struct ObserverListThreadSafeTraits {
+ static void Destruct(const ObserverListThreadSafe<T>* x) {
+ delete x;
+ }
+};
+
+template <class ObserverType>
+class ObserverListThreadSafe
+ : public base::RefCountedThreadSafe<
+ ObserverListThreadSafe<ObserverType>,
+ ObserverListThreadSafeTraits<ObserverType> > {
+ public:
+ typedef typename ObserverList<ObserverType>::NotificationType
+ NotificationType;
+
+ ObserverListThreadSafe()
+ : type_(ObserverListBase<ObserverType>::NOTIFY_ALL) {}
+ explicit ObserverListThreadSafe(NotificationType type) : type_(type) {}
+
+ // Add an observer to the list. An observer should not be added to
+ // the same list more than once.
+ void AddObserver(ObserverType* obs) {
+ // If there is not a current MessageLoop, it is impossible to notify on it,
+ // so do not add the observer.
+ if (!MessageLoop::current())
+ return;
+
+ ObserverList<ObserverType>* list = NULL;
+ base::PlatformThreadId thread_id = base::PlatformThread::CurrentId();
+ {
+ base::AutoLock lock(list_lock_);
+ if (observer_lists_.find(thread_id) == observer_lists_.end())
+ observer_lists_[thread_id] = new ObserverListContext(type_);
+ list = &(observer_lists_[thread_id]->list);
+ }
+ list->AddObserver(obs);
+ }
+
+ // Remove an observer from the list if it is in the list.
+ // If there are pending notifications in-transit to the observer, they will
+ // be aborted.
+ // If the observer to be removed is in the list, RemoveObserver MUST
+ // be called from the same thread which called AddObserver.
+ void RemoveObserver(ObserverType* obs) {
+ ObserverListContext* context = NULL;
+ ObserverList<ObserverType>* list = NULL;
+ base::PlatformThreadId thread_id = base::PlatformThread::CurrentId();
+ {
+ base::AutoLock lock(list_lock_);
+ typename ObserversListMap::iterator it = observer_lists_.find(thread_id);
+ if (it == observer_lists_.end()) {
+ // This will happen if we try to remove an observer on a thread
+ // we never added an observer for.
+ return;
+ }
+ context = it->second;
+ list = &context->list;
+
+ // If we're about to remove the last observer from the list,
+ // then we can remove this observer_list entirely.
+ if (list->HasObserver(obs) && list->size() == 1)
+ observer_lists_.erase(it);
+ }
+ list->RemoveObserver(obs);
+
+ // If RemoveObserver is called from a notification, the size will be
+ // nonzero. Instead of deleting here, the NotifyWrapper will delete
+ // when it finishes iterating.
+ if (list->size() == 0)
+ delete context;
+ }
+
+ // Verifies that the list is currently empty (i.e. there are no observers).
+ void AssertEmpty() const {
+ base::AutoLock lock(list_lock_);
+ DCHECK(observer_lists_.empty());
+ }
+
+ // Notify methods.
+ // Make a thread-safe callback to each Observer in the list.
+ // Note, these calls are effectively asynchronous. You cannot assume
+ // that at the completion of the Notify call that all Observers have
+ // been Notified. The notification may still be pending delivery.
+ template <class Method>
+ void Notify(Method m) {
+ UnboundMethod<ObserverType, Method, Tuple0> method(m, MakeTuple());
+ Notify<Method, Tuple0>(method);
+ }
+
+ template <class Method, class A>
+ void Notify(Method m, const A& a) {
+ UnboundMethod<ObserverType, Method, Tuple1<A> > method(m, MakeTuple(a));
+ Notify<Method, Tuple1<A> >(method);
+ }
+
+ template <class Method, class A, class B>
+ void Notify(Method m, const A& a, const B& b) {
+ UnboundMethod<ObserverType, Method, Tuple2<A, B> > method(
+ m, MakeTuple(a, b));
+ Notify<Method, Tuple2<A, B> >(method);
+ }
+
+ template <class Method, class A, class B, class C>
+ void Notify(Method m, const A& a, const B& b, const C& c) {
+ UnboundMethod<ObserverType, Method, Tuple3<A, B, C> > method(
+ m, MakeTuple(a, b, c));
+ Notify<Method, Tuple3<A, B, C> >(method);
+ }
+
+ template <class Method, class A, class B, class C, class D>
+ void Notify(Method m, const A& a, const B& b, const C& c, const D& d) {
+ UnboundMethod<ObserverType, Method, Tuple4<A, B, C, D> > method(
+ m, MakeTuple(a, b, c, d));
+ Notify<Method, Tuple4<A, B, C, D> >(method);
+ }
+
+ // TODO(mbelshe): Add more wrappers for Notify() with more arguments.
+
+ private:
+ // See comment above ObserverListThreadSafeTraits' definition.
+ friend struct ObserverListThreadSafeTraits<ObserverType>;
+
+ struct ObserverListContext {
+ explicit ObserverListContext(NotificationType type)
+ : loop(base::MessageLoopProxy::current()),
+ list(type) {
+ }
+
+ scoped_refptr<base::MessageLoopProxy> loop;
+ ObserverList<ObserverType> list;
+
+ DISALLOW_COPY_AND_ASSIGN(ObserverListContext);
+ };
+
+ ~ObserverListThreadSafe() {
+ typename ObserversListMap::const_iterator it;
+ for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it)
+ delete (*it).second;
+ observer_lists_.clear();
+ }
+
+ template <class Method, class Params>
+ void Notify(const UnboundMethod<ObserverType, Method, Params>& method) {
+ base::AutoLock lock(list_lock_);
+ typename ObserversListMap::iterator it;
+ for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) {
+ ObserverListContext* context = (*it).second;
+ context->loop->PostTask(
+ FROM_HERE,
+ base::Bind(&ObserverListThreadSafe<ObserverType>::
+ template NotifyWrapper<Method, Params>, this, context, method));
+ }
+ }
+
+ // Wrapper which is called to fire the notifications for each thread's
+ // ObserverList. This function MUST be called on the thread which owns
+ // the unsafe ObserverList.
+ template <class Method, class Params>
+ void NotifyWrapper(ObserverListContext* context,
+ const UnboundMethod<ObserverType, Method, Params>& method) {
+
+ // Check that this list still needs notifications.
+ {
+ base::AutoLock lock(list_lock_);
+ typename ObserversListMap::iterator it =
+ observer_lists_.find(base::PlatformThread::CurrentId());
+
+ // The ObserverList could have been removed already. In fact, it could
+ // have been removed and then re-added! If the master list's loop
+ // does not match this one, then we do not need to finish this
+ // notification.
+ if (it == observer_lists_.end() || it->second != context)
+ return;
+ }
+
+ {
+ typename ObserverList<ObserverType>::Iterator it(context->list);
+ ObserverType* obs;
+ while ((obs = it.GetNext()) != NULL)
+ method.Run(obs);
+ }
+
+ // If there are no more observers on the list, we can now delete it.
+ if (context->list.size() == 0) {
+ {
+ base::AutoLock lock(list_lock_);
+ // Remove |list| if it's not already removed.
+ // This can happen if multiple observers got removed in a notification.
+ // See http://crbug.com/55725.
+ typename ObserversListMap::iterator it =
+ observer_lists_.find(base::PlatformThread::CurrentId());
+ if (it != observer_lists_.end() && it->second == context)
+ observer_lists_.erase(it);
+ }
+ delete context;
+ }
+ }
+
+ // Key by PlatformThreadId because in tests, clients can attempt to remove
+ // observers without a MessageLoop. If this were keyed by MessageLoop, that
+ // operation would be silently ignored, leaving garbage in the ObserverList.
+ typedef std::map<base::PlatformThreadId, ObserverListContext*>
+ ObserversListMap;
+
+ mutable base::Lock list_lock_; // Protects the observer_lists_.
+ ObserversListMap observer_lists_;
+ const NotificationType type_;
+
+ DISALLOW_COPY_AND_ASSIGN(ObserverListThreadSafe);
+};
+
+#endif // BASE_OBSERVER_LIST_THREADSAFE_H_
diff --git a/src/base/observer_list_unittest.cc b/src/base/observer_list_unittest.cc
new file mode 100644
index 0000000..baa8f3a
--- /dev/null
+++ b/src/base/observer_list_unittest.cc
@@ -0,0 +1,561 @@
+// Copyright (c) 2012 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/observer_list.h"
+#include "base/observer_list_threadsafe.h"
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop.h"
+#include "base/threading/platform_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::PlatformThread;
+using base::Time;
+
+namespace {
+
+class Foo {
+ public:
+ virtual void Observe(int x) = 0;
+ virtual ~Foo() {}
+};
+
+class Adder : public Foo {
+ public:
+ explicit Adder(int scaler) : total(0), scaler_(scaler) {}
+ virtual void Observe(int x) OVERRIDE {
+ total += x * scaler_;
+ }
+ virtual ~Adder() {}
+ int total;
+
+ private:
+ int scaler_;
+};
+
+class Disrupter : public Foo {
+ public:
+ Disrupter(ObserverList<Foo>* list, Foo* doomed)
+ : list_(list),
+ doomed_(doomed) {
+ }
+ virtual ~Disrupter() {}
+ virtual void Observe(int x) OVERRIDE {
+ list_->RemoveObserver(doomed_);
+ }
+
+ private:
+ ObserverList<Foo>* list_;
+ Foo* doomed_;
+};
+
+class ThreadSafeDisrupter : public Foo {
+ public:
+ ThreadSafeDisrupter(ObserverListThreadSafe<Foo>* list, Foo* doomed)
+ : list_(list),
+ doomed_(doomed) {
+ }
+ virtual ~ThreadSafeDisrupter() {}
+ virtual void Observe(int x) OVERRIDE {
+ list_->RemoveObserver(doomed_);
+ }
+
+ private:
+ ObserverListThreadSafe<Foo>* list_;
+ Foo* doomed_;
+};
+
+template <typename ObserverListType>
+class AddInObserve : public Foo {
+ public:
+ explicit AddInObserve(ObserverListType* observer_list)
+ : added(false),
+ observer_list(observer_list),
+ adder(1) {
+ }
+
+ virtual void Observe(int x) OVERRIDE {
+ if (!added) {
+ added = true;
+ observer_list->AddObserver(&adder);
+ }
+ }
+
+ bool added;
+ ObserverListType* observer_list;
+ Adder adder;
+};
+
+
+static const int kThreadRunTime = 2000; // ms to run the multi-threaded test.
+
+// A thread for use in the ThreadSafeObserver test
+// which will add and remove itself from the notification
+// list repeatedly.
+class AddRemoveThread : public PlatformThread::Delegate,
+ public Foo {
+ public:
+ AddRemoveThread(ObserverListThreadSafe<Foo>* list, bool notify)
+ : list_(list),
+ loop_(NULL),
+ in_list_(false),
+ start_(Time::Now()),
+ count_observes_(0),
+ count_addtask_(0),
+ do_notifies_(notify),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
+ }
+
+ virtual ~AddRemoveThread() {
+ }
+
+ virtual void ThreadMain() OVERRIDE {
+ loop_ = new MessageLoop(); // Fire up a message loop.
+ loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&AddRemoveThread::AddTask, weak_factory_.GetWeakPtr()));
+ loop_->Run();
+ //LOG(ERROR) << "Loop 0x" << std::hex << loop_ << " done. " <<
+ // count_observes_ << ", " << count_addtask_;
+ delete loop_;
+ loop_ = reinterpret_cast<MessageLoop*>(0xdeadbeef);
+ delete this;
+ }
+
+ bool Started() {
+ return loop_ != NULL;
+ }
+
+ // This task just keeps posting to itself in an attempt
+ // to race with the notifier.
+ void AddTask() {
+ count_addtask_++;
+
+ if ((Time::Now() - start_).InMilliseconds() > kThreadRunTime) {
+ VLOG(1) << "DONE!";
+ return;
+ }
+
+ if (!in_list_) {
+ list_->AddObserver(this);
+ in_list_ = true;
+ }
+
+ if (do_notifies_) {
+ list_->Notify(&Foo::Observe, 10);
+ }
+
+ loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&AddRemoveThread::AddTask, weak_factory_.GetWeakPtr()));
+ }
+
+ void Quit() {
+ loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure());
+ }
+
+ virtual void Observe(int x) OVERRIDE {
+ count_observes_++;
+
+ // If we're getting called after we removed ourselves from
+ // the list, that is very bad!
+ DCHECK(in_list_);
+
+ // This callback should fire on the appropriate thread
+ EXPECT_EQ(loop_, MessageLoop::current());
+
+ list_->RemoveObserver(this);
+ in_list_ = false;
+ }
+
+ private:
+ ObserverListThreadSafe<Foo>* list_;
+ MessageLoop* loop_;
+ bool in_list_; // Are we currently registered for notifications.
+ // in_list_ is only used on |this| thread.
+ Time start_; // The time we started the test.
+
+ int count_observes_; // Number of times we observed.
+ int count_addtask_; // Number of times thread AddTask was called
+ bool do_notifies_; // Whether these threads should do notifications.
+
+ base::WeakPtrFactory<AddRemoveThread> weak_factory_;
+};
+
+TEST(ObserverListTest, BasicTest) {
+ ObserverList<Foo> observer_list;
+ Adder a(1), b(-1), c(1), d(-1), e(-1);
+ Disrupter evil(&observer_list, &c);
+
+ observer_list.AddObserver(&a);
+ observer_list.AddObserver(&b);
+
+ FOR_EACH_OBSERVER(Foo, observer_list, Observe(10));
+
+ observer_list.AddObserver(&evil);
+ observer_list.AddObserver(&c);
+ observer_list.AddObserver(&d);
+
+ // Removing an observer not in the list should do nothing.
+ observer_list.RemoveObserver(&e);
+
+ FOR_EACH_OBSERVER(Foo, observer_list, Observe(10));
+
+ EXPECT_EQ(20, a.total);
+ EXPECT_EQ(-20, b.total);
+ EXPECT_EQ(0, c.total);
+ EXPECT_EQ(-10, d.total);
+ EXPECT_EQ(0, e.total);
+}
+
+TEST(ObserverListThreadSafeTest, BasicTest) {
+ MessageLoop loop;
+
+ scoped_refptr<ObserverListThreadSafe<Foo> > observer_list(
+ new ObserverListThreadSafe<Foo>);
+ Adder a(1);
+ Adder b(-1);
+ Adder c(1);
+ Adder d(-1);
+ ThreadSafeDisrupter evil(observer_list.get(), &c);
+
+ observer_list->AddObserver(&a);
+ observer_list->AddObserver(&b);
+
+ observer_list->Notify(&Foo::Observe, 10);
+ loop.RunUntilIdle();
+
+ observer_list->AddObserver(&evil);
+ observer_list->AddObserver(&c);
+ observer_list->AddObserver(&d);
+
+ observer_list->Notify(&Foo::Observe, 10);
+ loop.RunUntilIdle();
+
+ EXPECT_EQ(20, a.total);
+ EXPECT_EQ(-20, b.total);
+ EXPECT_EQ(0, c.total);
+ EXPECT_EQ(-10, d.total);
+}
+
+TEST(ObserverListThreadSafeTest, RemoveObserver) {
+ MessageLoop loop;
+
+ scoped_refptr<ObserverListThreadSafe<Foo> > observer_list(
+ new ObserverListThreadSafe<Foo>);
+ Adder a(1), b(1);
+
+ // A workaround for the compiler bug. See http://crbug.com/121960.
+ EXPECT_NE(&a, &b);
+
+ // Should do nothing.
+ observer_list->RemoveObserver(&a);
+ observer_list->RemoveObserver(&b);
+
+ observer_list->Notify(&Foo::Observe, 10);
+ loop.RunUntilIdle();
+
+ EXPECT_EQ(0, a.total);
+ EXPECT_EQ(0, b.total);
+
+ observer_list->AddObserver(&a);
+
+ // Should also do nothing.
+ observer_list->RemoveObserver(&b);
+
+ observer_list->Notify(&Foo::Observe, 10);
+ loop.RunUntilIdle();
+
+ EXPECT_EQ(10, a.total);
+ EXPECT_EQ(0, b.total);
+}
+
+TEST(ObserverListThreadSafeTest, WithoutMessageLoop) {
+ scoped_refptr<ObserverListThreadSafe<Foo> > observer_list(
+ new ObserverListThreadSafe<Foo>);
+
+ Adder a(1), b(1), c(1);
+
+ // No MessageLoop, so these should not be added.
+ observer_list->AddObserver(&a);
+ observer_list->AddObserver(&b);
+
+ {
+ // Add c when there's a loop.
+ MessageLoop loop;
+ observer_list->AddObserver(&c);
+
+ observer_list->Notify(&Foo::Observe, 10);
+ loop.RunUntilIdle();
+
+ EXPECT_EQ(0, a.total);
+ EXPECT_EQ(0, b.total);
+ EXPECT_EQ(10, c.total);
+
+ // Now add a when there's a loop.
+ observer_list->AddObserver(&a);
+
+ // Remove c when there's a loop.
+ observer_list->RemoveObserver(&c);
+
+ // Notify again.
+ observer_list->Notify(&Foo::Observe, 20);
+ loop.RunUntilIdle();
+
+ EXPECT_EQ(20, a.total);
+ EXPECT_EQ(0, b.total);
+ EXPECT_EQ(10, c.total);
+ }
+
+ // Removing should always succeed with or without a loop.
+ observer_list->RemoveObserver(&a);
+
+ // Notifying should not fail but should also be a no-op.
+ MessageLoop loop;
+ observer_list->AddObserver(&b);
+ observer_list->Notify(&Foo::Observe, 30);
+ loop.RunUntilIdle();
+
+ EXPECT_EQ(20, a.total);
+ EXPECT_EQ(30, b.total);
+ EXPECT_EQ(10, c.total);
+}
+
+class FooRemover : public Foo {
+ public:
+ explicit FooRemover(ObserverListThreadSafe<Foo>* list) : list_(list) {}
+ virtual ~FooRemover() {}
+
+ void AddFooToRemove(Foo* foo) {
+ foos_.push_back(foo);
+ }
+
+ virtual void Observe(int x) OVERRIDE {
+ std::vector<Foo*> tmp;
+ tmp.swap(foos_);
+ for (std::vector<Foo*>::iterator it = tmp.begin();
+ it != tmp.end(); ++it) {
+ list_->RemoveObserver(*it);
+ }
+ }
+
+ private:
+ const scoped_refptr<ObserverListThreadSafe<Foo> > list_;
+ std::vector<Foo*> foos_;
+};
+
+TEST(ObserverListThreadSafeTest, RemoveMultipleObservers) {
+ MessageLoop loop;
+ scoped_refptr<ObserverListThreadSafe<Foo> > observer_list(
+ new ObserverListThreadSafe<Foo>);
+
+ FooRemover a(observer_list);
+ Adder b(1);
+
+ observer_list->AddObserver(&a);
+ observer_list->AddObserver(&b);
+
+ a.AddFooToRemove(&a);
+ a.AddFooToRemove(&b);
+
+ observer_list->Notify(&Foo::Observe, 1);
+ loop.RunUntilIdle();
+}
+
+// A test driver for a multi-threaded notification loop. Runs a number
+// of observer threads, each of which constantly adds/removes itself
+// from the observer list. Optionally, if cross_thread_notifies is set
+// to true, the observer threads will also trigger notifications to
+// all observers.
+static void ThreadSafeObserverHarness(int num_threads,
+ bool cross_thread_notifies) {
+ MessageLoop loop;
+
+ const int kMaxThreads = 15;
+ num_threads = num_threads > kMaxThreads ? kMaxThreads : num_threads;
+
+ scoped_refptr<ObserverListThreadSafe<Foo> > observer_list(
+ new ObserverListThreadSafe<Foo>);
+ Adder a(1);
+ Adder b(-1);
+ Adder c(1);
+ Adder d(-1);
+
+ observer_list->AddObserver(&a);
+ observer_list->AddObserver(&b);
+
+ AddRemoveThread* threaded_observer[kMaxThreads];
+ base::PlatformThreadHandle threads[kMaxThreads];
+ for (int index = 0; index < num_threads; index++) {
+ threaded_observer[index] = new AddRemoveThread(observer_list.get(), false);
+ EXPECT_TRUE(PlatformThread::Create(0,
+ threaded_observer[index], &threads[index]));
+ }
+
+ // Wait for all threads to start do avoid NULL ptr dereference
+ // when calling Quit()
+ for (int index = 0; index < num_threads; ++index) {
+ while (!threaded_observer[index]->Started()) {
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
+ }
+ }
+
+ Time start = Time::Now();
+ while (true) {
+ if ((Time::Now() - start).InMilliseconds() > kThreadRunTime)
+ break;
+
+ observer_list->Notify(&Foo::Observe, 10);
+
+ loop.RunUntilIdle();
+ }
+
+ for (int index = 0; index < num_threads; index++) {
+ threaded_observer[index]->Quit();
+ PlatformThread::Join(threads[index]);
+ }
+}
+
+TEST(ObserverListThreadSafeTest, CrossThreadObserver) {
+ // Use 7 observer threads. Notifications only come from
+ // the main thread.
+ ThreadSafeObserverHarness(7, false);
+}
+
+TEST(ObserverListThreadSafeTest, CrossThreadNotifications) {
+ // Use 3 observer threads. Notifications will fire from
+ // the main thread and all 3 observer threads.
+ ThreadSafeObserverHarness(3, true);
+}
+
+TEST(ObserverListThreadSafeTest, OutlivesMessageLoop) {
+ MessageLoop* loop = new MessageLoop;
+ scoped_refptr<ObserverListThreadSafe<Foo> > observer_list(
+ new ObserverListThreadSafe<Foo>);
+
+ Adder a(1);
+ observer_list->AddObserver(&a);
+ delete loop;
+ // Test passes if we don't crash here.
+ observer_list->Notify(&Foo::Observe, 1);
+}
+
+TEST(ObserverListTest, Existing) {
+ ObserverList<Foo> observer_list(ObserverList<Foo>::NOTIFY_EXISTING_ONLY);
+ Adder a(1);
+ AddInObserve<ObserverList<Foo> > b(&observer_list);
+
+ observer_list.AddObserver(&a);
+ observer_list.AddObserver(&b);
+
+ FOR_EACH_OBSERVER(Foo, observer_list, Observe(1));
+
+ EXPECT_TRUE(b.added);
+ // B's adder should not have been notified because it was added during
+ // notificaiton.
+ EXPECT_EQ(0, b.adder.total);
+
+ // Notify again to make sure b's adder is notified.
+ FOR_EACH_OBSERVER(Foo, observer_list, Observe(1));
+ EXPECT_EQ(1, b.adder.total);
+}
+
+// Same as above, but for ObserverListThreadSafe
+TEST(ObserverListThreadSafeTest, Existing) {
+ MessageLoop loop;
+ scoped_refptr<ObserverListThreadSafe<Foo> > observer_list(
+ new ObserverListThreadSafe<Foo>(ObserverList<Foo>::NOTIFY_EXISTING_ONLY));
+ Adder a(1);
+ AddInObserve<ObserverListThreadSafe<Foo> > b(observer_list.get());
+
+ observer_list->AddObserver(&a);
+ observer_list->AddObserver(&b);
+
+ observer_list->Notify(&Foo::Observe, 1);
+ loop.RunUntilIdle();
+
+ EXPECT_TRUE(b.added);
+ // B's adder should not have been notified because it was added during
+ // notificaiton.
+ EXPECT_EQ(0, b.adder.total);
+
+ // Notify again to make sure b's adder is notified.
+ observer_list->Notify(&Foo::Observe, 1);
+ loop.RunUntilIdle();
+ EXPECT_EQ(1, b.adder.total);
+}
+
+class AddInClearObserve : public Foo {
+ public:
+ explicit AddInClearObserve(ObserverList<Foo>* list)
+ : list_(list), added_(false), adder_(1) {}
+
+ virtual void Observe(int /* x */) OVERRIDE {
+ list_->Clear();
+ list_->AddObserver(&adder_);
+ added_ = true;
+ }
+
+ bool added() const { return added_; }
+ const Adder& adder() const { return adder_; }
+
+ private:
+ ObserverList<Foo>* const list_;
+
+ bool added_;
+ Adder adder_;
+};
+
+TEST(ObserverListTest, ClearNotifyAll) {
+ ObserverList<Foo> observer_list;
+ AddInClearObserve a(&observer_list);
+
+ observer_list.AddObserver(&a);
+
+ FOR_EACH_OBSERVER(Foo, observer_list, Observe(1));
+ EXPECT_TRUE(a.added());
+ EXPECT_EQ(1, a.adder().total)
+ << "Adder should observe once and have sum of 1.";
+}
+
+TEST(ObserverListTest, ClearNotifyExistingOnly) {
+ ObserverList<Foo> observer_list(ObserverList<Foo>::NOTIFY_EXISTING_ONLY);
+ AddInClearObserve a(&observer_list);
+
+ observer_list.AddObserver(&a);
+
+ FOR_EACH_OBSERVER(Foo, observer_list, Observe(1));
+ EXPECT_TRUE(a.added());
+ EXPECT_EQ(0, a.adder().total)
+ << "Adder should not observe, so sum should still be 0.";
+}
+
+class ListDestructor : public Foo {
+ public:
+ explicit ListDestructor(ObserverList<Foo>* list) : list_(list) {}
+ virtual ~ListDestructor() {}
+
+ virtual void Observe(int x) OVERRIDE {
+ delete list_;
+ }
+
+ private:
+ ObserverList<Foo>* list_;
+};
+
+
+TEST(ObserverListTest, IteratorOutlivesList) {
+ ObserverList<Foo>* observer_list = new ObserverList<Foo>;
+ ListDestructor a(observer_list);
+ observer_list->AddObserver(&a);
+
+ FOR_EACH_OBSERVER(Foo, *observer_list, Observe(0));
+ // If this test fails, there'll be Valgrind errors when this function goes out
+ // of scope.
+}
+
+} // namespace
diff --git a/src/base/optional.cc b/src/base/optional.cc
new file mode 100644
index 0000000..59100ad
--- /dev/null
+++ b/src/base/optional.cc
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "optional.h"
+
+namespace base {
+
+const nullopt_t nullopt;
+const in_place_t in_place;
+
+} // namespace base
diff --git a/src/base/optional.h b/src/base/optional.h
new file mode 100644
index 0000000..c516d7a
--- /dev/null
+++ b/src/base/optional.h
@@ -0,0 +1,391 @@
+/*
+ * Copyright 2014 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BASE_OPTIONAL_H_
+#define BASE_OPTIONAL_H_
+
+#include <iosfwd>
+
+#include "base/base_export.h"
+#include "base/hash_tables.h"
+#include "base/logging.h"
+#include "base/memory/aligned_memory.h"
+
+namespace base {
+
+// This class is based off of std::experimental::optional:
+// http://en.cppreference.com/w/cpp/experimental/optional
+//
+// It is a template class where instances parameterized by type T contain
+// memory for an instance of type T, but it may or may not be constructed.
+// If it is not constructed, it cannot be accessed, and if it is, it can
+// be accessed. This allows one to check if the inner object exists or not
+// before using it, and is useful for functions that may or may not return
+// a value. Note that the memory for the object is stored internally, so
+// no heap allocations are made over the course of construction and destruction
+// of the internal object (unless the internal object allocates memory within
+// its constructor).
+//
+// Some functionality is left out. For example, most C++11 functionality
+// is not implemented, since we would like this to be friendly to non-C++11
+// compilers.
+//
+// In the future, if C++11 functionality is needed, it can be implemented
+// and surrounded by preprocessor guards to maintain compatibility with non
+// C++11 compilers.
+//
+
+// The nullopt_t type is used as a signal for an empty optional. If any
+// optional is assigned the value of nullopt, it will be disengaged.
+// For example,
+// base::optional<int> my_int_optional(5);
+// EXPECT_FALSE(!my_int_optional);
+// my_int_optional = base::nullopt;
+// EXPECT_TRUE(!my_int_optional);
+//
+struct nullopt_t {
+ nullopt_t() {}
+};
+extern const nullopt_t nullopt;
+
+// The in_place_t type is used to signal in-place construction of the internal
+// object. This is used by the in place constructor of optional, which forwards
+// its parameters to the internal object's constructor.
+// For example,
+// class Foo {
+// public:
+// Foo(int x, int y) { x_ = x; y_ = y; }
+// int x() const { return x_; }
+// int y() const { return y_; }
+//
+// private:
+// int x_;
+// int y_;
+// };
+//
+// ...
+//
+// base::optional<Foo> my_foo(base::in_place, 2, 3);
+// EXPECT_FALSE(!my_foo);
+// EXPECT_EQ(2, my_foo->x());
+// EXPECT_EQ(3, my_foo->y());
+//
+struct in_place_t {
+ in_place_t() {}
+};
+extern const in_place_t in_place;
+
+template <typename T>
+class BASE_EXPORT optional {
+ public:
+ // Construction via the default constructor results in an optional that is
+ // not engaged.
+ optional() { InitializeAsDisengaged(); }
+
+ optional(nullopt_t) { InitializeAsDisengaged(); }
+
+ // This non-explicit singleton constructor is provided so users can pass in a
+ // T wherever a optional<T> is expected.
+ optional(const T& value) { SetValue(value); }
+
+ optional(const optional<T>& other) {
+ if (other.engaged_) {
+ SetValue(other.value());
+ } else {
+ InitializeAsDisengaged();
+ }
+ }
+
+ // Destruct contained object upon optional's destruction.
+ ~optional() { EnsureDisengaged(); }
+
+ // Disengages the optional, calling the destructor of the contained object
+ // if it is engaged.
+ optional<T>& operator=(nullopt_t) {
+ EnsureDisengaged();
+ return *this;
+ }
+
+ // Reassigns the underlying optional to value passed in on the right hand
+ // side. This will destruct the lhs contained object first if it exists.
+ template <typename U>
+ optional<T>& operator=(const U& other) {
+ if (engaged_) {
+ value() = other;
+ } else {
+ SetValue(other);
+ }
+ return *this;
+ }
+
+ // Copy assignment
+ optional<T>& operator=(const optional<T>& other) {
+ if (engaged_ && other.engaged_) {
+ value() = other.value();
+ } else if (!engaged_ && other.engaged_) {
+ SetValue(other.value());
+ } else if (engaged_ && !other.engaged_) {
+ EnsureDisengaged();
+ }
+ // Do nothing if lhs and rhs are both not engaged.
+ return *this;
+ }
+
+ // Overloaded conversion to bool operator for determining whether the optional
+ // is engaged or not. It returns true if the optional is engaged, and false
+ // otherwise.
+#if defined(_MSC_VER) && _MSC_VER < 1800
+ // MSVC 2012 does not support explicit cast operators.
+ // http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
+
+ // For any compiler that doesn't support explicit bool operators, we instead
+ // use the Safe Bool Idiom: http://www.artima.com/cppsource/safebool.html
+ private:
+ // The type of SafeBoolIdiomType (pointer to data member of a private type) is
+ // limited in functionality so much that the only thing a user can do with it
+ // is test for null, or apply to operator==/operator!=. Since both operators
+ // == and != are already overloaded for optional, this leaves null tests,
+ // which we use for boolean testing.
+ class PrivateSafeBoolIdiomFakeMemberType;
+ typedef PrivateSafeBoolIdiomFakeMemberType optional::*SafeBoolIdiomType;
+ public:
+ operator const SafeBoolIdiomType() const {
+ // If we wish to return true, we cast engaged_ to our private type giving
+ // a non-null pointer to data member. Otherwise, we return NULL. The
+ // only thing the user can do with the return type is test for NULL.
+ return engaged_ ?
+ reinterpret_cast<const SafeBoolIdiomType>(&optional::engaged_) :
+ NULL;
+ }
+#else
+ explicit operator bool() const { return engaged_; }
+#endif
+
+ // Dereferences the internal object.
+ const T* operator->() const { return &(value()); }
+
+ T* operator->() { return &(value()); }
+
+ const T& operator*() const { return value(); }
+
+ T& operator*() { return value(); }
+
+ // Dereferences and returns the internal object.
+ const T& value() const {
+ DCHECK(engaged_) << "Attempted to access object in a disengaged optional.";
+ return *static_cast<const T*>(value_memory_.void_data());
+ }
+
+ T& value() {
+ DCHECK(engaged_) << "Attempted to access object in a disengaged optional.";
+ return *static_cast<T*>(value_memory_.void_data());
+ }
+
+ template <typename U>
+ T value_or(const U& value) const {
+ if (engaged_) {
+ return this->value();
+ } else {
+ return value;
+ }
+ }
+
+ // Swaps the values of two optionals.
+ void swap(optional<T>& other) {
+ if (engaged_ && other.engaged_) {
+ // Swap the value contents with each other.
+ std::swap(value(), other.value());
+ } else if (engaged_) {
+ other.SetValue(value());
+ EnsureDisengaged();
+ } else if (other.engaged_) {
+ SetValue(other.value());
+ other.EnsureDisengaged();
+ }
+ // If both the lhs and rhs are not engaged, we do nothing.
+ }
+
+// include the pump.py-generated declaration and impelmentation for
+// forwarding constructor and emplace.
+#include "optional_internal.h"
+
+ private:
+ // Sets a non-engaged optional to a specified value, and marks it as engaged.
+ template <typename U>
+ void SetValue(const U& value) {
+ new (value_memory_.void_data()) T(value);
+ engaged_ = true;
+#if !defined(NDEBUG)
+ value_ptr_ = static_cast<const T*>(value_memory_.void_data());
+#endif
+ }
+
+ // If an optional is engaged, it destructs the wrapped value and marks the
+ // optional as disengaged. Does nothing to a disengaged optional.
+ void EnsureDisengaged() {
+ if (engaged_) {
+ static_cast<T*>(value_memory_.void_data())->~T();
+ engaged_ = false;
+#if !defined(NDEBUG)
+ value_ptr_ = NULL;
+#endif
+ }
+ }
+
+ // Called upon object construction to initialize the object into a disengaged
+ // state.
+ void InitializeAsDisengaged() {
+ engaged_ = false;
+#if !defined(NDEBUG)
+ value_ptr_ = NULL;
+#endif
+ }
+
+ // The actual memory reserved for the object that may or may not exist.
+ base::AlignedMemory<sizeof(T), ALIGNOF(T)> value_memory_;
+ // This boolean tracks whether or not the object is constructed yet or not.
+ bool engaged_;
+#if !defined(NDEBUG)
+ // In debug builds, this member makes it easy to inspect the value contained
+ // in the optional via a debugger.
+ const T* value_ptr_;
+#endif
+};
+
+// Comparison between 2 optionals
+template <typename T>
+inline bool operator==(const optional<T>& lhs, const optional<T>& rhs) {
+ if (!lhs) {
+ return !rhs;
+ }
+
+ return rhs == lhs.value();
+}
+
+template <typename T>
+inline bool operator<(const optional<T>& lhs, const optional<T>& rhs) {
+ if (lhs && rhs) {
+ return lhs.value() < rhs.value();
+ } else {
+ // Handle all other cases simply in terms of whether the optionals are
+ // engaged or not.
+ return static_cast<bool>(lhs) < static_cast<bool>(rhs);
+ }
+}
+
+// Comparison with nullopt_t
+template <typename T>
+inline bool operator==(nullopt_t, const optional<T>& rhs) {
+ return !rhs;
+}
+
+template <typename T>
+inline bool operator==(const optional<T>& lhs, nullopt_t rhs) {
+ return rhs == lhs;
+}
+
+template <typename T>
+inline bool operator<(const optional<T>& /* lhs */, nullopt_t) {
+ return false;
+}
+
+template <typename T>
+inline bool operator<(nullopt_t, const optional<T>& rhs) {
+ return static_cast<bool>(rhs);
+}
+
+// Comparison between an optional and a value
+template <typename T>
+inline bool operator==(const optional<T>& lhs, const T& rhs) {
+ return (!lhs ? false : lhs.value() == rhs);
+}
+
+template <typename T>
+inline bool operator==(const T& lhs, const optional<T>& rhs) {
+ return rhs == lhs;
+}
+
+template <typename T>
+inline bool operator<(const T& lhs, const optional<T>& rhs) {
+ return rhs && lhs < rhs.value();
+}
+
+template <typename T>
+inline bool operator<(const optional<T>& lhs, const T& rhs) {
+ return !lhs || lhs.value() < rhs;
+}
+
+// This is a convenient but non-standard method, do not rely on it if you expect
+// the compatibility with upcoming C++ versions.
+template <typename T>
+inline std::ostream& operator<<(std::ostream& stream,
+ const optional<T>& maybe_value) {
+ if (maybe_value) {
+ stream << *maybe_value;
+ } else {
+ stream << "nullopt";
+ }
+ return stream;
+}
+
+template <typename T>
+optional<T> make_optional(const T& value) {
+ return optional<T>(value);
+}
+
+} // namespace base
+
+namespace BASE_HASH_NAMESPACE {
+
+#if defined(BASE_HASH_USE_HASH_STRUCT)
+template <typename T>
+struct hash<base::optional<T> > {
+ public:
+ size_t operator()(const base::optional<T>& value) const {
+ if (!value) {
+ return 0;
+ } else {
+ return value_hash_(value.value());
+ }
+ }
+
+ private:
+ hash<T> value_hash_;
+};
+
+#else
+template <typename T>
+inline size_t hash_value(const base::optional<T>& value) {
+ if (!value) {
+ return 0;
+ } else {
+ return hash_value(value.value());
+ }
+}
+
+#endif // defined(BASE_HASH_USE_HASH_STRUCT)
+} // namespace BASE_HASH_NAMESPACE
+
+namespace std {
+
+template <typename T>
+void swap(base::optional<T>& lhs, base::optional<T>& rhs) {
+ lhs.swap(rhs);
+}
+
+} // namespace std
+
+#endif // BASE_OPTIONAL_H_
diff --git a/src/base/optional_internal.h b/src/base/optional_internal.h
new file mode 100644
index 0000000..2af1f64
--- /dev/null
+++ b/src/base/optional_internal.h
@@ -0,0 +1,178 @@
+// This file was GENERATED by command:
+// pump.py optional_internal.h.pump
+// DO NOT EDIT BY HAND!!!
+
+//
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Begin forwarding constructor definitions ////////////////////////////////////
+ explicit optional(in_place_t) {
+ InitializeAsDisengaged();
+ new (value_memory_.void_data()) T();
+ engaged_ = true;
+#if !defined(NDEBUG)
+ value_ptr_ = static_cast<const T*>(value_memory_.void_data());
+#endif
+ }
+ template <typename P1>
+ explicit optional(in_place_t, const P1& p1) {
+ InitializeAsDisengaged();
+ new (value_memory_.void_data()) T(p1);
+ engaged_ = true;
+#if !defined(NDEBUG)
+ value_ptr_ = static_cast<const T*>(value_memory_.void_data());
+#endif
+ }
+ template <typename P1, typename P2>
+ explicit optional(in_place_t, const P1& p1, const P2& p2) {
+ InitializeAsDisengaged();
+ new (value_memory_.void_data()) T(p1, p2);
+ engaged_ = true;
+#if !defined(NDEBUG)
+ value_ptr_ = static_cast<const T*>(value_memory_.void_data());
+#endif
+ }
+ template <typename P1, typename P2, typename P3>
+ explicit optional(in_place_t, const P1& p1, const P2& p2, const P3& p3) {
+ InitializeAsDisengaged();
+ new (value_memory_.void_data()) T(p1, p2, p3);
+ engaged_ = true;
+#if !defined(NDEBUG)
+ value_ptr_ = static_cast<const T*>(value_memory_.void_data());
+#endif
+ }
+ template <typename P1, typename P2, typename P3, typename P4>
+ explicit optional(in_place_t, const P1& p1, const P2& p2, const P3& p3,
+ const P4& p4) {
+ InitializeAsDisengaged();
+ new (value_memory_.void_data()) T(p1, p2, p3, p4);
+ engaged_ = true;
+#if !defined(NDEBUG)
+ value_ptr_ = static_cast<const T*>(value_memory_.void_data());
+#endif
+ }
+ template <typename P1, typename P2, typename P3, typename P4, typename P5>
+ explicit optional(in_place_t, const P1& p1, const P2& p2, const P3& p3,
+ const P4& p4, const P5& p5) {
+ InitializeAsDisengaged();
+ new (value_memory_.void_data()) T(p1, p2, p3, p4, p5);
+ engaged_ = true;
+#if !defined(NDEBUG)
+ value_ptr_ = static_cast<const T*>(value_memory_.void_data());
+#endif
+ }
+ template <typename P1, typename P2, typename P3, typename P4, typename P5,
+ typename P6>
+ explicit optional(in_place_t, const P1& p1, const P2& p2, const P3& p3,
+ const P4& p4, const P5& p5, const P6& p6) {
+ InitializeAsDisengaged();
+ new (value_memory_.void_data()) T(p1, p2, p3, p4, p5, p6);
+ engaged_ = true;
+#if !defined(NDEBUG)
+ value_ptr_ = static_cast<const T*>(value_memory_.void_data());
+#endif
+ }
+ template <typename P1, typename P2, typename P3, typename P4, typename P5,
+ typename P6, typename P7>
+ explicit optional(in_place_t, const P1& p1, const P2& p2, const P3& p3,
+ const P4& p4, const P5& p5, const P6& p6, const P7& p7) {
+ InitializeAsDisengaged();
+ new (value_memory_.void_data()) T(p1, p2, p3, p4, p5, p6, p7);
+ engaged_ = true;
+#if !defined(NDEBUG)
+ value_ptr_ = static_cast<const T*>(value_memory_.void_data());
+#endif
+ }
+// End forwarding constructor definitions //////////////////////////////////////
+
+// Begin emplace(...) definitions //////////////////////////////////////////////
+ void emplace() {
+ EnsureDisengaged();
+ new (value_memory_.void_data()) T();
+ engaged_ = true;
+#if !defined(NDEBUG)
+ value_ptr_ = static_cast<const T*>(value_memory_.void_data());
+#endif
+ }
+ template <typename P1>
+ void emplace(const P1& p1) {
+ EnsureDisengaged();
+ new (value_memory_.void_data()) T(p1);
+ engaged_ = true;
+#if !defined(NDEBUG)
+ value_ptr_ = static_cast<const T*>(value_memory_.void_data());
+#endif
+ }
+ template <typename P1, typename P2>
+ void emplace(const P1& p1, const P2& p2) {
+ EnsureDisengaged();
+ new (value_memory_.void_data()) T(p1, p2);
+ engaged_ = true;
+#if !defined(NDEBUG)
+ value_ptr_ = static_cast<const T*>(value_memory_.void_data());
+#endif
+ }
+ template <typename P1, typename P2, typename P3>
+ void emplace(const P1& p1, const P2& p2, const P3& p3) {
+ EnsureDisengaged();
+ new (value_memory_.void_data()) T(p1, p2, p3);
+ engaged_ = true;
+#if !defined(NDEBUG)
+ value_ptr_ = static_cast<const T*>(value_memory_.void_data());
+#endif
+ }
+ template <typename P1, typename P2, typename P3, typename P4>
+ void emplace(const P1& p1, const P2& p2, const P3& p3, const P4& p4) {
+ EnsureDisengaged();
+ new (value_memory_.void_data()) T(p1, p2, p3, p4);
+ engaged_ = true;
+#if !defined(NDEBUG)
+ value_ptr_ = static_cast<const T*>(value_memory_.void_data());
+#endif
+ }
+ template <typename P1, typename P2, typename P3, typename P4, typename P5>
+ void emplace(const P1& p1, const P2& p2, const P3& p3, const P4& p4,
+ const P5& p5) {
+ EnsureDisengaged();
+ new (value_memory_.void_data()) T(p1, p2, p3, p4, p5);
+ engaged_ = true;
+#if !defined(NDEBUG)
+ value_ptr_ = static_cast<const T*>(value_memory_.void_data());
+#endif
+ }
+ template <typename P1, typename P2, typename P3, typename P4, typename P5,
+ typename P6>
+ void emplace(const P1& p1, const P2& p2, const P3& p3, const P4& p4,
+ const P5& p5, const P6& p6) {
+ EnsureDisengaged();
+ new (value_memory_.void_data()) T(p1, p2, p3, p4, p5, p6);
+ engaged_ = true;
+#if !defined(NDEBUG)
+ value_ptr_ = static_cast<const T*>(value_memory_.void_data());
+#endif
+ }
+ template <typename P1, typename P2, typename P3, typename P4, typename P5,
+ typename P6, typename P7>
+ void emplace(const P1& p1, const P2& p2, const P3& p3, const P4& p4,
+ const P5& p5, const P6& p6, const P7& p7) {
+ EnsureDisengaged();
+ new (value_memory_.void_data()) T(p1, p2, p3, p4, p5, p6, p7);
+ engaged_ = true;
+#if !defined(NDEBUG)
+ value_ptr_ = static_cast<const T*>(value_memory_.void_data());
+#endif
+ }
+// End emplace(...) definitions ////////////////////////////////////////////////
diff --git a/src/base/optional_internal.h.pump b/src/base/optional_internal.h.pump
new file mode 100644
index 0000000..f11d2b2
--- /dev/null
+++ b/src/base/optional_internal.h.pump
@@ -0,0 +1,72 @@
+//
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+$$ This is a pump file for generating the interface and implementation of
+$$ optional's forwarding constructor and emplace() method. Pump is a python
+$$ script that is part of the Google Test suite of utilities. Description
+$$ can be found here:
+$$
+$$ http://code.google.com/p/googletest/wiki/PumpManual
+$$
+
+$var MAX_ARITY = 7
+
+$range ARITY 0..MAX_ARITY
+
+// Begin forwarding constructor definitions ////////////////////////////////////
+
+$for ARITY [[
+
+$range ARG 1..ARITY
+
+$if ARITY > 0 [[
+ template <$for ARG , [[typename P$(ARG)]]>
+
+]]
+ explicit optional(in_place_t$if ARITY > 0 [[, ]]
+ $for ARG , [[const P$(ARG)& p$(ARG)]]) {
+ InitializeAsDisengaged();
+ new (value_memory_.void_data()) T($for ARG , [[p$(ARG)]]);
+ engaged_ = true;
+#if !defined(NDEBUG)
+ value_ptr_ = static_cast<const T*>(value_memory_.void_data());
+#endif
+ }
+
+]]
+// End forwarding constructor definitions //////////////////////////////////////
+
+// Begin emplace(...) definitions //////////////////////////////////////////////
+
+$for ARITY [[
+
+$range ARG 1..ARITY
+
+$if ARITY > 0 [[
+ template <$for ARG , [[typename P$(ARG)]]>
+
+]]
+ void emplace($for ARG , [[const P$(ARG)& p$(ARG)]]) {
+ EnsureDisengaged();
+ new (value_memory_.void_data()) T($for ARG , [[p$(ARG)]]);
+ engaged_ = true;
+#if !defined(NDEBUG)
+ value_ptr_ = static_cast<const T*>(value_memory_.void_data());
+#endif
+ }
+
+]]
+// End emplace(...) definitions ////////////////////////////////////////////////
diff --git a/src/base/optional_unittest.cc b/src/base/optional_unittest.cc
new file mode 100644
index 0000000..3fe99c8
--- /dev/null
+++ b/src/base/optional_unittest.cc
@@ -0,0 +1,730 @@
+/*
+ * Copyright 2014 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/hash_tables.h"
+#include "base/optional.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::InSequence;
+
+typedef base::optional<int> OptionalInt;
+
+TEST(OptionalTest, EnsureDefaultConstructorGivesDisengagedOptional) {
+ OptionalInt test;
+ EXPECT_TRUE(!test);
+}
+
+TEST(OptionalTest, EnsureNullOptConstructorGivesDisengagedOptional) {
+ OptionalInt test(base::nullopt);
+ EXPECT_TRUE(!test);
+}
+
+TEST(OptionalTest, InitializeConstructor) {
+ OptionalInt test(2);
+ EXPECT_FALSE(!test);
+ EXPECT_EQ(2, test.value());
+}
+
+TEST(OptionalTest, BoolCastOperator) {
+ OptionalInt test;
+ EXPECT_FALSE(static_cast<bool>(test));
+
+ test = 5;
+ EXPECT_TRUE(static_cast<bool>(test));
+}
+
+TEST(OptionalTest, InitializeAssign) {
+ OptionalInt test = 2;
+ EXPECT_FALSE(!test);
+ EXPECT_EQ(2, test.value());
+}
+
+TEST(OptionalTest, ReassignValue) {
+ OptionalInt test(2);
+ test = 5;
+ EXPECT_FALSE(!test);
+ EXPECT_EQ(5, test.value());
+}
+
+TEST(OptionalTest, CopyAssignment) {
+ OptionalInt a;
+ OptionalInt b = 2;
+ OptionalInt c = 3;
+ a = b;
+ EXPECT_FALSE(!a);
+ EXPECT_EQ(2, a.value());
+
+ a = c;
+ EXPECT_FALSE(!a);
+ EXPECT_EQ(3, a.value());
+}
+
+TEST(OptionalTest, ClearAssignment) {
+ OptionalInt test(2);
+ test = OptionalInt();
+ EXPECT_FALSE(test);
+}
+
+TEST(OptionalTest, EnsureAssignmentOfNullOptResultsInDisengagement) {
+ OptionalInt test(2);
+ test = base::nullopt;
+ EXPECT_FALSE(test);
+}
+
+TEST(OptionalTest, EnsureAssignmentOfDefaultOptionalResultsInDisengagement) {
+ OptionalInt test(2);
+ test = OptionalInt();
+ EXPECT_FALSE(test);
+}
+
+TEST(OptionalTest, CopyConstruction) {
+ OptionalInt test1(2);
+ OptionalInt test2(test1);
+
+ EXPECT_FALSE(!test2);
+ EXPECT_EQ(2, test2.value());
+}
+
+TEST(OptionalTest, Swap) {
+ OptionalInt test1(1);
+ OptionalInt test2(2);
+
+ // Swap two engaged optionals.
+ test1.swap(test2);
+ EXPECT_FALSE(!test1);
+ EXPECT_EQ(2, test1.value());
+ EXPECT_EQ(1, test2.value());
+
+ // Swap two optionals where only one is engaged.
+ test1 = base::nullopt;
+ test1.swap(test2);
+ EXPECT_FALSE(test2);
+ EXPECT_FALSE(!test1);
+ EXPECT_EQ(1, test1.value());
+
+ // Swap two optionals where only one is engaged, except the other way around.
+ test1.swap(test2);
+ EXPECT_FALSE(test1);
+ EXPECT_FALSE(!test2);
+ EXPECT_EQ(1, test2.value());
+
+ // Swap two disengaged optionals.
+ test2 = base::nullopt;
+ test1.swap(test2);
+ EXPECT_FALSE(test1);
+ EXPECT_FALSE(test2);
+}
+
+TEST(OptionalTest, StdSwap) {
+ OptionalInt test1(1);
+ OptionalInt test2(2);
+
+ // Swap two engaged optionals.
+ std::swap(test1, test2);
+ EXPECT_FALSE(!test1);
+ EXPECT_EQ(2, test1.value());
+ EXPECT_EQ(1, test2.value());
+
+ // Swap two optionals where only one is engaged.
+ test1 = base::nullopt;
+ std::swap(test1, test2);
+ EXPECT_FALSE(test2);
+ EXPECT_FALSE(!test1);
+ EXPECT_EQ(1, test1.value());
+
+ // Swap two optionals where only one is engaged, except the other way around.
+ std::swap(test1, test2);
+ EXPECT_FALSE(test1);
+ EXPECT_FALSE(!test2);
+ EXPECT_EQ(1, test2.value());
+
+ // Swap two disengaged optionals.
+ test2 = base::nullopt;
+ std::swap(test1, test2);
+ EXPECT_FALSE(test1);
+ EXPECT_FALSE(test2);
+}
+
+TEST(OptionalTest, SwapWithSelf) {
+ OptionalInt test(1);
+
+ test.swap(test);
+ EXPECT_EQ(1, test.value());
+
+ std::swap(test, test);
+ EXPECT_EQ(1, test.value());
+}
+
+TEST(OptionalTest, AsteriskDereference) {
+ OptionalInt test(2);
+ EXPECT_EQ(2, *test);
+
+ *test = 5;
+ EXPECT_EQ(5, test.value());
+}
+
+namespace {
+struct TestStruct {
+ int foobar;
+};
+} // namespace
+
+TEST(OptionalTest, ArrowDereference) {
+ TestStruct a;
+ a.foobar = 2;
+
+ base::optional<TestStruct> test(a);
+ EXPECT_EQ(2, test->foobar);
+
+ test->foobar = 5;
+ EXPECT_EQ(5, test.value().foobar);
+
+ // Test const arrow dereference.
+ const base::optional<TestStruct> test_const(a);
+ EXPECT_EQ(2, test_const->foobar);
+}
+
+namespace {
+class NoDefaultTest {
+ public:
+ explicit NoDefaultTest(int i) { foobar_ = i; }
+ int foobar() const { return foobar_; }
+
+ private:
+ NoDefaultTest();
+
+ int foobar_;
+};
+} // namespace
+
+TEST(OptionalTest, NoDefaultConstructorIsSupported) {
+ // First test with an object passed in upon construction
+ base::optional<NoDefaultTest> test1(NoDefaultTest(2));
+ EXPECT_EQ(2, test1.value().foobar());
+
+ // Now test with an object assignment after construction
+ base::optional<NoDefaultTest> test2;
+ test2 = NoDefaultTest(5);
+ EXPECT_EQ(5, test2.value().foobar());
+}
+
+TEST(OptionalTest, TestEquivalenceComparisons) {
+ OptionalInt test1 = 1;
+ OptionalInt test2 = 2;
+ OptionalInt test3;
+ OptionalInt test4 = 2;
+
+ EXPECT_FALSE(test1 == test2);
+ EXPECT_FALSE(test2 == test1);
+ EXPECT_FALSE(test1 == test3);
+ EXPECT_FALSE(test3 == test1);
+ EXPECT_FALSE(2 == test1);
+ EXPECT_FALSE(test1 == 2);
+ EXPECT_EQ(1, test1);
+ EXPECT_EQ(test1, 1);
+ EXPECT_EQ(test4, test2);
+ EXPECT_EQ(test2, test4);
+
+ // Test nullopt comparisons
+ EXPECT_TRUE(test3 == base::nullopt);
+ EXPECT_TRUE(base::nullopt == test3);
+ EXPECT_FALSE(test1 == base::nullopt);
+ EXPECT_FALSE(base::nullopt == test1);
+}
+
+TEST(OptionalTest, TestLessThanComparisons) {
+ OptionalInt test1 = 1;
+ OptionalInt test2 = 2;
+ OptionalInt test3;
+ OptionalInt test4 = 2;
+
+ EXPECT_TRUE(test1 < test2);
+ EXPECT_FALSE(test2 < test1);
+ EXPECT_FALSE(test1 < test1);
+ EXPECT_TRUE(test3 < test1);
+ EXPECT_FALSE(test1 < test3);
+ EXPECT_FALSE(test3 < test3);
+
+ EXPECT_TRUE(base::nullopt < test1);
+ EXPECT_FALSE(test1 < base::nullopt);
+
+ EXPECT_TRUE(test1 < 2);
+ EXPECT_FALSE(2 < test1);
+}
+
+TEST(OptionalTest, EnsureOptionalWorksWithFunnyAlignments) {
+ struct {
+ char c;
+ base::optional<uint64_t> number;
+ } foo;
+
+ EXPECT_FALSE(!!foo.number);
+ foo.number = 1;
+ EXPECT_TRUE(!!foo.number);
+ EXPECT_EQ(1, foo.number.value());
+}
+
+namespace {
+
+class DestructorCallTester {
+ public:
+ DestructorCallTester() {};
+ DestructorCallTester(const DestructorCallTester&) {};
+ ~DestructorCallTester() { Die(); }
+ MOCK_METHOD0(Die, void());
+};
+
+} // namespace
+
+TEST(OptionalTest, EnsureDestructorIsCalled) {
+ {
+ // Ensure destructor is called upon optional destruction
+ base::optional<DestructorCallTester> test(base::in_place);
+ EXPECT_CALL(test.value(), Die());
+ }
+
+ {
+ // Ensure destructor is called upon assignment to null
+ base::optional<DestructorCallTester> test1(base::in_place);
+ base::optional<DestructorCallTester> test2(base::in_place);
+ {
+ InSequence s;
+ EXPECT_CALL(test1.value(), Die());
+ EXPECT_CALL(test2.value(), Die());
+ }
+ test1 = base::nullopt;
+ }
+}
+
+namespace {
+
+// This class counts all calls to the set of methods declared in the class.
+// It can be used to verify that certain methods are indeed called.
+class MethodCallCounter {
+ public:
+ MethodCallCounter() {
+ ResetCounts();
+ ++default_constructor_calls_;
+ }
+
+ MethodCallCounter(const MethodCallCounter& other) {
+ // A very non-standard copy constructor, since this is a test object
+ // intended to count method calls on this object and only this object.
+ ResetCounts();
+ ++copy_constructor_calls_;
+ }
+
+ MethodCallCounter& operator=(const MethodCallCounter& other) {
+ ++operator_equals_calls_;
+ return *this;
+ }
+
+ int default_constructor_calls() const { return default_constructor_calls_; }
+ int copy_constructor_calls() const { return copy_constructor_calls_; }
+ int operator_equals_calls() const { return operator_equals_calls_; }
+
+ private:
+ void ResetCounts() {
+ default_constructor_calls_ = 0;
+ copy_constructor_calls_ = 0;
+ operator_equals_calls_ = 0;
+ }
+
+ int default_constructor_calls_;
+ int copy_constructor_calls_;
+ int operator_equals_calls_;
+};
+
+} // namespace
+
+TEST(OptionalTest, CopyConstructorIsCalledByValueCopyConstructor) {
+ MethodCallCounter original_counter;
+ base::optional<MethodCallCounter> test(original_counter);
+ EXPECT_EQ(0, test->default_constructor_calls());
+ EXPECT_EQ(1, test->copy_constructor_calls());
+ EXPECT_EQ(0, test->operator_equals_calls());
+}
+
+TEST(OptionalTest, CopyConstructorIsCalledByOptionalCopyConstructor) {
+ MethodCallCounter original_counter;
+ base::optional<MethodCallCounter> test1(original_counter);
+ base::optional<MethodCallCounter> test2(test1);
+ EXPECT_EQ(0, test2->default_constructor_calls());
+ EXPECT_EQ(1, test2->copy_constructor_calls());
+ EXPECT_EQ(0, test2->operator_equals_calls());
+}
+
+TEST(OptionalTest, CopyConstructorIsCalledByValueAssignment) {
+ MethodCallCounter original_counter;
+ base::optional<MethodCallCounter> test;
+ test = original_counter;
+ EXPECT_EQ(0, test->default_constructor_calls());
+ EXPECT_EQ(1, test->copy_constructor_calls());
+ EXPECT_EQ(0, test->operator_equals_calls());
+}
+
+TEST(OptionalTest, CopyConstructorIsCalledByOptionalAssignment) {
+ MethodCallCounter original_counter;
+ base::optional<MethodCallCounter> test1(original_counter);
+ base::optional<MethodCallCounter> test2;
+ test2 = test1;
+ EXPECT_EQ(0, test2->default_constructor_calls());
+ EXPECT_EQ(1, test2->copy_constructor_calls());
+ EXPECT_EQ(0, test2->operator_equals_calls());
+}
+
+TEST(OptionalTest, AssignmentIsCalledByValueAssignment) {
+ MethodCallCounter original_counter;
+ base::optional<MethodCallCounter> test(original_counter);
+ test = original_counter;
+ EXPECT_EQ(0, test->default_constructor_calls());
+ EXPECT_EQ(1, test->copy_constructor_calls());
+ EXPECT_EQ(1, test->operator_equals_calls());
+}
+
+TEST(OptionalTest, AssignmentIsCalledByOptionalAssignment) {
+ MethodCallCounter original_counter;
+ base::optional<MethodCallCounter> test1(original_counter);
+ base::optional<MethodCallCounter> test2(original_counter);
+ test2 = test1;
+ EXPECT_EQ(0, test2->default_constructor_calls());
+ EXPECT_EQ(1, test2->copy_constructor_calls());
+ EXPECT_EQ(1, test2->operator_equals_calls());
+}
+
+TEST(OptionalTest, EnsureSelfAssignmentIsOkay) {
+ // Ensure that values are as we expect them to be.
+ OptionalInt test1(5);
+
+ test1 = test1;
+
+ EXPECT_FALSE(!test1);
+ EXPECT_EQ(5, test1.value());
+
+ // Ensure that the methods we expect to be called are actually called.
+ base::optional<MethodCallCounter> test2(base::in_place);
+
+ test2 = test2;
+
+ EXPECT_EQ(1, test2->default_constructor_calls());
+ EXPECT_EQ(0, test2->copy_constructor_calls());
+ EXPECT_EQ(1, test2->operator_equals_calls());
+}
+
+namespace {
+
+// Helper classes to ensure that we can assign different values to optionals
+// so long as the wrapped value can be assigned and constructed from the other.
+struct XType {
+ XType(int number) { number_ = number; }
+
+ int number_;
+};
+
+struct YType {
+ explicit YType(int number) { number_ = number; }
+
+ explicit YType(const XType& x_type) { number_ = x_type.number_; }
+
+ YType& operator=(const XType& x_type) {
+ number_ = x_type.number_;
+ return *this;
+ }
+
+ int number_;
+};
+
+} // namespace
+
+TEST(OptionalTest, CopyConstructorIsCalledByValueAssignmentFromOtherType) {
+ XType x_type(1);
+ base::optional<YType> test;
+ test = x_type;
+ EXPECT_FALSE(!test);
+ EXPECT_EQ(1, test->number_);
+}
+
+TEST(OptionalTest, AssignmentIsCalledByValueAssignmentFromOtherType) {
+ XType x_type(1);
+ base::optional<YType> test(base::in_place, 2);
+ test = x_type;
+ EXPECT_FALSE(!test);
+ EXPECT_EQ(1, test->number_);
+}
+
+TEST(OptionalTest, TestMakeOptional) {
+ OptionalInt test = base::make_optional(5);
+
+ EXPECT_FALSE(!test);
+ EXPECT_EQ(5, test.value());
+}
+
+TEST(OptionalTest, ValueOrTest) {
+ OptionalInt test;
+
+ EXPECT_EQ(4, test.value_or(4));
+
+ test = 2;
+
+ EXPECT_EQ(2, test.value_or(4));
+}
+
+TEST(OptionalTest, ConstOptionalsTest) {
+ const OptionalInt test1(5);
+
+ EXPECT_FALSE(!test1);
+ EXPECT_EQ(5, test1.value());
+ EXPECT_EQ(5, *test1);
+
+ const OptionalInt test2(test1);
+
+ EXPECT_FALSE(!test2);
+ EXPECT_EQ(5, test2.value());
+ EXPECT_EQ(5, *test2);
+
+ OptionalInt test3;
+ test3 = test2;
+
+ EXPECT_FALSE(!test3);
+ EXPECT_EQ(5, test3.value());
+ EXPECT_EQ(5, *test3);
+}
+
+TEST(OptionalTest, EmplaceInt) {
+ OptionalInt test;
+
+ test.emplace(5);
+ EXPECT_FALSE(!test);
+ EXPECT_EQ(5, test.value());
+}
+
+TEST(OptionalTest, EmplaceWithDefaultConstructor) {
+ base::optional<MethodCallCounter> test;
+ test.emplace();
+
+ EXPECT_FALSE(!test);
+ EXPECT_EQ(1, test->default_constructor_calls());
+ EXPECT_EQ(0, test->copy_constructor_calls());
+ EXPECT_EQ(0, test->operator_equals_calls());
+}
+
+namespace {
+
+class NoDefaultOrCopyConstructor {
+ public:
+ NoDefaultOrCopyConstructor(int x, int y) {
+ x_ = x;
+ y_ = y;
+ }
+
+ int x() const { return x_; }
+ int y() const { return y_; }
+
+ private:
+ NoDefaultOrCopyConstructor();
+ NoDefaultOrCopyConstructor(const NoDefaultOrCopyConstructor&);
+
+ int x_;
+ int y_;
+};
+
+} // namespace
+
+TEST(OptionalTest, EmplaceObjectWithNoDefaultOrCopyConstructor) {
+ base::optional<NoDefaultOrCopyConstructor> test;
+ test.emplace(1, 2);
+ EXPECT_FALSE(!test);
+ EXPECT_EQ(1, test->x());
+ EXPECT_EQ(2, test->y());
+}
+
+namespace {
+
+class NonConstPointerInConstructor {
+ public:
+ explicit NonConstPointerInConstructor(int* param) { param_ = param; }
+
+ void SetParam(int value) { *param_ = value; }
+
+ private:
+ int* param_;
+};
+
+} // namespace
+
+TEST(OptionalTest, EmplaceObjectWithNonConstPointer) {
+ int value = 0;
+ base::optional<NonConstPointerInConstructor> test;
+ test.emplace(&value);
+ test->SetParam(5);
+ EXPECT_EQ(5, value);
+}
+
+TEST(OptionalTest, ForwardingConstructorInt) {
+ OptionalInt test(base::in_place, 5);
+ EXPECT_FALSE(!test);
+ EXPECT_EQ(5, test.value());
+}
+
+TEST(OptionalTest, ForwardingConstructorWithDefaultConstructor) {
+ base::optional<MethodCallCounter> test(base::in_place);
+ EXPECT_FALSE(!test);
+ EXPECT_EQ(1, test->default_constructor_calls());
+ EXPECT_EQ(0, test->copy_constructor_calls());
+ EXPECT_EQ(0, test->operator_equals_calls());
+}
+
+TEST(OptionalTest, ForwardingConstructorObjectWithNoDefaultOrCopyConstructor) {
+ base::optional<NoDefaultOrCopyConstructor> test(base::in_place, 1, 2);
+ EXPECT_FALSE(!test);
+ EXPECT_EQ(1, test->x());
+ EXPECT_EQ(2, test->y());
+}
+
+TEST(OptionalTest, ForwardingConstructorObjectWithNonConstPointer) {
+ int value = 0;
+ base::optional<NonConstPointerInConstructor> test(base::in_place, &value);
+ test->SetParam(5);
+ EXPECT_EQ(5, value);
+}
+
+namespace {
+typedef base::optional<std::string> OptionalString;
+} // namespace
+
+TEST(OptionalTest, OptionalStringTest) {
+ {
+ OptionalString test;
+ EXPECT_TRUE(!test);
+
+ test = std::string("foo");
+ EXPECT_STREQ("foo", test->c_str());
+ }
+
+ {
+ OptionalString test(std::string("foo"));
+ EXPECT_FALSE(!test);
+
+ EXPECT_STREQ("foo", test->c_str());
+ OptionalString test2(test);
+ EXPECT_STREQ("foo", test2->c_str());
+ }
+}
+
+TEST(OptionalTest, OptionalStringInSetTest) {
+ // Optional strings in map test
+ std::set<OptionalString> optional_string_set;
+
+ optional_string_set.insert(std::string("foo"));
+ optional_string_set.insert(std::string("bar"));
+
+ EXPECT_TRUE(optional_string_set.find(std::string("foo")) !=
+ optional_string_set.end());
+ EXPECT_TRUE(optional_string_set.find(std::string("bar")) !=
+ optional_string_set.end());
+ EXPECT_FALSE(optional_string_set.find(OptionalString()) !=
+ optional_string_set.end());
+
+ optional_string_set.insert(OptionalString());
+
+ EXPECT_TRUE(optional_string_set.find(std::string("foo")) !=
+ optional_string_set.end());
+ EXPECT_TRUE(optional_string_set.find(std::string("bar")) !=
+ optional_string_set.end());
+ EXPECT_TRUE(optional_string_set.find(OptionalString()) !=
+ optional_string_set.end());
+}
+
+TEST(OptionalTest, StdVectorOfOptionalsWorksFine) {
+ std::vector<OptionalInt> test_vector;
+
+ test_vector.reserve(test_vector.capacity() + 1);
+ test_vector.resize(test_vector.capacity());
+
+ // Make sure all current optionals are disengaged.
+ for (std::vector<OptionalInt>::const_iterator iter = test_vector.begin();
+ iter != test_vector.end();
+ ++iter) {
+ EXPECT_TRUE(!*iter);
+ }
+ EXPECT_TRUE(!test_vector[0]);
+
+ test_vector.clear();
+ test_vector.resize(test_vector.capacity() + 1, 5);
+ for (std::vector<OptionalInt>::const_iterator iter = test_vector.begin();
+ iter != test_vector.end();
+ ++iter) {
+ ASSERT_FALSE(!*iter);
+ EXPECT_EQ(5, **iter);
+ }
+ ASSERT_FALSE(!test_vector[0]);
+ EXPECT_EQ(5, *test_vector[0]);
+
+ test_vector.push_back(8);
+ ASSERT_FALSE(!test_vector.back());
+ EXPECT_EQ(8, *test_vector.back());
+}
+
+TEST(OptionalTest, OptionalStringInHashSetTest) {
+ // Optional strings in map test
+ base::hash_set<OptionalString> optional_string_set;
+
+ optional_string_set.insert(std::string("foo"));
+ optional_string_set.insert(std::string("bar"));
+
+ EXPECT_TRUE(optional_string_set.find(std::string("foo")) !=
+ optional_string_set.end());
+ EXPECT_TRUE(optional_string_set.find(std::string("bar")) !=
+ optional_string_set.end());
+ EXPECT_FALSE(optional_string_set.find(OptionalString()) !=
+ optional_string_set.end());
+
+ optional_string_set.insert(OptionalString());
+
+ EXPECT_TRUE(optional_string_set.find(std::string("foo")) !=
+ optional_string_set.end());
+ EXPECT_TRUE(optional_string_set.find(std::string("bar")) !=
+ optional_string_set.end());
+ EXPECT_TRUE(optional_string_set.find(OptionalString()) !=
+ optional_string_set.end());
+}
+
+namespace {
+
+OptionalInt ConditionallyMakeOptional(bool should_make, int value) {
+ if (should_make) {
+ return OptionalInt(value);
+ } else {
+ return base::nullopt;
+ }
+}
+
+} // namespace
+
+TEST(OptionalTest, OptionalCanBeReturnedFromFunction) {
+ OptionalInt test1 = ConditionallyMakeOptional(true, 5);
+ ASSERT_FALSE(!test1);
+ EXPECT_EQ(5, test1.value());
+
+ OptionalInt test2 = ConditionallyMakeOptional(false, 5);
+ EXPECT_TRUE(!test2);
+}
diff --git a/src/base/os_compat_android.cc b/src/base/os_compat_android.cc
new file mode 100644
index 0000000..d434c5b
--- /dev/null
+++ b/src/base/os_compat_android.cc
@@ -0,0 +1,155 @@
+// Copyright (c) 2012 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/os_compat_android.h"
+
+#include <errno.h>
+#include <math.h>
+#include <sys/stat.h>
+#include <time64.h>
+
+#include "base/rand_util.h"
+#include "base/string_piece.h"
+#include "base/stringprintf.h"
+
+// There is no futimes() avaiable in Bionic, so we provide our own
+// implementation until it is there.
+extern "C" {
+
+int futimes(int fd, const struct timeval tv[2]) {
+ const std::string fd_path = StringPrintf("/proc/self/fd/%d", fd);
+ return utimes(fd_path.c_str(), tv);
+}
+
+// Android has only timegm64() and no timegm().
+// We replicate the behaviour of timegm() when the result overflows time_t.
+time_t timegm(struct tm* const t) {
+ // time_t is signed on Android.
+ static const time_t kTimeMax = ~(1 << (sizeof(time_t) * CHAR_BIT - 1));
+ static const time_t kTimeMin = (1 << (sizeof(time_t) * CHAR_BIT - 1));
+ time64_t result = timegm64(t);
+ if (result < kTimeMin || result > kTimeMax)
+ return -1;
+ return result;
+}
+
+// The following is only needed when building with GCC 4.6 or higher
+// (i.e. not with Android GCC 4.4.3, nor with Clang).
+//
+// GCC is now capable of optimizing successive calls to sin() and cos() into
+// a single call to sincos(). This means that source code that looks like:
+//
+// double c, s;
+// c = cos(angle);
+// s = sin(angle);
+//
+// Will generate machine code that looks like:
+//
+// double c, s;
+// sincos(angle, &s, &c);
+//
+// Unfortunately, sincos() and friends are not part of the Android libm.so
+// library provided by the NDK for API level 9. When the optimization kicks
+// in, it makes the final build fail with a puzzling message (puzzling
+// because 'sincos' doesn't appear anywhere in the sources!).
+//
+// To solve this, we provide our own implementation of the sincos() function
+// and related friends. Note that we must also explicitely tell GCC to disable
+// optimizations when generating these. Otherwise, the generated machine code
+// for each function would simply end up calling itself, resulting in a
+// runtime crash due to stack overflow.
+//
+#if defined(__GNUC__) && !defined(__clang__)
+
+// For the record, Clang does not support the 'optimize' attribute.
+// In the unlikely event that it begins performing this optimization too,
+// we'll have to find a different way to achieve this. NOTE: Tested with O1
+// which still performs the optimization.
+//
+#define GCC_NO_OPTIMIZE __attribute__((optimize("O0")))
+
+GCC_NO_OPTIMIZE
+void sincos(double angle, double* s, double *c) {
+ *c = cos(angle);
+ *s = sin(angle);
+}
+
+GCC_NO_OPTIMIZE
+void sincosf(float angle, float* s, float* c) {
+ *c = cosf(angle);
+ *s = sinf(angle);
+}
+
+#endif // __GNUC__ && !__clang__
+
+// An implementation of mkdtemp, since it is not exposed by the NDK
+// for native API level 9 that we target.
+//
+// For any changes in the mkdtemp function, you should manually run the unittest
+// OsCompatAndroidTest.DISABLED_TestMkdTemp in your local machine to check if it
+// passes. Please don't enable it, since it creates a directory and may be
+// source of flakyness.
+char* mkdtemp(char* path) {
+ if (path == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ const int path_len = strlen(path);
+
+ // The last six characters of 'path' must be XXXXXX.
+ const base::StringPiece kSuffix("XXXXXX");
+ const int kSuffixLen = kSuffix.length();
+ if (!base::StringPiece(path, path_len).ends_with(kSuffix)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ // If the path contains a directory, as in /tmp/foo/XXXXXXXX, make sure
+ // that /tmp/foo exists, otherwise we're going to loop a really long
+ // time for nothing below
+ char* dirsep = strrchr(path, '/');
+ if (dirsep != NULL) {
+ struct stat st;
+ int ret;
+
+ *dirsep = '\0'; // Terminating directory path temporarily
+
+ ret = stat(path, &st);
+
+ *dirsep = '/'; // Restoring directory separator
+ if (ret < 0) // Directory probably does not exist
+ return NULL;
+ if (!S_ISDIR(st.st_mode)) { // Not a directory
+ errno = ENOTDIR;
+ return NULL;
+ }
+ }
+
+ // Max number of tries using different random suffixes.
+ const int kMaxTries = 100;
+
+ // Now loop until we CAN create a directory by that name or we reach the max
+ // number of tries.
+ for (int i = 0; i < kMaxTries; ++i) {
+ // Fill the suffix XXXXXX with a random string composed of a-z chars.
+ for (int pos = 0; pos < kSuffixLen; ++pos) {
+ char rand_char = static_cast<char>(base::RandInt('a', 'z'));
+ path[path_len - kSuffixLen + pos] = rand_char;
+ }
+ if (mkdir(path, 0700) == 0) {
+ // We just created the directory succesfully.
+ return path;
+ }
+ if (errno != EEXIST) {
+ // The directory doesn't exist, but an error occured
+ return NULL;
+ }
+ }
+
+ // We reached the max number of tries.
+ return NULL;
+}
+
+} // extern "C"
diff --git a/src/base/os_compat_android.h b/src/base/os_compat_android.h
new file mode 100644
index 0000000..0f25444
--- /dev/null
+++ b/src/base/os_compat_android.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 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_OS_COMPAT_ANDROID_H_
+#define BASE_OS_COMPAT_ANDROID_H_
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <utime.h>
+
+// Not implemented in Bionic.
+extern "C" int futimes(int fd, const struct timeval tv[2]);
+
+// Not exposed or implemented in Bionic.
+extern "C" char* mkdtemp(char* path);
+
+// Android has no timegm().
+extern "C" time_t timegm(struct tm* const t);
+
+// The lockf() function is not available on Android; we translate to flock().
+#define F_LOCK LOCK_EX
+#define F_ULOCK LOCK_UN
+inline int lockf(int fd, int cmd, off_t ignored_len) {
+ return flock(fd, cmd);
+}
+
+#endif // BASE_OS_COMPAT_ANDROID_H_
diff --git a/src/base/os_compat_android_unittest.cc b/src/base/os_compat_android_unittest.cc
new file mode 100644
index 0000000..c749b6a
--- /dev/null
+++ b/src/base/os_compat_android_unittest.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 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/os_compat_android.h"
+
+#include "base/file_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+typedef testing::Test OsCompatAndroidTest;
+
+// Keep this Unittest DISABLED_ , because it actually creates a directory in the
+// device and it may be source of flakyness. For any changes in the mkdtemp
+// function, you should run this unittest in your local machine to check if it
+// passes.
+TEST_F(OsCompatAndroidTest, DISABLED_TestMkdTemp) {
+ FilePath tmp_dir;
+ EXPECT_TRUE(file_util::GetTempDir(&tmp_dir));
+
+ // Not six XXXXXX at the suffix of the path.
+ FilePath sub_dir = tmp_dir.Append("XX");
+ std::string sub_dir_string = sub_dir.value();
+ // this should be OK since mkdtemp just replaces characters in place
+ char* buffer = const_cast<char*>(sub_dir_string.c_str());
+ EXPECT_EQ(NULL, mkdtemp(buffer));
+
+ // Directory does not exist
+ char invalid_path2[] = "doesntoexist/foobarXXXXXX";
+ EXPECT_EQ(NULL, mkdtemp(invalid_path2));
+
+ // Successfully create a tmp dir.
+ FilePath sub_dir2 = tmp_dir.Append("XXXXXX");
+ std::string sub_dir2_string = sub_dir2.value();
+ // this should be OK since mkdtemp just replaces characters in place
+ char* buffer2 = const_cast<char*>(sub_dir2_string.c_str());
+ EXPECT_TRUE(mkdtemp(buffer2) != NULL);
+}
+
+} // namespace base
diff --git a/src/base/os_compat_nacl.cc b/src/base/os_compat_nacl.cc
new file mode 100644
index 0000000..58fe93e
--- /dev/null
+++ b/src/base/os_compat_nacl.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 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/os_compat_nacl.h"
+
+#include <stdlib.h>
+#include <time.h>
+
+#if !defined (__GLIBC__)
+
+extern "C" {
+// Native Client has no timegm().
+time_t timegm(struct tm* tm) {
+ time_t ret;
+ char* tz;
+ tz = getenv("TZ");
+ setenv("TZ", "", 1);
+ tzset();
+ ret = mktime(tm);
+ if (tz)
+ setenv("TZ", tz, 1);
+ else
+ unsetenv("TZ");
+ tzset();
+ return ret;
+}
+} // extern "C"
+
+#endif // !defined (__GLIBC__)
diff --git a/src/base/os_compat_nacl.h b/src/base/os_compat_nacl.h
new file mode 100644
index 0000000..13e0e3f
--- /dev/null
+++ b/src/base/os_compat_nacl.h
@@ -0,0 +1,16 @@
+// Copyright (c) 2012 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_OS_COMPAT_NACL_H_
+#define BASE_OS_COMPAT_NACL_H_
+
+#include <sys/types.h>
+
+#if !defined (__GLIBC__)
+// NaCl has no timegm().
+extern "C" time_t timegm(struct tm* const t);
+#endif // !defined (__GLIBC__)
+
+#endif // BASE_OS_COMPAT_NACL_H_
+
diff --git a/src/base/path_service.cc b/src/base/path_service.cc
new file mode 100644
index 0000000..a514916
--- /dev/null
+++ b/src/base/path_service.cc
@@ -0,0 +1,346 @@
+// Copyright (c) 2012 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/path_service.h"
+
+#ifdef OS_WIN
+#include <windows.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#endif
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/hash_tables.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/synchronization/lock.h"
+
+namespace base {
+ bool PathProvider(int key, FilePath* result);
+#if defined(OS_WIN)
+ bool PathProviderWin(int key, FilePath* result);
+#elif defined(OS_STARBOARD)
+ bool PathProviderStarboard(int key, FilePath* result);
+#elif defined(__LB_SHELL__)
+ bool PathProviderShell(int key, FilePath* result);
+#elif defined(OS_MACOSX)
+ bool PathProviderMac(int key, FilePath* result);
+#elif defined(OS_ANDROID)
+ bool PathProviderAndroid(int key, FilePath* result);
+#elif defined(OS_POSIX)
+ // PathProviderPosix is the default path provider on POSIX OSes other than
+ // Mac and Android.
+ bool PathProviderPosix(int key, FilePath* result);
+#endif
+}
+
+namespace {
+
+typedef base::hash_map<int, FilePath> PathMap;
+
+// We keep a linked list of providers. In a debug build we ensure that no two
+// providers claim overlapping keys.
+struct Provider {
+ PathService::ProviderFunc func;
+ struct Provider* next;
+#ifndef NDEBUG
+ int key_start;
+ int key_end;
+#endif
+ bool is_static;
+};
+
+Provider base_provider = {
+ base::PathProvider,
+ NULL,
+#ifndef NDEBUG
+ base::PATH_START,
+ base::PATH_END,
+#endif
+ true
+};
+
+#if defined(OS_WIN)
+Provider base_provider_win = {
+ base::PathProviderWin,
+ &base_provider,
+#ifndef NDEBUG
+ base::PATH_WIN_START,
+ base::PATH_WIN_END,
+#endif
+ true
+};
+#endif
+
+#if defined(OS_MACOSX)
+Provider base_provider_mac = {
+ base::PathProviderMac,
+ &base_provider,
+#ifndef NDEBUG
+ base::PATH_MAC_START,
+ base::PATH_MAC_END,
+#endif
+ true
+};
+#endif
+
+#if defined(OS_ANDROID)
+Provider base_provider_android = {
+ base::PathProviderAndroid,
+ &base_provider,
+#ifndef NDEBUG
+ base::PATH_ANDROID_START,
+ base::PATH_ANDROID_END,
+#endif
+ true
+};
+#endif
+
+#if defined(OS_STARBOARD)
+Provider base_provider_starboard = {
+ base::PathProviderStarboard,
+ &base_provider,
+#ifndef NDEBUG
+ base::PATH_STARBOARD_START,
+ base::PATH_STARBOARD_END,
+#endif
+ true
+};
+#endif
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
+Provider base_provider_posix = {
+#if defined(__LB_SHELL__)
+ base::PathProviderShell,
+#else
+ base::PathProviderPosix,
+#endif
+ &base_provider,
+#ifndef NDEBUG
+ base::PATH_POSIX_START,
+ base::PATH_POSIX_END,
+#endif
+ true
+};
+#endif
+
+
+struct PathData {
+ base::Lock lock;
+ PathMap cache; // Cache mappings from path key to path value.
+ PathMap overrides; // Track path overrides.
+ Provider* providers; // Linked list of path service providers.
+
+ PathData() {
+#if defined(OS_WIN)
+ providers = &base_provider_win;
+#elif defined(OS_MACOSX)
+ providers = &base_provider_mac;
+#elif defined(OS_ANDROID)
+ providers = &base_provider_android;
+#elif defined(OS_POSIX)
+ providers = &base_provider_posix;
+#elif defined(OS_STARBOARD)
+ providers = &base_provider_starboard;
+#endif
+ }
+
+ ~PathData() {
+ Provider* p = providers;
+ while (p) {
+ Provider* next = p->next;
+ if (!p->is_static)
+ delete p;
+ p = next;
+ }
+ }
+};
+
+static base::LazyInstance<PathData> g_path_data = LAZY_INSTANCE_INITIALIZER;
+
+static PathData* GetPathData() {
+ return g_path_data.Pointer();
+}
+
+// Tries to find |key| in the cache. |path_data| should be locked by the caller!
+bool LockedGetFromCache(int key, const PathData* path_data, FilePath* result) {
+ // check for a cached version
+ PathMap::const_iterator it = path_data->cache.find(key);
+ if (it != path_data->cache.end()) {
+ *result = it->second;
+ return true;
+ }
+ return false;
+}
+
+// Tries to find |key| in the overrides map. |path_data| should be locked by the
+// caller!
+bool LockedGetFromOverrides(int key, PathData* path_data, FilePath* result) {
+ // check for an overridden version.
+ PathMap::const_iterator it = path_data->overrides.find(key);
+ if (it != path_data->overrides.end()) {
+ path_data->cache[key] = it->second;
+ *result = it->second;
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+// TODO(brettw): this function does not handle long paths (filename > MAX_PATH)
+// characters). This isn't supported very well by Windows right now, so it is
+// moot, but we should keep this in mind for the future.
+// static
+bool PathService::Get(int key, FilePath* result) {
+ PathData* path_data = GetPathData();
+ DCHECK(path_data);
+ DCHECK(result);
+ DCHECK_GE(key, base::DIR_CURRENT);
+
+ // special case the current directory because it can never be cached
+ if (key == base::DIR_CURRENT) {
+#if defined(OS_STARBOARD)
+ NOTREACHED() << "DIR_CURRENT not supported in Starboard.";
+ return false;
+#else
+ return file_util::GetCurrentDirectory(result);
+#endif
+ }
+
+ Provider* provider = NULL;
+ {
+ base::AutoLock scoped_lock(path_data->lock);
+ if (LockedGetFromCache(key, path_data, result))
+ return true;
+
+ if (LockedGetFromOverrides(key, path_data, result))
+ return true;
+
+ // Get the beginning of the list while it is still locked.
+ provider = path_data->providers;
+ }
+
+ FilePath path;
+
+ // Iterating does not need the lock because only the list head might be
+ // modified on another thread.
+ while (provider) {
+ if (provider->func(key, &path))
+ break;
+ DCHECK(path.empty()) << "provider should not have modified path";
+ provider = provider->next;
+ }
+
+ if (path.empty())
+ return false;
+
+ *result = path;
+
+ base::AutoLock scoped_lock(path_data->lock);
+ path_data->cache[key] = path;
+
+ return true;
+}
+
+// static
+bool PathService::Override(int key, const FilePath& path) {
+ // Just call the full function with true for the value of |create|.
+ return OverrideAndCreateIfNeeded(key, path, true);
+}
+
+// static
+bool PathService::OverrideAndCreateIfNeeded(int key,
+ const FilePath& path,
+ bool create) {
+ PathData* path_data = GetPathData();
+ DCHECK(path_data);
+ DCHECK_GT(key, base::DIR_CURRENT) << "invalid path key";
+
+ FilePath file_path = path;
+
+ // For some locations this will fail if called from inside the sandbox there-
+ // fore we protect this call with a flag.
+ if (create) {
+ // Make sure the directory exists. We need to do this before we translate
+ // this to the absolute path because on POSIX, AbsolutePath fails if called
+ // on a non-existent path.
+ if (!file_util::PathExists(file_path) &&
+ !file_util::CreateDirectory(file_path))
+ return false;
+ }
+
+ // We need to have an absolute path, as extensions and plugins don't like
+ // relative paths, and will gladly crash the browser in CHECK()s if they get a
+ // relative path.
+#if defined(OS_STARBOARD)
+ if (!file_path.IsAbsolute())
+ return false;
+#else
+ if (!file_util::AbsolutePath(&file_path))
+ return false;
+#endif
+
+ base::AutoLock scoped_lock(path_data->lock);
+
+ // Clear the cache now. Some of its entries could have depended
+ // on the value we are overriding, and are now out of sync with reality.
+ path_data->cache.clear();
+
+ path_data->overrides[key] = file_path;
+
+ return true;
+}
+
+// static
+bool PathService::RemoveOverride(int key) {
+ PathData* path_data = GetPathData();
+ DCHECK(path_data);
+
+ base::AutoLock scoped_lock(path_data->lock);
+
+ if (path_data->overrides.find(key) == path_data->overrides.end())
+ return false;
+
+ // Clear the cache now. Some of its entries could have depended on the value
+ // we are going to remove, and are now out of sync.
+ path_data->cache.clear();
+
+ path_data->overrides.erase(key);
+
+ return true;
+}
+
+// static
+void PathService::RegisterProvider(ProviderFunc func, int key_start,
+ int key_end) {
+ PathData* path_data = GetPathData();
+ DCHECK(path_data);
+ DCHECK_GT(key_end, key_start);
+
+ Provider* p;
+
+ p = new Provider;
+ p->is_static = false;
+ p->func = func;
+#ifndef NDEBUG
+ p->key_start = key_start;
+ p->key_end = key_end;
+#endif
+
+ base::AutoLock scoped_lock(path_data->lock);
+
+#ifndef NDEBUG
+ Provider *iter = path_data->providers;
+ while (iter) {
+ DCHECK(key_start >= iter->key_end || key_end <= iter->key_start) <<
+ "path provider collision";
+ iter = iter->next;
+ }
+#endif
+
+ p->next = path_data->providers;
+ path_data->providers = p;
+}
diff --git a/src/base/path_service.h b/src/base/path_service.h
new file mode 100644
index 0000000..94b0db3
--- /dev/null
+++ b/src/base/path_service.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2012 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_PATH_SERVICE_H_
+#define BASE_PATH_SERVICE_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/base_paths.h"
+#include "base/gtest_prod_util.h"
+#include "build/build_config.h"
+
+class FilePath;
+
+namespace base {
+class ScopedPathOverride;
+} // namespace
+
+// The path service is a global table mapping keys to file system paths. It is
+// OK to use this service from multiple threads.
+//
+class BASE_EXPORT PathService {
+ public:
+ // Retrieves a path to a special directory or file and places it into the
+ // string pointed to by 'path'. If you ask for a directory it is guaranteed
+ // to NOT have a path separator at the end. For example, "c:\windows\temp"
+ // Directories are also guaranteed to exist when this function succeeds.
+ //
+ // Returns true if the directory or file was successfully retrieved. On
+ // failure, 'path' will not be changed.
+ static bool Get(int key, FilePath* path);
+
+ // Overrides the path to a special directory or file. This cannot be used to
+ // change the value of DIR_CURRENT, but that should be obvious. Also, if the
+ // path specifies a directory that does not exist, the directory will be
+ // created by this method. This method returns true if successful.
+ //
+ // If the given path is relative, then it will be resolved against
+ // DIR_CURRENT.
+ //
+ // WARNING: Consumers of PathService::Get may expect paths to be constant
+ // over the lifetime of the app, so this method should be used with caution.
+ static bool Override(int key, const FilePath& path);
+
+ // This function does the same as PathService::Override but it takes an extra
+ // parameter |create| which guides whether the directory to be overriden must
+ // be created in case it doesn't exist already.
+ static bool OverrideAndCreateIfNeeded(int key,
+ const FilePath& path,
+ bool create);
+
+ // To extend the set of supported keys, you can register a path provider,
+ // which is just a function mirroring PathService::Get. The ProviderFunc
+ // returns false if it cannot provide a non-empty path for the given key.
+ // Otherwise, true is returned.
+ //
+ // WARNING: This function could be called on any thread from which the
+ // PathService is used, so a the ProviderFunc MUST BE THREADSAFE.
+ //
+ typedef bool (*ProviderFunc)(int, FilePath*);
+
+ // Call to register a path provider. You must specify the range "[key_start,
+ // key_end)" of supported path keys.
+ static void RegisterProvider(ProviderFunc provider,
+ int key_start,
+ int key_end);
+
+ private:
+ friend class base::ScopedPathOverride;
+ FRIEND_TEST_ALL_PREFIXES(PathServiceTest, RemoveOverride);
+
+ // Removes an override for a special directory or file. Returns true if there
+ // was an override to remove or false if none was present.
+ // NOTE: This function is intended to be used by tests only!
+ static bool RemoveOverride(int key);
+};
+
+#endif // BASE_PATH_SERVICE_H_
diff --git a/src/base/path_service_unittest.cc b/src/base/path_service_unittest.cc
new file mode 100644
index 0000000..51e5ccc
--- /dev/null
+++ b/src/base/path_service_unittest.cc
@@ -0,0 +1,218 @@
+// Copyright (c) 2012 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/path_service.h"
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/string_util.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest-spi.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+#if defined(OS_WIN)
+#include <userenv.h>
+#include "base/win/windows_version.h"
+// userenv.dll is required for GetDefaultUserProfileDirectory().
+#pragma comment(lib, "userenv.lib")
+#endif
+
+namespace {
+
+// Returns true if PathService::Get returns true and sets the path parameter
+// to non-empty for the given PathService::DirType enumeration value.
+bool ReturnsValidPath(int dir_type) {
+ FilePath path;
+ bool result = PathService::Get(dir_type, &path);
+ // Some paths might not exist on some platforms in which case confirming
+ // |result| is true and !path.empty() is the best we can do.
+ bool check_path_exists = true;
+#if defined(__LB_SHELL__)
+ if (dir_type == base::DIR_USER_DESKTOP)
+ check_path_exists = false;
+#endif
+#if defined(OS_POSIX)
+ // If chromium has never been started on this account, the cache path may not
+ // exist.
+ if (dir_type == base::DIR_CACHE)
+ check_path_exists = false;
+#endif
+#if defined(OS_LINUX)
+ // On the linux try-bots: a path is returned (e.g. /home/chrome-bot/Desktop),
+ // but it doesn't exist.
+ if (dir_type == base::DIR_USER_DESKTOP)
+ check_path_exists = false;
+#endif
+#if defined(OS_WIN)
+ if (dir_type == base::DIR_DEFAULT_USER_QUICK_LAUNCH) {
+ // On Windows XP, the Quick Launch folder for the "Default User" doesn't
+ // exist by default. At least confirm that the path returned begins with the
+ // Default User's profile path.
+ if (base::win::GetVersion() < base::win::VERSION_VISTA) {
+ wchar_t default_profile_path[MAX_PATH];
+ DWORD size = arraysize(default_profile_path);
+ return (result &&
+ ::GetDefaultUserProfileDirectory(default_profile_path, &size) &&
+ StartsWith(path.value(), default_profile_path, false));
+ }
+ } else if (dir_type == base::DIR_TASKBAR_PINS) {
+ // There is no pinned-to-taskbar shortcuts prior to Win7.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ check_path_exists = false;
+ }
+#endif
+ return result && !path.empty() && (!check_path_exists ||
+ file_util::PathExists(path));
+}
+
+#if defined(OS_WIN)
+// Function to test any directory keys that are not supported on some versions
+// of Windows. Checks that the function fails and that the returned path is
+// empty.
+bool ReturnsInvalidPath(int dir_type) {
+ FilePath path;
+ bool result = PathService::Get(dir_type, &path);
+ return !result && path.empty();
+}
+#endif
+
+} // namespace
+
+// On the Mac this winds up using some autoreleased objects, so we need to
+// be a PlatformTest.
+typedef PlatformTest PathServiceTest;
+
+// Test that all PathService::Get calls return a value and a true result
+// in the development environment. (This test was created because a few
+// later changes to Get broke the semantics of the function and yielded the
+// correct value while returning false.)
+TEST_F(PathServiceTest, Get) {
+ for (int key = base::PATH_START + 1; key < base::PATH_END; ++key) {
+#if defined(OS_ANDROID)
+ if (key == base::FILE_MODULE || key == base::DIR_USER_DESKTOP)
+ continue; // Android doesn't implement FILE_MODULE and DIR_USER_DESKTOP;
+#elif defined(OS_IOS)
+ if (key == base::DIR_USER_DESKTOP)
+ continue; // iOS doesn't implement DIR_USER_DESKTOP;
+#elif defined(__LB_SHELL__)
+ if (key == base::FILE_MODULE || key == base::FILE_EXE ||
+ key == base::DIR_USER_DESKTOP)
+ continue; // lb_shell doesn't implement FILE_MODULE, FILE_EXE and
+ // DIR_USER_DESKTOP;
+#elif defined(OS_STARBOARD)
+ if (key == base::FILE_MODULE || key == base::DIR_USER_DESKTOP ||
+ key == base::DIR_CURRENT)
+ continue;
+#endif
+ EXPECT_PRED1(ReturnsValidPath, key);
+ }
+#if defined(OS_WIN)
+ for (int key = base::PATH_WIN_START + 1; key < base::PATH_WIN_END; ++key) {
+ bool valid = true;
+ switch(key) {
+ case base::DIR_LOCAL_APP_DATA_LOW:
+ // DIR_LOCAL_APP_DATA_LOW is not supported prior Vista and is expected
+ // to fail.
+ valid = base::win::GetVersion() >= base::win::VERSION_VISTA;
+ break;
+ case base::DIR_APP_SHORTCUTS:
+ // DIR_APP_SHORTCUTS is not supported prior Windows 8 and is expected to
+ // fail.
+ valid = base::win::GetVersion() >= base::win::VERSION_WIN8;
+ break;
+ }
+
+ if (valid)
+ EXPECT_TRUE(ReturnsValidPath(key)) << key;
+ else
+ EXPECT_TRUE(ReturnsInvalidPath(key)) << key;
+ }
+#elif defined(OS_MACOSX)
+ for (int key = base::PATH_MAC_START + 1; key < base::PATH_MAC_END; ++key) {
+ EXPECT_PRED1(ReturnsValidPath, key);
+ }
+#elif defined(OS_ANDROID)
+ for (int key = base::PATH_ANDROID_START + 1; key < base::PATH_ANDROID_END;
+ ++key) {
+ EXPECT_PRED1(ReturnsValidPath, key);
+ }
+#elif defined(OS_POSIX)
+ for (int key = base::PATH_POSIX_START + 1; key < base::PATH_POSIX_END;
+ ++key) {
+ EXPECT_PRED1(ReturnsValidPath, key);
+ }
+#endif
+}
+
+// test that all versions of the Override function of PathService do what they
+// are supposed to do.
+TEST_F(PathServiceTest, Override) {
+ int my_special_key = 666;
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath fake_cache_dir(temp_dir.path().AppendASCII("cache"));
+ // PathService::Override should always create the path provided if it doesn't
+ // exist.
+ EXPECT_TRUE(PathService::Override(my_special_key, fake_cache_dir));
+ EXPECT_TRUE(file_util::PathExists(fake_cache_dir));
+
+ FilePath fake_cache_dir2(temp_dir.path().AppendASCII("cache2"));
+ // PathService::OverrideAndCreateIfNeeded should obey the |create| parameter.
+ PathService::OverrideAndCreateIfNeeded(my_special_key,
+ fake_cache_dir2,
+ false);
+ EXPECT_FALSE(file_util::PathExists(fake_cache_dir2));
+ EXPECT_TRUE(PathService::OverrideAndCreateIfNeeded(my_special_key,
+ fake_cache_dir2,
+ true));
+ EXPECT_TRUE(file_util::PathExists(fake_cache_dir2));
+}
+
+// Check if multiple overrides can co-exist.
+TEST_F(PathServiceTest, OverrideMultiple) {
+ int my_special_key = 666;
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath fake_cache_dir1(temp_dir.path().AppendASCII("1"));
+ EXPECT_TRUE(PathService::Override(my_special_key, fake_cache_dir1));
+ EXPECT_TRUE(file_util::PathExists(fake_cache_dir1));
+ ASSERT_EQ(1, file_util::WriteFile(fake_cache_dir1.AppendASCII("t1"), ".", 1));
+
+ FilePath fake_cache_dir2(temp_dir.path().AppendASCII("2"));
+ EXPECT_TRUE(PathService::Override(my_special_key + 1, fake_cache_dir2));
+ EXPECT_TRUE(file_util::PathExists(fake_cache_dir2));
+ ASSERT_EQ(1, file_util::WriteFile(fake_cache_dir2.AppendASCII("t2"), ".", 1));
+
+ FilePath result;
+ EXPECT_TRUE(PathService::Get(my_special_key, &result));
+ // Override might have changed the path representation but our test file
+ // should be still there.
+ EXPECT_TRUE(file_util::PathExists(result.AppendASCII("t1")));
+ EXPECT_TRUE(PathService::Get(my_special_key + 1, &result));
+ EXPECT_TRUE(file_util::PathExists(result.AppendASCII("t2")));
+}
+
+TEST_F(PathServiceTest, RemoveOverride) {
+ // Before we start the test we have to call RemoveOverride at least once to
+ // clear any overrides that might have been left from other tests.
+ PathService::RemoveOverride(base::DIR_TEMP);
+
+ FilePath original_user_data_dir;
+ EXPECT_TRUE(PathService::Get(base::DIR_TEMP, &original_user_data_dir));
+ EXPECT_FALSE(PathService::RemoveOverride(base::DIR_TEMP));
+
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ EXPECT_TRUE(PathService::Override(base::DIR_TEMP, temp_dir.path()));
+ FilePath new_user_data_dir;
+ EXPECT_TRUE(PathService::Get(base::DIR_TEMP, &new_user_data_dir));
+ EXPECT_NE(original_user_data_dir, new_user_data_dir);
+
+ EXPECT_TRUE(PathService::RemoveOverride(base::DIR_TEMP));
+ EXPECT_TRUE(PathService::Get(base::DIR_TEMP, &new_user_data_dir));
+ EXPECT_EQ(original_user_data_dir, new_user_data_dir);
+}
diff --git a/src/base/pending_task.cc b/src/base/pending_task.cc
new file mode 100644
index 0000000..b288f28
--- /dev/null
+++ b/src/base/pending_task.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/pending_task.h"
+
+#include "base/tracked_objects.h"
+
+namespace base {
+
+#if _MSC_VER >= 1700
+// This a temporary fix for compiling on VS2012. http://crbug.com/154744
+PendingTask::PendingTask() : sequence_num(-1), nestable(false) {
+}
+#endif
+
+PendingTask::PendingTask(const tracked_objects::Location& posted_from,
+ const base::Closure& task)
+ : base::TrackingInfo(posted_from, TimeTicks()),
+ task(task),
+ posted_from(posted_from),
+ sequence_num(0),
+ nestable(true) {
+}
+
+PendingTask::PendingTask(const tracked_objects::Location& posted_from,
+ const base::Closure& task,
+ TimeTicks delayed_run_time,
+ bool nestable)
+ : base::TrackingInfo(posted_from, delayed_run_time),
+ task(task),
+ posted_from(posted_from),
+ sequence_num(0),
+ nestable(nestable) {
+}
+
+PendingTask::~PendingTask() {
+}
+
+bool PendingTask::operator<(const PendingTask& other) const {
+ // Since the top of a priority queue is defined as the "greatest" element, we
+ // need to invert the comparison here. We want the smaller time to be at the
+ // top of the heap.
+
+ if (delayed_run_time < other.delayed_run_time)
+ return false;
+
+ if (delayed_run_time > other.delayed_run_time)
+ return true;
+
+ // If the times happen to match, then we use the sequence number to decide.
+ // Compare the difference to support integer roll-over.
+ return (sequence_num - other.sequence_num) > 0;
+}
+
+void TaskQueue::Swap(TaskQueue* queue) {
+ c.swap(queue->c); // Calls std::deque::swap.
+}
+
+} // namespace base
diff --git a/src/base/pending_task.h b/src/base/pending_task.h
new file mode 100644
index 0000000..6e4a2dd
--- /dev/null
+++ b/src/base/pending_task.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PENDING_TASK_H_
+#define PENDING_TASK_H_
+
+#include <queue>
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/time.h"
+#include "base/tracking_info.h"
+
+namespace base {
+
+// Contains data about a pending task. Stored in TaskQueue and DelayedTaskQueue
+// for use by classes that queue and execute tasks.
+struct BASE_EXPORT PendingTask : public TrackingInfo {
+#if _MSC_VER >= 1700
+ PendingTask();
+#endif
+ PendingTask(const tracked_objects::Location& posted_from,
+ const Closure& task);
+ PendingTask(const tracked_objects::Location& posted_from,
+ const Closure& task,
+ TimeTicks delayed_run_time,
+ bool nestable);
+ ~PendingTask();
+
+ // Used to support sorting.
+ bool operator<(const PendingTask& other) const;
+
+ // The task to run.
+ Closure task;
+
+ // The site this PendingTask was posted from.
+ tracked_objects::Location posted_from;
+
+ // Secondary sort key for run time.
+ int sequence_num;
+
+ // OK to dispatch from a nested loop.
+ bool nestable;
+};
+
+// Wrapper around std::queue specialized for PendingTask which adds a Swap
+// helper method.
+class BASE_EXPORT TaskQueue : public std::queue<PendingTask> {
+ public:
+ void Swap(TaskQueue* queue);
+};
+
+// PendingTasks are sorted by their |delayed_run_time| property.
+typedef std::priority_queue<base::PendingTask> DelayedTaskQueue;
+
+} // namespace base
+
+#endif // PENDING_TASK_H_
diff --git a/src/base/perftimer.cc b/src/base/perftimer.cc
new file mode 100644
index 0000000..4c64c5e
--- /dev/null
+++ b/src/base/perftimer.cc
@@ -0,0 +1,45 @@
+// 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/perftimer.h"
+
+#include <stdio.h>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+
+static FILE* perf_log_file = NULL;
+
+bool InitPerfLog(const FilePath& log_file) {
+ if (perf_log_file) {
+ // trying to initialize twice
+ NOTREACHED();
+ return false;
+ }
+
+ perf_log_file = file_util::OpenFile(log_file, "w");
+ return perf_log_file != NULL;
+}
+
+void FinalizePerfLog() {
+ if (!perf_log_file) {
+ // trying to cleanup without initializing
+ NOTREACHED();
+ return;
+ }
+ file_util::CloseFile(perf_log_file);
+}
+
+void LogPerfResult(const char* test_name, double value, const char* units) {
+ if (!perf_log_file) {
+ NOTREACHED();
+ return;
+ }
+
+ fprintf(perf_log_file, "%s\t%g\t%s\n", test_name, value, units);
+ printf("%s\t%g\t%s\n", test_name, value, units);
+}
diff --git a/src/base/perftimer.h b/src/base/perftimer.h
new file mode 100644
index 0000000..1ac1a7d
--- /dev/null
+++ b/src/base/perftimer.h
@@ -0,0 +1,83 @@
+// 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_PERFTIMER_H_
+#define BASE_PERFTIMER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/time.h"
+
+class FilePath;
+
+// ----------------------------------------------------------------------
+// Initializes and finalizes the perf log. These functions should be
+// called at the beginning and end (respectively) of running all the
+// performance tests. The init function returns true on success.
+// ----------------------------------------------------------------------
+bool InitPerfLog(const FilePath& log_path);
+void FinalizePerfLog();
+
+// ----------------------------------------------------------------------
+// LogPerfResult
+// Writes to the perf result log the given 'value' resulting from the
+// named 'test'. The units are to aid in reading the log by people.
+// ----------------------------------------------------------------------
+void LogPerfResult(const char* test_name, double value, const char* units);
+
+// ----------------------------------------------------------------------
+// PerfTimer
+// A simple wrapper around Now()
+// ----------------------------------------------------------------------
+class PerfTimer {
+ public:
+ PerfTimer() {
+ begin_ = base::TimeTicks::Now();
+ }
+
+ // Returns the time elapsed since object construction
+ base::TimeDelta Elapsed() const {
+ return base::TimeTicks::Now() - begin_;
+ }
+
+ private:
+ base::TimeTicks begin_;
+};
+
+// ----------------------------------------------------------------------
+// PerfTimeLogger
+// Automates calling LogPerfResult for the common case where you want
+// to measure the time that something took. Call Done() when the test
+// is complete if you do extra work after the test or there are stack
+// objects with potentially expensive constructors. Otherwise, this
+// class with automatically log on destruction.
+// ----------------------------------------------------------------------
+class PerfTimeLogger {
+ public:
+ explicit PerfTimeLogger(const char* test_name)
+ : logged_(false),
+ test_name_(test_name) {
+ }
+
+ ~PerfTimeLogger() {
+ if (!logged_)
+ Done();
+ }
+
+ void Done() {
+ // we use a floating-point millisecond value because it is more
+ // intuitive than microseconds and we want more precision than
+ // integer milliseconds
+ LogPerfResult(test_name_.c_str(), timer_.Elapsed().InMillisecondsF(), "ms");
+ logged_ = true;
+ }
+
+ private:
+ bool logged_;
+ std::string test_name_;
+ PerfTimer timer_;
+};
+
+#endif // BASE_PERFTIMER_H_
diff --git a/src/base/perftimer_starboard.cc b/src/base/perftimer_starboard.cc
new file mode 100644
index 0000000..1226e5f
--- /dev/null
+++ b/src/base/perftimer_starboard.cc
@@ -0,0 +1,64 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Written for Starboard, but should work for any platform that implements
+// base::PlatformFile.
+
+#include "base/perftimer.h"
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/platform_file.h"
+#include "base/stringprintf.h"
+
+static base::PlatformFile perf_log_file = base::kInvalidPlatformFileValue;
+
+bool InitPerfLog(const FilePath& log_file) {
+ if (perf_log_file != base::kInvalidPlatformFileValue) {
+ // Avoid double initialization.
+ NOTREACHED();
+ return false;
+ }
+
+ perf_log_file = base::CreatePlatformFile(
+ log_file, base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_WRITE,
+ NULL, NULL);
+ return perf_log_file != base::kInvalidPlatformFileValue;
+}
+
+void FinalizePerfLog() {
+ if (perf_log_file == base::kInvalidPlatformFileValue) {
+ // The caller is trying to cleanup without initializing.
+ NOTREACHED();
+ return;
+ }
+ base::ClosePlatformFile(perf_log_file);
+}
+
+void LogPerfResult(const char* test_name, double value, const char* units) {
+ if (perf_log_file == base::kInvalidPlatformFileValue) {
+ NOTREACHED();
+ return;
+ }
+
+ std::string message =
+ base::StringPrintf("%s\t%g\t%s\n", test_name, value, units);
+ base::WritePlatformFileAtCurrentPos(perf_log_file, message.c_str(),
+ message.length());
+ DLOG(INFO) << message;
+}
diff --git a/src/base/pickle.cc b/src/base/pickle.cc
new file mode 100644
index 0000000..43121c9
--- /dev/null
+++ b/src/base/pickle.cc
@@ -0,0 +1,378 @@
+// Copyright (c) 2012 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/pickle.h"
+
+#include <algorithm> // for max()
+
+// Must come after <algorithm>
+#if defined(OS_STARBOARD)
+#include "starboard/memory.h"
+#define realloc SbMemoryReallocate
+#define free SbMemoryFree
+#define memcpy SbMemoryCopy
+#else
+#include <stdlib.h>
+#endif
+
+//------------------------------------------------------------------------------
+
+// static
+const int Pickle::kPayloadUnit = 64;
+
+static const size_t kCapacityReadOnly = static_cast<size_t>(-1);
+
+PickleIterator::PickleIterator(const Pickle& pickle)
+ : read_ptr_(pickle.payload()),
+ read_end_ptr_(pickle.end_of_payload()) {
+}
+
+template <typename Type>
+inline bool PickleIterator::ReadBuiltinType(Type* result) {
+ const char* read_from = GetReadPointerAndAdvance<Type>();
+ if (!read_from)
+ return false;
+ if (sizeof(Type) > sizeof(uint32))
+ memcpy(result, read_from, sizeof(*result));
+ else
+ *result = *reinterpret_cast<const Type*>(read_from);
+ return true;
+}
+
+template<typename Type>
+inline const char* PickleIterator::GetReadPointerAndAdvance() {
+ const char* current_read_ptr = read_ptr_;
+ if (read_ptr_ + sizeof(Type) > read_end_ptr_)
+ return NULL;
+ if (sizeof(Type) < sizeof(uint32))
+ read_ptr_ += AlignInt(sizeof(Type), sizeof(uint32));
+ else
+ read_ptr_ += sizeof(Type);
+ return current_read_ptr;
+}
+
+const char* PickleIterator::GetReadPointerAndAdvance(int num_bytes) {
+ if (num_bytes < 0 || read_end_ptr_ - read_ptr_ < num_bytes)
+ return NULL;
+ const char* current_read_ptr = read_ptr_;
+ read_ptr_ += AlignInt(num_bytes, sizeof(uint32));
+ return current_read_ptr;
+}
+
+inline const char* PickleIterator::GetReadPointerAndAdvance(int num_elements,
+ size_t size_element) {
+ // Check for int32 overflow.
+ int64 num_bytes = static_cast<int64>(num_elements) * size_element;
+ int num_bytes32 = static_cast<int>(num_bytes);
+ if (num_bytes != static_cast<int64>(num_bytes32))
+ return NULL;
+ return GetReadPointerAndAdvance(num_bytes32);
+}
+
+bool PickleIterator::ReadBool(bool* result) {
+#if defined(__LB_SHELL__) || defined(OS_STARBOARD)
+ // Endian-agnostic method
+ int value;
+ bool status = ReadBuiltinType(&value);
+ if (status && result)
+ *result = (value != 0);
+ return status;
+#else
+ return ReadBuiltinType(result);
+#endif
+}
+
+bool PickleIterator::ReadInt(int* result) {
+ return ReadBuiltinType(result);
+}
+
+bool PickleIterator::ReadLong(long* result) {
+ return ReadBuiltinType(result);
+}
+
+bool PickleIterator::ReadUInt16(uint16* result) {
+ return ReadBuiltinType(result);
+}
+
+bool PickleIterator::ReadUInt32(uint32* result) {
+ return ReadBuiltinType(result);
+}
+
+bool PickleIterator::ReadInt64(int64* result) {
+ return ReadBuiltinType(result);
+}
+
+bool PickleIterator::ReadUInt64(uint64* result) {
+ return ReadBuiltinType(result);
+}
+
+bool PickleIterator::ReadFloat(float* result) {
+ return ReadBuiltinType(result);
+}
+
+bool PickleIterator::ReadString(std::string* result) {
+ int len;
+ if (!ReadInt(&len))
+ return false;
+ const char* read_from = GetReadPointerAndAdvance(len);
+ if (!read_from)
+ return false;
+
+ result->assign(read_from, len);
+ return true;
+}
+
+bool PickleIterator::ReadWString(std::wstring* result) {
+ int len;
+ if (!ReadInt(&len))
+ return false;
+ const char* read_from = GetReadPointerAndAdvance(len, sizeof(wchar_t));
+ if (!read_from)
+ return false;
+
+ result->assign(reinterpret_cast<const wchar_t*>(read_from), len);
+ return true;
+}
+
+bool PickleIterator::ReadString16(string16* result) {
+ int len;
+ if (!ReadInt(&len))
+ return false;
+ const char* read_from = GetReadPointerAndAdvance(len, sizeof(char16));
+ if (!read_from)
+ return false;
+
+ result->assign(reinterpret_cast<const char16*>(read_from), len);
+ return true;
+}
+
+bool PickleIterator::ReadData(const char** data, int* length) {
+ *length = 0;
+ *data = 0;
+
+ if (!ReadInt(length))
+ return false;
+
+ return ReadBytes(data, *length);
+}
+
+bool PickleIterator::ReadBytes(const char** data, int length) {
+ const char* read_from = GetReadPointerAndAdvance(length);
+ if (!read_from)
+ return false;
+ *data = read_from;
+ return true;
+}
+
+// Payload is uint32 aligned.
+
+Pickle::Pickle()
+ : header_(NULL),
+ header_size_(sizeof(Header)),
+ capacity_(0),
+ variable_buffer_offset_(0) {
+ Resize(kPayloadUnit);
+ header_->payload_size = 0;
+}
+
+Pickle::Pickle(int header_size)
+ : header_(NULL),
+ header_size_(AlignInt(header_size, sizeof(uint32))),
+ capacity_(0),
+ variable_buffer_offset_(0) {
+ DCHECK_GE(static_cast<size_t>(header_size), sizeof(Header));
+ DCHECK_LE(header_size, kPayloadUnit);
+ Resize(kPayloadUnit);
+ header_->payload_size = 0;
+}
+
+Pickle::Pickle(const char* data, int data_len)
+ : header_(reinterpret_cast<Header*>(const_cast<char*>(data))),
+ header_size_(0),
+ capacity_(kCapacityReadOnly),
+ variable_buffer_offset_(0) {
+ if (data_len >= static_cast<int>(sizeof(Header)))
+ header_size_ = data_len - header_->payload_size;
+
+ if (header_size_ > static_cast<unsigned int>(data_len))
+ header_size_ = 0;
+
+ if (header_size_ != AlignInt(header_size_, sizeof(uint32)))
+ header_size_ = 0;
+
+ // If there is anything wrong with the data, we're not going to use it.
+ if (!header_size_)
+ header_ = NULL;
+}
+
+Pickle::Pickle(const Pickle& other)
+ : header_(NULL),
+ header_size_(other.header_size_),
+ capacity_(0),
+ variable_buffer_offset_(other.variable_buffer_offset_) {
+ size_t payload_size = header_size_ + other.header_->payload_size;
+ bool resized = Resize(payload_size);
+ CHECK(resized); // Realloc failed.
+ memcpy(header_, other.header_, payload_size);
+}
+
+Pickle::~Pickle() {
+ if (capacity_ != kCapacityReadOnly)
+ free(header_);
+}
+
+Pickle& Pickle::operator=(const Pickle& other) {
+ if (this == &other) {
+ NOTREACHED();
+ return *this;
+ }
+ if (capacity_ == kCapacityReadOnly) {
+ header_ = NULL;
+ capacity_ = 0;
+ }
+ if (header_size_ != other.header_size_) {
+ free(header_);
+ header_ = NULL;
+ header_size_ = other.header_size_;
+ }
+ bool resized = Resize(other.header_size_ + other.header_->payload_size);
+ CHECK(resized); // Realloc failed.
+ memcpy(header_, other.header_,
+ other.header_size_ + other.header_->payload_size);
+ variable_buffer_offset_ = other.variable_buffer_offset_;
+ return *this;
+}
+
+bool Pickle::WriteString(const std::string& value) {
+ if (!WriteInt(static_cast<int>(value.size())))
+ return false;
+
+ return WriteBytes(value.data(), static_cast<int>(value.size()));
+}
+
+bool Pickle::WriteWString(const std::wstring& value) {
+ if (!WriteInt(static_cast<int>(value.size())))
+ return false;
+
+ return WriteBytes(value.data(),
+ static_cast<int>(value.size() * sizeof(wchar_t)));
+}
+
+bool Pickle::WriteString16(const string16& value) {
+ if (!WriteInt(static_cast<int>(value.size())))
+ return false;
+
+ return WriteBytes(value.data(),
+ static_cast<int>(value.size()) * sizeof(char16));
+}
+
+bool Pickle::WriteData(const char* data, int length) {
+ return length >= 0 && WriteInt(length) && WriteBytes(data, length);
+}
+
+bool Pickle::WriteBytes(const void* data, int data_len) {
+ DCHECK_NE(kCapacityReadOnly, capacity_) << "oops: pickle is readonly";
+
+ char* dest = BeginWrite(data_len);
+ if (!dest)
+ return false;
+
+ memcpy(dest, data, data_len);
+
+ EndWrite(dest, data_len);
+ return true;
+}
+
+char* Pickle::BeginWriteData(int length) {
+ DCHECK_EQ(variable_buffer_offset_, 0U) <<
+ "There can only be one variable buffer in a Pickle";
+
+ if (length < 0 || !WriteInt(length))
+ return NULL;
+
+ char *data_ptr = BeginWrite(length);
+ if (!data_ptr)
+ return NULL;
+
+ variable_buffer_offset_ =
+ data_ptr - reinterpret_cast<char*>(header_) - sizeof(int);
+
+ // EndWrite doesn't necessarily have to be called after the write operation,
+ // so we call it here to pad out what the caller will eventually write.
+ EndWrite(data_ptr, length);
+ return data_ptr;
+}
+
+void Pickle::TrimWriteData(int new_length) {
+ DCHECK_NE(variable_buffer_offset_, 0U);
+
+ // Fetch the the variable buffer size
+ int* cur_length = reinterpret_cast<int*>(
+ reinterpret_cast<char*>(header_) + variable_buffer_offset_);
+
+ if (new_length < 0 || new_length > *cur_length) {
+ NOTREACHED() << "Invalid length in TrimWriteData.";
+ return;
+ }
+
+ // Update the payload size and variable buffer size
+ header_->payload_size -= (*cur_length - new_length);
+ *cur_length = new_length;
+}
+
+char* Pickle::BeginWrite(size_t length) {
+ // write at a uint32-aligned offset from the beginning of the header
+ size_t offset = AlignInt(header_->payload_size, sizeof(uint32));
+
+ size_t new_size = offset + length;
+ size_t needed_size = header_size_ + new_size;
+ if (needed_size > capacity_ && !Resize(std::max(capacity_ * 2, needed_size)))
+ return NULL;
+
+#ifdef ARCH_CPU_64_BITS
+ DCHECK_LE(length, kuint32max);
+#endif
+
+ header_->payload_size = static_cast<uint32>(new_size);
+ return payload() + offset;
+}
+
+void Pickle::EndWrite(char* dest, int length) {
+ // Zero-pad to keep tools like valgrind from complaining about uninitialized
+ // memory.
+ if (length % sizeof(uint32))
+ memset(dest + length, 0, sizeof(uint32) - (length % sizeof(uint32)));
+}
+
+bool Pickle::Resize(size_t new_capacity) {
+ new_capacity = AlignInt(new_capacity, kPayloadUnit);
+
+ CHECK_NE(capacity_, kCapacityReadOnly);
+ void* p = realloc(header_, new_capacity);
+ if (!p)
+ return false;
+
+ header_ = reinterpret_cast<Header*>(p);
+ capacity_ = new_capacity;
+ return true;
+}
+
+// static
+const char* Pickle::FindNext(size_t header_size,
+ const char* start,
+ const char* end) {
+ DCHECK_EQ(header_size, AlignInt(header_size, sizeof(uint32)));
+ DCHECK_LE(header_size, static_cast<size_t>(kPayloadUnit));
+
+ if (static_cast<size_t>(end - start) < sizeof(Header))
+ return NULL;
+
+ const Header* hdr = reinterpret_cast<const Header*>(start);
+ const char* payload_base = start + header_size;
+ const char* payload_end = payload_base + hdr->payload_size;
+ if (payload_end < payload_base)
+ return NULL;
+
+ return (payload_end > end) ? NULL : payload_end;
+}
diff --git a/src/base/pickle.h b/src/base/pickle.h
new file mode 100644
index 0000000..cd587de
--- /dev/null
+++ b/src/base/pickle.h
@@ -0,0 +1,350 @@
+// Copyright (c) 2012 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_PICKLE_H__
+#define BASE_PICKLE_H__
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/logging.h"
+#include "base/string16.h"
+
+class Pickle;
+
+// PickleIterator reads data from a Pickle. The Pickle object must remain valid
+// while the PickleIterator object is in use.
+class BASE_EXPORT PickleIterator {
+ public:
+ PickleIterator() : read_ptr_(NULL), read_end_ptr_(NULL) {}
+ explicit PickleIterator(const Pickle& pickle);
+
+ // Methods for reading the payload of the Pickle. To read from the start of
+ // the Pickle, create a PickleIterator from a Pickle. If successful, these
+ // methods return true. Otherwise, false is returned to indicate that the
+ // result could not be extracted.
+ bool ReadBool(bool* result) WARN_UNUSED_RESULT;
+ bool ReadInt(int* result) WARN_UNUSED_RESULT;
+ bool ReadLong(long* result) WARN_UNUSED_RESULT;
+ bool ReadUInt16(uint16* result) WARN_UNUSED_RESULT;
+ bool ReadUInt32(uint32* result) WARN_UNUSED_RESULT;
+ bool ReadInt64(int64* result) WARN_UNUSED_RESULT;
+ bool ReadUInt64(uint64* result) WARN_UNUSED_RESULT;
+ bool ReadFloat(float* result) WARN_UNUSED_RESULT;
+ bool ReadString(std::string* result) WARN_UNUSED_RESULT;
+ bool ReadWString(std::wstring* result) WARN_UNUSED_RESULT;
+ bool ReadString16(string16* result) WARN_UNUSED_RESULT;
+ bool ReadData(const char** data, int* length) WARN_UNUSED_RESULT;
+ bool ReadBytes(const char** data, int length) WARN_UNUSED_RESULT;
+
+ // Safer version of ReadInt() checks for the result not being negative.
+ // Use it for reading the object sizes.
+ bool ReadLength(int* result) WARN_UNUSED_RESULT {
+ return ReadInt(result) && *result >= 0;
+ }
+
+ // Skips bytes in the read buffer and returns true if there are at least
+ // num_bytes available. Otherwise, does nothing and returns false.
+ bool SkipBytes(int num_bytes) WARN_UNUSED_RESULT {
+ return !!GetReadPointerAndAdvance(num_bytes);
+ }
+
+ private:
+ // Aligns 'i' by rounding it up to the next multiple of 'alignment'
+ static size_t AlignInt(size_t i, int alignment) {
+ return i + (alignment - (i % alignment)) % alignment;
+ }
+
+ // Read Type from Pickle.
+ template <typename Type>
+ inline bool ReadBuiltinType(Type* result);
+
+ // Get read pointer for Type and advance read pointer.
+ template<typename Type>
+ inline const char* GetReadPointerAndAdvance();
+
+ // Get read pointer for |num_bytes| and advance read pointer. This method
+ // checks num_bytes for negativity and wrapping.
+ const char* GetReadPointerAndAdvance(int num_bytes);
+
+ // Get read pointer for (num_elements * size_element) bytes and advance read
+ // pointer. This method checks for int overflow, negativity and wrapping.
+ inline const char* GetReadPointerAndAdvance(int num_elements,
+ size_t size_element);
+
+ // Pointers to the Pickle data.
+ const char* read_ptr_;
+ const char* read_end_ptr_;
+
+ FRIEND_TEST_ALL_PREFIXES(PickleTest, GetReadPointerAndAdvance);
+};
+
+// This class provides facilities for basic binary value packing and unpacking.
+//
+// The Pickle class supports appending primitive values (ints, strings, etc.)
+// to a pickle instance. The Pickle instance grows its internal memory buffer
+// dynamically to hold the sequence of primitive values. The internal memory
+// buffer is exposed as the "data" of the Pickle. This "data" can be passed
+// to a Pickle object to initialize it for reading.
+//
+// When reading from a Pickle object, it is important for the consumer to know
+// what value types to read and in what order to read them as the Pickle does
+// not keep track of the type of data written to it.
+//
+// The Pickle's data has a header which contains the size of the Pickle's
+// payload. It can optionally support additional space in the header. That
+// space is controlled by the header_size parameter passed to the Pickle
+// constructor.
+//
+class BASE_EXPORT Pickle {
+ public:
+ // Initialize a Pickle object using the default header size.
+ Pickle();
+
+ // Initialize a Pickle object with the specified header size in bytes, which
+ // must be greater-than-or-equal-to sizeof(Pickle::Header). The header size
+ // will be rounded up to ensure that the header size is 32bit-aligned.
+ explicit Pickle(int header_size);
+
+ // Initializes a Pickle from a const block of data. The data is not copied;
+ // instead the data is merely referenced by this Pickle. Only const methods
+ // should be used on the Pickle when initialized this way. The header
+ // padding size is deduced from the data length.
+ Pickle(const char* data, int data_len);
+
+ // Initializes a Pickle as a deep copy of another Pickle.
+ Pickle(const Pickle& other);
+
+ // Note: There are no virtual methods in this class. This destructor is
+ // virtual as an element of defensive coding. Other classes have derived from
+ // this class, and there is a *chance* that they will cast into this base
+ // class before destruction. At least one such class does have a virtual
+ // destructor, suggesting at least some need to call more derived destructors.
+ virtual ~Pickle();
+
+ // Performs a deep copy.
+ Pickle& operator=(const Pickle& other);
+
+ // Returns the size of the Pickle's data.
+ size_t size() const { return header_size_ + header_->payload_size; }
+
+ // Returns the data for this Pickle.
+ const void* data() const { return header_; }
+
+ // For compatibility, these older style read methods pass through to the
+ // PickleIterator methods.
+ // TODO(jbates) Remove these methods.
+ bool ReadBool(PickleIterator* iter, bool* result) const {
+ return iter->ReadBool(result);
+ }
+ bool ReadInt(PickleIterator* iter, int* result) const {
+ return iter->ReadInt(result);
+ }
+ bool ReadLong(PickleIterator* iter, long* result) const {
+ return iter->ReadLong(result);
+ }
+ bool ReadUInt16(PickleIterator* iter, uint16* result) const {
+ return iter->ReadUInt16(result);
+ }
+ bool ReadUInt32(PickleIterator* iter, uint32* result) const {
+ return iter->ReadUInt32(result);
+ }
+ bool ReadInt64(PickleIterator* iter, int64* result) const {
+ return iter->ReadInt64(result);
+ }
+ bool ReadUInt64(PickleIterator* iter, uint64* result) const {
+ return iter->ReadUInt64(result);
+ }
+ bool ReadFloat(PickleIterator* iter, float* result) const {
+ return iter->ReadFloat(result);
+ }
+ bool ReadString(PickleIterator* iter, std::string* result) const {
+ return iter->ReadString(result);
+ }
+ bool ReadWString(PickleIterator* iter, std::wstring* result) const {
+ return iter->ReadWString(result);
+ }
+ bool ReadString16(PickleIterator* iter, string16* result) const {
+ return iter->ReadString16(result);
+ }
+ // A pointer to the data will be placed in *data, and the length will be
+ // placed in *length. This buffer will be into the message's buffer so will
+ // be scoped to the lifetime of the message (or until the message data is
+ // mutated).
+ bool ReadData(PickleIterator* iter, const char** data, int* length) const {
+ return iter->ReadData(data, length);
+ }
+ // A pointer to the data will be placed in *data. The caller specifies the
+ // number of bytes to read, and ReadBytes will validate this length. The
+ // returned buffer will be into the message's buffer so will be scoped to the
+ // lifetime of the message (or until the message data is mutated).
+ bool ReadBytes(PickleIterator* iter, const char** data, int length) const {
+ return iter->ReadBytes(data, length);
+ }
+
+ // Safer version of ReadInt() checks for the result not being negative.
+ // Use it for reading the object sizes.
+ bool ReadLength(PickleIterator* iter, int* result) const {
+ return iter->ReadLength(result);
+ }
+
+ // Methods for adding to the payload of the Pickle. These values are
+ // appended to the end of the Pickle's payload. When reading values from a
+ // Pickle, it is important to read them in the order in which they were added
+ // to the Pickle.
+ bool WriteBool(bool value) {
+ return WriteInt(value ? 1 : 0);
+ }
+ bool WriteInt(int value) {
+ return WriteBytes(&value, sizeof(value));
+ }
+ // WARNING: DO NOT USE THIS METHOD IF PICKLES ARE PERSISTED IN ANY WAY.
+ // It will write whatever a "long" is on this architecture. On 32-bit
+ // platforms, it is 32 bits. On 64-bit platforms, it is 64 bits. If persisted
+ // pickles are still around after upgrading to 64-bit, or if they are copied
+ // between dissimilar systems, YOUR PICKLES WILL HAVE GONE BAD.
+ bool WriteLongUsingDangerousNonPortableLessPersistableForm(long value) {
+ return WriteBytes(&value, sizeof(value));
+ }
+ bool WriteUInt16(uint16 value) {
+ return WriteBytes(&value, sizeof(value));
+ }
+ bool WriteUInt32(uint32 value) {
+ return WriteBytes(&value, sizeof(value));
+ }
+ bool WriteInt64(int64 value) {
+ return WriteBytes(&value, sizeof(value));
+ }
+ bool WriteUInt64(uint64 value) {
+ return WriteBytes(&value, sizeof(value));
+ }
+ bool WriteFloat(float value) {
+ return WriteBytes(&value, sizeof(value));
+ }
+ bool WriteString(const std::string& value);
+ bool WriteWString(const std::wstring& value);
+ bool WriteString16(const string16& value);
+ // "Data" is a blob with a length. When you read it out you will be given the
+ // length. See also WriteBytes.
+ bool WriteData(const char* data, int length);
+ // "Bytes" is a blob with no length. The caller must specify the lenght both
+ // when reading and writing. It is normally used to serialize PoD types of a
+ // known size. See also WriteData.
+ bool WriteBytes(const void* data, int data_len);
+
+ // Same as WriteData, but allows the caller to write directly into the
+ // Pickle. This saves a copy in cases where the data is not already
+ // available in a buffer. The caller should take care to not write more
+ // than the length it declares it will. Use ReadData to get the data.
+ // Returns NULL on failure.
+ //
+ // The returned pointer will only be valid until the next write operation
+ // on this Pickle.
+ char* BeginWriteData(int length);
+
+ // For Pickles which contain variable length buffers (e.g. those created
+ // with BeginWriteData), the Pickle can
+ // be 'trimmed' if the amount of data required is less than originally
+ // requested. For example, you may have created a buffer with 10K of data,
+ // but decided to only fill 10 bytes of that data. Use this function
+ // to trim the buffer so that we don't send 9990 bytes of unused data.
+ // You cannot increase the size of the variable buffer; only shrink it.
+ // This function assumes that the length of the variable buffer has
+ // not been changed.
+ void TrimWriteData(int length);
+
+ // Payload follows after allocation of Header (header size is customizable).
+ struct Header {
+ uint32 payload_size; // Specifies the size of the payload.
+ };
+
+ // Returns the header, cast to a user-specified type T. The type T must be a
+ // subclass of Header and its size must correspond to the header_size passed
+ // to the Pickle constructor.
+ template <class T>
+ T* headerT() {
+ DCHECK_EQ(header_size_, sizeof(T));
+ return static_cast<T*>(header_);
+ }
+ template <class T>
+ const T* headerT() const {
+ DCHECK_EQ(header_size_, sizeof(T));
+ return static_cast<const T*>(header_);
+ }
+
+ // The payload is the pickle data immediately following the header.
+ size_t payload_size() const { return header_->payload_size; }
+ const char* payload() const {
+ return reinterpret_cast<const char*>(header_) + header_size_;
+ }
+
+ protected:
+ char* payload() {
+ return reinterpret_cast<char*>(header_) + header_size_;
+ }
+
+ // Returns the address of the byte immediately following the currently valid
+ // header + payload.
+ char* end_of_payload() {
+ // We must have a valid header_.
+ return payload() + payload_size();
+ }
+ const char* end_of_payload() const {
+ // This object may be invalid.
+ return header_ ? payload() + payload_size() : NULL;
+ }
+
+ size_t capacity() const {
+ return capacity_;
+ }
+
+ // Resizes the buffer for use when writing the specified amount of data. The
+ // location that the data should be written at is returned, or NULL if there
+ // was an error. Call EndWrite with the returned offset and the given length
+ // to pad out for the next write.
+ char* BeginWrite(size_t length);
+
+ // Completes the write operation by padding the data with NULL bytes until it
+ // is padded. Should be paired with BeginWrite, but it does not necessarily
+ // have to be called after the data is written.
+ void EndWrite(char* dest, int length);
+
+ // Resize the capacity, note that the input value should include the size of
+ // the header: new_capacity = sizeof(Header) + desired_payload_capacity.
+ // A realloc() failure will cause a Resize failure... and caller should check
+ // the return result for true (i.e., successful resizing).
+ bool Resize(size_t new_capacity);
+
+ // Aligns 'i' by rounding it up to the next multiple of 'alignment'
+ static size_t AlignInt(size_t i, int alignment) {
+ return i + (alignment - (i % alignment)) % alignment;
+ }
+
+ // Find the end of the pickled data that starts at range_start. Returns NULL
+ // if the entire Pickle is not found in the given data range.
+ static const char* FindNext(size_t header_size,
+ const char* range_start,
+ const char* range_end);
+
+ // The allocation granularity of the payload.
+ static const int kPayloadUnit;
+
+ private:
+ friend class PickleIterator;
+
+ Header* header_;
+ size_t header_size_; // Supports extra data between header and payload.
+ // Allocation size of payload (or -1 if allocation is const).
+ size_t capacity_;
+ size_t variable_buffer_offset_; // IF non-zero, then offset to a buffer.
+
+ FRIEND_TEST_ALL_PREFIXES(PickleTest, Resize);
+ FRIEND_TEST_ALL_PREFIXES(PickleTest, FindNext);
+ FRIEND_TEST_ALL_PREFIXES(PickleTest, FindNextWithIncompleteHeader);
+};
+
+#endif // BASE_PICKLE_H__
diff --git a/src/base/pickle_unittest.cc b/src/base/pickle_unittest.cc
new file mode 100644
index 0000000..c866acd
--- /dev/null
+++ b/src/base/pickle_unittest.cc
@@ -0,0 +1,342 @@
+// Copyright (c) 2012 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/memory/scoped_ptr.h"
+#include "base/pickle.h"
+#include "base/string16.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const int testint = 2093847192;
+const std::string teststr("Hello world"); // note non-aligned string length
+const std::wstring testwstr(L"Hello, world");
+const char testdata[] = "AAA\0BBB\0";
+const int testdatalen = arraysize(testdata) - 1;
+const bool testbool1 = false;
+const bool testbool2 = true;
+const uint16 testuint16 = 32123;
+const float testfloat = 3.1415926935f;
+
+// checks that the result
+void VerifyResult(const Pickle& pickle) {
+ PickleIterator iter(pickle);
+
+ int outint;
+ EXPECT_TRUE(pickle.ReadInt(&iter, &outint));
+ EXPECT_EQ(testint, outint);
+
+ std::string outstr;
+ EXPECT_TRUE(pickle.ReadString(&iter, &outstr));
+ EXPECT_EQ(teststr, outstr);
+
+ std::wstring outwstr;
+ EXPECT_TRUE(pickle.ReadWString(&iter, &outwstr));
+ EXPECT_EQ(testwstr, outwstr);
+
+ bool outbool;
+ EXPECT_TRUE(pickle.ReadBool(&iter, &outbool));
+ EXPECT_EQ(testbool1, outbool);
+ EXPECT_TRUE(pickle.ReadBool(&iter, &outbool));
+ EXPECT_EQ(testbool2, outbool);
+
+ uint16 outuint16;
+ EXPECT_TRUE(pickle.ReadUInt16(&iter, &outuint16));
+ EXPECT_EQ(testuint16, outuint16);
+
+ float outfloat;
+ EXPECT_TRUE(pickle.ReadFloat(&iter, &outfloat));
+ EXPECT_EQ(testfloat, outfloat);
+
+ const char* outdata;
+ int outdatalen;
+ EXPECT_TRUE(pickle.ReadData(&iter, &outdata, &outdatalen));
+ EXPECT_EQ(testdatalen, outdatalen);
+ EXPECT_EQ(memcmp(testdata, outdata, outdatalen), 0);
+
+ EXPECT_TRUE(pickle.ReadData(&iter, &outdata, &outdatalen));
+ EXPECT_EQ(testdatalen, outdatalen);
+ EXPECT_EQ(memcmp(testdata, outdata, outdatalen), 0);
+
+ // reads past the end should fail
+ EXPECT_FALSE(pickle.ReadInt(&iter, &outint));
+}
+
+} // namespace
+
+TEST(PickleTest, EncodeDecode) {
+ Pickle pickle;
+
+ EXPECT_TRUE(pickle.WriteInt(testint));
+ EXPECT_TRUE(pickle.WriteString(teststr));
+ EXPECT_TRUE(pickle.WriteWString(testwstr));
+ EXPECT_TRUE(pickle.WriteBool(testbool1));
+ EXPECT_TRUE(pickle.WriteBool(testbool2));
+ EXPECT_TRUE(pickle.WriteUInt16(testuint16));
+ EXPECT_TRUE(pickle.WriteFloat(testfloat));
+ EXPECT_TRUE(pickle.WriteData(testdata, testdatalen));
+
+ // Over allocate BeginWriteData so we can test TrimWriteData.
+ char* dest = pickle.BeginWriteData(testdatalen + 100);
+ EXPECT_TRUE(dest);
+ memcpy(dest, testdata, testdatalen);
+
+ pickle.TrimWriteData(testdatalen);
+
+ VerifyResult(pickle);
+
+ // test copy constructor
+ Pickle pickle2(pickle);
+ VerifyResult(pickle2);
+
+ // test operator=
+ Pickle pickle3;
+ pickle3 = pickle;
+ VerifyResult(pickle3);
+}
+
+// Tests that we can handle really small buffers.
+TEST(PickleTest, SmallBuffer) {
+ scoped_array<char> buffer(new char[1]);
+
+ // We should not touch the buffer.
+ Pickle pickle(buffer.get(), 1);
+
+ PickleIterator iter(pickle);
+ int data;
+ EXPECT_FALSE(pickle.ReadInt(&iter, &data));
+}
+
+// Tests that we can handle improper headers.
+TEST(PickleTest, BigSize) {
+ int buffer[] = { 0x56035200, 25, 40, 50 };
+
+ Pickle pickle(reinterpret_cast<char*>(buffer), sizeof(buffer));
+
+ PickleIterator iter(pickle);
+ int data;
+ EXPECT_FALSE(pickle.ReadInt(&iter, &data));
+}
+
+TEST(PickleTest, UnalignedSize) {
+ int buffer[] = { 10, 25, 40, 50 };
+
+ Pickle pickle(reinterpret_cast<char*>(buffer), sizeof(buffer));
+
+ PickleIterator iter(pickle);
+ int data;
+ EXPECT_FALSE(pickle.ReadInt(&iter, &data));
+}
+
+TEST(PickleTest, ZeroLenStr) {
+ Pickle pickle;
+ EXPECT_TRUE(pickle.WriteString(""));
+
+ PickleIterator iter(pickle);
+ std::string outstr;
+ EXPECT_TRUE(pickle.ReadString(&iter, &outstr));
+ EXPECT_EQ("", outstr);
+}
+
+TEST(PickleTest, ZeroLenWStr) {
+ Pickle pickle;
+ EXPECT_TRUE(pickle.WriteWString(L""));
+
+ PickleIterator iter(pickle);
+ std::string outstr;
+ EXPECT_TRUE(pickle.ReadString(&iter, &outstr));
+ EXPECT_EQ("", outstr);
+}
+
+TEST(PickleTest, BadLenStr) {
+ Pickle pickle;
+ EXPECT_TRUE(pickle.WriteInt(-2));
+
+ PickleIterator iter(pickle);
+ std::string outstr;
+ EXPECT_FALSE(pickle.ReadString(&iter, &outstr));
+}
+
+TEST(PickleTest, BadLenWStr) {
+ Pickle pickle;
+ EXPECT_TRUE(pickle.WriteInt(-1));
+
+ PickleIterator iter(pickle);
+ std::wstring woutstr;
+ EXPECT_FALSE(pickle.ReadWString(&iter, &woutstr));
+}
+
+TEST(PickleTest, FindNext) {
+ Pickle pickle;
+ EXPECT_TRUE(pickle.WriteInt(1));
+ EXPECT_TRUE(pickle.WriteString("Domo"));
+
+ const char* start = reinterpret_cast<const char*>(pickle.data());
+ const char* end = start + pickle.size();
+
+ EXPECT_TRUE(end == Pickle::FindNext(pickle.header_size_, start, end));
+ EXPECT_TRUE(NULL == Pickle::FindNext(pickle.header_size_, start, end - 1));
+ EXPECT_TRUE(end == Pickle::FindNext(pickle.header_size_, start, end + 1));
+}
+
+TEST(PickleTest, FindNextWithIncompleteHeader) {
+ size_t header_size = sizeof(Pickle::Header);
+ scoped_array<char> buffer(new char[header_size - 1]);
+ memset(buffer.get(), 0x1, header_size - 1);
+
+ const char* start = buffer.get();
+ const char* end = start + header_size - 1;
+
+ EXPECT_TRUE(NULL == Pickle::FindNext(header_size, start, end));
+}
+
+TEST(PickleTest, GetReadPointerAndAdvance) {
+ Pickle pickle;
+
+ PickleIterator iter(pickle);
+ EXPECT_FALSE(iter.GetReadPointerAndAdvance(1));
+
+ EXPECT_TRUE(pickle.WriteInt(1));
+ EXPECT_TRUE(pickle.WriteInt(2));
+ int bytes = sizeof(int) * 2;
+
+ EXPECT_TRUE(PickleIterator(pickle).GetReadPointerAndAdvance(0));
+ EXPECT_TRUE(PickleIterator(pickle).GetReadPointerAndAdvance(1));
+ EXPECT_FALSE(PickleIterator(pickle).GetReadPointerAndAdvance(-1));
+ EXPECT_TRUE(PickleIterator(pickle).GetReadPointerAndAdvance(bytes));
+ EXPECT_FALSE(PickleIterator(pickle).GetReadPointerAndAdvance(bytes + 1));
+ EXPECT_FALSE(PickleIterator(pickle).GetReadPointerAndAdvance(INT_MAX));
+ EXPECT_FALSE(PickleIterator(pickle).GetReadPointerAndAdvance(INT_MIN));
+}
+
+TEST(PickleTest, Resize) {
+ size_t unit = Pickle::kPayloadUnit;
+ scoped_array<char> data(new char[unit]);
+ char* data_ptr = data.get();
+ for (size_t i = 0; i < unit; i++)
+ data_ptr[i] = 'G';
+
+ // construct a message that will be exactly the size of one payload unit,
+ // note that any data will have a 4-byte header indicating the size
+ const size_t payload_size_after_header = unit - sizeof(uint32);
+ Pickle pickle;
+ pickle.WriteData(data_ptr,
+ static_cast<int>(payload_size_after_header - sizeof(uint32)));
+ size_t cur_payload = payload_size_after_header;
+
+ // note: we assume 'unit' is a power of 2
+ EXPECT_EQ(unit, pickle.capacity());
+ EXPECT_EQ(pickle.payload_size(), payload_size_after_header);
+
+ // fill out a full page (noting data header)
+ pickle.WriteData(data_ptr, static_cast<int>(unit - sizeof(uint32)));
+ cur_payload += unit;
+ EXPECT_EQ(unit * 2, pickle.capacity());
+ EXPECT_EQ(cur_payload, pickle.payload_size());
+
+ // one more byte should double the capacity
+ pickle.WriteData(data_ptr, 1);
+ cur_payload += 5;
+ EXPECT_EQ(unit * 4, pickle.capacity());
+ EXPECT_EQ(cur_payload, pickle.payload_size());
+}
+
+namespace {
+
+struct CustomHeader : Pickle::Header {
+ int blah;
+};
+
+} // namespace
+
+TEST(PickleTest, HeaderPadding) {
+ const uint32 kMagic = 0x12345678;
+
+ Pickle pickle(sizeof(CustomHeader));
+ pickle.WriteInt(kMagic);
+
+ // this should not overwrite the 'int' payload
+ pickle.headerT<CustomHeader>()->blah = 10;
+
+ PickleIterator iter(pickle);
+ int result;
+ ASSERT_TRUE(pickle.ReadInt(&iter, &result));
+
+ EXPECT_EQ(static_cast<uint32>(result), kMagic);
+}
+
+TEST(PickleTest, EqualsOperator) {
+ Pickle source;
+ source.WriteInt(1);
+
+ Pickle copy_refs_source_buffer(static_cast<const char*>(source.data()),
+ source.size());
+ Pickle copy;
+ copy = copy_refs_source_buffer;
+ ASSERT_EQ(source.size(), copy.size());
+}
+
+TEST(PickleTest, EvilLengths) {
+ Pickle source;
+ std::string str(100000, 'A');
+ EXPECT_TRUE(source.WriteData(str.c_str(), 100000));
+ // ReadString16 used to have its read buffer length calculation wrong leading
+ // to out-of-bounds reading.
+ PickleIterator iter(source);
+ string16 str16;
+ EXPECT_FALSE(source.ReadString16(&iter, &str16));
+
+ // And check we didn't break ReadString16.
+ str16 = (wchar_t) 'A';
+ Pickle str16_pickle;
+ EXPECT_TRUE(str16_pickle.WriteString16(str16));
+ iter = PickleIterator(str16_pickle);
+ EXPECT_TRUE(str16_pickle.ReadString16(&iter, &str16));
+ EXPECT_EQ(1U, str16.length());
+
+ // Check we don't fail in a length check with invalid String16 size.
+ // (1<<31) * sizeof(char16) == 0, so this is particularly evil.
+ Pickle bad_len;
+ EXPECT_TRUE(bad_len.WriteInt(1 << 31));
+ iter = PickleIterator(bad_len);
+ EXPECT_FALSE(bad_len.ReadString16(&iter, &str16));
+
+ // Check we don't fail in a length check with large WStrings.
+ Pickle big_len;
+ EXPECT_TRUE(big_len.WriteInt(1 << 30));
+ iter = PickleIterator(big_len);
+ std::wstring wstr;
+ EXPECT_FALSE(big_len.ReadWString(&iter, &wstr));
+}
+
+// Check we can write zero bytes of data and 'data' can be NULL.
+TEST(PickleTest, ZeroLength) {
+ Pickle pickle;
+ EXPECT_TRUE(pickle.WriteData(NULL, 0));
+
+ PickleIterator iter(pickle);
+ const char* outdata;
+ int outdatalen;
+ EXPECT_TRUE(pickle.ReadData(&iter, &outdata, &outdatalen));
+ EXPECT_EQ(0, outdatalen);
+ // We can't assert that outdata is NULL.
+}
+
+// Check that ReadBytes works properly with an iterator initialized to NULL.
+TEST(PickleTest, ReadBytes) {
+ Pickle pickle;
+ int data = 0x7abcd;
+ EXPECT_TRUE(pickle.WriteBytes(&data, sizeof(data)));
+
+ PickleIterator iter(pickle);
+ const char* outdata_char = NULL;
+ EXPECT_TRUE(pickle.ReadBytes(&iter, &outdata_char, sizeof(data)));
+
+ int outdata;
+ memcpy(&outdata, outdata_char, sizeof(outdata));
+ EXPECT_EQ(data, outdata);
+}
diff --git a/src/base/platform_file.cc b/src/base/platform_file.cc
new file mode 100644
index 0000000..aa113d3
--- /dev/null
+++ b/src/base/platform_file.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/platform_file.h"
+
+namespace base {
+
+PlatformFileInfo::PlatformFileInfo()
+ : size(0),
+ is_directory(false),
+ is_symbolic_link(false) {
+}
+
+PlatformFileInfo::~PlatformFileInfo() {}
+
+PlatformFile CreatePlatformFile(const FilePath& name,
+ int flags,
+ bool* created,
+ PlatformFileError* error) {
+ if (name.ReferencesParent()) {
+ if (error)
+ *error = PLATFORM_FILE_ERROR_ACCESS_DENIED;
+ return kInvalidPlatformFileValue;
+ }
+ return CreatePlatformFileUnsafe(name, flags, created, error);
+}
+
+} // namespace base
diff --git a/src/base/platform_file.h b/src/base/platform_file.h
new file mode 100644
index 0000000..c3d3ef1
--- /dev/null
+++ b/src/base/platform_file.h
@@ -0,0 +1,251 @@
+// Copyright (c) 2012 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_PLATFORM_FILE_H_
+#define BASE_PLATFORM_FILE_H_
+
+#include "build/build_config.h"
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#if defined(OS_STARBOARD)
+#include "starboard/file.h"
+#endif
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/time.h"
+
+namespace base {
+
+#if defined(OS_WIN)
+typedef HANDLE PlatformFile;
+const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE;
+#elif defined(OS_POSIX)
+typedef int PlatformFile;
+const PlatformFile kInvalidPlatformFileValue = -1;
+#elif defined(OS_STARBOARD)
+typedef SbFile PlatformFile;
+const PlatformFile kInvalidPlatformFileValue = kSbFileInvalid;
+#endif
+
+// PLATFORM_FILE_(OPEN|CREATE).* are mutually exclusive. You should specify
+// exactly one of the five (possibly combining with other flags) when opening
+// or creating a file.
+enum PlatformFileFlags {
+ PLATFORM_FILE_OPEN = 1 << 0, // Opens a file, only if it exists.
+ PLATFORM_FILE_CREATE = 1 << 1, // Creates a new file, only if it
+ // does not already exist.
+ PLATFORM_FILE_OPEN_ALWAYS = 1 << 2, // May create a new file.
+ PLATFORM_FILE_CREATE_ALWAYS = 1 << 3, // May overwrite an old file.
+ PLATFORM_FILE_OPEN_TRUNCATED = 1 << 4, // Opens a file and truncates it,
+ // only if it exists.
+ PLATFORM_FILE_READ = 1 << 5,
+ PLATFORM_FILE_WRITE = 1 << 6,
+ PLATFORM_FILE_EXCLUSIVE_READ = 1 << 7, // EXCLUSIVE is opposite of Windows
+ // SHARE
+ PLATFORM_FILE_EXCLUSIVE_WRITE = 1 << 8,
+ PLATFORM_FILE_ASYNC = 1 << 9,
+ PLATFORM_FILE_TEMPORARY = 1 << 10, // Used on Windows only
+ PLATFORM_FILE_HIDDEN = 1 << 11, // Used on Windows only
+#if !defined(__LB_SHELL__) && !defined(OS_STARBOARD)
+ // TODO: Increase scope of elimination for Starboard.
+ PLATFORM_FILE_DELETE_ON_CLOSE = 1 << 12,
+#endif
+ PLATFORM_FILE_WRITE_ATTRIBUTES = 1 << 13, // Used on Windows only
+ PLATFORM_FILE_ENUMERATE = 1 << 14, // May enumerate directory
+
+ PLATFORM_FILE_SHARE_DELETE = 1 << 15, // Used on Windows only
+
+ PLATFORM_FILE_TERMINAL_DEVICE = 1 << 16, // Serial port flags
+ PLATFORM_FILE_BACKUP_SEMANTICS = 1 << 17, // Used on Windows only
+};
+
+// PLATFORM_FILE_ERROR_ACCESS_DENIED is returned when a call fails because of
+// a filesystem restriction. PLATFORM_FILE_ERROR_SECURITY is returned when a
+// browser policy doesn't allow the operation to be executed.
+enum PlatformFileError {
+ PLATFORM_FILE_OK = 0,
+ PLATFORM_FILE_ERROR_FAILED = -1,
+ PLATFORM_FILE_ERROR_IN_USE = -2,
+ PLATFORM_FILE_ERROR_EXISTS = -3,
+ PLATFORM_FILE_ERROR_NOT_FOUND = -4,
+ PLATFORM_FILE_ERROR_ACCESS_DENIED = -5,
+ PLATFORM_FILE_ERROR_TOO_MANY_OPENED = -6,
+ PLATFORM_FILE_ERROR_NO_MEMORY = -7,
+ PLATFORM_FILE_ERROR_NO_SPACE = -8,
+ PLATFORM_FILE_ERROR_NOT_A_DIRECTORY = -9,
+ PLATFORM_FILE_ERROR_INVALID_OPERATION = -10,
+ PLATFORM_FILE_ERROR_SECURITY = -11,
+ PLATFORM_FILE_ERROR_ABORT = -12,
+ PLATFORM_FILE_ERROR_NOT_A_FILE = -13,
+ PLATFORM_FILE_ERROR_NOT_EMPTY = -14,
+ PLATFORM_FILE_ERROR_INVALID_URL = -15,
+ // Put new entries here and increment PLATFORM_FILE_ERROR_MAX.
+ PLATFORM_FILE_ERROR_MAX = -16
+};
+
+// This explicit mapping matches both FILE_ on Windows and SEEK_ on Linux.
+enum PlatformFileWhence {
+ PLATFORM_FILE_FROM_BEGIN = 0,
+ PLATFORM_FILE_FROM_CURRENT = 1,
+ PLATFORM_FILE_FROM_END = 2
+};
+
+// Used to hold information about a given file.
+// If you add more fields to this structure (platform-specific fields are OK),
+// make sure to update all functions that use it in file_util_{win|posix}.cc
+// too, and the ParamTraits<base::PlatformFileInfo> implementation in
+// chrome/common/common_param_traits.cc.
+struct BASE_EXPORT PlatformFileInfo {
+ PlatformFileInfo();
+ ~PlatformFileInfo();
+
+ // The size of the file in bytes. Undefined when is_directory is true.
+ int64 size;
+
+ // True if the file corresponds to a directory.
+ bool is_directory;
+
+ // True if the file corresponds to a symbolic link.
+ bool is_symbolic_link;
+
+ // The last modified time of a file.
+ base::Time last_modified;
+
+ // The last accessed time of a file.
+ base::Time last_accessed;
+
+ // The creation time of a file.
+ base::Time creation_time;
+};
+
+// Creates or opens the given file. If |created| is provided, it will be set to
+// true if a new file was created [or an old one truncated to zero length to
+// simulate a new file, which can happen with PLATFORM_FILE_CREATE_ALWAYS], and
+// false otherwise. |error| can be NULL.
+//
+// This function fails with 'access denied' if the |name| contains path
+// traversal ('..') components.
+BASE_EXPORT PlatformFile CreatePlatformFile(const FilePath& name,
+ int flags,
+ bool* created,
+ PlatformFileError* error);
+
+// Same as CreatePlatformFile but allows paths with traversal (like \..\)
+// components. Use only with extreme care.
+BASE_EXPORT PlatformFile CreatePlatformFileUnsafe(const FilePath& name,
+ int flags,
+ bool* created,
+ PlatformFileError* error);
+
+// Closes a file handle. Returns |true| on success and |false| otherwise.
+BASE_EXPORT bool ClosePlatformFile(PlatformFile file);
+
+// Changes current position in the file to an |offset| relative to an origin
+// defined by |whence|. Returns the resultant current position in the file
+// (relative to the start) or -1 in case of error.
+BASE_EXPORT int64 SeekPlatformFile(PlatformFile file,
+ PlatformFileWhence whence,
+ int64 offset);
+
+// Reads the given number of bytes (or until EOF is reached) starting with the
+// given offset. Returns the number of bytes read, or -1 on error. Note that
+// this function makes a best effort to read all data on all platforms, so it is
+// not intended for stream oriented files but instead for cases when the normal
+// expectation is that actually |size| bytes are read unless there is an error.
+BASE_EXPORT int ReadPlatformFile(PlatformFile file, int64 offset,
+ char* data, int size);
+
+// Same as above but without seek.
+BASE_EXPORT int ReadPlatformFileAtCurrentPos(PlatformFile file,
+ char* data, int size);
+
+// Reads the given number of bytes (or until EOF is reached) starting with the
+// given offset, but does not make any effort to read all data on all platforms.
+// Returns the number of bytes read, or -1 on error.
+BASE_EXPORT int ReadPlatformFileNoBestEffort(PlatformFile file, int64 offset,
+ char* data, int size);
+
+// Same as above but without seek.
+BASE_EXPORT int ReadPlatformFileCurPosNoBestEffort(PlatformFile file,
+ char* data, int size);
+
+// Writes the given buffer into the file at the given offset, overwritting any
+// data that was previously there. Returns the number of bytes written, or -1
+// on error. Note that this function makes a best effort to write all data on
+// all platforms.
+BASE_EXPORT int WritePlatformFile(PlatformFile file, int64 offset,
+ const char* data, int size);
+
+// Save as above but without seek.
+BASE_EXPORT int WritePlatformFileAtCurrentPos(PlatformFile file,
+ const char* data, int size);
+
+// Save as above but does not make any effort to write all data on all
+// platforms. Returns the number of bytes written, or -1 on error.
+BASE_EXPORT int WritePlatformFileCurPosNoBestEffort(PlatformFile file,
+ const char* data, int size);
+
+// Truncates the given file to the given length. If |length| is greater than
+// the current size of the file, the file is extended with zeros. If the file
+// doesn't exist, |false| is returned.
+BASE_EXPORT bool TruncatePlatformFile(PlatformFile file, int64 length);
+
+// Flushes the buffers of the given file.
+BASE_EXPORT bool FlushPlatformFile(PlatformFile file);
+
+// Touches the given file.
+BASE_EXPORT bool TouchPlatformFile(PlatformFile file,
+ const Time& last_access_time,
+ const Time& last_modified_time);
+
+// Returns some information for the given file.
+BASE_EXPORT bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info);
+
+// Use this class to pass ownership of a PlatformFile to a receiver that may or
+// may not want to accept it. This class does not own the storage for the
+// PlatformFile.
+//
+// EXAMPLE:
+//
+// void MaybeProcessFile(PassPlatformFile pass_file) {
+// if (...) {
+// PlatformFile file = pass_file.ReleaseValue();
+// // Now, we are responsible for closing |file|.
+// }
+// }
+//
+// void OpenAndMaybeProcessFile(const FilePath& path) {
+// PlatformFile file = CreatePlatformFile(path, ...);
+// MaybeProcessFile(PassPlatformFile(&file));
+// if (file != kInvalidPlatformFileValue)
+// ClosePlatformFile(file);
+// }
+//
+class BASE_EXPORT PassPlatformFile {
+ public:
+ explicit PassPlatformFile(PlatformFile* value) : value_(value) {
+ }
+
+ // Called to retrieve the PlatformFile stored in this object. The caller
+ // gains ownership of the PlatformFile and is now responsible for closing it.
+ // Any subsequent calls to this method will return an invalid PlatformFile.
+ PlatformFile ReleaseValue() {
+ PlatformFile temp = *value_;
+ *value_ = kInvalidPlatformFileValue;
+ return temp;
+ }
+
+ private:
+ PlatformFile* value_;
+};
+
+} // namespace base
+
+#endif // BASE_PLATFORM_FILE_H_
diff --git a/src/base/platform_file_posix.cc b/src/base/platform_file_posix.cc
new file mode 100644
index 0000000..1b0b5a7
--- /dev/null
+++ b/src/base/platform_file_posix.cc
@@ -0,0 +1,348 @@
+// Copyright (c) 2012 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/platform_file.h"
+
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/utf_string_conversions.h"
+
+#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
+#include "base/os_compat_android.h"
+#endif
+
+namespace base {
+
+// Make sure our Whence mappings match the system headers.
+COMPILE_ASSERT(PLATFORM_FILE_FROM_BEGIN == SEEK_SET &&
+ PLATFORM_FILE_FROM_CURRENT == SEEK_CUR &&
+ PLATFORM_FILE_FROM_END == SEEK_END, whence_matches_system);
+
+#if defined(OS_BSD) || defined(OS_MACOSX) || defined(__LB_WIIU__) || \
+ defined(__LB_PS3__) || defined(__LB_XB1__) || defined(__LB_PS4__) || \
+ defined(__LB_XB360__)
+typedef struct stat stat_wrapper_t;
+static int CallFstat(int fd, stat_wrapper_t *sb) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return fstat(fd, sb);
+}
+#else
+typedef struct stat64 stat_wrapper_t;
+static int CallFstat(int fd, stat_wrapper_t *sb) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return fstat64(fd, sb);
+}
+#endif
+
+// TODO(erikkay): does it make sense to support PLATFORM_FILE_EXCLUSIVE_* here?
+PlatformFile CreatePlatformFileUnsafe(const FilePath& name,
+ int flags,
+ bool* created,
+ PlatformFileError* error) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ int open_flags = 0;
+ if (flags & PLATFORM_FILE_CREATE)
+ open_flags = O_CREAT | O_EXCL;
+
+ if (created)
+ *created = false;
+
+ if (flags & PLATFORM_FILE_CREATE_ALWAYS) {
+ DCHECK(!open_flags);
+ open_flags = O_CREAT | O_TRUNC;
+ }
+
+ if (flags & PLATFORM_FILE_OPEN_TRUNCATED) {
+ DCHECK(!open_flags);
+ DCHECK(flags & PLATFORM_FILE_WRITE);
+ open_flags = O_TRUNC;
+ }
+
+ if (!open_flags && !(flags & PLATFORM_FILE_OPEN) &&
+ !(flags & PLATFORM_FILE_OPEN_ALWAYS)) {
+ NOTREACHED();
+ errno = EOPNOTSUPP;
+ if (error)
+ *error = PLATFORM_FILE_ERROR_FAILED;
+ return kInvalidPlatformFileValue;
+ }
+
+ if (flags & PLATFORM_FILE_WRITE && flags & PLATFORM_FILE_READ) {
+ open_flags |= O_RDWR;
+ } else if (flags & PLATFORM_FILE_WRITE) {
+ open_flags |= O_WRONLY;
+ } else if (!(flags & PLATFORM_FILE_READ) &&
+ !(flags & PLATFORM_FILE_WRITE_ATTRIBUTES) &&
+ !(flags & PLATFORM_FILE_OPEN_ALWAYS)) {
+ NOTREACHED();
+ }
+
+#if defined(__LB_XB1__) || defined(__LB_XB360__)
+ open_flags |= O_BINARY;
+#else
+ if (flags & PLATFORM_FILE_TERMINAL_DEVICE)
+ open_flags |= O_NOCTTY | O_NDELAY;
+#endif
+
+ COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_equal_zero);
+
+ int mode = S_IRUSR | S_IWUSR;
+#if defined(OS_CHROMEOS)
+ mode |= S_IRGRP | S_IROTH;
+#endif
+
+ int descriptor =
+ HANDLE_EINTR(open(name.value().c_str(), open_flags, mode));
+
+ if (flags & PLATFORM_FILE_OPEN_ALWAYS) {
+ if (descriptor < 0) {
+ open_flags |= O_CREAT;
+ if (flags & PLATFORM_FILE_EXCLUSIVE_READ ||
+ flags & PLATFORM_FILE_EXCLUSIVE_WRITE) {
+ open_flags |= O_EXCL; // together with O_CREAT implies O_NOFOLLOW
+ }
+ descriptor = HANDLE_EINTR(
+ open(name.value().c_str(), open_flags, mode));
+ if (created && descriptor >= 0)
+ *created = true;
+ }
+ }
+
+ if (created && (descriptor >= 0) &&
+ (flags & (PLATFORM_FILE_CREATE_ALWAYS | PLATFORM_FILE_CREATE)))
+ *created = true;
+
+#if !defined (__LB_SHELL__)
+ if ((descriptor >= 0) && (flags & PLATFORM_FILE_DELETE_ON_CLOSE)) {
+ unlink(name.value().c_str());
+ }
+#endif
+
+ if (error) {
+ if (descriptor >= 0)
+ *error = PLATFORM_FILE_OK;
+ else {
+ switch (errno) {
+ case EACCES:
+ case EISDIR:
+ case EROFS:
+ case EPERM:
+ *error = PLATFORM_FILE_ERROR_ACCESS_DENIED;
+ break;
+ case ETXTBSY:
+ *error = PLATFORM_FILE_ERROR_IN_USE;
+ break;
+ case EEXIST:
+ *error = PLATFORM_FILE_ERROR_EXISTS;
+ break;
+ case ENOENT:
+ *error = PLATFORM_FILE_ERROR_NOT_FOUND;
+ break;
+ case EMFILE:
+ *error = PLATFORM_FILE_ERROR_TOO_MANY_OPENED;
+ break;
+ case ENOMEM:
+ *error = PLATFORM_FILE_ERROR_NO_MEMORY;
+ break;
+ case ENOSPC:
+ *error = PLATFORM_FILE_ERROR_NO_SPACE;
+ break;
+ case ENOTDIR:
+ *error = PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
+ break;
+ default:
+ *error = PLATFORM_FILE_ERROR_FAILED;
+ }
+ }
+ }
+
+ return descriptor;
+}
+
+bool ClosePlatformFile(PlatformFile file) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (file < 0) {
+ errno = EINVAL;
+ return false;
+ }
+ return !HANDLE_EINTR(close(file));
+}
+
+int64 SeekPlatformFile(PlatformFile file,
+ PlatformFileWhence whence,
+ int64 offset) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (file < 0 || offset < 0)
+ return -1;
+
+ return lseek(file, static_cast<off_t>(offset), static_cast<int>(whence));
+}
+
+#if !defined(__LB_PS3__)
+int ReadPlatformFile(PlatformFile file, int64 offset, char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (file < 0 || size < 0)
+ return -1;
+
+ int bytes_read = 0;
+ int rv;
+ do {
+ rv = HANDLE_EINTR(pread(file, data + bytes_read,
+ size - bytes_read, offset + bytes_read));
+ if (rv <= 0)
+ break;
+
+ bytes_read += rv;
+ } while (bytes_read < size);
+
+ return bytes_read ? bytes_read : rv;
+}
+#endif
+
+int ReadPlatformFileAtCurrentPos(PlatformFile file, char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (file < 0 || size < 0)
+ return -1;
+
+ int bytes_read = 0;
+ int rv;
+ do {
+ rv = HANDLE_EINTR(read(file, data, size));
+ if (rv <= 0)
+ break;
+
+ bytes_read += rv;
+ } while (bytes_read < size);
+
+ return bytes_read ? bytes_read : rv;
+}
+
+#if !defined(__LB_PS3__)
+int ReadPlatformFileNoBestEffort(PlatformFile file, int64 offset,
+ char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (file < 0)
+ return -1;
+
+ return HANDLE_EINTR(pread(file, data, size, offset));
+}
+#endif
+
+int ReadPlatformFileCurPosNoBestEffort(PlatformFile file,
+ char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (file < 0 || size < 0)
+ return -1;
+
+ return HANDLE_EINTR(read(file, data, size));
+}
+
+#if !defined(__LB_PS3__)
+int WritePlatformFile(PlatformFile file, int64 offset,
+ const char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (file < 0 || size < 0)
+ return -1;
+
+ int bytes_written = 0;
+ int rv;
+ do {
+ rv = HANDLE_EINTR(pwrite(file, data + bytes_written,
+ size - bytes_written, offset + bytes_written));
+ if (rv <= 0)
+ break;
+
+ bytes_written += rv;
+ } while (bytes_written < size);
+
+ return bytes_written ? bytes_written : rv;
+}
+#endif
+
+int WritePlatformFileAtCurrentPos(PlatformFile file,
+ const char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (file < 0 || size < 0)
+ return -1;
+
+ int bytes_written = 0;
+ int rv;
+ do {
+ rv = HANDLE_EINTR(write(file, data, size));
+ if (rv <= 0)
+ break;
+
+ bytes_written += rv;
+ } while (bytes_written < size);
+
+ return bytes_written ? bytes_written : rv;
+}
+
+int WritePlatformFileCurPosNoBestEffort(PlatformFile file,
+ const char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (file < 0 || size < 0)
+ return -1;
+
+ return HANDLE_EINTR(write(file, data, size));
+}
+
+bool TruncatePlatformFile(PlatformFile file, int64 length) {
+ base::ThreadRestrictions::AssertIOAllowed();
+#if defined(__LB_PS3__)
+ // On PS3, when writing specifically to the temp partition, you must sync
+ // file contents to disk after truncating if you want the truncation
+ // reflected in subsequent file operations.
+ return ((file >= 0) && !HANDLE_EINTR(ftruncate(file, length)) && !fsync(file));
+#else
+ return ((file >= 0) && !HANDLE_EINTR(ftruncate(file, length)));
+#endif
+}
+
+bool FlushPlatformFile(PlatformFile file) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return !HANDLE_EINTR(fsync(file));
+}
+
+#if !defined(__LB_WIIU__) && !defined(__LB_PS3__) && !defined(__LB_XB1__) && \
+ !defined(__LB_XB360__)
+bool TouchPlatformFile(PlatformFile file, const base::Time& last_access_time,
+ const base::Time& last_modified_time) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (file < 0)
+ return false;
+
+ timeval times[2];
+ times[0] = last_access_time.ToTimeVal();
+ times[1] = last_modified_time.ToTimeVal();
+ return !futimes(file, times);
+}
+#endif
+
+bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) {
+ if (!info || file < 0)
+ return false;
+
+ stat_wrapper_t file_info;
+ if (CallFstat(file, &file_info))
+ return false;
+
+ info->is_directory = S_ISDIR(file_info.st_mode);
+ info->is_symbolic_link = S_ISLNK(file_info.st_mode);
+ info->size = file_info.st_size;
+ info->last_modified = base::Time::FromTimeT(file_info.st_mtime);
+ info->last_accessed = base::Time::FromTimeT(file_info.st_atime);
+ info->creation_time = base::Time::FromTimeT(file_info.st_ctime);
+ return true;
+}
+
+} // namespace base
diff --git a/src/base/platform_file_starboard.cc b/src/base/platform_file_starboard.cc
new file mode 100644
index 0000000..c28fd65
--- /dev/null
+++ b/src/base/platform_file_starboard.cc
@@ -0,0 +1,309 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Adapted from platform_file_posix.cc
+
+#include "base/platform_file.h"
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/threading/thread_restrictions.h"
+#include "starboard/file.h"
+
+namespace base {
+
+// Make sure our Whence mappings match the system headers.
+COMPILE_ASSERT(
+ PLATFORM_FILE_FROM_BEGIN == static_cast<int>(kSbFileFromBegin) &&
+ PLATFORM_FILE_FROM_CURRENT == static_cast<int>(kSbFileFromCurrent) &&
+ PLATFORM_FILE_FROM_END == static_cast<int>(kSbFileFromEnd),
+ whence_matches_system);
+
+PlatformFile CreatePlatformFileUnsafe(const FilePath& name,
+ int flags,
+ bool *created,
+ PlatformFileError* error) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ int open_flags = 0;
+
+ switch (flags & (PLATFORM_FILE_OPEN | PLATFORM_FILE_CREATE |
+ PLATFORM_FILE_OPEN_ALWAYS | PLATFORM_FILE_CREATE_ALWAYS |
+ PLATFORM_FILE_OPEN_TRUNCATED)) {
+ case PLATFORM_FILE_OPEN:
+ open_flags = kSbFileOpenOnly;
+ break;
+
+ case PLATFORM_FILE_CREATE:
+ open_flags = kSbFileCreateOnly;
+ break;
+
+ case PLATFORM_FILE_OPEN_ALWAYS:
+ open_flags = kSbFileOpenAlways;
+ break;
+
+ case PLATFORM_FILE_CREATE_ALWAYS:
+ open_flags = kSbFileCreateAlways;
+ break;
+
+ case PLATFORM_FILE_OPEN_TRUNCATED:
+ open_flags = kSbFileOpenTruncated;
+ DCHECK(flags & PLATFORM_FILE_WRITE);
+ break;
+
+ default:
+ NOTREACHED() << "Passed incompatible flags: " << flags;
+ if (error) {
+ *error = PLATFORM_FILE_ERROR_FAILED;
+ }
+ return kInvalidPlatformFileValue;
+ }
+
+ DCHECK(flags & PLATFORM_FILE_WRITE || flags & PLATFORM_FILE_READ);
+
+ if (flags & PLATFORM_FILE_READ) {
+ open_flags |= kSbFileRead;
+ }
+
+ if (flags & PLATFORM_FILE_WRITE) {
+ open_flags |= kSbFileWrite;
+ }
+
+ SbFileError sb_error;
+ SbFile file = SbFileOpen(name.value().c_str(), open_flags, created,
+ &sb_error);
+
+ if (error) {
+ if (SbFileIsValid(file)) {
+ *error = PLATFORM_FILE_OK;
+ } else {
+ switch (sb_error) {
+ case kSbFileErrorFailed:
+ case kSbFileErrorInUse:
+ case kSbFileErrorExists:
+ case kSbFileErrorNotFound:
+ case kSbFileErrorAccessDenied:
+ case kSbFileErrorTooManyOpened:
+ case kSbFileErrorNoMemory:
+ case kSbFileErrorNoSpace:
+ case kSbFileErrorNotADirectory:
+ case kSbFileErrorInvalidOperation:
+ case kSbFileErrorSecurity:
+ case kSbFileErrorAbort:
+ case kSbFileErrorNotAFile:
+ case kSbFileErrorNotEmpty:
+ case kSbFileErrorInvalidUrl:
+ // Starboard error codes are designed to match Chromium's exactly.
+ *error = static_cast<PlatformFileError>(sb_error);
+ break;
+ default:
+ NOTREACHED() << "Unrecognized SbFileError: " << sb_error;
+ break;
+ }
+ }
+ }
+
+ return file;
+}
+
+bool ClosePlatformFile(PlatformFile file) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return SbFileClose(file);
+}
+
+int64 SeekPlatformFile(PlatformFile file,
+ PlatformFileWhence whence,
+ int64 offset) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return SbFileSeek(file, static_cast<SbFileWhence>(whence), offset);
+}
+
+int ReadPlatformFile(PlatformFile file, int64 offset, char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (size < 0) {
+ return -1;
+ }
+
+ int original_position = SbFileSeek(file, kSbFileFromCurrent, 0);
+ if (original_position < 0) {
+ return -1;
+ }
+
+ int position = SbFileSeek(file, kSbFileFromBegin, offset);
+ int result = 0;
+ if (position == offset) {
+ result = ReadPlatformFileAtCurrentPos(file, data, size);
+ }
+
+ // Restore position regardless of result of write.
+ position = SbFileSeek(file, kSbFileFromBegin, original_position);
+ if (result < 0) {
+ return result;
+ }
+
+ if (position < 0) {
+ return -1;
+ }
+
+ return result;
+}
+
+int ReadPlatformFileAtCurrentPos(PlatformFile file, char *data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (file < 0 || size < 0)
+ return -1;
+
+ int bytes_read = 0;
+ int rv;
+ do {
+ rv = SbFileRead(file, data, size);
+ if (rv <= 0) {
+ break;
+ }
+
+ bytes_read += rv;
+ } while (bytes_read < size);
+
+ return bytes_read ? bytes_read : rv;
+}
+
+int ReadPlatformFileNoBestEffort(PlatformFile file,
+ int64 offset,
+ char *data,
+ int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ int original_position = SbFileSeek(file, kSbFileFromCurrent, 0);
+ if (original_position < 0) {
+ return -1;
+ }
+
+ int position = SbFileSeek(file, kSbFileFromBegin, offset);
+ int result = 0;
+ if (position == offset) {
+ result = SbFileRead(file, data, size);
+ }
+
+ // Restore position regardless of result of read.
+ position = SbFileSeek(file, kSbFileFromBegin, original_position);
+ if (result < 0) {
+ return result;
+ }
+
+ if (position < 0) {
+ return -1;
+ }
+
+ return result;
+}
+
+int ReadPlatformFileCurPosNoBestEffort(PlatformFile file,
+ char *data,
+ int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return SbFileRead(file, data, size);
+}
+
+int WritePlatformFile(PlatformFile file,
+ int64 offset,
+ const char *data,
+ int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (size < 0) {
+ return -1;
+ }
+
+ int original_position = SbFileSeek(file, kSbFileFromCurrent, 0);
+ if (original_position < 0) {
+ return -1;
+ }
+
+ int position = SbFileSeek(file, kSbFileFromBegin, offset);
+ int result = 0;
+ if (position == offset) {
+ result = WritePlatformFileAtCurrentPos(file, data, size);
+ }
+
+ // Restore position regardless of result of write.
+ position = SbFileSeek(file, kSbFileFromBegin, original_position);
+ if (result < 0) {
+ return result;
+ }
+
+ if (position < 0) {
+ return -1;
+ }
+
+ return result;
+}
+
+int WritePlatformFileAtCurrentPos(PlatformFile file,
+ const char* data,
+ int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (size < 0) {
+ return -1;
+ }
+
+ int bytes_written = 0;
+ int result;
+ do {
+ result = SbFileWrite(file, data, size);
+ if (result <= 0) {
+ break;
+ }
+
+ bytes_written += result;
+ } while (bytes_written < size);
+
+ return bytes_written ? bytes_written : result;
+}
+
+int WritePlatformFileCurPosNoBestEffort(PlatformFile file,
+ const char *data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (size < 0) {
+ return -1;
+ }
+
+ return SbFileWrite(file, data, size);
+}
+
+bool TruncatePlatformFile(PlatformFile file, int64 length) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return SbFileTruncate(file, length);
+}
+
+bool FlushPlatformFile(PlatformFile file) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return SbFileFlush(file);
+}
+
+bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo *info) {
+ if (!info || file < 0)
+ return false;
+
+ SbFileInfo file_info;
+ if (!SbFileGetInfo(file, &file_info))
+ return false;
+
+ info->is_directory = file_info.is_directory;
+ info->is_symbolic_link = file_info.is_symbolic_link;
+ info->size = file_info.size;
+ info->last_modified = base::Time::FromSbTime(file_info.last_modified);
+ info->last_accessed = base::Time::FromSbTime(file_info.last_accessed);
+ info->creation_time = base::Time::FromSbTime(file_info.creation_time);
+ return true;
+}
+
+} // namespace base
diff --git a/src/base/platform_file_unittest.cc b/src/base/platform_file_unittest.cc
new file mode 100644
index 0000000..e73d930
--- /dev/null
+++ b/src/base/platform_file_unittest.cc
@@ -0,0 +1,354 @@
+// Copyright (c) 2012 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/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/platform_file.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Reads from a file the given number of bytes, or until EOF is reached.
+// Returns the number of bytes read.
+int ReadFully(base::PlatformFile file, int64 offset, char* data, int size) {
+ return base::ReadPlatformFile(file, offset, data, size);
+}
+
+// Writes the given number of bytes to a file.
+// Returns the number of bytes written.
+int WriteFully(base::PlatformFile file, int64 offset,
+ const char* data, int size) {
+ return base::WritePlatformFile(file, offset, data, size);
+}
+
+} // namespace
+
+TEST(PlatformFile, CreatePlatformFile) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath file_path = temp_dir.path().AppendASCII("create_file_1");
+
+ // Open a file that doesn't exist.
+ base::PlatformFileError error_code = base::PLATFORM_FILE_OK;
+ base::PlatformFile file = base::CreatePlatformFile(
+ file_path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
+ NULL, &error_code);
+ EXPECT_EQ(base::kInvalidPlatformFileValue, file);
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, error_code);
+
+ // Open or create a file.
+ bool created = false;
+ error_code = base::PLATFORM_FILE_OK;
+ file = base::CreatePlatformFile(
+ file_path, base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_READ,
+ &created, &error_code);
+ EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_TRUE(created);
+ EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
+ base::ClosePlatformFile(file);
+
+ // Open an existing file.
+ created = false;
+ file = base::CreatePlatformFile(
+ file_path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
+ &created, &error_code);
+ EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_FALSE(created);
+ EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
+ base::ClosePlatformFile(file);
+
+ // Create a file that exists.
+ file = base::CreatePlatformFile(
+ file_path, base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_READ,
+ &created, &error_code);
+ EXPECT_EQ(base::kInvalidPlatformFileValue, file);
+ EXPECT_FALSE(created);
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, error_code);
+
+ // Create or overwrite a file.
+ error_code = base::PLATFORM_FILE_OK;
+ file = base::CreatePlatformFile(
+ file_path, base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_READ,
+ &created, &error_code);
+ EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_TRUE(created);
+ EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
+ base::ClosePlatformFile(file);
+
+#if !defined(__LB_SHELL__) && !defined(OS_STARBOARD)
+ // Create a delete-on-close file.
+ created = false;
+ file_path = temp_dir.path().AppendASCII("create_file_2");
+ file = base::CreatePlatformFile(
+ file_path,
+ base::PLATFORM_FILE_OPEN_ALWAYS |
+ base::PLATFORM_FILE_DELETE_ON_CLOSE |
+ base::PLATFORM_FILE_READ,
+ &created, &error_code);
+ EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_TRUE(created);
+ EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
+
+ EXPECT_TRUE(base::ClosePlatformFile(file));
+ EXPECT_FALSE(file_util::PathExists(file_path));
+#endif
+}
+
+TEST(PlatformFile, DeleteOpenFile) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath file_path = temp_dir.path().AppendASCII("create_file_1");
+
+ // Create a file.
+ bool created = false;
+ base::PlatformFileError error_code = base::PLATFORM_FILE_OK;
+ base::PlatformFile file = base::CreatePlatformFile(
+ file_path,
+ base::PLATFORM_FILE_OPEN_ALWAYS |
+ base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_SHARE_DELETE,
+ &created, &error_code);
+ EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_TRUE(created);
+ EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
+
+#if !defined(__LB_SHELL__) && !defined(OS_STARBOARD)
+ // Open an existing file and mark it as delete on close.
+ created = false;
+ base::PlatformFile same_file = base::CreatePlatformFile(
+ file_path,
+ base::PLATFORM_FILE_OPEN |
+ base::PLATFORM_FILE_DELETE_ON_CLOSE |
+ base::PLATFORM_FILE_READ,
+ &created, &error_code);
+ EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_FALSE(created);
+ EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
+
+ // Close both handles and check that the file is gone.
+ base::ClosePlatformFile(file);
+ base::ClosePlatformFile(same_file);
+ EXPECT_FALSE(file_util::PathExists(file_path));
+#else
+ base::ClosePlatformFile(file);
+#endif
+}
+
+TEST(PlatformFile, ReadWritePlatformFile) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath file_path = temp_dir.path().AppendASCII("read_write_file");
+ base::PlatformFile file = base::CreatePlatformFile(
+ file_path,
+ base::PLATFORM_FILE_CREATE |
+ base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_WRITE,
+ NULL, NULL);
+ EXPECT_NE(base::kInvalidPlatformFileValue, file);
+
+ char data_to_write[] = "test";
+ const int kTestDataSize = 4;
+
+ // Write 0 bytes to the file.
+ int bytes_written = WriteFully(file, 0, data_to_write, 0);
+ EXPECT_EQ(0, bytes_written);
+
+ // Write "test" to the file.
+ bytes_written = WriteFully(file, 0, data_to_write, kTestDataSize);
+ EXPECT_EQ(kTestDataSize, bytes_written);
+
+ // Read from EOF.
+ char data_read_1[32];
+ int bytes_read = ReadFully(file, kTestDataSize, data_read_1, kTestDataSize);
+ EXPECT_EQ(0, bytes_read);
+
+ // Read from somewhere in the middle of the file.
+ const int kPartialReadOffset = 1;
+ bytes_read = ReadFully(file, kPartialReadOffset, data_read_1, kTestDataSize);
+ EXPECT_EQ(kTestDataSize - kPartialReadOffset, bytes_read);
+ for (int i = 0; i < bytes_read; i++)
+ EXPECT_EQ(data_to_write[i + kPartialReadOffset], data_read_1[i]);
+
+ // Read 0 bytes.
+ bytes_read = ReadFully(file, 0, data_read_1, 0);
+ EXPECT_EQ(0, bytes_read);
+
+ // Read the entire file.
+ bytes_read = ReadFully(file, 0, data_read_1, kTestDataSize);
+ EXPECT_EQ(kTestDataSize, bytes_read);
+ for (int i = 0; i < bytes_read; i++)
+ EXPECT_EQ(data_to_write[i], data_read_1[i]);
+
+ // Read again, but using the trivial native wrapper.
+ bytes_read = base::ReadPlatformFileNoBestEffort(file, 0, data_read_1,
+ kTestDataSize);
+ EXPECT_LE(bytes_read, kTestDataSize);
+ for (int i = 0; i < bytes_read; i++)
+ EXPECT_EQ(data_to_write[i], data_read_1[i]);
+
+ // Write past the end of the file.
+ const int kOffsetBeyondEndOfFile = 10;
+ const int kPartialWriteLength = 2;
+ bytes_written = WriteFully(file, kOffsetBeyondEndOfFile,
+ data_to_write, kPartialWriteLength);
+ EXPECT_EQ(kPartialWriteLength, bytes_written);
+
+ // Make sure the file was extended.
+ int64 file_size = 0;
+#if defined(__LB_SHELL__)
+ // At this point, the file buffer may not have been flushed to disk,
+ // so reading the new file size from disk is flaky. Instead, read the file
+ // size from the file descriptor using GetPlatformFileInfo.
+ base::PlatformFileInfo file_info;
+ EXPECT_TRUE(base::GetPlatformFileInfo(file, &file_info));
+ file_size = file_info.size;
+#else
+ EXPECT_TRUE(file_util::GetFileSize(file_path, &file_size));
+#endif
+ EXPECT_EQ(kOffsetBeyondEndOfFile + kPartialWriteLength, file_size);
+
+ // Make sure the file was zero-padded.
+ char data_read_2[32];
+ bytes_read = ReadFully(file, 0, data_read_2, static_cast<int>(file_size));
+ EXPECT_EQ(file_size, bytes_read);
+ for (int i = 0; i < kTestDataSize; i++)
+ EXPECT_EQ(data_to_write[i], data_read_2[i]);
+ for (int i = kTestDataSize; i < kOffsetBeyondEndOfFile; i++)
+ EXPECT_EQ(0, data_read_2[i]);
+ for (int i = kOffsetBeyondEndOfFile; i < file_size; i++)
+ EXPECT_EQ(data_to_write[i - kOffsetBeyondEndOfFile], data_read_2[i]);
+
+ // Close the file handle to allow the temp directory to be deleted.
+ base::ClosePlatformFile(file);
+}
+
+TEST(PlatformFile, TruncatePlatformFile) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath file_path = temp_dir.path().AppendASCII("truncate_file");
+ base::PlatformFile file = base::CreatePlatformFile(
+ file_path,
+ base::PLATFORM_FILE_CREATE |
+ base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_WRITE,
+ NULL, NULL);
+ EXPECT_NE(base::kInvalidPlatformFileValue, file);
+
+ // Write "test" to the file.
+ char data_to_write[] = "test";
+ int kTestDataSize = 4;
+ int bytes_written = WriteFully(file, 0, data_to_write, kTestDataSize);
+ EXPECT_EQ(kTestDataSize, bytes_written);
+
+ // Extend the file.
+ const int kExtendedFileLength = 10;
+ int64 file_size = 0;
+ EXPECT_TRUE(base::TruncatePlatformFile(file, kExtendedFileLength));
+ EXPECT_TRUE(file_util::GetFileSize(file_path, &file_size));
+ EXPECT_EQ(kExtendedFileLength, file_size);
+
+ // Make sure the file was zero-padded.
+ char data_read[32];
+ int bytes_read = ReadFully(file, 0, data_read, static_cast<int>(file_size));
+ EXPECT_EQ(file_size, bytes_read);
+ for (int i = 0; i < kTestDataSize; i++)
+ EXPECT_EQ(data_to_write[i], data_read[i]);
+ for (int i = kTestDataSize; i < file_size; i++)
+ EXPECT_EQ(0, data_read[i]);
+
+ // Truncate the file.
+ const int kTruncatedFileLength = 2;
+ EXPECT_TRUE(base::TruncatePlatformFile(file, kTruncatedFileLength));
+ EXPECT_TRUE(file_util::GetFileSize(file_path, &file_size));
+ EXPECT_EQ(kTruncatedFileLength, file_size);
+
+ // Make sure the file was truncated.
+ bytes_read = ReadFully(file, 0, data_read, kTestDataSize);
+ EXPECT_EQ(file_size, bytes_read);
+ for (int i = 0; i < file_size; i++)
+ EXPECT_EQ(data_to_write[i], data_read[i]);
+
+ // Close the file handle to allow the temp directory to be deleted.
+ base::ClosePlatformFile(file);
+}
+
+#if !defined(OS_STARBOARD)
+// Flakily fails: http://crbug.com/86494
+TEST(PlatformFile, DISABLED_TouchGetInfoPlatformFile) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ base::PlatformFile file = base::CreatePlatformFile(
+ temp_dir.path().AppendASCII("touch_get_info_file"),
+ base::PLATFORM_FILE_CREATE |
+ base::PLATFORM_FILE_WRITE |
+ base::PLATFORM_FILE_WRITE_ATTRIBUTES,
+ NULL, NULL);
+ EXPECT_NE(base::kInvalidPlatformFileValue, file);
+
+ // Get info for a newly created file.
+ base::PlatformFileInfo info;
+ EXPECT_TRUE(base::GetPlatformFileInfo(file, &info));
+
+ // Add 2 seconds to account for possible rounding errors on
+ // filesystems that use a 1s or 2s timestamp granularity.
+ base::Time now = base::Time::Now() + base::TimeDelta::FromSeconds(2);
+ EXPECT_EQ(0, info.size);
+ EXPECT_FALSE(info.is_directory);
+ EXPECT_FALSE(info.is_symbolic_link);
+ EXPECT_LE(info.last_accessed.ToInternalValue(), now.ToInternalValue());
+ EXPECT_LE(info.last_modified.ToInternalValue(), now.ToInternalValue());
+ EXPECT_LE(info.creation_time.ToInternalValue(), now.ToInternalValue());
+ base::Time creation_time = info.creation_time;
+
+ // Write "test" to the file.
+ char data[] = "test";
+ const int kTestDataSize = 4;
+ int bytes_written = WriteFully(file, 0, data, kTestDataSize);
+ EXPECT_EQ(kTestDataSize, bytes_written);
+
+ // Change the last_accessed and last_modified dates.
+ // It's best to add values that are multiples of 2 (in seconds)
+ // to the current last_accessed and last_modified times, because
+ // FATxx uses a 2s timestamp granularity.
+ base::Time new_last_accessed =
+ info.last_accessed + base::TimeDelta::FromSeconds(234);
+ base::Time new_last_modified =
+ info.last_modified + base::TimeDelta::FromMinutes(567);
+
+#if !defined(__LB_PS3__)
+ // base::TouchPlatformFile() will never work on PS3 as long as PS3 does not
+ // implement futime().
+ EXPECT_TRUE(base::TouchPlatformFile(file, new_last_accessed,
+ new_last_modified));
+#endif
+
+ // Make sure the file info was updated accordingly.
+ EXPECT_TRUE(base::GetPlatformFileInfo(file, &info));
+ EXPECT_EQ(info.size, kTestDataSize);
+ EXPECT_FALSE(info.is_directory);
+ EXPECT_FALSE(info.is_symbolic_link);
+
+ // ext2/ext3 and HPS/HPS+ seem to have a timestamp granularity of 1s.
+#if !defined(__LB_PS3__) && !defined(__LB_WIIU__)
+#if defined(OS_POSIX)
+ EXPECT_EQ(info.last_accessed.ToTimeVal().tv_sec,
+ new_last_accessed.ToTimeVal().tv_sec);
+ EXPECT_EQ(info.last_modified.ToTimeVal().tv_sec,
+ new_last_modified.ToTimeVal().tv_sec);
+#else
+ EXPECT_EQ(info.last_accessed.ToInternalValue(),
+ new_last_accessed.ToInternalValue());
+ EXPECT_EQ(info.last_modified.ToInternalValue(),
+ new_last_modified.ToInternalValue());
+#endif
+#endif
+
+ EXPECT_EQ(info.creation_time.ToInternalValue(),
+ creation_time.ToInternalValue());
+
+ // Close the file handle to allow the temp directory to be deleted.
+ base::ClosePlatformFile(file);
+}
+#endif
diff --git a/src/base/platform_file_win.cc b/src/base/platform_file_win.cc
new file mode 100644
index 0000000..7c72f9e
--- /dev/null
+++ b/src/base/platform_file_win.cc
@@ -0,0 +1,262 @@
+// Copyright (c) 2012 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/platform_file.h"
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace base {
+PlatformFile CreatePlatformFileUnsafe(const FilePath& name,
+ int flags,
+ bool* created,
+ PlatformFileError* error) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ DWORD disposition = 0;
+ if (created)
+ *created = false;
+
+ if (flags & PLATFORM_FILE_OPEN)
+ disposition = OPEN_EXISTING;
+
+ if (flags & PLATFORM_FILE_CREATE) {
+ DCHECK(!disposition);
+ disposition = CREATE_NEW;
+ }
+
+ if (flags & PLATFORM_FILE_OPEN_ALWAYS) {
+ DCHECK(!disposition);
+ disposition = OPEN_ALWAYS;
+ }
+
+ if (flags & PLATFORM_FILE_CREATE_ALWAYS) {
+ DCHECK(!disposition);
+ disposition = CREATE_ALWAYS;
+ }
+
+ if (flags & PLATFORM_FILE_OPEN_TRUNCATED) {
+ DCHECK(!disposition);
+ DCHECK(flags & PLATFORM_FILE_WRITE);
+ disposition = TRUNCATE_EXISTING;
+ }
+
+ if (!disposition) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ DWORD access = (flags & PLATFORM_FILE_READ) ? GENERIC_READ : 0;
+ if (flags & PLATFORM_FILE_WRITE)
+ access |= GENERIC_WRITE;
+ if (flags & PLATFORM_FILE_WRITE_ATTRIBUTES)
+ access |= FILE_WRITE_ATTRIBUTES;
+
+ DWORD sharing = (flags & PLATFORM_FILE_EXCLUSIVE_READ) ? 0 : FILE_SHARE_READ;
+ if (!(flags & PLATFORM_FILE_EXCLUSIVE_WRITE))
+ sharing |= FILE_SHARE_WRITE;
+ if (flags & PLATFORM_FILE_SHARE_DELETE)
+ sharing |= FILE_SHARE_DELETE;
+
+ DWORD create_flags = 0;
+ if (flags & PLATFORM_FILE_ASYNC)
+ create_flags |= FILE_FLAG_OVERLAPPED;
+ if (flags & PLATFORM_FILE_TEMPORARY)
+ create_flags |= FILE_ATTRIBUTE_TEMPORARY;
+ if (flags & PLATFORM_FILE_HIDDEN)
+ create_flags |= FILE_ATTRIBUTE_HIDDEN;
+ if (flags & PLATFORM_FILE_DELETE_ON_CLOSE)
+ create_flags |= FILE_FLAG_DELETE_ON_CLOSE;
+ if (flags & PLATFORM_FILE_BACKUP_SEMANTICS)
+ create_flags |= FILE_FLAG_BACKUP_SEMANTICS;
+
+ HANDLE file = CreateFile(name.value().c_str(), access, sharing, NULL,
+ disposition, create_flags, NULL);
+
+ if (created && (INVALID_HANDLE_VALUE != file)) {
+ if (flags & (PLATFORM_FILE_OPEN_ALWAYS))
+ *created = (ERROR_ALREADY_EXISTS != GetLastError());
+ else if (flags & (PLATFORM_FILE_CREATE_ALWAYS | PLATFORM_FILE_CREATE))
+ *created = true;
+ }
+
+ if (error) {
+ if (file != kInvalidPlatformFileValue)
+ *error = PLATFORM_FILE_OK;
+ else {
+ DWORD last_error = GetLastError();
+ switch (last_error) {
+ case ERROR_SHARING_VIOLATION:
+ *error = PLATFORM_FILE_ERROR_IN_USE;
+ break;
+ case ERROR_FILE_EXISTS:
+ *error = PLATFORM_FILE_ERROR_EXISTS;
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ *error = PLATFORM_FILE_ERROR_NOT_FOUND;
+ break;
+ case ERROR_ACCESS_DENIED:
+ *error = PLATFORM_FILE_ERROR_ACCESS_DENIED;
+ break;
+ default:
+ *error = PLATFORM_FILE_ERROR_FAILED;
+ }
+ }
+ }
+
+ return file;
+}
+
+bool ClosePlatformFile(PlatformFile file) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return (CloseHandle(file) != 0);
+}
+
+int64 SeekPlatformFile(PlatformFile file,
+ PlatformFileWhence whence,
+ int64 offset) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (file < 0 || offset < 0)
+ return -1;
+
+ LARGE_INTEGER distance, res;
+ distance.QuadPart = offset;
+ DWORD move_method = static_cast<DWORD>(whence);
+ if (!SetFilePointerEx(file, distance, &res, move_method))
+ return -1;
+ return res.QuadPart;
+}
+
+int ReadPlatformFile(PlatformFile file, int64 offset, char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (file == kInvalidPlatformFileValue)
+ return -1;
+
+ LARGE_INTEGER offset_li;
+ offset_li.QuadPart = offset;
+
+ OVERLAPPED overlapped = {0};
+ overlapped.Offset = offset_li.LowPart;
+ overlapped.OffsetHigh = offset_li.HighPart;
+
+ DWORD bytes_read;
+ if (::ReadFile(file, data, size, &bytes_read, &overlapped) != 0)
+ return bytes_read;
+ else if (ERROR_HANDLE_EOF == GetLastError())
+ return 0;
+
+ return -1;
+}
+
+int ReadPlatformFileAtCurrentPos(PlatformFile file, char* data, int size) {
+ return ReadPlatformFile(file, 0, data, size);
+}
+
+int ReadPlatformFileNoBestEffort(PlatformFile file, int64 offset, char* data,
+ int size) {
+ return ReadPlatformFile(file, offset, data, size);
+}
+
+int ReadPlatformFileCurPosNoBestEffort(PlatformFile file,
+ char* data, int size) {
+ return ReadPlatformFile(file, 0, data, size);
+}
+
+int WritePlatformFile(PlatformFile file, int64 offset,
+ const char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (file == kInvalidPlatformFileValue)
+ return -1;
+
+ LARGE_INTEGER offset_li;
+ offset_li.QuadPart = offset;
+
+ OVERLAPPED overlapped = {0};
+ overlapped.Offset = offset_li.LowPart;
+ overlapped.OffsetHigh = offset_li.HighPart;
+
+ DWORD bytes_written;
+ if (::WriteFile(file, data, size, &bytes_written, &overlapped) != 0)
+ return bytes_written;
+
+ return -1;
+}
+
+int WritePlatformFileAtCurrentPos(PlatformFile file, const char* data,
+ int size) {
+ return WritePlatformFile(file, 0, data, size);
+}
+
+int WritePlatformFileCurPosNoBestEffort(PlatformFile file,
+ const char* data, int size) {
+ return WritePlatformFile(file, 0, data, size);
+}
+
+bool TruncatePlatformFile(PlatformFile file, int64 length) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (file == kInvalidPlatformFileValue)
+ return false;
+
+ // Get the current file pointer.
+ LARGE_INTEGER file_pointer;
+ LARGE_INTEGER zero;
+ zero.QuadPart = 0;
+ if (::SetFilePointerEx(file, zero, &file_pointer, FILE_CURRENT) == 0)
+ return false;
+
+ LARGE_INTEGER length_li;
+ length_li.QuadPart = length;
+ // If length > file size, SetFilePointerEx() should extend the file
+ // with zeroes on all Windows standard file systems (NTFS, FATxx).
+ if (!::SetFilePointerEx(file, length_li, NULL, FILE_BEGIN))
+ return false;
+
+ // Set the new file length and move the file pointer to its old position.
+ // This is consistent with ftruncate()'s behavior, even when the file
+ // pointer points to a location beyond the end of the file.
+ return ((::SetEndOfFile(file) != 0) &&
+ (::SetFilePointerEx(file, file_pointer, NULL, FILE_BEGIN) != 0));
+}
+
+bool FlushPlatformFile(PlatformFile file) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ return ((file != kInvalidPlatformFileValue) && ::FlushFileBuffers(file));
+}
+
+bool TouchPlatformFile(PlatformFile file, const base::Time& last_access_time,
+ const base::Time& last_modified_time) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (file == kInvalidPlatformFileValue)
+ return false;
+
+ FILETIME last_access_filetime = last_access_time.ToFileTime();
+ FILETIME last_modified_filetime = last_modified_time.ToFileTime();
+ return (::SetFileTime(file, NULL, &last_access_filetime,
+ &last_modified_filetime) != 0);
+}
+
+bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (!info)
+ return false;
+
+ BY_HANDLE_FILE_INFORMATION file_info;
+ if (GetFileInformationByHandle(file, &file_info) == 0)
+ return false;
+
+ LARGE_INTEGER size;
+ size.HighPart = file_info.nFileSizeHigh;
+ size.LowPart = file_info.nFileSizeLow;
+ info->size = size.QuadPart;
+ info->is_directory =
+ (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ info->is_symbolic_link = false; // Windows doesn't have symbolic links.
+ info->last_modified = base::Time::FromFileTime(file_info.ftLastWriteTime);
+ info->last_accessed = base::Time::FromFileTime(file_info.ftLastAccessTime);
+ info->creation_time = base::Time::FromFileTime(file_info.ftCreationTime);
+ return true;
+}
+
+} // namespace base
diff --git a/src/base/port.h b/src/base/port.h
new file mode 100644
index 0000000..af4e450
--- /dev/null
+++ b/src/base/port.h
@@ -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.
+
+#ifndef BASE_PORT_H_
+#define BASE_PORT_H_
+
+#include <stdarg.h>
+#include "build/build_config.h"
+
+#ifdef COMPILER_MSVC
+#define GG_LONGLONG(x) x##I64
+#define GG_ULONGLONG(x) x##UI64
+#else
+#define GG_LONGLONG(x) x##LL
+#define GG_ULONGLONG(x) x##ULL
+#endif
+
+// Per C99 7.8.14, define __STDC_CONSTANT_MACROS before including <stdint.h>
+// to get the INTn_C and UINTn_C macros for integer constants. It's difficult
+// to guarantee any specific ordering of header includes, so it's difficult to
+// guarantee that the INTn_C macros can be defined by including <stdint.h> at
+// any specific point. Provide GG_INTn_C macros instead.
+
+#define GG_INT8_C(x) (x)
+#define GG_INT16_C(x) (x)
+#define GG_INT32_C(x) (x)
+#define GG_INT64_C(x) GG_LONGLONG(x)
+
+#define GG_UINT8_C(x) (x ## U)
+#define GG_UINT16_C(x) (x ## U)
+#define GG_UINT32_C(x) (x ## U)
+#define GG_UINT64_C(x) GG_ULONGLONG(x)
+
+// It's possible for functions that use a va_list, such as StringPrintf, to
+// invalidate the data in it upon use. The fix is to make a copy of the
+// structure before using it and use that copy instead. va_copy is provided
+// for this purpose. MSVC does not provide va_copy, so define an
+// implementation here. It is not guaranteed that assignment is a copy, so the
+// StringUtil.VariableArgsFunc unit test tests this capability.
+#if defined(COMPILER_GCC)
+#define GG_VA_COPY(a, b) (va_copy(a, b))
+#elif defined(COMPILER_MSVC)
+#define GG_VA_COPY(a, b) (a = b)
+#endif
+
+// Define an OS-neutral wrapper for shared library entry points
+#if defined(OS_WIN)
+#define API_CALL __stdcall
+#else
+#define API_CALL
+#endif
+
+#endif // BASE_PORT_H_
diff --git a/src/base/posix/eintr_wrapper.h b/src/base/posix/eintr_wrapper.h
new file mode 100644
index 0000000..689e74e
--- /dev/null
+++ b/src/base/posix/eintr_wrapper.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2012 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_POSIX_EINTR_WRAPPER_H_
+#define BASE_POSIX_EINTR_WRAPPER_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_POSIX) && !defined(COBALT_WIN)
+
+#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_POSIX_EINTR_WRAPPER_H_
diff --git a/src/base/posix/file_descriptor_shuffle.cc b/src/base/posix/file_descriptor_shuffle.cc
new file mode 100644
index 0000000..b5b7339
--- /dev/null
+++ b/src/base/posix/file_descriptor_shuffle.cc
@@ -0,0 +1,96 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/posix/file_descriptor_shuffle.h"
+
+#include <unistd.h>
+#include <stddef.h>
+#include <ostream>
+
+#include "base/posix/eintr_wrapper.h"
+#include "base/logging.h"
+
+namespace base {
+
+bool PerformInjectiveMultimapDestructive(
+ InjectiveMultimap* m, InjectionDelegate* delegate) {
+ static const size_t kMaxExtraFDs = 16;
+ int extra_fds[kMaxExtraFDs];
+ unsigned next_extra_fd = 0;
+
+ // DANGER: this function may not allocate.
+
+ for (InjectiveMultimap::iterator i = m->begin(); i != m->end(); ++i) {
+ int temp_fd = -1;
+
+ // We DCHECK the injectiveness of the mapping.
+ for (InjectiveMultimap::iterator j = i + 1; j != m->end(); ++j) {
+ DCHECK(i->dest != j->dest) << "Both fd " << i->source
+ << " and " << j->source << " map to " << i->dest;
+ }
+
+ const bool is_identity = i->source == i->dest;
+
+ for (InjectiveMultimap::iterator j = i + 1; j != m->end(); ++j) {
+ if (!is_identity && i->dest == j->source) {
+ if (temp_fd == -1) {
+ if (!delegate->Duplicate(&temp_fd, i->dest))
+ return false;
+ if (next_extra_fd < kMaxExtraFDs) {
+ extra_fds[next_extra_fd++] = temp_fd;
+ } else {
+ RAW_LOG(ERROR, "PerformInjectiveMultimapDestructive overflowed "
+ "extra_fds. Leaking file descriptors!");
+ }
+ }
+
+ j->source = temp_fd;
+ j->close = false;
+ }
+
+ if (i->close && i->source == j->dest)
+ i->close = false;
+
+ if (i->close && i->source == j->source) {
+ i->close = false;
+ j->close = true;
+ }
+ }
+
+ if (!is_identity) {
+ if (!delegate->Move(i->source, i->dest))
+ return false;
+ }
+
+ if (!is_identity && i->close)
+ delegate->Close(i->source);
+ }
+
+ for (unsigned i = 0; i < next_extra_fd; i++)
+ delegate->Close(extra_fds[i]);
+
+ return true;
+}
+
+bool PerformInjectiveMultimap(const InjectiveMultimap& m_in,
+ InjectionDelegate* delegate) {
+ InjectiveMultimap m(m_in);
+ return PerformInjectiveMultimapDestructive(&m, delegate);
+}
+
+bool FileDescriptorTableInjection::Duplicate(int* result, int fd) {
+ *result = HANDLE_EINTR(dup(fd));
+ return *result >= 0;
+}
+
+bool FileDescriptorTableInjection::Move(int src, int dest) {
+ return HANDLE_EINTR(dup2(src, dest)) != -1;
+}
+
+void FileDescriptorTableInjection::Close(int fd) {
+ int ret = HANDLE_EINTR(close(fd));
+ DPCHECK(ret == 0);
+}
+
+} // namespace base
diff --git a/src/base/posix/file_descriptor_shuffle.h b/src/base/posix/file_descriptor_shuffle.h
new file mode 100644
index 0000000..6888c3e
--- /dev/null
+++ b/src/base/posix/file_descriptor_shuffle.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_POSIX_FILE_DESCRIPTOR_SHUFFLE_H_
+#define BASE_POSIX_FILE_DESCRIPTOR_SHUFFLE_H_
+
+// This code exists to perform the shuffling of file descriptors which is
+// commonly needed when forking subprocesses. The naive approve is very simple,
+// just call dup2 to setup the desired descriptors, but wrong. It's tough to
+// handle the edge cases (like mapping 0 -> 1, 1 -> 0) correctly.
+//
+// In order to unittest this code, it's broken into the abstract action (an
+// injective multimap) and the concrete code for dealing with file descriptors.
+// Users should use the code like this:
+// base::InjectiveMultimap file_descriptor_map;
+// file_descriptor_map.push_back(base::InjectionArc(devnull, 0, true));
+// file_descriptor_map.push_back(base::InjectionArc(devnull, 2, true));
+// file_descriptor_map.push_back(base::InjectionArc(pipe[1], 1, true));
+// base::ShuffleFileDescriptors(file_descriptor_map);
+//
+// and trust the the Right Thing will get done.
+
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+
+namespace base {
+
+// A Delegate which performs the actions required to perform an injective
+// multimapping in place.
+class InjectionDelegate {
+ public:
+ // Duplicate |fd|, an element of the domain, and write a fresh element of the
+ // domain into |result|. Returns true iff successful.
+ virtual bool Duplicate(int* result, int fd) = 0;
+ // Destructively move |src| to |dest|, overwriting |dest|. Returns true iff
+ // successful.
+ virtual bool Move(int src, int dest) = 0;
+ // Delete an element of the domain.
+ virtual void Close(int fd) = 0;
+
+ protected:
+ virtual ~InjectionDelegate() {}
+};
+
+// An implementation of the InjectionDelegate interface using the file
+// descriptor table of the current process as the domain.
+class FileDescriptorTableInjection : public InjectionDelegate {
+ virtual bool Duplicate(int* result, int fd) OVERRIDE;
+ virtual bool Move(int src, int dest) OVERRIDE;
+ virtual void Close(int fd) OVERRIDE;
+};
+
+// A single arc of the directed graph which describes an injective multimapping.
+struct InjectionArc {
+ InjectionArc(int in_source, int in_dest, bool in_close)
+ : source(in_source),
+ dest(in_dest),
+ close(in_close) {
+ }
+
+ int source;
+ int dest;
+ bool close; // if true, delete the source element after performing the
+ // mapping.
+};
+
+typedef std::vector<InjectionArc> InjectiveMultimap;
+
+BASE_EXPORT bool PerformInjectiveMultimap(const InjectiveMultimap& map,
+ InjectionDelegate* delegate);
+
+BASE_EXPORT bool PerformInjectiveMultimapDestructive(
+ InjectiveMultimap* map,
+ InjectionDelegate* delegate);
+
+// This function will not call malloc but will mutate |map|
+static inline bool ShuffleFileDescriptors(InjectiveMultimap* map) {
+ FileDescriptorTableInjection delegate;
+ return PerformInjectiveMultimapDestructive(map, &delegate);
+}
+
+} // namespace base
+
+#endif // BASE_POSIX_FILE_DESCRIPTOR_SHUFFLE_H_
diff --git a/src/base/posix/file_descriptor_shuffle_unittest.cc b/src/base/posix/file_descriptor_shuffle_unittest.cc
new file mode 100644
index 0000000..9e1b250
--- /dev/null
+++ b/src/base/posix/file_descriptor_shuffle_unittest.cc
@@ -0,0 +1,287 @@
+// Copyright (c) 2012 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/posix/file_descriptor_shuffle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// 'Duplicated' file descriptors start at this number
+const int kDuplicateBase = 1000;
+
+} // namespace
+
+namespace base {
+
+struct Action {
+ enum Type {
+ CLOSE,
+ MOVE,
+ DUPLICATE,
+ };
+
+ Action(Type in_type, int in_fd1, int in_fd2 = -1)
+ : type(in_type),
+ fd1(in_fd1),
+ fd2(in_fd2) {
+ }
+
+ bool operator==(const Action& other) const {
+ return other.type == type &&
+ other.fd1 == fd1 &&
+ other.fd2 == fd2;
+ }
+
+ Type type;
+ int fd1;
+ int fd2;
+};
+
+class InjectionTracer : public InjectionDelegate {
+ public:
+ InjectionTracer()
+ : next_duplicate_(kDuplicateBase) {
+ }
+
+ virtual bool Duplicate(int* result, int fd) OVERRIDE {
+ *result = next_duplicate_++;
+ actions_.push_back(Action(Action::DUPLICATE, *result, fd));
+ return true;
+ }
+
+ virtual bool Move(int src, int dest) OVERRIDE {
+ actions_.push_back(Action(Action::MOVE, src, dest));
+ return true;
+ }
+
+ virtual void Close(int fd) OVERRIDE {
+ actions_.push_back(Action(Action::CLOSE, fd));
+ }
+
+ const std::vector<Action>& actions() const { return actions_; }
+
+ private:
+ int next_duplicate_;
+ std::vector<Action> actions_;
+};
+
+TEST(FileDescriptorShuffleTest, Empty) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ EXPECT_EQ(0u, tracer.actions().size());
+}
+
+TEST(FileDescriptorShuffleTest, Noop) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 0, false));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ EXPECT_EQ(0u, tracer.actions().size());
+}
+
+TEST(FileDescriptorShuffleTest, NoopAndClose) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 0, true));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ EXPECT_EQ(0u, tracer.actions().size());
+}
+
+TEST(FileDescriptorShuffleTest, Simple1) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, false));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ ASSERT_EQ(1u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
+}
+
+TEST(FileDescriptorShuffleTest, Simple2) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, false));
+ map.push_back(InjectionArc(2, 3, false));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ ASSERT_EQ(2u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 2, 3));
+}
+
+TEST(FileDescriptorShuffleTest, Simple3) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, true));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ ASSERT_EQ(2u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::CLOSE, 0));
+}
+
+TEST(FileDescriptorShuffleTest, Simple4) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(10, 0, true));
+ map.push_back(InjectionArc(1, 1, true));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ ASSERT_EQ(2u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 10, 0));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::CLOSE, 10));
+}
+
+TEST(FileDescriptorShuffleTest, Cycle) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, false));
+ map.push_back(InjectionArc(1, 0, false));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ ASSERT_EQ(4u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] ==
+ Action(Action::DUPLICATE, kDuplicateBase, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0));
+ EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase));
+}
+
+TEST(FileDescriptorShuffleTest, CycleAndClose1) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, true));
+ map.push_back(InjectionArc(1, 0, false));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ ASSERT_EQ(4u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] ==
+ Action(Action::DUPLICATE, kDuplicateBase, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0));
+ EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase));
+}
+
+TEST(FileDescriptorShuffleTest, CycleAndClose2) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, false));
+ map.push_back(InjectionArc(1, 0, true));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ ASSERT_EQ(4u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] ==
+ Action(Action::DUPLICATE, kDuplicateBase, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0));
+ EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase));
+}
+
+TEST(FileDescriptorShuffleTest, CycleAndClose3) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, true));
+ map.push_back(InjectionArc(1, 0, true));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ ASSERT_EQ(4u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] ==
+ Action(Action::DUPLICATE, kDuplicateBase, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0));
+ EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase));
+}
+
+TEST(FileDescriptorShuffleTest, Fanout) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, false));
+ map.push_back(InjectionArc(0, 2, false));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ ASSERT_EQ(2u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2));
+}
+
+TEST(FileDescriptorShuffleTest, FanoutAndClose1) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, true));
+ map.push_back(InjectionArc(0, 2, false));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ ASSERT_EQ(3u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2));
+ EXPECT_TRUE(tracer.actions()[2] == Action(Action::CLOSE, 0));
+}
+
+TEST(FileDescriptorShuffleTest, FanoutAndClose2) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, false));
+ map.push_back(InjectionArc(0, 2, true));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ ASSERT_EQ(3u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2));
+ EXPECT_TRUE(tracer.actions()[2] == Action(Action::CLOSE, 0));
+}
+
+TEST(FileDescriptorShuffleTest, FanoutAndClose3) {
+ InjectiveMultimap map;
+ InjectionTracer tracer;
+ map.push_back(InjectionArc(0, 1, true));
+ map.push_back(InjectionArc(0, 2, true));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
+ ASSERT_EQ(3u, tracer.actions().size());
+ EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
+ EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2));
+ EXPECT_TRUE(tracer.actions()[2] == Action(Action::CLOSE, 0));
+}
+
+class FailingDelegate : public InjectionDelegate {
+ public:
+ virtual bool Duplicate(int* result, int fd) OVERRIDE {
+ return false;
+ }
+
+ virtual bool Move(int src, int dest) OVERRIDE {
+ return false;
+ }
+
+ virtual void Close(int fd) OVERRIDE {}
+};
+
+TEST(FileDescriptorShuffleTest, EmptyWithFailure) {
+ InjectiveMultimap map;
+ FailingDelegate failing;
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &failing));
+}
+
+TEST(FileDescriptorShuffleTest, NoopWithFailure) {
+ InjectiveMultimap map;
+ FailingDelegate failing;
+ map.push_back(InjectionArc(0, 0, false));
+
+ EXPECT_TRUE(PerformInjectiveMultimap(map, &failing));
+}
+
+TEST(FileDescriptorShuffleTest, Simple1WithFailure) {
+ InjectiveMultimap map;
+ FailingDelegate failing;
+ map.push_back(InjectionArc(0, 1, false));
+
+ EXPECT_FALSE(PerformInjectiveMultimap(map, &failing));
+}
+
+} // namespace base
diff --git a/src/base/posix/global_descriptors.cc b/src/base/posix/global_descriptors.cc
new file mode 100644
index 0000000..bcca443
--- /dev/null
+++ b/src/base/posix/global_descriptors.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 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/posix/global_descriptors.h"
+
+#include <vector>
+#include <utility>
+
+#include "base/logging.h"
+
+namespace base {
+
+// static
+GlobalDescriptors* GlobalDescriptors::GetInstance() {
+ typedef Singleton<base::GlobalDescriptors,
+ LeakySingletonTraits<base::GlobalDescriptors> >
+ GlobalDescriptorsSingleton;
+ return GlobalDescriptorsSingleton::get();
+}
+
+int GlobalDescriptors::Get(Key key) const {
+ const int ret = MaybeGet(key);
+
+ if (ret == -1)
+ DLOG(FATAL) << "Unknown global descriptor: " << key;
+ return ret;
+}
+
+int GlobalDescriptors::MaybeGet(Key key) const {
+ for (Mapping::const_iterator
+ i = descriptors_.begin(); i != descriptors_.end(); ++i) {
+ if (i->first == key)
+ return i->second;
+ }
+
+ return -1;
+}
+
+void GlobalDescriptors::Set(Key key, int fd) {
+ for (Mapping::iterator
+ i = descriptors_.begin(); i != descriptors_.end(); ++i) {
+ if (i->first == key) {
+ i->second = fd;
+ return;
+ }
+ }
+
+ descriptors_.push_back(std::make_pair(key, fd));
+}
+
+void GlobalDescriptors::Reset(const Mapping& mapping) {
+ descriptors_ = mapping;
+}
+
+GlobalDescriptors::GlobalDescriptors() {}
+
+GlobalDescriptors::~GlobalDescriptors() {}
+
+} // namespace base
diff --git a/src/base/posix/global_descriptors.h b/src/base/posix/global_descriptors.h
new file mode 100644
index 0000000..c7b9f87
--- /dev/null
+++ b/src/base/posix/global_descriptors.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2012 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_POSIX_GLOBAL_DESCRIPTORS_H_
+#define BASE_POSIX_GLOBAL_DESCRIPTORS_H_
+
+#include "build/build_config.h"
+
+#include <vector>
+#include <utility>
+
+#include <stdint.h>
+
+#include "base/memory/singleton.h"
+
+namespace base {
+
+// It's common practice to install file descriptors into well known slot
+// numbers before execing a child; stdin, stdout and stderr are ubiqutous
+// examples.
+//
+// However, when using a zygote model, this becomes troublesome. Since the
+// descriptors which need to be in these slots generally aren't known, any code
+// could open a resource and take one of the reserved descriptors. Simply
+// overwriting the slot isn't a viable solution.
+//
+// We could try to fill the reserved slots as soon as possible, but this is a
+// fragile solution since global constructors etc are able to open files.
+//
+// Instead, we retreat from the idea of installing descriptors in specific
+// slots and add a layer of indirection in the form of this singleton object.
+// It maps from an abstract key to a descriptor. If independent modules each
+// need to define keys, then values should be chosen randomly so as not to
+// collide.
+class BASE_EXPORT GlobalDescriptors {
+ public:
+ typedef uint32_t Key;
+ typedef std::pair<Key, int> KeyFDPair;
+ typedef std::vector<KeyFDPair> Mapping;
+
+ // Often we want a canonical descriptor for a given Key. In this case, we add
+ // the following constant to the key value:
+ static const int kBaseDescriptor = 3; // 0, 1, 2 are already taken.
+
+ // Return the singleton instance of GlobalDescriptors.
+ static GlobalDescriptors* GetInstance();
+
+ // Get a descriptor given a key. It is a fatal error if the key is not known.
+ int Get(Key key) const;
+
+ // Get a descriptor give a key. Returns -1 on error.
+ int MaybeGet(Key key) const;
+
+ // Set the descriptor for the given key.
+ void Set(Key key, int fd);
+
+ void Reset(const Mapping& mapping);
+
+ private:
+ friend struct DefaultSingletonTraits<GlobalDescriptors>;
+ GlobalDescriptors();
+ ~GlobalDescriptors();
+
+ Mapping descriptors_;
+};
+
+} // namespace base
+
+#endif // BASE_POSIX_GLOBAL_DESCRIPTORS_H_
diff --git a/src/base/posix/unix_domain_socket.cc b/src/base/posix/unix_domain_socket.cc
new file mode 100644
index 0000000..5bfcef1
--- /dev/null
+++ b/src/base/posix/unix_domain_socket.cc
@@ -0,0 +1,153 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/posix/unix_domain_socket.h"
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+
+#include "base/logging.h"
+#include "base/pickle.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/stl_util.h"
+
+const size_t UnixDomainSocket::kMaxFileDescriptors = 16;
+
+// static
+bool UnixDomainSocket::SendMsg(int fd,
+ const void* buf,
+ size_t length,
+ const std::vector<int>& fds) {
+ struct msghdr msg;
+ memset(&msg, 0, sizeof(msg));
+ struct iovec iov = {const_cast<void*>(buf), length};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ char* control_buffer = NULL;
+ if (fds.size()) {
+ const unsigned control_len = CMSG_SPACE(sizeof(int) * fds.size());
+ control_buffer = new char[control_len];
+
+ struct cmsghdr *cmsg;
+ msg.msg_control = control_buffer;
+ msg.msg_controllen = control_len;
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fds.size());
+ memcpy(CMSG_DATA(cmsg), &fds[0], sizeof(int) * fds.size());
+ msg.msg_controllen = cmsg->cmsg_len;
+ }
+
+ // When available, take advantage of MSG_NOSIGNAL to avoid
+ // a SIGPIPE if the other end breaks the connection.
+#if defined(MSG_NOSIGNAL)
+ const int flags = MSG_NOSIGNAL;
+#else
+ const int flags = 0;
+#endif
+ const ssize_t r = HANDLE_EINTR(sendmsg(fd, &msg, flags));
+ const bool ret = static_cast<ssize_t>(length) == r;
+ delete[] control_buffer;
+ return ret;
+}
+
+// static
+ssize_t UnixDomainSocket::RecvMsg(int fd,
+ void* buf,
+ size_t length,
+ std::vector<int>* fds) {
+ fds->clear();
+
+ struct msghdr msg;
+ memset(&msg, 0, sizeof(msg));
+ struct iovec iov = {buf, length};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ char control_buffer[CMSG_SPACE(sizeof(int) * kMaxFileDescriptors)];
+ msg.msg_control = control_buffer;
+ msg.msg_controllen = sizeof(control_buffer);
+
+ const ssize_t r = HANDLE_EINTR(recvmsg(fd, &msg, 0));
+ if (r == -1)
+ return -1;
+
+ int* wire_fds = NULL;
+ unsigned wire_fds_len = 0;
+
+ if (msg.msg_controllen > 0) {
+ struct cmsghdr* cmsg;
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
+ const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
+ DCHECK(payload_len % sizeof(int) == 0);
+ wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ wire_fds_len = payload_len / sizeof(int);
+ break;
+ }
+ }
+ }
+
+ if (msg.msg_flags & MSG_TRUNC || msg.msg_flags & MSG_CTRUNC) {
+ for (unsigned i = 0; i < wire_fds_len; ++i)
+ close(wire_fds[i]);
+ errno = EMSGSIZE;
+ return -1;
+ }
+
+ fds->resize(wire_fds_len);
+ memcpy(vector_as_array(fds), wire_fds, sizeof(int) * wire_fds_len);
+
+ return r;
+}
+
+// static
+ssize_t UnixDomainSocket::SendRecvMsg(int fd,
+ uint8_t* reply,
+ unsigned max_reply_len,
+ int* result_fd,
+ const Pickle& request) {
+ int fds[2];
+
+ // This socketpair is only used for the IPC and is cleaned up before
+ // returning.
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) == -1)
+ return -1;
+
+ std::vector<int> fd_vector;
+ fd_vector.push_back(fds[1]);
+ if (!SendMsg(fd, request.data(), request.size(), fd_vector)) {
+ close(fds[0]);
+ close(fds[1]);
+ return -1;
+ }
+ close(fds[1]);
+
+ fd_vector.clear();
+ const ssize_t reply_len = RecvMsg(fds[0], reply, max_reply_len, &fd_vector);
+ close(fds[0]);
+ if (reply_len == -1)
+ return -1;
+
+ if ((!fd_vector.empty() && result_fd == NULL) || fd_vector.size() > 1) {
+ for (std::vector<int>::const_iterator
+ i = fd_vector.begin(); i != fd_vector.end(); ++i) {
+ close(*i);
+ }
+
+ NOTREACHED();
+
+ return -1;
+ }
+
+ if (result_fd)
+ *result_fd = fd_vector.empty() ? -1 : fd_vector[0];
+
+ return reply_len;
+}
diff --git a/src/base/posix/unix_domain_socket.h b/src/base/posix/unix_domain_socket.h
new file mode 100644
index 0000000..cb2a0b8
--- /dev/null
+++ b/src/base/posix/unix_domain_socket.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_POSIX_UNIX_DOMAIN_SOCKET_H_
+#define BASE_POSIX_UNIX_DOMAIN_SOCKET_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <vector>
+
+#include "base/base_export.h"
+
+class Pickle;
+
+class BASE_EXPORT UnixDomainSocket {
+ public:
+ // Maximum number of file descriptors that can be read by RecvMsg().
+ static const size_t kMaxFileDescriptors;
+
+ // Use sendmsg to write the given msg and include a vector of file
+ // descriptors. Returns true if successful.
+ static bool SendMsg(int fd,
+ const void* msg,
+ size_t length,
+ const std::vector<int>& fds);
+
+ // Use recvmsg to read a message and an array of file descriptors. Returns
+ // -1 on failure. Note: will read, at most, |kMaxFileDescriptors| descriptors.
+ static ssize_t RecvMsg(int fd,
+ void* msg,
+ size_t length,
+ std::vector<int>* fds);
+
+ // Perform a sendmsg/recvmsg pair.
+ // 1. This process creates a UNIX DGRAM socketpair.
+ // 2. This proces writes a request to |fd| with an SCM_RIGHTS control
+ // message containing on end of the fresh socket pair.
+ // 3. This process blocks reading from the other end of the fresh
+ // socketpair.
+ // 4. The target process receives the request, processes it and writes the
+ // reply to the end of the socketpair contained in the request.
+ // 5. This process wakes up and continues.
+ //
+ // fd: descriptor to send the request on
+ // reply: buffer for the reply
+ // reply_len: size of |reply|
+ // result_fd: (may be NULL) the file descriptor returned in the reply
+ // (if any)
+ // request: the bytes to send in the request
+ static ssize_t SendRecvMsg(int fd,
+ uint8_t* reply,
+ unsigned reply_len,
+ int* result_fd,
+ const Pickle& request);
+};
+
+#endif // BASE_POSIX_UNIX_DOMAIN_SOCKET_POSIX_H_
diff --git a/src/base/pr_time_unittest.cc b/src/base/pr_time_unittest.cc
new file mode 100644
index 0000000..da1ab96
--- /dev/null
+++ b/src/base/pr_time_unittest.cc
@@ -0,0 +1,189 @@
+// Copyright (c) 2012 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"
+
+#if !defined(OS_STARBOARD)
+#include <time.h>
+#endif
+
+#include "base/compiler_specific.h"
+#include "base/test/time_helpers.h"
+#include "base/third_party/nspr/prtime.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::Time;
+
+namespace {
+
+// time_t representation of 15th Oct 2007 12:45:00 PDT
+PRTime comparison_time_pdt = 1192477500 * Time::kMicrosecondsPerSecond;
+
+// Specialized test fixture allowing time strings without timezones to be
+// tested by comparing them to a known time in the local zone.
+class PRTimeTest : public testing::Test {
+ // TODO: This test breaks the platform seal by including time.h, and
+ // using that to check the NS PRTime code against. Do something else instead
+ // that is more platform-abstracted.
+
+ protected:
+ virtual void SetUp() OVERRIDE {
+#if defined(OS_STARBOARD)
+ Time local_time = base::test::time_helpers::TestDateToTime(
+ base::test::time_helpers::kTimeZoneLocal);
+ comparison_time_local_ = (local_time - Time::UnixEpoch()).ToInternalValue();
+#else
+ // Use mktime to get a time_t, and turn it into a PRTime by converting
+ // seconds to microseconds. Use 15th Oct 2007 12:45:00 local. This
+ // must be a time guaranteed to be outside of a DST fallback hour in
+ // any timezone.
+ struct tm local_comparison_tm = {
+ 0, // second
+ 45, // minute
+ 12, // hour
+ 15, // day of month
+ 10 - 1, // month
+ 2007 - 1900, // year
+ 0, // day of week (ignored, output only)
+ 0, // day of year (ignored, output only)
+ -1 // DST in effect, -1 tells mktime to figure it out
+ };
+ comparison_time_local_ = mktime(&local_comparison_tm) *
+ Time::kMicrosecondsPerSecond;
+#endif
+ ASSERT_GT(comparison_time_local_, 0);
+ }
+
+ PRTime comparison_time_local_;
+};
+
+#if !defined(OS_STARBOARD)
+// More of the no local time on Starboard issue. We can't use these standard
+// functions to check NSPR Time against because they don't always work on all
+// platforms, making these tests inherently flaky and non-portable.
+
+// Tests the PR_ParseTimeString nspr helper function for
+// a variety of time strings.
+TEST_F(PRTimeTest, ParseTimeTest1) {
+ time_t current_time = 0;
+ time(¤t_time);
+
+ const int BUFFER_SIZE = 64;
+ struct tm local_time = {0};
+ char time_buf[BUFFER_SIZE] = {0};
+#if defined(OS_WIN)
+ localtime_s(&local_time, ¤t_time);
+ asctime_s(time_buf, arraysize(time_buf), &local_time);
+#elif defined(__STDC_LIB_EXT1__)
+ localtime_s(¤t_time, &local_time);
+ asctime_s(time_buf, arraysize(time_buf), &local_time);
+#elif defined(OS_POSIX)
+ localtime_r(¤t_time, &local_time);
+ asctime_r(&local_time, time_buf);
+#endif
+
+ PRTime current_time64 = static_cast<PRTime>(current_time) * PR_USEC_PER_SEC;
+
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString(time_buf, PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(current_time64, parsed_time);
+}
+#endif
+
+TEST_F(PRTimeTest, ParseTimeTest2) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("Mon, 15 Oct 2007 19:45:00 GMT",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(parsed_time, comparison_time_pdt);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest3) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("15 Oct 07 12:45:00", PR_FALSE,
+ &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(parsed_time, comparison_time_local_);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest4) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("15 Oct 07 19:45 GMT", PR_FALSE,
+ &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(parsed_time, comparison_time_pdt);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest5) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("Mon Oct 15 12:45 PDT 2007",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(parsed_time, comparison_time_pdt);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest6) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("Monday, Oct 15, 2007 12:45 PM",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(parsed_time, comparison_time_local_);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest7) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("10/15/07 12:45:00 PM", PR_FALSE,
+ &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(parsed_time, comparison_time_local_);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest8) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("15-OCT-2007 12:45pm", PR_FALSE,
+ &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(parsed_time, comparison_time_local_);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest9) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("16 Oct 2007 4:45-JST (Tuesday)",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(parsed_time, comparison_time_pdt);
+}
+
+// This test should not crash when compiled with Visual C++ 2005 (see
+// http://crbug.com/4387).
+TEST_F(PRTimeTest, ParseTimeTestOutOfRange) {
+ PRTime parsed_time = 0;
+ // Note the lack of timezone in the time string. The year has to be 3001.
+ // The date has to be after 23:59:59, December 31, 3000, US Pacific Time, so
+ // we use January 2, 3001 to make sure it's after the magic maximum in any
+ // timezone.
+ PRStatus result = PR_ParseTimeString("Sun Jan 2 00:00:00 3001",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+}
+
+TEST_F(PRTimeTest, ParseTimeTestNotNormalized1) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("Mon Oct 15 12:44:60 PDT 2007",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(comparison_time_pdt, parsed_time);
+}
+
+TEST_F(PRTimeTest, ParseTimeTestNotNormalized2) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("Sun Oct 14 36:45 PDT 2007",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(comparison_time_pdt, parsed_time);
+}
+
+} // namespace
diff --git a/src/base/prefs/README b/src/base/prefs/README
new file mode 100644
index 0000000..a49604e
--- /dev/null
+++ b/src/base/prefs/README
@@ -0,0 +1,10 @@
+Prefs is a general-purpose key-value store for application preferences.
+
+At the moment, src/base/prefs has a bunch of dependencies back to
+src/chrome, and thus should only be used by code under src/chrome.
+
+We are working to remove these dependencies; once we do so,
+src/base/prefs will be available as a separate module, 'base_prefs',
+from src/base/base.gyp.
+
+Until that happens, please do not try to reuse outside of chrome/.
diff --git a/src/base/prefs/base_prefs_export.h b/src/base/prefs/base_prefs_export.h
new file mode 100644
index 0000000..039e7a9
--- /dev/null
+++ b/src/base/prefs/base_prefs_export.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 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_PREFS_BASE_PREFS_EXPORT_H_
+#define BASE_PREFS_BASE_PREFS_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(_MSC_VER)
+
+#if defined(BASE_PREFS_IMPLEMENTATION)
+#define BASE_PREFS_EXPORT __declspec(dllexport)
+#else
+#define BASE_PREFS_EXPORT __declspec(dllimport)
+#endif // defined(BASE_PREFS_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(BASE_PREFS_IMPLEMENTATION)
+#define BASE_PREFS_EXPORT __attribute__((visibility("default")))
+#else
+#define BASE_PREFS_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define BASE_PREFS_EXPORT
+#endif
+
+#endif // BASE_PREFS_BASE_PREFS_EXPORT_H_
diff --git a/src/base/prefs/default_pref_store.cc b/src/base/prefs/default_pref_store.cc
new file mode 100644
index 0000000..8e2644c
--- /dev/null
+++ b/src/base/prefs/default_pref_store.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 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/prefs/default_pref_store.h"
+#include "base/logging.h"
+
+using base::Value;
+
+DefaultPrefStore::DefaultPrefStore() {}
+
+bool DefaultPrefStore::GetValue(
+ const std::string& key,
+ const base::Value** result) const {
+ return prefs_.GetValue(key, result);
+}
+
+void DefaultPrefStore::SetDefaultValue(const std::string& key, Value* value) {
+ DCHECK(!GetValue(key, NULL));
+ prefs_.SetValue(key, value);
+}
+
+void DefaultPrefStore::RemoveDefaultValue(const std::string& key) {
+ DCHECK(GetValue(key, NULL));
+ prefs_.RemoveValue(key);
+}
+
+base::Value::Type DefaultPrefStore::GetType(const std::string& key) const {
+ const Value* value = NULL;
+ return GetValue(key, &value) ? value->GetType() : Value::TYPE_NULL;
+}
+
+DefaultPrefStore::const_iterator DefaultPrefStore::begin() const {
+ return prefs_.begin();
+}
+
+DefaultPrefStore::const_iterator DefaultPrefStore::end() const {
+ return prefs_.end();
+}
+
+DefaultPrefStore::~DefaultPrefStore() {}
diff --git a/src/base/prefs/default_pref_store.h b/src/base/prefs/default_pref_store.h
new file mode 100644
index 0000000..996bf8b
--- /dev/null
+++ b/src/base/prefs/default_pref_store.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2012 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_PREFS_DEFAULT_PREF_STORE_H_
+#define BASE_PREFS_DEFAULT_PREF_STORE_H_
+
+#include <string>
+
+#include "base/prefs/base_prefs_export.h"
+#include "base/prefs/pref_store.h"
+#include "base/prefs/pref_value_map.h"
+#include "base/values.h"
+
+// This PrefStore keeps track of default preference values set when a
+// preference is registered with the PrefService.
+class BASE_PREFS_EXPORT DefaultPrefStore : public PrefStore {
+ public:
+ typedef PrefValueMap::const_iterator const_iterator;
+
+ DefaultPrefStore();
+
+ virtual bool GetValue(const std::string& key,
+ const base::Value** result) const OVERRIDE;
+
+ // Stores a new |value| for |key|. Assumes ownership of |value|.
+ void SetDefaultValue(const std::string& key, Value* value);
+
+ // Removes the value for |key|.
+ void RemoveDefaultValue(const std::string& key);
+
+ // Returns the registered type for |key| or Value::TYPE_NULL if the |key|
+ // has not been registered.
+ base::Value::Type GetType(const std::string& key) const;
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ protected:
+ virtual ~DefaultPrefStore();
+
+ private:
+ PrefValueMap prefs_;
+
+ DISALLOW_COPY_AND_ASSIGN(DefaultPrefStore);
+};
+
+#endif // BASE_PREFS_DEFAULT_PREF_STORE_H_
diff --git a/src/base/prefs/json_pref_store.cc b/src/base/prefs/json_pref_store.cc
new file mode 100644
index 0000000..0447975
--- /dev/null
+++ b/src/base/prefs/json_pref_store.cc
@@ -0,0 +1,357 @@
+// Copyright (c) 2012 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/prefs/json_pref_store.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/file_util.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop_proxy.h"
+#include "base/sequenced_task_runner.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "base/values.h"
+
+namespace {
+
+// Some extensions we'll tack on to copies of the Preferences files.
+const FilePath::CharType* kBadExtension = FILE_PATH_LITERAL("bad");
+
+// Differentiates file loading between origin thread and passed
+// (aka file) thread.
+class FileThreadDeserializer
+ : public base::RefCountedThreadSafe<FileThreadDeserializer> {
+ public:
+ FileThreadDeserializer(JsonPrefStore* delegate,
+ base::SequencedTaskRunner* sequenced_task_runner)
+ : no_dir_(false),
+ error_(PersistentPrefStore::PREF_READ_ERROR_NONE),
+ delegate_(delegate),
+ sequenced_task_runner_(sequenced_task_runner),
+ origin_loop_proxy_(base::MessageLoopProxy::current()) {
+ }
+
+ void Start(const FilePath& path) {
+ DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
+ sequenced_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&FileThreadDeserializer::ReadFileAndReport,
+ this, path));
+ }
+
+ // Deserializes JSON on the sequenced task runner.
+ void ReadFileAndReport(const FilePath& path) {
+ DCHECK(sequenced_task_runner_->RunsTasksOnCurrentThread());
+
+ value_.reset(DoReading(path, &error_, &no_dir_));
+
+ origin_loop_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&FileThreadDeserializer::ReportOnOriginThread, this));
+ }
+
+ // Reports deserialization result on the origin thread.
+ void ReportOnOriginThread() {
+ DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
+ delegate_->OnFileRead(value_.release(), error_, no_dir_);
+ }
+
+ static Value* DoReading(const FilePath& path,
+ PersistentPrefStore::PrefReadError* error,
+ bool* no_dir) {
+ int error_code;
+ std::string error_msg;
+ JSONFileValueSerializer serializer(path);
+ Value* value = serializer.Deserialize(&error_code, &error_msg);
+ HandleErrors(value, path, error_code, error_msg, error);
+ *no_dir = !file_util::PathExists(path.DirName());
+ return value;
+ }
+
+ static void HandleErrors(const Value* value,
+ const FilePath& path,
+ int error_code,
+ const std::string& error_msg,
+ PersistentPrefStore::PrefReadError* error);
+
+ private:
+ friend class base::RefCountedThreadSafe<FileThreadDeserializer>;
+ ~FileThreadDeserializer() {}
+
+ bool no_dir_;
+ PersistentPrefStore::PrefReadError error_;
+ scoped_ptr<Value> value_;
+ const scoped_refptr<JsonPrefStore> delegate_;
+ const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
+ const scoped_refptr<base::MessageLoopProxy> origin_loop_proxy_;
+};
+
+// static
+void FileThreadDeserializer::HandleErrors(
+ const Value* value,
+ const FilePath& path,
+ int error_code,
+ const std::string& error_msg,
+ PersistentPrefStore::PrefReadError* error) {
+ *error = PersistentPrefStore::PREF_READ_ERROR_NONE;
+ if (!value) {
+ DVLOG(1) << "Error while loading JSON file: " << error_msg
+ << ", file: " << path.value();
+ switch (error_code) {
+ case JSONFileValueSerializer::JSON_ACCESS_DENIED:
+ *error = PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED;
+ break;
+ case JSONFileValueSerializer::JSON_CANNOT_READ_FILE:
+ *error = PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER;
+ break;
+ case JSONFileValueSerializer::JSON_FILE_LOCKED:
+ *error = PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED;
+ break;
+ case JSONFileValueSerializer::JSON_NO_SUCH_FILE:
+ *error = PersistentPrefStore::PREF_READ_ERROR_NO_FILE;
+ break;
+ default:
+ *error = PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE;
+ // JSON errors indicate file corruption of some sort.
+ // Since the file is corrupt, move it to the side and continue with
+ // empty preferences. This will result in them losing their settings.
+ // We keep the old file for possible support and debugging assistance
+ // as well as to detect if they're seeing these errors repeatedly.
+ // TODO(erikkay) Instead, use the last known good file.
+ FilePath bad = path.ReplaceExtension(kBadExtension);
+
+ // If they've ever had a parse error before, put them in another bucket.
+ // TODO(erikkay) if we keep this error checking for very long, we may
+ // want to differentiate between recent and long ago errors.
+ if (file_util::PathExists(bad))
+ *error = PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT;
+ file_util::Move(path, bad);
+ break;
+ }
+ } else if (!value->IsType(Value::TYPE_DICTIONARY)) {
+ *error = PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE;
+ }
+}
+
+} // namespace
+
+scoped_refptr<base::SequencedTaskRunner> JsonPrefStore::GetTaskRunnerForFile(
+ const FilePath& filename,
+ base::SequencedWorkerPool* worker_pool) {
+ std::string token("json_pref_store-");
+ token.append(filename.AsUTF8Unsafe());
+ return worker_pool->GetSequencedTaskRunnerWithShutdownBehavior(
+ worker_pool->GetNamedSequenceToken(token),
+ base::SequencedWorkerPool::BLOCK_SHUTDOWN);
+}
+
+JsonPrefStore::JsonPrefStore(const FilePath& filename,
+ base::SequencedTaskRunner* sequenced_task_runner)
+ : path_(filename),
+ sequenced_task_runner_(sequenced_task_runner),
+ prefs_(new DictionaryValue()),
+ read_only_(false),
+ writer_(filename, sequenced_task_runner),
+ error_delegate_(NULL),
+ initialized_(false),
+ read_error_(PREF_READ_ERROR_OTHER) {
+}
+
+bool JsonPrefStore::GetValue(const std::string& key,
+ const Value** result) const {
+ Value* tmp = NULL;
+ if (!prefs_->Get(key, &tmp))
+ return false;
+
+ if (result)
+ *result = tmp;
+ return true;
+}
+
+void JsonPrefStore::AddObserver(PrefStore::Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void JsonPrefStore::RemoveObserver(PrefStore::Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+size_t JsonPrefStore::NumberOfObservers() const {
+ return observers_.size();
+}
+
+bool JsonPrefStore::IsInitializationComplete() const {
+ return initialized_;
+}
+
+bool JsonPrefStore::GetMutableValue(const std::string& key,
+ Value** result) {
+ return prefs_->Get(key, result);
+}
+
+void JsonPrefStore::SetValue(const std::string& key, Value* value) {
+ DCHECK(value);
+ scoped_ptr<Value> new_value(value);
+ Value* old_value = NULL;
+ prefs_->Get(key, &old_value);
+ if (!old_value || !value->Equals(old_value)) {
+ prefs_->Set(key, new_value.release());
+ ReportValueChanged(key);
+ }
+}
+
+void JsonPrefStore::SetValueSilently(const std::string& key, Value* value) {
+ DCHECK(value);
+ scoped_ptr<Value> new_value(value);
+ Value* old_value = NULL;
+ prefs_->Get(key, &old_value);
+ if (!old_value || !value->Equals(old_value)) {
+ prefs_->Set(key, new_value.release());
+ if (!read_only_)
+ writer_.ScheduleWrite(this);
+ }
+}
+
+void JsonPrefStore::RemoveValue(const std::string& key) {
+ if (prefs_->Remove(key, NULL))
+ ReportValueChanged(key);
+}
+
+void JsonPrefStore::MarkNeedsEmptyValue(const std::string& key) {
+ keys_need_empty_value_.insert(key);
+}
+
+bool JsonPrefStore::ReadOnly() const {
+ return read_only_;
+}
+
+PersistentPrefStore::PrefReadError JsonPrefStore::GetReadError() const {
+ return read_error_;
+}
+
+PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() {
+ if (path_.empty()) {
+ OnFileRead(NULL, PREF_READ_ERROR_FILE_NOT_SPECIFIED, false);
+ return PREF_READ_ERROR_FILE_NOT_SPECIFIED;
+ }
+
+ PrefReadError error;
+ bool no_dir;
+ Value* value = FileThreadDeserializer::DoReading(path_, &error, &no_dir);
+ OnFileRead(value, error, no_dir);
+ return error;
+}
+
+void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate *error_delegate) {
+ initialized_ = false;
+ error_delegate_.reset(error_delegate);
+ if (path_.empty()) {
+ OnFileRead(NULL, PREF_READ_ERROR_FILE_NOT_SPECIFIED, false);
+ return;
+ }
+
+ // Start async reading of the preferences file. It will delete itself
+ // in the end.
+ scoped_refptr<FileThreadDeserializer> deserializer(
+ new FileThreadDeserializer(this, sequenced_task_runner_.get()));
+ deserializer->Start(path_);
+}
+
+void JsonPrefStore::CommitPendingWrite() {
+ if (writer_.HasPendingWrite() && !read_only_)
+ writer_.DoScheduledWrite();
+}
+
+void JsonPrefStore::ReportValueChanged(const std::string& key) {
+ FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
+ if (!read_only_)
+ writer_.ScheduleWrite(this);
+}
+
+void JsonPrefStore::OnFileRead(Value* value_owned,
+ PersistentPrefStore::PrefReadError error,
+ bool no_dir) {
+ scoped_ptr<Value> value(value_owned);
+ read_error_ = error;
+
+ if (no_dir) {
+ FOR_EACH_OBSERVER(PrefStore::Observer,
+ observers_,
+ OnInitializationCompleted(false));
+ return;
+ }
+
+ initialized_ = true;
+
+ switch (error) {
+ case PREF_READ_ERROR_ACCESS_DENIED:
+ case PREF_READ_ERROR_FILE_OTHER:
+ case PREF_READ_ERROR_FILE_LOCKED:
+ case PREF_READ_ERROR_JSON_TYPE:
+ case PREF_READ_ERROR_FILE_NOT_SPECIFIED:
+ read_only_ = true;
+ break;
+ case PREF_READ_ERROR_NONE:
+ DCHECK(value.get());
+ prefs_.reset(static_cast<DictionaryValue*>(value.release()));
+ break;
+ case PREF_READ_ERROR_NO_FILE:
+ // If the file just doesn't exist, maybe this is first run. In any case
+ // there's no harm in writing out default prefs in this case.
+ break;
+ case PREF_READ_ERROR_JSON_PARSE:
+ case PREF_READ_ERROR_JSON_REPEAT:
+ break;
+ default:
+ NOTREACHED() << "Unknown error: " << error;
+ }
+
+ if (error_delegate_.get() && error != PREF_READ_ERROR_NONE)
+ error_delegate_->OnError(error);
+
+ FOR_EACH_OBSERVER(PrefStore::Observer,
+ observers_,
+ OnInitializationCompleted(true));
+}
+
+JsonPrefStore::~JsonPrefStore() {
+ CommitPendingWrite();
+}
+
+bool JsonPrefStore::SerializeData(std::string* output) {
+ // TODO(tc): Do we want to prune webkit preferences that match the default
+ // value?
+ JSONStringValueSerializer serializer(output);
+ serializer.set_pretty_print(true);
+ scoped_ptr<DictionaryValue> copy(prefs_->DeepCopyWithoutEmptyChildren());
+
+ // Iterates |keys_need_empty_value_| and if the key exists in |prefs_|,
+ // ensure its empty ListValue or DictonaryValue is preserved.
+ for (std::set<std::string>::const_iterator
+ it = keys_need_empty_value_.begin();
+ it != keys_need_empty_value_.end();
+ ++it) {
+ const std::string& key = *it;
+
+ base::Value* value = NULL;
+ if (!prefs_->Get(key, &value))
+ continue;
+
+ if (value->IsType(base::Value::TYPE_LIST)) {
+ const base::ListValue* list = NULL;
+ if (value->GetAsList(&list) && list->empty())
+ copy->Set(key, new base::ListValue);
+ } else if (value->IsType(base::Value::TYPE_DICTIONARY)) {
+ const base::DictionaryValue* dict = NULL;
+ if (value->GetAsDictionary(&dict) && dict->empty())
+ copy->Set(key, new base::DictionaryValue);
+ }
+ }
+
+ return serializer.Serialize(*(copy.get()));
+}
diff --git a/src/base/prefs/json_pref_store.h b/src/base/prefs/json_pref_store.h
new file mode 100644
index 0000000..49dd71a
--- /dev/null
+++ b/src/base/prefs/json_pref_store.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2012 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_PREFS_JSON_PREF_STORE_H_
+#define BASE_PREFS_JSON_PREF_STORE_H_
+
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/files/important_file_writer.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop_proxy.h"
+#include "base/observer_list.h"
+#include "base/prefs/base_prefs_export.h"
+#include "base/prefs/persistent_pref_store.h"
+
+namespace base {
+class DictionaryValue;
+class SequencedWorkerPool;
+class SequencedTaskRunner;
+class Value;
+}
+
+class FilePath;
+
+// A writable PrefStore implementation that is used for user preferences.
+class BASE_PREFS_EXPORT JsonPrefStore
+ : public PersistentPrefStore,
+ public base::ImportantFileWriter::DataSerializer {
+ public:
+ // Returns instance of SequencedTaskRunner which guarantees that file
+ // operations on the same file will be executed in sequenced order.
+ static scoped_refptr<base::SequencedTaskRunner> GetTaskRunnerForFile(
+ const FilePath& pref_filename,
+ base::SequencedWorkerPool* worker_pool);
+
+ // |sequenced_task_runner| is must be a shutdown-blocking task runner, ideally
+ // created by GetTaskRunnerForFile() method above.
+ JsonPrefStore(const FilePath& pref_filename,
+ base::SequencedTaskRunner* sequenced_task_runner);
+
+ // PrefStore overrides:
+ virtual bool GetValue(const std::string& key,
+ const base::Value** result) const OVERRIDE;
+ virtual void AddObserver(PrefStore::Observer* observer) OVERRIDE;
+ virtual void RemoveObserver(PrefStore::Observer* observer) OVERRIDE;
+ virtual size_t NumberOfObservers() const OVERRIDE;
+ virtual bool IsInitializationComplete() const OVERRIDE;
+
+ // PersistentPrefStore overrides:
+ virtual bool GetMutableValue(const std::string& key,
+ base::Value** result) OVERRIDE;
+ virtual void SetValue(const std::string& key, base::Value* value) OVERRIDE;
+ virtual void SetValueSilently(const std::string& key,
+ base::Value* value) OVERRIDE;
+ virtual void RemoveValue(const std::string& key) OVERRIDE;
+ virtual void MarkNeedsEmptyValue(const std::string& key) OVERRIDE;
+ virtual bool ReadOnly() const OVERRIDE;
+ virtual PrefReadError GetReadError() const OVERRIDE;
+ virtual PrefReadError ReadPrefs() OVERRIDE;
+ virtual void ReadPrefsAsync(ReadErrorDelegate* error_delegate) OVERRIDE;
+ virtual void CommitPendingWrite() OVERRIDE;
+ virtual void ReportValueChanged(const std::string& key) OVERRIDE;
+
+ // This method is called after JSON file has been read. Method takes
+ // ownership of the |value| pointer. Note, this method is used with
+ // asynchronous file reading, so class exposes it only for the internal needs.
+ // (read: do not call it manually).
+ void OnFileRead(base::Value* value_owned, PrefReadError error, bool no_dir);
+
+ private:
+ virtual ~JsonPrefStore();
+
+ // ImportantFileWriter::DataSerializer overrides:
+ virtual bool SerializeData(std::string* output) OVERRIDE;
+
+ FilePath path_;
+ const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
+
+ scoped_ptr<base::DictionaryValue> prefs_;
+
+ bool read_only_;
+
+ // Helper for safely writing pref data.
+ base::ImportantFileWriter writer_;
+
+ ObserverList<PrefStore::Observer, true> observers_;
+
+ scoped_ptr<ReadErrorDelegate> error_delegate_;
+
+ bool initialized_;
+ PrefReadError read_error_;
+
+ std::set<std::string> keys_need_empty_value_;
+
+ DISALLOW_COPY_AND_ASSIGN(JsonPrefStore);
+};
+
+#endif // BASE_PREFS_JSON_PREF_STORE_H_
diff --git a/src/base/prefs/json_pref_store_unittest.cc b/src/base/prefs/json_pref_store_unittest.cc
new file mode 100644
index 0000000..dc6f95e
--- /dev/null
+++ b/src/base/prefs/json_pref_store_unittest.cc
@@ -0,0 +1,300 @@
+// Copyright (c) 2012 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/prefs/json_pref_store.h"
+
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/path_service.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "base/threading/thread.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+const char kHomePage[] = "homepage";
+
+class MockPrefStoreObserver : public PrefStore::Observer {
+ public:
+ MOCK_METHOD1(OnPrefValueChanged, void (const std::string&));
+ MOCK_METHOD1(OnInitializationCompleted, void (bool));
+};
+
+class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate {
+ public:
+ MOCK_METHOD1(OnError, void(PersistentPrefStore::PrefReadError));
+};
+
+} // namespace
+
+class JsonPrefStoreTest : public testing::Test {
+ protected:
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+ ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir_));
+ data_dir_ = data_dir_.AppendASCII("base");
+ data_dir_ = data_dir_.AppendASCII("prefs");
+ data_dir_ = data_dir_.AppendASCII("test");
+ data_dir_ = data_dir_.AppendASCII("data");
+ data_dir_ = data_dir_.AppendASCII("pref_service");
+ LOG(WARNING) << data_dir_.value().c_str();
+ ASSERT_TRUE(file_util::PathExists(data_dir_));
+ }
+
+ // The path to temporary directory used to contain the test operations.
+ base::ScopedTempDir temp_dir_;
+ // The path to the directory where the test data is stored.
+ FilePath data_dir_;
+ // A message loop that we can use as the file thread message loop.
+ MessageLoop message_loop_;
+};
+
+// Test fallback behavior for a nonexistent file.
+TEST_F(JsonPrefStoreTest, NonExistentFile) {
+ FilePath bogus_input_file = data_dir_.AppendASCII("read.txt");
+ ASSERT_FALSE(file_util::PathExists(bogus_input_file));
+ scoped_refptr<JsonPrefStore> pref_store =
+ new JsonPrefStore(
+ bogus_input_file, message_loop_.message_loop_proxy());
+ EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
+ pref_store->ReadPrefs());
+ EXPECT_FALSE(pref_store->ReadOnly());
+}
+
+// Test fallback behavior for an invalid file.
+TEST_F(JsonPrefStoreTest, InvalidFile) {
+ FilePath invalid_file_original = data_dir_.AppendASCII("invalid.json");
+ FilePath invalid_file = temp_dir_.path().AppendASCII("invalid.json");
+ ASSERT_TRUE(file_util::CopyFile(invalid_file_original, invalid_file));
+ scoped_refptr<JsonPrefStore> pref_store =
+ new JsonPrefStore(
+ invalid_file, message_loop_.message_loop_proxy());
+ EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE,
+ pref_store->ReadPrefs());
+ EXPECT_FALSE(pref_store->ReadOnly());
+
+ // The file should have been moved aside.
+ EXPECT_FALSE(file_util::PathExists(invalid_file));
+ FilePath moved_aside = temp_dir_.path().AppendASCII("invalid.bad");
+ EXPECT_TRUE(file_util::PathExists(moved_aside));
+ EXPECT_TRUE(file_util::TextContentsEqual(invalid_file_original,
+ moved_aside));
+}
+
+// This function is used to avoid code duplication while testing synchronous and
+// asynchronous version of the JsonPrefStore loading.
+void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store,
+ const FilePath& output_file,
+ const FilePath& golden_output_file) {
+ const char kNewWindowsInTabs[] = "tabs.new_windows_in_tabs";
+ const char kMaxTabs[] = "tabs.max_tabs";
+ const char kLongIntPref[] = "long_int.pref";
+
+ std::string cnn("http://www.cnn.com");
+
+ const Value* actual;
+ EXPECT_TRUE(pref_store->GetValue(kHomePage, &actual));
+ std::string string_value;
+ EXPECT_TRUE(actual->GetAsString(&string_value));
+ EXPECT_EQ(cnn, string_value);
+
+ const char kSomeDirectory[] = "some_directory";
+
+ EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
+ FilePath::StringType path;
+ EXPECT_TRUE(actual->GetAsString(&path));
+ EXPECT_EQ(FilePath::StringType(FILE_PATH_LITERAL("/usr/local/")), path);
+ FilePath some_path(FILE_PATH_LITERAL("/usr/sbin/"));
+
+ pref_store->SetValue(kSomeDirectory, new StringValue(some_path.value()));
+ EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
+ EXPECT_TRUE(actual->GetAsString(&path));
+ EXPECT_EQ(some_path.value(), path);
+
+ // Test reading some other data types from sub-dictionaries.
+ EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
+ bool boolean = false;
+ EXPECT_TRUE(actual->GetAsBoolean(&boolean));
+ EXPECT_TRUE(boolean);
+
+ pref_store->SetValue(kNewWindowsInTabs, new FundamentalValue(false));
+ EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
+ EXPECT_TRUE(actual->GetAsBoolean(&boolean));
+ EXPECT_FALSE(boolean);
+
+ EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
+ int integer = 0;
+ EXPECT_TRUE(actual->GetAsInteger(&integer));
+ EXPECT_EQ(20, integer);
+ pref_store->SetValue(kMaxTabs, new FundamentalValue(10));
+ EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
+ EXPECT_TRUE(actual->GetAsInteger(&integer));
+ EXPECT_EQ(10, integer);
+
+ pref_store->SetValue(kLongIntPref,
+ new StringValue(base::Int64ToString(214748364842LL)));
+ EXPECT_TRUE(pref_store->GetValue(kLongIntPref, &actual));
+ EXPECT_TRUE(actual->GetAsString(&string_value));
+ int64 value;
+ base::StringToInt64(string_value, &value);
+ EXPECT_EQ(214748364842LL, value);
+
+ // Serialize and compare to expected output.
+ ASSERT_TRUE(file_util::PathExists(golden_output_file));
+ pref_store->CommitPendingWrite();
+ MessageLoop::current()->RunUntilIdle();
+ EXPECT_TRUE(file_util::TextContentsEqual(golden_output_file, output_file));
+ ASSERT_TRUE(file_util::Delete(output_file, false));
+}
+
+TEST_F(JsonPrefStoreTest, Basic) {
+ ASSERT_TRUE(file_util::CopyFile(data_dir_.AppendASCII("read.json"),
+ temp_dir_.path().AppendASCII("write.json")));
+
+ // Test that the persistent value can be loaded.
+ FilePath input_file = temp_dir_.path().AppendASCII("write.json");
+ ASSERT_TRUE(file_util::PathExists(input_file));
+ scoped_refptr<JsonPrefStore> pref_store =
+ new JsonPrefStore(
+ input_file, message_loop_.message_loop_proxy());
+ ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
+ ASSERT_FALSE(pref_store->ReadOnly());
+
+ // The JSON file looks like this:
+ // {
+ // "homepage": "http://www.cnn.com",
+ // "some_directory": "/usr/local/",
+ // "tabs": {
+ // "new_windows_in_tabs": true,
+ // "max_tabs": 20
+ // }
+ // }
+
+ RunBasicJsonPrefStoreTest(pref_store,
+ input_file,
+ data_dir_.AppendASCII("write.golden.json"));
+}
+
+TEST_F(JsonPrefStoreTest, BasicAsync) {
+ ASSERT_TRUE(file_util::CopyFile(data_dir_.AppendASCII("read.json"),
+ temp_dir_.path().AppendASCII("write.json")));
+
+ // Test that the persistent value can be loaded.
+ FilePath input_file = temp_dir_.path().AppendASCII("write.json");
+ ASSERT_TRUE(file_util::PathExists(input_file));
+ scoped_refptr<JsonPrefStore> pref_store =
+ new JsonPrefStore(
+ input_file, message_loop_.message_loop_proxy());
+
+ {
+ MockPrefStoreObserver mock_observer;
+ pref_store->AddObserver(&mock_observer);
+
+ MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
+ pref_store->ReadPrefsAsync(mock_error_delegate);
+
+ EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
+ EXPECT_CALL(*mock_error_delegate,
+ OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
+ message_loop_.RunUntilIdle();
+ pref_store->RemoveObserver(&mock_observer);
+
+ ASSERT_FALSE(pref_store->ReadOnly());
+ }
+
+ // The JSON file looks like this:
+ // {
+ // "homepage": "http://www.cnn.com",
+ // "some_directory": "/usr/local/",
+ // "tabs": {
+ // "new_windows_in_tabs": true,
+ // "max_tabs": 20
+ // }
+ // }
+
+ RunBasicJsonPrefStoreTest(pref_store,
+ input_file,
+ data_dir_.AppendASCII("write.golden.json"));
+}
+
+// Tests asynchronous reading of the file when there is no file.
+TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) {
+ FilePath bogus_input_file = data_dir_.AppendASCII("read.txt");
+ ASSERT_FALSE(file_util::PathExists(bogus_input_file));
+ scoped_refptr<JsonPrefStore> pref_store =
+ new JsonPrefStore(
+ bogus_input_file, message_loop_.message_loop_proxy());
+ MockPrefStoreObserver mock_observer;
+ pref_store->AddObserver(&mock_observer);
+
+ MockReadErrorDelegate *mock_error_delegate = new MockReadErrorDelegate;
+ pref_store->ReadPrefsAsync(mock_error_delegate);
+
+ EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
+ EXPECT_CALL(*mock_error_delegate,
+ OnError(PersistentPrefStore::PREF_READ_ERROR_NO_FILE)).Times(1);
+ message_loop_.RunUntilIdle();
+ pref_store->RemoveObserver(&mock_observer);
+
+ EXPECT_FALSE(pref_store->ReadOnly());
+}
+
+TEST_F(JsonPrefStoreTest, NeedsEmptyValue) {
+ FilePath pref_file = temp_dir_.path().AppendASCII("write.json");
+
+ ASSERT_TRUE(file_util::CopyFile(
+ data_dir_.AppendASCII("read.need_empty_value.json"),
+ pref_file));
+
+ // Test that the persistent value can be loaded.
+ ASSERT_TRUE(file_util::PathExists(pref_file));
+ scoped_refptr<JsonPrefStore> pref_store =
+ new JsonPrefStore(
+ pref_file, message_loop_.message_loop_proxy());
+ ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
+ ASSERT_FALSE(pref_store->ReadOnly());
+
+ // The JSON file looks like this:
+ // {
+ // "list": [ 1 ],
+ // "list_needs_empty_value": [ 2 ],
+ // "dict": {
+ // "dummy": true,
+ // },
+ // "dict_needs_empty_value": {
+ // "dummy": true,
+ // },
+ // }
+
+ // Set flag to preserve empty values for the following keys.
+ pref_store->MarkNeedsEmptyValue("list_needs_empty_value");
+ pref_store->MarkNeedsEmptyValue("dict_needs_empty_value");
+
+ // Set all keys to empty values.
+ pref_store->SetValue("list", new base::ListValue);
+ pref_store->SetValue("list_needs_empty_value", new base::ListValue);
+ pref_store->SetValue("dict", new base::DictionaryValue);
+ pref_store->SetValue("dict_needs_empty_value", new base::DictionaryValue);
+
+ // Write to file.
+ pref_store->CommitPendingWrite();
+ MessageLoop::current()->RunUntilIdle();
+
+ // Compare to expected output.
+ FilePath golden_output_file =
+ data_dir_.AppendASCII("write.golden.need_empty_value.json");
+ ASSERT_TRUE(file_util::PathExists(golden_output_file));
+ EXPECT_TRUE(file_util::TextContentsEqual(golden_output_file, pref_file));
+}
+
+} // namespace base
diff --git a/src/base/prefs/overlay_user_pref_store.cc b/src/base/prefs/overlay_user_pref_store.cc
new file mode 100644
index 0000000..3a74a5c
--- /dev/null
+++ b/src/base/prefs/overlay_user_pref_store.cc
@@ -0,0 +1,182 @@
+// Copyright (c) 2012 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/prefs/overlay_user_pref_store.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+
+OverlayUserPrefStore::OverlayUserPrefStore(
+ PersistentPrefStore* underlay)
+ : underlay_(underlay) {
+ underlay_->AddObserver(this);
+}
+
+bool OverlayUserPrefStore::IsSetInOverlay(const std::string& key) const {
+ return overlay_.GetValue(key, NULL);
+}
+
+void OverlayUserPrefStore::AddObserver(PrefStore::Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void OverlayUserPrefStore::RemoveObserver(PrefStore::Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+size_t OverlayUserPrefStore::NumberOfObservers() const {
+ return observers_.size();
+}
+
+bool OverlayUserPrefStore::IsInitializationComplete() const {
+ return underlay_->IsInitializationComplete();
+}
+
+bool OverlayUserPrefStore::GetValue(const std::string& key,
+ const Value** result) const {
+ // If the |key| shall NOT be stored in the overlay store, there must not
+ // be an entry.
+ DCHECK(ShallBeStoredInOverlay(key) || !overlay_.GetValue(key, NULL));
+
+ if (overlay_.GetValue(key, result))
+ return true;
+ return underlay_->GetValue(GetUnderlayKey(key), result);
+}
+
+bool OverlayUserPrefStore::GetMutableValue(const std::string& key,
+ Value** result) {
+ if (!ShallBeStoredInOverlay(key))
+ return underlay_->GetMutableValue(GetUnderlayKey(key), result);
+
+ if (overlay_.GetValue(key, result))
+ return true;
+
+ // Try to create copy of underlay if the overlay does not contain a value.
+ Value* underlay_value = NULL;
+ if (!underlay_->GetMutableValue(GetUnderlayKey(key), &underlay_value))
+ return false;
+
+ *result = underlay_value->DeepCopy();
+ overlay_.SetValue(key, *result);
+ return true;
+}
+
+void OverlayUserPrefStore::SetValue(const std::string& key,
+ Value* value) {
+ if (!ShallBeStoredInOverlay(key)) {
+ underlay_->SetValue(GetUnderlayKey(key), value);
+ return;
+ }
+
+ if (overlay_.SetValue(key, value))
+ ReportValueChanged(key);
+}
+
+void OverlayUserPrefStore::SetValueSilently(const std::string& key,
+ Value* value) {
+ if (!ShallBeStoredInOverlay(key)) {
+ underlay_->SetValueSilently(GetUnderlayKey(key), value);
+ return;
+ }
+
+ overlay_.SetValue(key, value);
+}
+
+void OverlayUserPrefStore::RemoveValue(const std::string& key) {
+ if (!ShallBeStoredInOverlay(key)) {
+ underlay_->RemoveValue(GetUnderlayKey(key));
+ return;
+ }
+
+ if (overlay_.RemoveValue(key))
+ ReportValueChanged(key);
+}
+
+void OverlayUserPrefStore::MarkNeedsEmptyValue(const std::string& key) {
+ if (!ShallBeStoredInOverlay(key))
+ underlay_->MarkNeedsEmptyValue(key);
+}
+
+bool OverlayUserPrefStore::ReadOnly() const {
+ return false;
+}
+
+PersistentPrefStore::PrefReadError OverlayUserPrefStore::GetReadError() const {
+ return PersistentPrefStore::PREF_READ_ERROR_NONE;
+}
+
+PersistentPrefStore::PrefReadError OverlayUserPrefStore::ReadPrefs() {
+ // We do not read intentionally.
+ OnInitializationCompleted(true);
+ return PersistentPrefStore::PREF_READ_ERROR_NONE;
+}
+
+void OverlayUserPrefStore::ReadPrefsAsync(
+ ReadErrorDelegate* error_delegate_raw) {
+ scoped_ptr<ReadErrorDelegate> error_delegate(error_delegate_raw);
+ // We do not read intentionally.
+ OnInitializationCompleted(true);
+}
+
+void OverlayUserPrefStore::CommitPendingWrite() {
+ underlay_->CommitPendingWrite();
+ // We do not write our content intentionally.
+}
+
+void OverlayUserPrefStore::ReportValueChanged(const std::string& key) {
+ FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
+}
+
+void OverlayUserPrefStore::OnPrefValueChanged(const std::string& key) {
+ if (!overlay_.GetValue(GetOverlayKey(key), NULL))
+ ReportValueChanged(GetOverlayKey(key));
+}
+
+void OverlayUserPrefStore::OnInitializationCompleted(bool succeeded) {
+ FOR_EACH_OBSERVER(PrefStore::Observer, observers_,
+ OnInitializationCompleted(succeeded));
+}
+
+void OverlayUserPrefStore::RegisterOverlayPref(const std::string& key) {
+ RegisterOverlayPref(key, key);
+}
+
+void OverlayUserPrefStore::RegisterOverlayPref(
+ const std::string& overlay_key,
+ const std::string& underlay_key) {
+ DCHECK(!overlay_key.empty()) << "Overlay key is empty";
+ DCHECK(overlay_to_underlay_names_map_.find(overlay_key) ==
+ overlay_to_underlay_names_map_.end()) <<
+ "Overlay key already registered";
+ DCHECK(!underlay_key.empty()) << "Underlay key is empty";
+ DCHECK(underlay_to_overlay_names_map_.find(underlay_key) ==
+ underlay_to_overlay_names_map_.end()) <<
+ "Underlay key already registered";
+ overlay_to_underlay_names_map_[overlay_key] = underlay_key;
+ underlay_to_overlay_names_map_[underlay_key] = overlay_key;
+}
+
+OverlayUserPrefStore::~OverlayUserPrefStore() {
+ underlay_->RemoveObserver(this);
+}
+
+const std::string& OverlayUserPrefStore::GetOverlayKey(
+ const std::string& underlay_key) const {
+ NamesMap::const_iterator i =
+ underlay_to_overlay_names_map_.find(underlay_key);
+ return i != underlay_to_overlay_names_map_.end() ? i->second : underlay_key;
+}
+
+const std::string& OverlayUserPrefStore::GetUnderlayKey(
+ const std::string& overlay_key) const {
+ NamesMap::const_iterator i =
+ overlay_to_underlay_names_map_.find(overlay_key);
+ return i != overlay_to_underlay_names_map_.end() ? i->second : overlay_key;
+}
+
+bool OverlayUserPrefStore::ShallBeStoredInOverlay(
+ const std::string& key) const {
+ return overlay_to_underlay_names_map_.find(key) !=
+ overlay_to_underlay_names_map_.end();
+}
diff --git a/src/base/prefs/overlay_user_pref_store.h b/src/base/prefs/overlay_user_pref_store.h
new file mode 100644
index 0000000..120d405
--- /dev/null
+++ b/src/base/prefs/overlay_user_pref_store.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2012 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_PREFS_OVERLAY_USER_PREF_STORE_H_
+#define BASE_PREFS_OVERLAY_USER_PREF_STORE_H_
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "base/prefs/base_prefs_export.h"
+#include "base/prefs/persistent_pref_store.h"
+#include "base/prefs/pref_value_map.h"
+
+// PersistentPrefStore that directs all write operations into an in-memory
+// PrefValueMap. Read operations are first answered by the PrefValueMap.
+// If the PrefValueMap does not contain a value for the requested key,
+// the look-up is passed on to an underlying PersistentPrefStore |underlay_|.
+class BASE_PREFS_EXPORT OverlayUserPrefStore : public PersistentPrefStore,
+ public PrefStore::Observer {
+ public:
+ explicit OverlayUserPrefStore(PersistentPrefStore* underlay);
+
+ // Returns true if a value has been set for the |key| in this
+ // OverlayUserPrefStore, i.e. if it potentially overrides a value
+ // from the |underlay_|.
+ virtual bool IsSetInOverlay(const std::string& key) const;
+
+ // Methods of PrefStore.
+ virtual void AddObserver(PrefStore::Observer* observer) OVERRIDE;
+ virtual void RemoveObserver(PrefStore::Observer* observer) OVERRIDE;
+ virtual size_t NumberOfObservers() const OVERRIDE;
+ virtual bool IsInitializationComplete() const OVERRIDE;
+ virtual bool GetValue(const std::string& key,
+ const base::Value** result) const OVERRIDE;
+
+ // Methods of PersistentPrefStore.
+ virtual bool GetMutableValue(const std::string& key,
+ base::Value** result) OVERRIDE;
+ virtual void SetValue(const std::string& key, base::Value* value) OVERRIDE;
+ virtual void SetValueSilently(const std::string& key,
+ base::Value* value) OVERRIDE;
+ virtual void RemoveValue(const std::string& key) OVERRIDE;
+ virtual void MarkNeedsEmptyValue(const std::string& key) OVERRIDE;
+ virtual bool ReadOnly() const OVERRIDE;
+ virtual PrefReadError GetReadError() const OVERRIDE;
+ virtual PrefReadError ReadPrefs() OVERRIDE;
+ virtual void ReadPrefsAsync(ReadErrorDelegate* delegate) OVERRIDE;
+ virtual void CommitPendingWrite() OVERRIDE;
+ virtual void ReportValueChanged(const std::string& key) OVERRIDE;
+
+ // Methods of PrefStore::Observer.
+ virtual void OnPrefValueChanged(const std::string& key) OVERRIDE;
+ virtual void OnInitializationCompleted(bool succeeded) OVERRIDE;
+
+ void RegisterOverlayPref(const std::string& key);
+ void RegisterOverlayPref(const std::string& overlay_key,
+ const std::string& underlay_key);
+
+ protected:
+ virtual ~OverlayUserPrefStore();
+
+ private:
+ typedef std::map<std::string, std::string> NamesMap;
+
+ const std::string& GetOverlayKey(const std::string& underlay_key) const;
+ const std::string& GetUnderlayKey(const std::string& overlay_key) const;
+
+ // Returns true if |key| corresponds to a preference that shall be stored in
+ // an in-memory PrefStore that is not persisted to disk.
+ bool ShallBeStoredInOverlay(const std::string& key) const;
+
+ ObserverList<PrefStore::Observer, true> observers_;
+ PrefValueMap overlay_;
+ scoped_refptr<PersistentPrefStore> underlay_;
+ NamesMap overlay_to_underlay_names_map_;
+ NamesMap underlay_to_overlay_names_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(OverlayUserPrefStore);
+};
+
+#endif // BASE_PREFS_OVERLAY_USER_PREF_STORE_H_
diff --git a/src/base/prefs/overlay_user_pref_store_unittest.cc b/src/base/prefs/overlay_user_pref_store_unittest.cc
new file mode 100644
index 0000000..c4e980b
--- /dev/null
+++ b/src/base/prefs/overlay_user_pref_store_unittest.cc
@@ -0,0 +1,278 @@
+// Copyright (c) 2012 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/prefs/overlay_user_pref_store.h"
+
+#include "base/prefs/pref_store_observer_mock.h"
+#include "base/prefs/testing_pref_store.h"
+#include "base/values.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::Mock;
+using ::testing::StrEq;
+
+namespace base {
+namespace {
+
+const char kBrowserWindowPlacement[] = "browser.window_placement";
+const char kShowBookmarkBar[] = "bookmark_bar.show_on_all_tabs";
+
+const char* overlay_key = kBrowserWindowPlacement;
+const char* regular_key = kShowBookmarkBar;
+// With the removal of the kWebKitGlobalXXX prefs, we'll no longer have real
+// prefs using the overlay pref store, so make up keys here.
+const char* mapped_overlay_key = "test.per_tab.javascript_enabled";
+const char* mapped_underlay_key = "test.per_profile.javascript_enabled";
+
+} // namespace
+
+class OverlayUserPrefStoreTest : public testing::Test {
+ protected:
+ OverlayUserPrefStoreTest()
+ : underlay_(new TestingPrefStore()),
+ overlay_(new OverlayUserPrefStore(underlay_.get())) {
+ overlay_->RegisterOverlayPref(overlay_key);
+ overlay_->RegisterOverlayPref(mapped_overlay_key, mapped_underlay_key);
+ }
+
+ virtual ~OverlayUserPrefStoreTest() {}
+
+ scoped_refptr<TestingPrefStore> underlay_;
+ scoped_refptr<OverlayUserPrefStore> overlay_;
+};
+
+TEST_F(OverlayUserPrefStoreTest, Observer) {
+ PrefStoreObserverMock obs;
+ overlay_->AddObserver(&obs);
+
+ // Check that underlay first value is reported.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1);
+ underlay_->SetValue(overlay_key, new FundamentalValue(42));
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Check that underlay overwriting is reported.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1);
+ underlay_->SetValue(overlay_key, new FundamentalValue(43));
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Check that overwriting change in overlay is reported.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1);
+ overlay_->SetValue(overlay_key, new FundamentalValue(44));
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Check that hidden underlay change is not reported.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(0);
+ underlay_->SetValue(overlay_key, new FundamentalValue(45));
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Check that overlay remove is reported.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1);
+ overlay_->RemoveValue(overlay_key);
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Check that underlay remove is reported.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1);
+ underlay_->RemoveValue(overlay_key);
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Check respecting of silence.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(0);
+ overlay_->SetValueSilently(overlay_key, new FundamentalValue(46));
+ Mock::VerifyAndClearExpectations(&obs);
+
+ overlay_->RemoveObserver(&obs);
+
+ // Check successful unsubscription.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(0);
+ underlay_->SetValue(overlay_key, new FundamentalValue(47));
+ overlay_->SetValue(overlay_key, new FundamentalValue(48));
+ Mock::VerifyAndClearExpectations(&obs);
+}
+
+TEST_F(OverlayUserPrefStoreTest, GetAndSet) {
+ const Value* value = NULL;
+ EXPECT_FALSE(overlay_->GetValue(overlay_key, &value));
+ EXPECT_FALSE(underlay_->GetValue(overlay_key, &value));
+
+ underlay_->SetValue(overlay_key, new FundamentalValue(42));
+
+ // Value shines through:
+ EXPECT_TRUE(overlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(42).Equals(value));
+
+ EXPECT_TRUE(underlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(42).Equals(value));
+
+ overlay_->SetValue(overlay_key, new FundamentalValue(43));
+
+ EXPECT_TRUE(overlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(43).Equals(value));
+
+ EXPECT_TRUE(underlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(42).Equals(value));
+
+ overlay_->RemoveValue(overlay_key);
+
+ // Value shines through:
+ EXPECT_TRUE(overlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(42).Equals(value));
+
+ EXPECT_TRUE(underlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(42).Equals(value));
+}
+
+// Check that GetMutableValue does not return the dictionary of the underlay.
+TEST_F(OverlayUserPrefStoreTest, ModifyDictionaries) {
+ underlay_->SetValue(overlay_key, new DictionaryValue);
+
+ Value* modify = NULL;
+ EXPECT_TRUE(overlay_->GetMutableValue(overlay_key, &modify));
+ ASSERT_TRUE(modify);
+ ASSERT_TRUE(modify->IsType(Value::TYPE_DICTIONARY));
+ static_cast<DictionaryValue*>(modify)->SetInteger(overlay_key, 42);
+
+ Value* original_in_underlay = NULL;
+ EXPECT_TRUE(underlay_->GetMutableValue(overlay_key, &original_in_underlay));
+ ASSERT_TRUE(original_in_underlay);
+ ASSERT_TRUE(original_in_underlay->IsType(Value::TYPE_DICTIONARY));
+ EXPECT_TRUE(static_cast<DictionaryValue*>(original_in_underlay)->empty());
+
+ Value* modified = NULL;
+ EXPECT_TRUE(overlay_->GetMutableValue(overlay_key, &modified));
+ ASSERT_TRUE(modified);
+ ASSERT_TRUE(modified->IsType(Value::TYPE_DICTIONARY));
+ EXPECT_TRUE(Value::Equals(modify, static_cast<DictionaryValue*>(modified)));
+}
+
+// Here we consider a global preference that is not overlayed.
+TEST_F(OverlayUserPrefStoreTest, GlobalPref) {
+ PrefStoreObserverMock obs;
+ overlay_->AddObserver(&obs);
+
+ const Value* value = NULL;
+
+ // Check that underlay first value is reported.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(1);
+ underlay_->SetValue(regular_key, new FundamentalValue(42));
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Check that underlay overwriting is reported.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(1);
+ underlay_->SetValue(regular_key, new FundamentalValue(43));
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Check that we get this value from the overlay
+ EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(43).Equals(value));
+
+ // Check that overwriting change in overlay is reported.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(1);
+ overlay_->SetValue(regular_key, new FundamentalValue(44));
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Check that we get this value from the overlay and the underlay.
+ EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(44).Equals(value));
+ EXPECT_TRUE(underlay_->GetValue(regular_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(44).Equals(value));
+
+ // Check that overlay remove is reported.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(1);
+ overlay_->RemoveValue(regular_key);
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Check that value was removed from overlay and underlay
+ EXPECT_FALSE(overlay_->GetValue(regular_key, &value));
+ EXPECT_FALSE(underlay_->GetValue(regular_key, &value));
+
+ // Check respecting of silence.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(0);
+ overlay_->SetValueSilently(regular_key, new FundamentalValue(46));
+ Mock::VerifyAndClearExpectations(&obs);
+
+ overlay_->RemoveObserver(&obs);
+
+ // Check successful unsubscription.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(0);
+ underlay_->SetValue(regular_key, new FundamentalValue(47));
+ overlay_->SetValue(regular_key, new FundamentalValue(48));
+ Mock::VerifyAndClearExpectations(&obs);
+}
+
+// Check that names mapping works correctly.
+TEST_F(OverlayUserPrefStoreTest, NamesMapping) {
+ PrefStoreObserverMock obs;
+ overlay_->AddObserver(&obs);
+
+ const Value* value = NULL;
+
+ // Check that if there is no override in the overlay, changing underlay value
+ // is reported as changing an overlay value.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1);
+ underlay_->SetValue(mapped_underlay_key, new FundamentalValue(42));
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Check that underlay overwriting is reported.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1);
+ underlay_->SetValue(mapped_underlay_key, new FundamentalValue(43));
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Check that we get this value from the overlay with both keys
+ EXPECT_TRUE(overlay_->GetValue(mapped_overlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(43).Equals(value));
+ // In this case, overlay reads directly from the underlay.
+ EXPECT_TRUE(overlay_->GetValue(mapped_underlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(43).Equals(value));
+
+ // Check that overwriting change in overlay is reported.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1);
+ overlay_->SetValue(mapped_overlay_key, new FundamentalValue(44));
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Check that we get an overriden value from overlay, while reading the
+ // value from underlay still holds an old value.
+ EXPECT_TRUE(overlay_->GetValue(mapped_overlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(44).Equals(value));
+ EXPECT_TRUE(overlay_->GetValue(mapped_underlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(43).Equals(value));
+ EXPECT_TRUE(underlay_->GetValue(mapped_underlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(43).Equals(value));
+
+ // Check that hidden underlay change is not reported.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(0);
+ underlay_->SetValue(mapped_underlay_key, new FundamentalValue(45));
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Check that overlay remove is reported.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1);
+ overlay_->RemoveValue(mapped_overlay_key);
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Check that underlay remove is reported.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1);
+ underlay_->RemoveValue(mapped_underlay_key);
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Check that value was removed.
+ EXPECT_FALSE(overlay_->GetValue(mapped_overlay_key, &value));
+ EXPECT_FALSE(overlay_->GetValue(mapped_underlay_key, &value));
+
+ // Check respecting of silence.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(0);
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_underlay_key))).Times(0);
+ overlay_->SetValueSilently(mapped_overlay_key, new FundamentalValue(46));
+ Mock::VerifyAndClearExpectations(&obs);
+
+ overlay_->RemoveObserver(&obs);
+
+ // Check successful unsubscription.
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(0);
+ EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_underlay_key))).Times(0);
+ underlay_->SetValue(mapped_underlay_key, new FundamentalValue(47));
+ overlay_->SetValue(mapped_overlay_key, new FundamentalValue(48));
+ Mock::VerifyAndClearExpectations(&obs);
+}
+
+} // namespace base
diff --git a/src/base/prefs/persistent_pref_store.h b/src/base/prefs/persistent_pref_store.h
new file mode 100644
index 0000000..0baf02a
--- /dev/null
+++ b/src/base/prefs/persistent_pref_store.h
@@ -0,0 +1,95 @@
+// Copyright (c) 2012 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_PREFS_PERSISTENT_PREF_STORE_H_
+#define BASE_PREFS_PERSISTENT_PREF_STORE_H_
+
+#include <string>
+
+#include "base/prefs/base_prefs_export.h"
+#include "base/prefs/pref_store.h"
+
+// This interface is complementary to the PrefStore interface, declaring
+// additional functionality that adds support for setting values and persisting
+// the data to some backing store.
+class BASE_PREFS_EXPORT PersistentPrefStore : public PrefStore {
+ public:
+ // Unique integer code for each type of error so we can report them
+ // distinctly in a histogram.
+ // NOTE: Don't change the order here as it will change the server's meaning
+ // of the histogram.
+ enum PrefReadError {
+ PREF_READ_ERROR_NONE = 0,
+ PREF_READ_ERROR_JSON_PARSE,
+ PREF_READ_ERROR_JSON_TYPE,
+ PREF_READ_ERROR_ACCESS_DENIED,
+ PREF_READ_ERROR_FILE_OTHER,
+ PREF_READ_ERROR_FILE_LOCKED,
+ PREF_READ_ERROR_NO_FILE,
+ PREF_READ_ERROR_JSON_REPEAT,
+ PREF_READ_ERROR_OTHER,
+ PREF_READ_ERROR_FILE_NOT_SPECIFIED,
+ PREF_READ_ERROR_MAX_ENUM
+ };
+
+ class ReadErrorDelegate {
+ public:
+ virtual ~ReadErrorDelegate() {}
+
+ virtual void OnError(PrefReadError error) = 0;
+ };
+
+ // Equivalent to PrefStore::GetValue but returns a mutable value.
+ virtual bool GetMutableValue(const std::string& key,
+ base::Value** result) = 0;
+
+ // Triggers a value changed notification. This function needs to be called
+ // if one retrieves a list or dictionary with GetMutableValue and change its
+ // value. SetValue takes care of notifications itself. Note that
+ // ReportValueChanged will trigger notifications even if nothing has changed.
+ virtual void ReportValueChanged(const std::string& key) = 0;
+
+ // Sets a |value| for |key| in the store. Assumes ownership of |value|, which
+ // must be non-NULL.
+ virtual void SetValue(const std::string& key, base::Value* value) = 0;
+
+ // Same as SetValue, but doesn't generate notifications. This is used by
+ // PrefService::GetMutableUserPref() in order to put empty entries
+ // into the user pref store. Using SetValue is not an option since existing
+ // tests rely on the number of notifications generated.
+ virtual void SetValueSilently(const std::string& key, base::Value* value) = 0;
+
+ // Removes the value for |key|.
+ virtual void RemoveValue(const std::string& key) = 0;
+
+ // Marks that the |key| with empty ListValue/DictionaryValue needs to be
+ // persisted.
+ virtual void MarkNeedsEmptyValue(const std::string& key) = 0;
+
+ // Whether the store is in a pseudo-read-only mode where changes are not
+ // actually persisted to disk. This happens in some cases when there are
+ // read errors during startup.
+ virtual bool ReadOnly() const = 0;
+
+ // Gets the read error. Only valid if IsInitializationComplete() returns true.
+ virtual PrefReadError GetReadError() const = 0;
+
+ // Reads the preferences from disk. Notifies observers via
+ // "PrefStore::OnInitializationCompleted" when done.
+ virtual PrefReadError ReadPrefs() = 0;
+
+ // Reads the preferences from disk asynchronously. Notifies observers via
+ // "PrefStore::OnInitializationCompleted" when done. Also it fires
+ // |error_delegate| if it is not NULL and reading error has occurred.
+ // Owns |error_delegate|.
+ virtual void ReadPrefsAsync(ReadErrorDelegate* error_delegate) = 0;
+
+ // Lands any pending writes to disk.
+ virtual void CommitPendingWrite() = 0;
+
+ protected:
+ virtual ~PersistentPrefStore() {}
+};
+
+#endif // BASE_PREFS_PERSISTENT_PREF_STORE_H_
diff --git a/src/base/prefs/pref_notifier.h b/src/base/prefs/pref_notifier.h
new file mode 100644
index 0000000..e0df260
--- /dev/null
+++ b/src/base/prefs/pref_notifier.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_PREFS_PREF_NOTIFIER_H_
+#define BASE_PREFS_PREF_NOTIFIER_H_
+
+#include <string>
+
+// Delegate interface used by PrefValueStore to notify its owner about changes
+// to the preference values.
+// TODO(mnissler, danno): Move this declaration to pref_value_store.h once we've
+// cleaned up all public uses of this interface.
+class PrefNotifier {
+ public:
+ virtual ~PrefNotifier() {}
+
+ // Sends out a change notification for the preference identified by
+ // |pref_name|.
+ virtual void OnPreferenceChanged(const std::string& pref_name) = 0;
+
+ // Broadcasts the intialization completed notification.
+ virtual void OnInitializationCompleted(bool succeeded) = 0;
+};
+
+#endif // BASE_PREFS_PREF_NOTIFIER_H_
diff --git a/src/base/prefs/pref_observer.h b/src/base/prefs/pref_observer.h
new file mode 100644
index 0000000..93a0454
--- /dev/null
+++ b/src/base/prefs/pref_observer.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2012 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_PREFS_PREF_OBSERVER_H_
+#define BASE_PREFS_PREF_OBSERVER_H_
+
+#include <string>
+
+class PrefServiceBase;
+
+// TODO(joi): Switch to base::Callback and remove this.
+class PrefObserver {
+ public:
+ virtual void OnPreferenceChanged(PrefServiceBase* service,
+ const std::string& pref_name) = 0;
+};
+
+#endif // BASE_PREFS_PREF_OBSERVER_H_
diff --git a/src/base/prefs/pref_store.cc b/src/base/prefs/pref_store.cc
new file mode 100644
index 0000000..0521654
--- /dev/null
+++ b/src/base/prefs/pref_store.cc
@@ -0,0 +1,13 @@
+// Copyright (c) 2012 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/prefs/pref_store.h"
+
+size_t PrefStore::NumberOfObservers() const {
+ return 0;
+}
+
+bool PrefStore::IsInitializationComplete() const {
+ return true;
+}
diff --git a/src/base/prefs/pref_store.h b/src/base/prefs/pref_store.h
new file mode 100644
index 0000000..2239528
--- /dev/null
+++ b/src/base/prefs/pref_store.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2012 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_PREFS_PREF_STORE_H_
+#define BASE_PREFS_PREF_STORE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/prefs/base_prefs_export.h"
+
+namespace base {
+class Value;
+}
+
+// This is an abstract interface for reading and writing from/to a persistent
+// preference store, used by PrefService. An implementation using a JSON file
+// can be found in JsonPrefStore, while an implementation without any backing
+// store for testing can be found in TestingPrefStore. Furthermore, there is
+// CommandLinePrefStore, which bridges command line options to preferences and
+// ConfigurationPolicyPrefStore, which is used for hooking up configuration
+// policy with the preference subsystem.
+class BASE_PREFS_EXPORT PrefStore : public base::RefCounted<PrefStore> {
+ public:
+ // Observer interface for monitoring PrefStore.
+ class BASE_PREFS_EXPORT Observer {
+ public:
+ // Called when the value for the given |key| in the store changes.
+ virtual void OnPrefValueChanged(const std::string& key) = 0;
+ // Notification about the PrefStore being fully initialized.
+ virtual void OnInitializationCompleted(bool succeeded) = 0;
+
+ protected:
+ virtual ~Observer() {}
+ };
+
+ PrefStore() {}
+
+ // Add and remove observers.
+ virtual void AddObserver(Observer* observer) {}
+ virtual void RemoveObserver(Observer* observer) {}
+ virtual size_t NumberOfObservers() const;
+
+ // Whether the store has completed all asynchronous initialization.
+ virtual bool IsInitializationComplete() const;
+
+ // Get the value for a given preference |key| and stores it in |*result|.
+ // |*result| is only modified if the return value is true and if |result|
+ // is not NULL. Ownership of the |*result| value remains with the PrefStore.
+ virtual bool GetValue(const std::string& key,
+ const base::Value** result) const = 0;
+
+ protected:
+ friend class base::RefCounted<PrefStore>;
+ virtual ~PrefStore() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrefStore);
+};
+
+#endif // BASE_PREFS_PREF_STORE_H_
diff --git a/src/base/prefs/pref_store_observer_mock.cc b/src/base/prefs/pref_store_observer_mock.cc
new file mode 100644
index 0000000..0970e63
--- /dev/null
+++ b/src/base/prefs/pref_store_observer_mock.cc
@@ -0,0 +1,9 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/prefs/pref_store_observer_mock.h"
+
+PrefStoreObserverMock::PrefStoreObserverMock() {}
+
+PrefStoreObserverMock::~PrefStoreObserverMock() {}
diff --git a/src/base/prefs/pref_store_observer_mock.h b/src/base/prefs/pref_store_observer_mock.h
new file mode 100644
index 0000000..8252c3b
--- /dev/null
+++ b/src/base/prefs/pref_store_observer_mock.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_PREFS_PREF_STORE_OBSERVER_MOCK_H_
+#define BASE_PREFS_PREF_STORE_OBSERVER_MOCK_H_
+
+#include "base/basictypes.h"
+#include "base/prefs/pref_store.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+// A gmock-ified implementation of PrefStore::Observer.
+class PrefStoreObserverMock : public PrefStore::Observer {
+ public:
+ PrefStoreObserverMock();
+ virtual ~PrefStoreObserverMock();
+
+ MOCK_METHOD1(OnPrefValueChanged, void(const std::string&));
+ MOCK_METHOD1(OnInitializationCompleted, void(bool));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrefStoreObserverMock);
+};
+
+#endif // BASE_PREFS_PREF_STORE_OBSERVER_MOCK_H_
diff --git a/src/base/prefs/pref_value_map.cc b/src/base/prefs/pref_value_map.cc
new file mode 100644
index 0000000..43d2a4c
--- /dev/null
+++ b/src/base/prefs/pref_value_map.cc
@@ -0,0 +1,151 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/prefs/pref_value_map.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "base/values.h"
+
+PrefValueMap::PrefValueMap() {}
+
+PrefValueMap::~PrefValueMap() {
+ Clear();
+}
+
+bool PrefValueMap::GetValue(const std::string& key, const Value** value) const {
+ const Map::const_iterator entry = prefs_.find(key);
+ if (entry != prefs_.end()) {
+ if (value)
+ *value = entry->second;
+ return true;
+ }
+
+ return false;
+}
+
+bool PrefValueMap::GetValue(const std::string& key, Value** value) {
+ const Map::const_iterator entry = prefs_.find(key);
+ if (entry != prefs_.end()) {
+ if (value)
+ *value = entry->second;
+ return true;
+ }
+
+ return false;
+}
+
+bool PrefValueMap::SetValue(const std::string& key, Value* value) {
+ DCHECK(value);
+ scoped_ptr<Value> value_ptr(value);
+ const Map::iterator entry = prefs_.find(key);
+ if (entry != prefs_.end()) {
+ if (Value::Equals(entry->second, value))
+ return false;
+ delete entry->second;
+ entry->second = value_ptr.release();
+ } else {
+ prefs_[key] = value_ptr.release();
+ }
+
+ return true;
+}
+
+bool PrefValueMap::RemoveValue(const std::string& key) {
+ const Map::iterator entry = prefs_.find(key);
+ if (entry != prefs_.end()) {
+ delete entry->second;
+ prefs_.erase(entry);
+ return true;
+ }
+
+ return false;
+}
+
+void PrefValueMap::Clear() {
+ STLDeleteValues(&prefs_);
+ prefs_.clear();
+}
+
+void PrefValueMap::Swap(PrefValueMap* other) {
+ prefs_.swap(other->prefs_);
+}
+
+PrefValueMap::iterator PrefValueMap::begin() {
+ return prefs_.begin();
+}
+
+PrefValueMap::iterator PrefValueMap::end() {
+ return prefs_.end();
+}
+
+PrefValueMap::const_iterator PrefValueMap::begin() const {
+ return prefs_.begin();
+}
+
+PrefValueMap::const_iterator PrefValueMap::end() const {
+ return prefs_.end();
+}
+
+bool PrefValueMap::GetBoolean(const std::string& key,
+ bool* value) const {
+ const Value* stored_value = NULL;
+ return GetValue(key, &stored_value) && stored_value->GetAsBoolean(value);
+}
+
+void PrefValueMap::SetBoolean(const std::string& key, bool value) {
+ SetValue(key, new base::FundamentalValue(value));
+}
+
+bool PrefValueMap::GetString(const std::string& key,
+ std::string* value) const {
+ const Value* stored_value = NULL;
+ return GetValue(key, &stored_value) && stored_value->GetAsString(value);
+}
+
+void PrefValueMap::SetString(const std::string& key,
+ const std::string& value) {
+ SetValue(key, new base::StringValue(value));
+}
+
+bool PrefValueMap::GetInteger(const std::string& key, int* value) const {
+ const Value* stored_value = NULL;
+ return GetValue(key, &stored_value) && stored_value->GetAsInteger(value);
+}
+
+void PrefValueMap::SetInteger(const std::string& key, const int value) {
+ SetValue(key, new base::FundamentalValue(value));
+}
+
+void PrefValueMap::GetDifferingKeys(
+ const PrefValueMap* other,
+ std::vector<std::string>* differing_keys) const {
+ differing_keys->clear();
+
+ // Walk over the maps in lockstep, adding everything that is different.
+ Map::const_iterator this_pref(prefs_.begin());
+ Map::const_iterator other_pref(other->prefs_.begin());
+ while (this_pref != prefs_.end() && other_pref != other->prefs_.end()) {
+ const int diff = this_pref->first.compare(other_pref->first);
+ if (diff == 0) {
+ if (!this_pref->second->Equals(other_pref->second))
+ differing_keys->push_back(this_pref->first);
+ ++this_pref;
+ ++other_pref;
+ } else if (diff < 0) {
+ differing_keys->push_back(this_pref->first);
+ ++this_pref;
+ } else if (diff > 0) {
+ differing_keys->push_back(other_pref->first);
+ ++other_pref;
+ }
+ }
+
+ // Add the remaining entries.
+ for ( ; this_pref != prefs_.end(); ++this_pref)
+ differing_keys->push_back(this_pref->first);
+ for ( ; other_pref != other->prefs_.end(); ++other_pref)
+ differing_keys->push_back(other_pref->first);
+}
diff --git a/src/base/prefs/pref_value_map.h b/src/base/prefs/pref_value_map.h
new file mode 100644
index 0000000..1d79127
--- /dev/null
+++ b/src/base/prefs/pref_value_map.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_PREFS_PREF_VALUE_MAP_H_
+#define BASE_PREFS_PREF_VALUE_MAP_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/prefs/base_prefs_export.h"
+
+namespace base {
+class Value;
+}
+
+// A generic string to value map used by the PrefStore implementations.
+class BASE_PREFS_EXPORT PrefValueMap {
+ public:
+ typedef std::map<std::string, base::Value*>::iterator iterator;
+ typedef std::map<std::string, base::Value*>::const_iterator const_iterator;
+
+ PrefValueMap();
+ virtual ~PrefValueMap();
+
+ // Gets the value for |key| and stores it in |value|. Ownership remains with
+ // the map. Returns true if a value is present. If not, |value| is not
+ // touched.
+ bool GetValue(const std::string& key, const base::Value** value) const;
+ bool GetValue(const std::string& key, base::Value** value);
+
+ // Sets a new |value| for |key|. Takes ownership of |value|, which must be
+ // non-NULL. Returns true if the value changed.
+ bool SetValue(const std::string& key, base::Value* value);
+
+ // Removes the value for |key| from the map. Returns true if a value was
+ // removed.
+ bool RemoveValue(const std::string& key);
+
+ // Clears the map.
+ void Clear();
+
+ // Swaps the contents of two maps.
+ void Swap(PrefValueMap* other);
+
+ iterator begin();
+ iterator end();
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ // Gets a boolean value for |key| and stores it in |value|. Returns true if
+ // the value was found and of the proper type.
+ bool GetBoolean(const std::string& key, bool* value) const;
+
+ // Sets the value for |key| to the boolean |value|.
+ void SetBoolean(const std::string& key, bool value);
+
+ // Gets a string value for |key| and stores it in |value|. Returns true if
+ // the value was found and of the proper type.
+ bool GetString(const std::string& key, std::string* value) const;
+
+ // Sets the value for |key| to the string |value|.
+ void SetString(const std::string& key, const std::string& value);
+
+ // Gets an int value for |key| and stores it in |value|. Returns true if
+ // the value was found and of the proper type.
+ bool GetInteger(const std::string& key, int* value) const;
+
+ // Sets the value for |key| to the int |value|.
+ void SetInteger(const std::string& key, const int value);
+
+ // Compares this value map against |other| and stores all key names that have
+ // different values in |differing_keys|. This includes keys that are present
+ // only in one of the maps.
+ void GetDifferingKeys(const PrefValueMap* other,
+ std::vector<std::string>* differing_keys) const;
+
+ private:
+ typedef std::map<std::string, base::Value*> Map;
+
+ Map prefs_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefValueMap);
+};
+
+#endif // BASE_PREFS_PREF_VALUE_MAP_H_
diff --git a/src/base/prefs/pref_value_map_unittest.cc b/src/base/prefs/pref_value_map_unittest.cc
new file mode 100644
index 0000000..8cc51ad
--- /dev/null
+++ b/src/base/prefs/pref_value_map_unittest.cc
@@ -0,0 +1,115 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/prefs/pref_value_map.h"
+
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+TEST(PrefValueMapTest, SetValue) {
+ PrefValueMap map;
+ const Value* result = NULL;
+ EXPECT_FALSE(map.GetValue("key", &result));
+ EXPECT_FALSE(result);
+
+ EXPECT_TRUE(map.SetValue("key", new StringValue("test")));
+ EXPECT_FALSE(map.SetValue("key", new StringValue("test")));
+ EXPECT_TRUE(map.SetValue("key", new StringValue("hi mom!")));
+
+ EXPECT_TRUE(map.GetValue("key", &result));
+ EXPECT_TRUE(StringValue("hi mom!").Equals(result));
+}
+
+TEST(PrefValueMapTest, GetAndSetIntegerValue) {
+ PrefValueMap map;
+ ASSERT_TRUE(map.SetValue("key", new FundamentalValue(5)));
+
+ int int_value = 0;
+ EXPECT_TRUE(map.GetInteger("key", &int_value));
+ EXPECT_EQ(5, int_value);
+
+ map.SetInteger("key", -14);
+ EXPECT_TRUE(map.GetInteger("key", &int_value));
+ EXPECT_EQ(-14, int_value);
+}
+
+TEST(PrefValueMapTest, RemoveValue) {
+ PrefValueMap map;
+ EXPECT_FALSE(map.RemoveValue("key"));
+
+ EXPECT_TRUE(map.SetValue("key", new StringValue("test")));
+ EXPECT_TRUE(map.GetValue("key", NULL));
+
+ EXPECT_TRUE(map.RemoveValue("key"));
+ EXPECT_FALSE(map.GetValue("key", NULL));
+
+ EXPECT_FALSE(map.RemoveValue("key"));
+}
+
+TEST(PrefValueMapTest, Clear) {
+ PrefValueMap map;
+ EXPECT_TRUE(map.SetValue("key", new StringValue("test")));
+ EXPECT_TRUE(map.GetValue("key", NULL));
+
+ map.Clear();
+
+ EXPECT_FALSE(map.GetValue("key", NULL));
+}
+
+TEST(PrefValueMapTest, GetDifferingKeys) {
+ PrefValueMap reference;
+ EXPECT_TRUE(reference.SetValue("b", new StringValue("test")));
+ EXPECT_TRUE(reference.SetValue("c", new StringValue("test")));
+ EXPECT_TRUE(reference.SetValue("e", new StringValue("test")));
+
+ PrefValueMap check;
+ std::vector<std::string> differing_paths;
+ std::vector<std::string> expected_differing_paths;
+
+ reference.GetDifferingKeys(&check, &differing_paths);
+ expected_differing_paths.push_back("b");
+ expected_differing_paths.push_back("c");
+ expected_differing_paths.push_back("e");
+ EXPECT_EQ(expected_differing_paths, differing_paths);
+
+ EXPECT_TRUE(check.SetValue("a", new StringValue("test")));
+ EXPECT_TRUE(check.SetValue("c", new StringValue("test")));
+ EXPECT_TRUE(check.SetValue("d", new StringValue("test")));
+
+ reference.GetDifferingKeys(&check, &differing_paths);
+ expected_differing_paths.clear();
+ expected_differing_paths.push_back("a");
+ expected_differing_paths.push_back("b");
+ expected_differing_paths.push_back("d");
+ expected_differing_paths.push_back("e");
+ EXPECT_EQ(expected_differing_paths, differing_paths);
+}
+
+TEST(PrefValueMapTest, SwapTwoMaps) {
+ PrefValueMap first_map;
+ EXPECT_TRUE(first_map.SetValue("a", new StringValue("test")));
+ EXPECT_TRUE(first_map.SetValue("b", new StringValue("test")));
+ EXPECT_TRUE(first_map.SetValue("c", new StringValue("test")));
+
+ PrefValueMap second_map;
+ EXPECT_TRUE(second_map.SetValue("d", new StringValue("test")));
+ EXPECT_TRUE(second_map.SetValue("e", new StringValue("test")));
+ EXPECT_TRUE(second_map.SetValue("f", new StringValue("test")));
+
+ first_map.Swap(&second_map);
+
+ EXPECT_TRUE(first_map.GetValue("d", NULL));
+ EXPECT_TRUE(first_map.GetValue("e", NULL));
+ EXPECT_TRUE(first_map.GetValue("f", NULL));
+
+ EXPECT_TRUE(second_map.GetValue("a", NULL));
+ EXPECT_TRUE(second_map.GetValue("b", NULL));
+ EXPECT_TRUE(second_map.GetValue("c", NULL));
+}
+
+} // namespace
+} // namespace base
diff --git a/src/base/prefs/public/pref_change_registrar.cc b/src/base/prefs/public/pref_change_registrar.cc
new file mode 100644
index 0000000..681d912
--- /dev/null
+++ b/src/base/prefs/public/pref_change_registrar.cc
@@ -0,0 +1,91 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/prefs/public/pref_change_registrar.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/prefs/public/pref_service_base.h"
+
+PrefChangeRegistrar::PrefChangeRegistrar() : service_(NULL) {}
+
+PrefChangeRegistrar::~PrefChangeRegistrar() {
+ // If you see an invalid memory access in this destructor, this
+ // PrefChangeRegistrar might be subscribed to an OffTheRecordProfileImpl that
+ // has been destroyed. This should not happen any more but be warned.
+ // Feel free to contact battre@chromium.org in case this happens.
+ RemoveAll();
+}
+
+void PrefChangeRegistrar::Init(PrefServiceBase* service) {
+ DCHECK(IsEmpty() || service_ == service);
+ service_ = service;
+}
+
+void PrefChangeRegistrar::Add(const char* path,
+ const base::Closure& obs) {
+ Add(path, base::Bind(&PrefChangeRegistrar::InvokeUnnamedCallback, obs));
+}
+
+void PrefChangeRegistrar::Add(const char* path,
+ const NamedChangeCallback& obs) {
+ if (!service_) {
+ NOTREACHED();
+ return;
+ }
+ DCHECK(!IsObserved(path)) << "Already had this pref registered.";
+
+ service_->AddPrefObserver(path, this);
+ observers_[path] = obs;
+}
+
+void PrefChangeRegistrar::Remove(const char* path) {
+ DCHECK(IsObserved(path));
+
+ observers_.erase(path);
+ service_->RemovePrefObserver(path, this);
+}
+
+void PrefChangeRegistrar::RemoveAll() {
+ for (ObserverMap::const_iterator it = observers_.begin();
+ it != observers_.end(); ++it) {
+ service_->RemovePrefObserver(it->first.c_str(), this);
+ }
+
+ observers_.clear();
+}
+
+bool PrefChangeRegistrar::IsEmpty() const {
+ return observers_.empty();
+}
+
+bool PrefChangeRegistrar::IsObserved(const std::string& pref) {
+ return observers_.find(pref) != observers_.end();
+}
+
+bool PrefChangeRegistrar::IsManaged() {
+ for (ObserverMap::const_iterator it = observers_.begin();
+ it != observers_.end(); ++it) {
+ const PrefServiceBase::Preference* pref =
+ service_->FindPreference(it->first.c_str());
+ if (pref && pref->IsManaged())
+ return true;
+ }
+ return false;
+}
+
+void PrefChangeRegistrar::OnPreferenceChanged(PrefServiceBase* service,
+ const std::string& pref) {
+ if (IsObserved(pref))
+ observers_[pref].Run(pref);
+}
+
+void PrefChangeRegistrar::InvokeUnnamedCallback(const base::Closure& callback,
+ const std::string& pref_name) {
+ callback.Run();
+}
+
+PrefServiceBase* PrefChangeRegistrar::prefs() {
+ return service_;
+}
diff --git a/src/base/prefs/public/pref_change_registrar.h b/src/base/prefs/public/pref_change_registrar.h
new file mode 100644
index 0000000..84df652
--- /dev/null
+++ b/src/base/prefs/public/pref_change_registrar.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_PREFS_PUBLIC_PREF_CHANGE_REGISTRAR_H_
+#define BASE_PREFS_PUBLIC_PREF_CHANGE_REGISTRAR_H_
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/prefs/base_prefs_export.h"
+#include "base/prefs/pref_observer.h"
+
+class PrefServiceBase;
+
+// Automatically manages the registration of one or more pref change observers
+// with a PrefStore. Functions much like NotificationRegistrar, but specifically
+// manages observers of preference changes. When the Registrar is destroyed,
+// all registered observers are automatically unregistered with the PrefStore.
+class BASE_PREFS_EXPORT PrefChangeRegistrar : public PrefObserver {
+ public:
+ // You can register this type of callback if you need to know the
+ // path of the preference that is changing.
+ typedef base::Callback<void(const std::string&)> NamedChangeCallback;
+
+ PrefChangeRegistrar();
+ virtual ~PrefChangeRegistrar();
+
+ // Must be called before adding or removing observers. Can be called more
+ // than once as long as the value of |service| doesn't change.
+ void Init(PrefServiceBase* service);
+
+ // Adds a pref observer for the specified pref |path| and |obs| observer
+ // object. All registered observers will be automatically unregistered
+ // when the registrar's destructor is called.
+ //
+ // The second version binds a callback that will receive the path of
+ // the preference that is changing as its parameter.
+ //
+ // Only one observer may be registered per path.
+ void Add(const char* path, const base::Closure& obs);
+ void Add(const char* path, const NamedChangeCallback& obs);
+
+ // Removes the pref observer registered for |path|.
+ void Remove(const char* path);
+
+ // Removes all observers that have been previously added with a call to Add.
+ void RemoveAll();
+
+ // Returns true if no pref observers are registered.
+ bool IsEmpty() const;
+
+ // Check whether |pref| is in the set of preferences being observed.
+ bool IsObserved(const std::string& pref);
+
+ // Check whether any of the observed preferences has the managed bit set.
+ bool IsManaged();
+
+ // Return the PrefServiceBase for this registrar.
+ PrefServiceBase* prefs();
+
+ private:
+ // PrefObserver:
+ virtual void OnPreferenceChanged(PrefServiceBase* service,
+ const std::string& pref_name) OVERRIDE;
+
+ static void InvokeUnnamedCallback(const base::Closure& callback,
+ const std::string& pref_name);
+
+ typedef std::map<std::string, NamedChangeCallback> ObserverMap;
+
+ ObserverMap observers_;
+ PrefServiceBase* service_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefChangeRegistrar);
+};
+
+#endif // BASE_PREFS_PUBLIC_PREF_CHANGE_REGISTRAR_H_
diff --git a/src/base/prefs/public/pref_change_registrar_unittest.cc b/src/base/prefs/public/pref_change_registrar_unittest.cc
new file mode 100644
index 0000000..d194c36
--- /dev/null
+++ b/src/base/prefs/public/pref_change_registrar_unittest.cc
@@ -0,0 +1,204 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/prefs/pref_observer.h"
+#include "base/prefs/public/pref_change_registrar.h"
+#include "chrome/test/base/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Mock;
+using testing::Eq;
+
+namespace base {
+namespace {
+
+const char kHomePage[] = "homepage";
+const char kHomePageIsNewTabPage[] = "homepage_is_newtabpage";
+const char kApplicationLocale[] = "intl.app_locale";
+
+// A mock provider that allows us to capture pref observer changes.
+class MockPrefService : public TestingPrefService {
+ public:
+ MockPrefService() {}
+ virtual ~MockPrefService() {}
+
+ MOCK_METHOD2(AddPrefObserver,
+ void(const char*, PrefObserver*));
+ MOCK_METHOD2(RemovePrefObserver,
+ void(const char*, PrefObserver*));
+};
+
+} // namespace
+
+class PrefChangeRegistrarTest : public testing::Test {
+ public:
+ PrefChangeRegistrarTest() {}
+ virtual ~PrefChangeRegistrarTest() {}
+
+ protected:
+ virtual void SetUp() OVERRIDE;
+
+ base::Closure observer() const {
+ return base::Bind(&base::DoNothing);
+ }
+
+ MockPrefService* service() const { return service_.get(); }
+
+ private:
+ scoped_ptr<MockPrefService> service_;
+};
+
+void PrefChangeRegistrarTest::SetUp() {
+ service_.reset(new MockPrefService());
+}
+
+TEST_F(PrefChangeRegistrarTest, AddAndRemove) {
+ PrefChangeRegistrar registrar;
+ registrar.Init(service());
+
+ // Test adding.
+ EXPECT_CALL(*service(),
+ AddPrefObserver(Eq(std::string("test.pref.1")), ®istrar));
+ EXPECT_CALL(*service(),
+ AddPrefObserver(Eq(std::string("test.pref.2")), ®istrar));
+ registrar.Add("test.pref.1", observer());
+ registrar.Add("test.pref.2", observer());
+ EXPECT_FALSE(registrar.IsEmpty());
+
+ // Test removing.
+ Mock::VerifyAndClearExpectations(service());
+ EXPECT_CALL(*service(),
+ RemovePrefObserver(Eq(std::string("test.pref.1")), ®istrar));
+ EXPECT_CALL(*service(),
+ RemovePrefObserver(Eq(std::string("test.pref.2")), ®istrar));
+ registrar.Remove("test.pref.1");
+ registrar.Remove("test.pref.2");
+ EXPECT_TRUE(registrar.IsEmpty());
+
+ // Explicitly check the expectations now to make sure that the Removes
+ // worked (rather than the registrar destructor doing the work).
+ Mock::VerifyAndClearExpectations(service());
+}
+
+TEST_F(PrefChangeRegistrarTest, AutoRemove) {
+ PrefChangeRegistrar registrar;
+ registrar.Init(service());
+
+ // Setup of auto-remove.
+ EXPECT_CALL(*service(),
+ AddPrefObserver(Eq(std::string("test.pref.1")), ®istrar));
+ registrar.Add("test.pref.1", observer());
+ Mock::VerifyAndClearExpectations(service());
+ EXPECT_FALSE(registrar.IsEmpty());
+
+ // Test auto-removing.
+ EXPECT_CALL(*service(),
+ RemovePrefObserver(Eq(std::string("test.pref.1")), ®istrar));
+}
+
+TEST_F(PrefChangeRegistrarTest, RemoveAll) {
+ PrefChangeRegistrar registrar;
+ registrar.Init(service());
+
+ EXPECT_CALL(*service(),
+ AddPrefObserver(Eq(std::string("test.pref.1")), ®istrar));
+ EXPECT_CALL(*service(),
+ AddPrefObserver(Eq(std::string("test.pref.2")), ®istrar));
+ registrar.Add("test.pref.1", observer());
+ registrar.Add("test.pref.2", observer());
+ Mock::VerifyAndClearExpectations(service());
+
+ EXPECT_CALL(*service(),
+ RemovePrefObserver(Eq(std::string("test.pref.1")), ®istrar));
+ EXPECT_CALL(*service(),
+ RemovePrefObserver(Eq(std::string("test.pref.2")), ®istrar));
+ registrar.RemoveAll();
+ EXPECT_TRUE(registrar.IsEmpty());
+
+ // Explicitly check the expectations now to make sure that the RemoveAll
+ // worked (rather than the registrar destructor doing the work).
+ Mock::VerifyAndClearExpectations(service());
+}
+
+class ObserveSetOfPreferencesTest : public testing::Test {
+ public:
+ virtual void SetUp() {
+ pref_service_.reset(new TestingPrefService);
+ pref_service_->RegisterStringPref(kHomePage,
+ "http://google.com",
+ PrefService::UNSYNCABLE_PREF);
+ pref_service_->RegisterBooleanPref(kHomePageIsNewTabPage,
+ false,
+ PrefService::UNSYNCABLE_PREF);
+ pref_service_->RegisterStringPref(kApplicationLocale,
+ "",
+ PrefService::UNSYNCABLE_PREF);
+ }
+
+ PrefChangeRegistrar* CreatePrefChangeRegistrar() {
+ PrefChangeRegistrar* pref_set = new PrefChangeRegistrar();
+ base::Closure callback = base::Bind(&base::DoNothing);
+ pref_set->Init(pref_service_.get());
+ pref_set->Add(kHomePage, callback);
+ pref_set->Add(kHomePageIsNewTabPage, callback);
+ return pref_set;
+ }
+
+ MOCK_METHOD1(OnPreferenceChanged, void(const std::string&));
+
+ scoped_ptr<TestingPrefService> pref_service_;
+};
+
+TEST_F(ObserveSetOfPreferencesTest, IsObserved) {
+ scoped_ptr<PrefChangeRegistrar> pref_set(CreatePrefChangeRegistrar());
+ EXPECT_TRUE(pref_set->IsObserved(kHomePage));
+ EXPECT_TRUE(pref_set->IsObserved(kHomePageIsNewTabPage));
+ EXPECT_FALSE(pref_set->IsObserved(kApplicationLocale));
+}
+
+TEST_F(ObserveSetOfPreferencesTest, IsManaged) {
+ scoped_ptr<PrefChangeRegistrar> pref_set(CreatePrefChangeRegistrar());
+ EXPECT_FALSE(pref_set->IsManaged());
+ pref_service_->SetManagedPref(kHomePage,
+ new StringValue("http://crbug.com"));
+ EXPECT_TRUE(pref_set->IsManaged());
+ pref_service_->SetManagedPref(kHomePageIsNewTabPage,
+ new FundamentalValue(true));
+ EXPECT_TRUE(pref_set->IsManaged());
+ pref_service_->RemoveManagedPref(kHomePage);
+ EXPECT_TRUE(pref_set->IsManaged());
+ pref_service_->RemoveManagedPref(kHomePageIsNewTabPage);
+ EXPECT_FALSE(pref_set->IsManaged());
+}
+
+TEST_F(ObserveSetOfPreferencesTest, Observe) {
+ using testing::_;
+ using testing::Mock;
+
+ PrefChangeRegistrar pref_set;
+ PrefChangeRegistrar::NamedChangeCallback callback = base::Bind(
+ &ObserveSetOfPreferencesTest::OnPreferenceChanged,
+ base::Unretained(this));
+ pref_set.Init(pref_service_.get());
+ pref_set.Add(kHomePage, callback);
+ pref_set.Add(kHomePageIsNewTabPage, callback);
+
+ EXPECT_CALL(*this, OnPreferenceChanged(kHomePage));
+ pref_service_->SetUserPref(kHomePage, new StringValue("http://crbug.com"));
+ Mock::VerifyAndClearExpectations(this);
+
+ EXPECT_CALL(*this, OnPreferenceChanged(kHomePageIsNewTabPage));
+ pref_service_->SetUserPref(kHomePageIsNewTabPage,
+ new FundamentalValue(true));
+ Mock::VerifyAndClearExpectations(this);
+
+ EXPECT_CALL(*this, OnPreferenceChanged(_)).Times(0);
+ pref_service_->SetUserPref(kApplicationLocale, new StringValue("en_US.utf8"));
+ Mock::VerifyAndClearExpectations(this);
+}
+
+} // namespace base
diff --git a/src/base/prefs/public/pref_member.cc b/src/base/prefs/public/pref_member.cc
new file mode 100644
index 0000000..82a1247
--- /dev/null
+++ b/src/base/prefs/public/pref_member.cc
@@ -0,0 +1,220 @@
+// Copyright (c) 2012 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/prefs/public/pref_member.h"
+
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/prefs/public/pref_service_base.h"
+#include "base/value_conversions.h"
+
+using base::MessageLoopProxy;
+
+namespace subtle {
+
+PrefMemberBase::PrefMemberBase()
+ : prefs_(NULL),
+ setting_value_(false) {
+}
+
+PrefMemberBase::~PrefMemberBase() {
+ Destroy();
+}
+
+void PrefMemberBase::Init(const char* pref_name,
+ PrefServiceBase* prefs,
+ const NamedChangeCallback& observer) {
+ observer_ = observer;
+ Init(pref_name, prefs);
+}
+
+void PrefMemberBase::Init(const char* pref_name,
+ PrefServiceBase* prefs) {
+ DCHECK(pref_name);
+ DCHECK(prefs);
+ DCHECK(pref_name_.empty()); // Check that Init is only called once.
+ prefs_ = prefs;
+ pref_name_ = pref_name;
+ // Check that the preference is registered.
+ DCHECK(prefs_->FindPreference(pref_name_.c_str()))
+ << pref_name << " not registered.";
+
+ // Add ourselves as a pref observer so we can keep our local value in sync.
+ prefs_->AddPrefObserver(pref_name, this);
+}
+
+void PrefMemberBase::Destroy() {
+ if (prefs_ && !pref_name_.empty()) {
+ prefs_->RemovePrefObserver(pref_name_.c_str(), this);
+ prefs_ = NULL;
+ }
+}
+
+void PrefMemberBase::MoveToThread(
+ const scoped_refptr<MessageLoopProxy>& message_loop) {
+ VerifyValuePrefName();
+ // Load the value from preferences if it hasn't been loaded so far.
+ if (!internal())
+ UpdateValueFromPref(base::Closure());
+ internal()->MoveToThread(message_loop);
+}
+
+void PrefMemberBase::OnPreferenceChanged(PrefServiceBase* service,
+ const std::string& pref_name) {
+ VerifyValuePrefName();
+ UpdateValueFromPref((!setting_value_ && !observer_.is_null()) ?
+ base::Bind(observer_, pref_name) : base::Closure());
+}
+
+void PrefMemberBase::UpdateValueFromPref(const base::Closure& callback) const {
+ VerifyValuePrefName();
+ const PrefServiceBase::Preference* pref =
+ prefs_->FindPreference(pref_name_.c_str());
+ DCHECK(pref);
+ if (!internal())
+ CreateInternal();
+ internal()->UpdateValue(pref->GetValue()->DeepCopy(),
+ pref->IsManaged(),
+ pref->IsUserModifiable(),
+ callback);
+}
+
+void PrefMemberBase::VerifyPref() const {
+ VerifyValuePrefName();
+ if (!internal())
+ UpdateValueFromPref(base::Closure());
+}
+
+void PrefMemberBase::InvokeUnnamedCallback(const base::Closure& callback,
+ const std::string& pref_name) {
+ callback.Run();
+}
+
+PrefMemberBase::Internal::Internal()
+ : thread_loop_(MessageLoopProxy::current()),
+ is_managed_(false) {
+}
+PrefMemberBase::Internal::~Internal() { }
+
+bool PrefMemberBase::Internal::IsOnCorrectThread() const {
+ // In unit tests, there may not be a message loop.
+ return thread_loop_ == NULL || thread_loop_->BelongsToCurrentThread();
+}
+
+void PrefMemberBase::Internal::UpdateValue(
+ Value* v,
+ bool is_managed,
+ bool is_user_modifiable,
+ const base::Closure& callback) const {
+ scoped_ptr<Value> value(v);
+ base::ScopedClosureRunner closure_runner(callback);
+ if (IsOnCorrectThread()) {
+ bool rv = UpdateValueInternal(*value);
+ DCHECK(rv);
+ is_managed_ = is_managed;
+ is_user_modifiable_ = is_user_modifiable;
+ } else {
+ bool may_run = thread_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&PrefMemberBase::Internal::UpdateValue, this,
+ value.release(), is_managed, is_user_modifiable,
+ closure_runner.Release()));
+ DCHECK(may_run);
+ }
+}
+
+void PrefMemberBase::Internal::MoveToThread(
+ const scoped_refptr<MessageLoopProxy>& message_loop) {
+ CheckOnCorrectThread();
+ thread_loop_ = message_loop;
+}
+
+bool PrefMemberVectorStringUpdate(const Value& value,
+ std::vector<std::string>* string_vector) {
+ if (!value.IsType(Value::TYPE_LIST))
+ return false;
+ const ListValue* list = static_cast<const ListValue*>(&value);
+
+ std::vector<std::string> local_vector;
+ for (ListValue::const_iterator it = list->begin(); it != list->end(); ++it) {
+ std::string string_value;
+ if (!(*it)->GetAsString(&string_value))
+ return false;
+
+ local_vector.push_back(string_value);
+ }
+
+ string_vector->swap(local_vector);
+ return true;
+}
+
+} // namespace subtle
+
+template <>
+void PrefMember<bool>::UpdatePref(const bool& value) {
+ prefs()->SetBoolean(pref_name().c_str(), value);
+}
+
+template <>
+bool PrefMember<bool>::Internal::UpdateValueInternal(const Value& value) const {
+ return value.GetAsBoolean(&value_);
+}
+
+template <>
+void PrefMember<int>::UpdatePref(const int& value) {
+ prefs()->SetInteger(pref_name().c_str(), value);
+}
+
+template <>
+bool PrefMember<int>::Internal::UpdateValueInternal(const Value& value) const {
+ return value.GetAsInteger(&value_);
+}
+
+template <>
+void PrefMember<double>::UpdatePref(const double& value) {
+ prefs()->SetDouble(pref_name().c_str(), value);
+}
+
+template <>
+bool PrefMember<double>::Internal::UpdateValueInternal(const Value& value)
+ const {
+ return value.GetAsDouble(&value_);
+}
+
+template <>
+void PrefMember<std::string>::UpdatePref(const std::string& value) {
+ prefs()->SetString(pref_name().c_str(), value);
+}
+
+template <>
+bool PrefMember<std::string>::Internal::UpdateValueInternal(const Value& value)
+ const {
+ return value.GetAsString(&value_);
+}
+
+template <>
+void PrefMember<FilePath>::UpdatePref(const FilePath& value) {
+ prefs()->SetFilePath(pref_name().c_str(), value);
+}
+
+template <>
+bool PrefMember<FilePath>::Internal::UpdateValueInternal(const Value& value)
+ const {
+ return base::GetValueAsFilePath(value, &value_);
+}
+
+template <>
+void PrefMember<std::vector<std::string> >::UpdatePref(
+ const std::vector<std::string>& value) {
+ ListValue list_value;
+ list_value.AppendStrings(value);
+ prefs()->Set(pref_name().c_str(), list_value);
+}
+
+template <>
+bool PrefMember<std::vector<std::string> >::Internal::UpdateValueInternal(
+ const Value& value) const {
+ return subtle::PrefMemberVectorStringUpdate(value, &value_);
+}
diff --git a/src/base/prefs/public/pref_member.h b/src/base/prefs/public/pref_member.h
new file mode 100644
index 0000000..52a91b5
--- /dev/null
+++ b/src/base/prefs/public/pref_member.h
@@ -0,0 +1,352 @@
+// Copyright (c) 2012 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.
+//
+// A helper class that stays in sync with a preference (bool, int, real,
+// string or filepath). For example:
+//
+// class MyClass {
+// public:
+// MyClass(PrefService* prefs) {
+// my_string_.Init(prefs::kHomePage, prefs);
+// }
+// private:
+// StringPrefMember my_string_;
+// };
+//
+// my_string_ should stay in sync with the prefs::kHomePage pref and will
+// update if either the pref changes or if my_string_.SetValue is called.
+//
+// An optional observer can be passed into the Init method which can be used to
+// notify MyClass of changes. Note that if you use SetValue(), the observer
+// will not be notified.
+
+#ifndef BASE_PREFS_PUBLIC_PREF_MEMBER_H_
+#define BASE_PREFS_PUBLIC_PREF_MEMBER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop_proxy.h"
+#include "base/prefs/base_prefs_export.h"
+#include "base/prefs/pref_observer.h"
+#include "base/values.h"
+
+class PrefServiceBase;
+
+namespace subtle {
+
+class BASE_PREFS_EXPORT PrefMemberBase : public PrefObserver {
+ public:
+ // Type of callback you can register if you need to know the name of
+ // the pref that is changing.
+ typedef base::Callback<void(const std::string&)> NamedChangeCallback;
+
+ PrefServiceBase* prefs() { return prefs_; }
+ const PrefServiceBase* prefs() const { return prefs_; }
+
+ protected:
+ class BASE_PREFS_EXPORT Internal
+ : public base::RefCountedThreadSafe<Internal> {
+ public:
+ Internal();
+
+ // Update the value, either by calling |UpdateValueInternal| directly
+ // or by dispatching to the right thread.
+ // Takes ownership of |value|.
+ void UpdateValue(base::Value* value,
+ bool is_managed,
+ bool is_user_modifiable,
+ const base::Closure& callback) const;
+
+ void MoveToThread(
+ const scoped_refptr<base::MessageLoopProxy>& message_loop);
+
+ // See PrefMember<> for description.
+ bool IsManaged() const {
+ return is_managed_;
+ }
+
+ bool IsUserModifiable() const {
+ return is_user_modifiable_;
+ }
+
+ protected:
+ friend class base::RefCountedThreadSafe<Internal>;
+ virtual ~Internal();
+
+ void CheckOnCorrectThread() const {
+ DCHECK(IsOnCorrectThread());
+ }
+
+ private:
+ // This method actually updates the value. It should only be called from
+ // the thread the PrefMember is on.
+ virtual bool UpdateValueInternal(const base::Value& value) const = 0;
+
+ bool IsOnCorrectThread() const;
+
+ scoped_refptr<base::MessageLoopProxy> thread_loop_;
+ mutable bool is_managed_;
+ mutable bool is_user_modifiable_;
+
+ DISALLOW_COPY_AND_ASSIGN(Internal);
+ };
+
+ PrefMemberBase();
+ virtual ~PrefMemberBase();
+
+ // See PrefMember<> for description.
+ void Init(const char* pref_name, PrefServiceBase* prefs,
+ const NamedChangeCallback& observer);
+ void Init(const char* pref_name, PrefServiceBase* prefs);
+
+ virtual void CreateInternal() const = 0;
+
+ // See PrefMember<> for description.
+ void Destroy();
+
+ void MoveToThread(const scoped_refptr<base::MessageLoopProxy>& message_loop);
+
+ // PrefObserver
+ virtual void OnPreferenceChanged(PrefServiceBase* service,
+ const std::string& pref_name) OVERRIDE;
+
+ void VerifyValuePrefName() const {
+ DCHECK(!pref_name_.empty());
+ }
+
+ // This method is used to do the actual sync with the preference.
+ // Note: it is logically const, because it doesn't modify the state
+ // seen by the outside world. It is just doing a lazy load behind the scenes.
+ void UpdateValueFromPref(const base::Closure& callback) const;
+
+ // Verifies the preference name, and lazily loads the preference value if
+ // it hasn't been loaded yet.
+ void VerifyPref() const;
+
+ const std::string& pref_name() const { return pref_name_; }
+
+ virtual Internal* internal() const = 0;
+
+ // Used to allow registering plain base::Closure callbacks.
+ static void InvokeUnnamedCallback(const base::Closure& callback,
+ const std::string& pref_name);
+
+ private:
+ // Ordered the members to compact the class instance.
+ std::string pref_name_;
+ NamedChangeCallback observer_;
+ PrefServiceBase* prefs_;
+
+ protected:
+ bool setting_value_;
+};
+
+// This function implements StringListPrefMember::UpdateValue().
+// It is exposed here for testing purposes.
+bool BASE_PREFS_EXPORT PrefMemberVectorStringUpdate(
+ const Value& value,
+ std::vector<std::string>* string_vector);
+
+} // namespace subtle
+
+template <typename ValueType>
+class PrefMember : public subtle::PrefMemberBase {
+ public:
+ // Defer initialization to an Init method so it's easy to make this class be
+ // a member variable.
+ PrefMember() {}
+ virtual ~PrefMember() {}
+
+ // Do the actual initialization of the class. Use the two-parameter
+ // version if you don't want any notifications of changes. This
+ // method should only be called on the UI thread.
+ void Init(const char* pref_name, PrefServiceBase* prefs,
+ const NamedChangeCallback& observer) {
+ subtle::PrefMemberBase::Init(pref_name, prefs, observer);
+ }
+ void Init(const char* pref_name, PrefServiceBase* prefs,
+ const base::Closure& observer) {
+ subtle::PrefMemberBase::Init(
+ pref_name, prefs,
+ base::Bind(&PrefMemberBase::InvokeUnnamedCallback, observer));
+ }
+ void Init(const char* pref_name, PrefServiceBase* prefs) {
+ subtle::PrefMemberBase::Init(pref_name, prefs);
+ }
+
+ // Unsubscribes the PrefMember from the PrefService. After calling this
+ // function, the PrefMember may not be used any more on the UI thread.
+ // Assuming |MoveToThread| was previously called, |GetValue|, |IsManaged|,
+ // and |IsUserModifiable| can still be called from the other thread but
+ // the results will no longer update from the PrefService.
+ // This method should only be called on the UI thread.
+ void Destroy() {
+ subtle::PrefMemberBase::Destroy();
+ }
+
+ // Moves the PrefMember to another thread, allowing read accesses from there.
+ // Changes from the PrefService will be propagated asynchronously
+ // via PostTask.
+ // This method should only be used from the thread the PrefMember is currently
+ // on, which is the UI thread by default.
+ void MoveToThread(const scoped_refptr<base::MessageLoopProxy>& message_loop) {
+ subtle::PrefMemberBase::MoveToThread(message_loop);
+ }
+
+ // Check whether the pref is managed, i.e. controlled externally through
+ // enterprise configuration management (e.g. windows group policy). Returns
+ // false for unknown prefs.
+ // This method should only be used from the thread the PrefMember is currently
+ // on, which is the UI thread unless changed by |MoveToThread|.
+ bool IsManaged() const {
+ VerifyPref();
+ return internal_->IsManaged();
+ }
+
+ // Checks whether the pref can be modified by the user. This returns false
+ // when the pref is managed by a policy or an extension, and when a command
+ // line flag overrides the pref.
+ // This method should only be used from the thread the PrefMember is currently
+ // on, which is the UI thread unless changed by |MoveToThread|.
+ bool IsUserModifiable() const {
+ VerifyPref();
+ return internal_->IsUserModifiable();
+ }
+
+ // Retrieve the value of the member variable.
+ // This method should only be used from the thread the PrefMember is currently
+ // on, which is the UI thread unless changed by |MoveToThread|.
+ ValueType GetValue() const {
+ VerifyPref();
+ return internal_->value();
+ }
+
+ // Provided as a convenience.
+ ValueType operator*() const {
+ return GetValue();
+ }
+
+ // Set the value of the member variable.
+ // This method should only be called on the UI thread.
+ void SetValue(const ValueType& value) {
+ VerifyValuePrefName();
+ setting_value_ = true;
+ UpdatePref(value);
+ setting_value_ = false;
+ }
+
+ // Returns the pref name.
+ const std::string& GetPrefName() const {
+ return pref_name();
+ }
+
+ private:
+ class Internal : public subtle::PrefMemberBase::Internal {
+ public:
+ Internal() : value_(ValueType()) {}
+
+ ValueType value() {
+ CheckOnCorrectThread();
+ return value_;
+ }
+
+ protected:
+ virtual ~Internal() {}
+
+ virtual BASE_PREFS_EXPORT bool UpdateValueInternal(
+ const base::Value& value) const OVERRIDE;
+
+ // We cache the value of the pref so we don't have to keep walking the pref
+ // tree.
+ mutable ValueType value_;
+
+ DISALLOW_COPY_AND_ASSIGN(Internal);
+ };
+
+ virtual Internal* internal() const OVERRIDE { return internal_; }
+ virtual void CreateInternal() const OVERRIDE {
+ internal_ = new Internal();
+ }
+
+ // This method is used to do the actual sync with pref of the specified type.
+ void BASE_PREFS_EXPORT UpdatePref(const ValueType& value);
+
+ mutable scoped_refptr<Internal> internal_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefMember);
+};
+
+// Declaration of template specialization need to be repeated here
+// specifically for each specialization (rather than just once above)
+// or at least one of our compilers won't be happy in all cases.
+// Specifically, it was failing on ChromeOS with a complaint about
+// PrefMember<FilePath>::UpdateValueInternal not being defined when
+// built in a chroot with the following parameters:
+//
+// FEATURES="noclean nostrip" USE="-chrome_debug -chrome_remoting
+// -chrome_internal -chrome_pdf component_build"
+// ~/trunk/goma/goma-wrapper cros_chrome_make --board=${BOARD}
+// --install --runhooks
+
+template <>
+BASE_PREFS_EXPORT void PrefMember<bool>::UpdatePref(const bool& value);
+
+template <>
+BASE_PREFS_EXPORT bool PrefMember<bool>::Internal::UpdateValueInternal(
+ const Value& value) const;
+
+template <>
+BASE_PREFS_EXPORT void PrefMember<int>::UpdatePref(const int& value);
+
+template <>
+BASE_PREFS_EXPORT bool PrefMember<int>::Internal::UpdateValueInternal(
+ const Value& value) const;
+
+template <>
+BASE_PREFS_EXPORT void PrefMember<double>::UpdatePref(const double& value);
+
+template <>
+BASE_PREFS_EXPORT bool PrefMember<double>::Internal::UpdateValueInternal(
+ const Value& value) const;
+
+template <>
+BASE_PREFS_EXPORT void PrefMember<std::string>::UpdatePref(
+ const std::string& value);
+
+template <>
+BASE_PREFS_EXPORT bool PrefMember<std::string>::Internal::UpdateValueInternal(
+ const Value& value) const;
+
+template <>
+BASE_PREFS_EXPORT void PrefMember<FilePath>::UpdatePref(const FilePath& value);
+
+template <>
+BASE_PREFS_EXPORT bool PrefMember<FilePath>::Internal::UpdateValueInternal(
+ const Value& value) const;
+
+template <>
+BASE_PREFS_EXPORT void PrefMember<std::vector<std::string> >::UpdatePref(
+ const std::vector<std::string>& value);
+
+template <>
+BASE_PREFS_EXPORT bool
+PrefMember<std::vector<std::string> >::Internal::UpdateValueInternal(
+ const Value& value) const;
+
+typedef PrefMember<bool> BooleanPrefMember;
+typedef PrefMember<int> IntegerPrefMember;
+typedef PrefMember<double> DoublePrefMember;
+typedef PrefMember<std::string> StringPrefMember;
+typedef PrefMember<FilePath> FilePathPrefMember;
+// This preference member is expensive for large string arrays.
+typedef PrefMember<std::vector<std::string> > StringListPrefMember;
+
+#endif // BASE_PREFS_PUBLIC_PREF_MEMBER_H_
diff --git a/src/base/prefs/public/pref_member_unittest.cc b/src/base/prefs/public/pref_member_unittest.cc
new file mode 100644
index 0000000..4203db3
--- /dev/null
+++ b/src/base/prefs/public/pref_member_unittest.cc
@@ -0,0 +1,316 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/prefs/public/pref_member.h"
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "chrome/test/base/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kBoolPref[] = "bool";
+const char kIntPref[] = "int";
+const char kDoublePref[] = "double";
+const char kStringPref[] = "string";
+const char kStringListPref[] = "string_list";
+
+void RegisterTestPrefs(PrefService* prefs) {
+ prefs->RegisterBooleanPref(kBoolPref, false, PrefService::UNSYNCABLE_PREF);
+ prefs->RegisterIntegerPref(kIntPref, 0, PrefService::UNSYNCABLE_PREF);
+ prefs->RegisterDoublePref(kDoublePref, 0.0, PrefService::UNSYNCABLE_PREF);
+ prefs->RegisterStringPref(kStringPref,
+ "default",
+ PrefService::UNSYNCABLE_PREF);
+ prefs->RegisterListPref(kStringListPref,
+ new ListValue(),
+ PrefService::UNSYNCABLE_PREF);
+}
+
+class GetPrefValueHelper
+ : public base::RefCountedThreadSafe<GetPrefValueHelper> {
+ public:
+ GetPrefValueHelper() : value_(false), pref_thread_("pref thread") {
+ pref_thread_.Start();
+ }
+
+ void Init(const char* pref_name, PrefService* prefs) {
+ pref_.Init(pref_name, prefs);
+ pref_.MoveToThread(pref_thread_.message_loop_proxy());
+ }
+
+ void Destroy() {
+ pref_.Destroy();
+ }
+
+ void FetchValue() {
+ base::WaitableEvent event(true, false);
+ ASSERT_TRUE(
+ pref_thread_.message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&GetPrefValueHelper::GetPrefValue, this, &event)));
+ event.Wait();
+ }
+
+ bool value() { return value_; }
+
+ private:
+ friend class base::RefCountedThreadSafe<GetPrefValueHelper>;
+ ~GetPrefValueHelper() {}
+
+ void GetPrefValue(base::WaitableEvent* event) {
+ value_ = pref_.GetValue();
+ event->Signal();
+ }
+
+ BooleanPrefMember pref_;
+ bool value_;
+
+ base::Thread pref_thread_; // The thread |pref_| runs on.
+};
+
+class PrefMemberTestClass {
+ public:
+ explicit PrefMemberTestClass(PrefService* prefs)
+ : observe_cnt_(0), prefs_(prefs) {
+ str_.Init(kStringPref, prefs,
+ base::Bind(&PrefMemberTestClass::OnPreferenceChanged,
+ base::Unretained(this)));
+ }
+
+ void OnPreferenceChanged(const std::string& pref_name) {
+ EXPECT_EQ(pref_name, kStringPref);
+ EXPECT_EQ(str_.GetValue(), prefs_->GetString(kStringPref));
+ ++observe_cnt_;
+ }
+
+ StringPrefMember str_;
+ int observe_cnt_;
+
+ private:
+ PrefService* prefs_;
+};
+
+} // anonymous namespace
+
+TEST(PrefMemberTest, BasicGetAndSet) {
+ TestingPrefService prefs;
+ RegisterTestPrefs(&prefs);
+
+ // Test bool
+ BooleanPrefMember boolean;
+ boolean.Init(kBoolPref, &prefs);
+
+ // Check the defaults
+ EXPECT_FALSE(prefs.GetBoolean(kBoolPref));
+ EXPECT_FALSE(boolean.GetValue());
+ EXPECT_FALSE(*boolean);
+
+ // Try changing through the member variable.
+ boolean.SetValue(true);
+ EXPECT_TRUE(boolean.GetValue());
+ EXPECT_TRUE(prefs.GetBoolean(kBoolPref));
+ EXPECT_TRUE(*boolean);
+
+ // Try changing back through the pref.
+ prefs.SetBoolean(kBoolPref, false);
+ EXPECT_FALSE(prefs.GetBoolean(kBoolPref));
+ EXPECT_FALSE(boolean.GetValue());
+ EXPECT_FALSE(*boolean);
+
+ // Test int
+ IntegerPrefMember integer;
+ integer.Init(kIntPref, &prefs);
+
+ // Check the defaults
+ EXPECT_EQ(0, prefs.GetInteger(kIntPref));
+ EXPECT_EQ(0, integer.GetValue());
+ EXPECT_EQ(0, *integer);
+
+ // Try changing through the member variable.
+ integer.SetValue(5);
+ EXPECT_EQ(5, integer.GetValue());
+ EXPECT_EQ(5, prefs.GetInteger(kIntPref));
+ EXPECT_EQ(5, *integer);
+
+ // Try changing back through the pref.
+ prefs.SetInteger(kIntPref, 2);
+ EXPECT_EQ(2, prefs.GetInteger(kIntPref));
+ EXPECT_EQ(2, integer.GetValue());
+ EXPECT_EQ(2, *integer);
+
+ // Test double
+ DoublePrefMember double_member;
+ double_member.Init(kDoublePref, &prefs);
+
+ // Check the defaults
+ EXPECT_EQ(0.0, prefs.GetDouble(kDoublePref));
+ EXPECT_EQ(0.0, double_member.GetValue());
+ EXPECT_EQ(0.0, *double_member);
+
+ // Try changing through the member variable.
+ double_member.SetValue(1.0);
+ EXPECT_EQ(1.0, double_member.GetValue());
+ EXPECT_EQ(1.0, prefs.GetDouble(kDoublePref));
+ EXPECT_EQ(1.0, *double_member);
+
+ // Try changing back through the pref.
+ prefs.SetDouble(kDoublePref, 3.0);
+ EXPECT_EQ(3.0, prefs.GetDouble(kDoublePref));
+ EXPECT_EQ(3.0, double_member.GetValue());
+ EXPECT_EQ(3.0, *double_member);
+
+ // Test string
+ StringPrefMember string;
+ string.Init(kStringPref, &prefs);
+
+ // Check the defaults
+ EXPECT_EQ("default", prefs.GetString(kStringPref));
+ EXPECT_EQ("default", string.GetValue());
+ EXPECT_EQ("default", *string);
+
+ // Try changing through the member variable.
+ string.SetValue("foo");
+ EXPECT_EQ("foo", string.GetValue());
+ EXPECT_EQ("foo", prefs.GetString(kStringPref));
+ EXPECT_EQ("foo", *string);
+
+ // Try changing back through the pref.
+ prefs.SetString(kStringPref, "bar");
+ EXPECT_EQ("bar", prefs.GetString(kStringPref));
+ EXPECT_EQ("bar", string.GetValue());
+ EXPECT_EQ("bar", *string);
+
+ // Test string list
+ ListValue expected_list;
+ std::vector<std::string> expected_vector;
+ StringListPrefMember string_list;
+ string_list.Init(kStringListPref, &prefs);
+
+ // Check the defaults
+ EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref)));
+ EXPECT_EQ(expected_vector, string_list.GetValue());
+ EXPECT_EQ(expected_vector, *string_list);
+
+ // Try changing through the pref member.
+ expected_list.AppendString("foo");
+ expected_vector.push_back("foo");
+ string_list.SetValue(expected_vector);
+
+ EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref)));
+ EXPECT_EQ(expected_vector, string_list.GetValue());
+ EXPECT_EQ(expected_vector, *string_list);
+
+ // Try adding through the pref.
+ expected_list.AppendString("bar");
+ expected_vector.push_back("bar");
+ prefs.Set(kStringListPref, expected_list);
+
+ EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref)));
+ EXPECT_EQ(expected_vector, string_list.GetValue());
+ EXPECT_EQ(expected_vector, *string_list);
+
+ // Try removing through the pref.
+ expected_list.Remove(0, NULL);
+ expected_vector.erase(expected_vector.begin());
+ prefs.Set(kStringListPref, expected_list);
+
+ EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref)));
+ EXPECT_EQ(expected_vector, string_list.GetValue());
+ EXPECT_EQ(expected_vector, *string_list);
+}
+
+TEST(PrefMemberTest, InvalidList) {
+ // Set the vector to an initial good value.
+ std::vector<std::string> expected_vector;
+ expected_vector.push_back("foo");
+
+ // Try to add a valid list first.
+ ListValue list;
+ list.AppendString("foo");
+ std::vector<std::string> vector;
+ EXPECT_TRUE(subtle::PrefMemberVectorStringUpdate(list, &vector));
+ EXPECT_EQ(expected_vector, vector);
+
+ // Now try to add an invalid list. |vector| should not be changed.
+ list.AppendInteger(0);
+ EXPECT_FALSE(subtle::PrefMemberVectorStringUpdate(list, &vector));
+ EXPECT_EQ(expected_vector, vector);
+}
+
+TEST(PrefMemberTest, TwoPrefs) {
+ // Make sure two DoublePrefMembers stay in sync.
+ TestingPrefService prefs;
+ RegisterTestPrefs(&prefs);
+
+ DoublePrefMember pref1;
+ pref1.Init(kDoublePref, &prefs);
+ DoublePrefMember pref2;
+ pref2.Init(kDoublePref, &prefs);
+
+ pref1.SetValue(2.3);
+ EXPECT_EQ(2.3, *pref2);
+
+ pref2.SetValue(3.5);
+ EXPECT_EQ(3.5, *pref1);
+
+ prefs.SetDouble(kDoublePref, 4.2);
+ EXPECT_EQ(4.2, *pref1);
+ EXPECT_EQ(4.2, *pref2);
+}
+
+TEST(PrefMemberTest, Observer) {
+ TestingPrefService prefs;
+ RegisterTestPrefs(&prefs);
+
+ PrefMemberTestClass test_obj(&prefs);
+ EXPECT_EQ("default", *test_obj.str_);
+
+ // Calling SetValue should not fire the observer.
+ test_obj.str_.SetValue("hello");
+ EXPECT_EQ(0, test_obj.observe_cnt_);
+ EXPECT_EQ("hello", prefs.GetString(kStringPref));
+
+ // Changing the pref does fire the observer.
+ prefs.SetString(kStringPref, "world");
+ EXPECT_EQ(1, test_obj.observe_cnt_);
+ EXPECT_EQ("world", *(test_obj.str_));
+
+ // Not changing the value should not fire the observer.
+ prefs.SetString(kStringPref, "world");
+ EXPECT_EQ(1, test_obj.observe_cnt_);
+ EXPECT_EQ("world", *(test_obj.str_));
+
+ prefs.SetString(kStringPref, "hello");
+ EXPECT_EQ(2, test_obj.observe_cnt_);
+ EXPECT_EQ("hello", prefs.GetString(kStringPref));
+}
+
+TEST(PrefMemberTest, NoInit) {
+ // Make sure not calling Init on a PrefMember doesn't cause problems.
+ IntegerPrefMember pref;
+}
+
+TEST(PrefMemberTest, MoveToThread) {
+ TestingPrefService prefs;
+ scoped_refptr<GetPrefValueHelper> helper(new GetPrefValueHelper());
+ RegisterTestPrefs(&prefs);
+ helper->Init(kBoolPref, &prefs);
+
+ helper->FetchValue();
+ EXPECT_FALSE(helper->value());
+
+ prefs.SetBoolean(kBoolPref, true);
+
+ helper->FetchValue();
+ EXPECT_TRUE(helper->value());
+
+ helper->Destroy();
+
+ helper->FetchValue();
+ EXPECT_TRUE(helper->value());
+}
diff --git a/src/base/prefs/public/pref_service_base.h b/src/base/prefs/public/pref_service_base.h
new file mode 100644
index 0000000..504632b
--- /dev/null
+++ b/src/base/prefs/public/pref_service_base.h
@@ -0,0 +1,268 @@
+// Copyright (c) 2012 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 the base interface for a preference services that provides
+// a way to access the application's current preferences.
+//
+// This base interface assumes all preferences are local. See
+// SyncablePrefServiceBase for the interface to a preference service
+// that stores preferences that can be synced.
+//
+// Chromium settings and storage represent user-selected preferences and
+// information and MUST not be extracted, overwritten or modified except
+// through Chromium defined APIs.
+
+#ifndef BASE_PREFS_PUBLIC_PREF_SERVICE_BASE_H_
+#define BASE_PREFS_PUBLIC_PREF_SERVICE_BASE_H_
+
+#include "base/values.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace subtle {
+class PrefMemberBase;
+}
+
+class FilePath;
+class PrefObserver;
+
+class PrefServiceBase {
+ public:
+ // Retrieves a PrefServiceBase for the given context.
+ static PrefServiceBase* FromBrowserContext(content::BrowserContext* context);
+
+ virtual ~PrefServiceBase() {}
+
+ // Enum used when registering preferences to determine if it should be synced
+ // or not. This is only used for profile prefs, not local state prefs.
+ // See the Register*Pref methods for profile prefs below.
+ enum PrefSyncStatus {
+ UNSYNCABLE_PREF,
+ SYNCABLE_PREF
+ };
+
+ // Interface to a single preference.
+ class Preference {
+ public:
+ virtual ~Preference() {}
+
+ // Returns the name of the Preference (i.e., the key, e.g.,
+ // browser.window_placement).
+ virtual const std::string name() const = 0;
+
+ // Returns the registered type of the preference.
+ virtual base::Value::Type GetType() const = 0;
+
+ // Returns the value of the Preference, falling back to the registered
+ // default value if no other has been set.
+ virtual const base::Value* GetValue() const = 0;
+
+ // Returns the value recommended by the admin, if any.
+ virtual const base::Value* GetRecommendedValue() const = 0;
+
+ // Returns true if the Preference is managed, i.e. set by an admin policy.
+ // Since managed prefs have the highest priority, this also indicates
+ // whether the pref is actually being controlled by the policy setting.
+ virtual bool IsManaged() const = 0;
+
+ // Returns true if the Preference is recommended, i.e. set by an admin
+ // policy but the user is allowed to change it.
+ virtual bool IsRecommended() const = 0;
+
+ // Returns true if the Preference has a value set by an extension, even if
+ // that value is being overridden by a higher-priority source.
+ virtual bool HasExtensionSetting() const = 0;
+
+ // Returns true if the Preference has a user setting, even if that value is
+ // being overridden by a higher-priority source.
+ virtual bool HasUserSetting() const = 0;
+
+ // Returns true if the Preference value is currently being controlled by an
+ // extension, and not by any higher-priority source.
+ virtual bool IsExtensionControlled() const = 0;
+
+ // Returns true if the Preference value is currently being controlled by a
+ // user setting, and not by any higher-priority source.
+ virtual bool IsUserControlled() const = 0;
+
+ // Returns true if the Preference is currently using its default value,
+ // and has not been set by any higher-priority source (even with the same
+ // value).
+ virtual bool IsDefaultValue() const = 0;
+
+ // Returns true if the user can change the Preference value, which is the
+ // case if no higher-priority source than the user store controls the
+ // Preference.
+ virtual bool IsUserModifiable() const = 0;
+
+ // Returns true if an extension can change the Preference value, which is
+ // the case if no higher-priority source than the extension store controls
+ // the Preference.
+ virtual bool IsExtensionModifiable() const = 0;
+ };
+
+ // Returns true if the preference for the given preference name is available
+ // and is managed.
+ virtual bool IsManagedPreference(const char* pref_name) const = 0;
+
+ // Returns |true| if a preference with the given name is available and its
+ // value can be changed by the user.
+ virtual bool IsUserModifiablePreference(const char* pref_name) const = 0;
+
+ // Make the PrefService aware of a pref.
+ // TODO(zea): split local state and profile prefs into their own subclasses.
+ // ---------- Local state prefs ----------
+ virtual void RegisterBooleanPref(const char* path,
+ bool default_value) = 0;
+ virtual void RegisterIntegerPref(const char* path,
+ int default_value) = 0;
+ virtual void RegisterDoublePref(const char* path,
+ double default_value) = 0;
+ virtual void RegisterStringPref(const char* path,
+ const std::string& default_value) = 0;
+ virtual void RegisterFilePathPref(const char* path,
+ const FilePath& default_value) = 0;
+ virtual void RegisterListPref(const char* path) = 0;
+ virtual void RegisterDictionaryPref(const char* path) = 0;
+ // These take ownership of the default_value:
+ virtual void RegisterListPref(const char* path,
+ base::ListValue* default_value) = 0;
+ virtual void RegisterDictionaryPref(
+ const char* path, base::DictionaryValue* default_value) = 0;
+ // These variants use a default value from the locale dll instead.
+ virtual void RegisterLocalizedBooleanPref(
+ const char* path, int locale_default_message_id) = 0;
+ virtual void RegisterLocalizedIntegerPref(
+ const char* path, int locale_default_message_id) = 0;
+ virtual void RegisterLocalizedDoublePref(
+ const char* path, int locale_default_message_id) = 0;
+ virtual void RegisterLocalizedStringPref(
+ const char* path, int locale_default_message_id) = 0;
+ virtual void RegisterInt64Pref(const char* path,
+ int64 default_value) = 0;
+
+ // ---------- Profile prefs ----------
+ // Profile prefs must specify whether the pref should be synchronized across
+ // machines or not (see PrefSyncStatus enum above).
+ virtual void RegisterBooleanPref(const char* path,
+ bool default_value,
+ PrefSyncStatus sync_status) = 0;
+ virtual void RegisterIntegerPref(const char* path,
+ int default_value,
+ PrefSyncStatus sync_status) = 0;
+ virtual void RegisterDoublePref(const char* path,
+ double default_value,
+ PrefSyncStatus sync_status) = 0;
+ virtual void RegisterStringPref(const char* path,
+ const std::string& default_value,
+ PrefSyncStatus sync_status) = 0;
+ virtual void RegisterFilePathPref(const char* path,
+ const FilePath& default_value,
+ PrefSyncStatus sync_status) = 0;
+ virtual void RegisterListPref(const char* path,
+ PrefSyncStatus sync_status) = 0;
+ virtual void RegisterDictionaryPref(const char* path,
+ PrefSyncStatus sync_status) = 0;
+ // These take ownership of the default_value:
+ virtual void RegisterListPref(const char* path,
+ base::ListValue* default_value,
+ PrefSyncStatus sync_status) = 0;
+ virtual void RegisterDictionaryPref(const char* path,
+ base::DictionaryValue* default_value,
+ PrefSyncStatus sync_status) = 0;
+ // These variants use a default value from the locale dll instead.
+ virtual void RegisterLocalizedBooleanPref(
+ const char* path,
+ int locale_default_message_id,
+ PrefSyncStatus sync_status) = 0;
+ virtual void RegisterLocalizedIntegerPref(
+ const char* path,
+ int locale_default_message_id,
+ PrefSyncStatus sync_status) = 0;
+ virtual void RegisterLocalizedDoublePref(
+ const char* path,
+ int locale_default_message_id,
+ PrefSyncStatus sync_status) = 0;
+ virtual void RegisterLocalizedStringPref(
+ const char* path,
+ int locale_default_message_id,
+ PrefSyncStatus sync_status) = 0;
+ virtual void RegisterInt64Pref(const char* path,
+ int64 default_value,
+ PrefSyncStatus sync_status) = 0;
+ virtual void RegisterUint64Pref(const char* path,
+ uint64 default_value,
+ PrefSyncStatus sync_status) = 0;
+ // Unregisters a preference.
+ virtual void UnregisterPreference(const char* path) = 0;
+
+ // Look up a preference. Returns NULL if the preference is not
+ // registered.
+ virtual const Preference* FindPreference(const char* pref_name) const = 0;
+
+ // If the path is valid and the value at the end of the path matches the type
+ // specified, it will return the specified value. Otherwise, the default
+ // value (set when the pref was registered) will be returned.
+ virtual bool GetBoolean(const char* path) const = 0;
+ virtual int GetInteger(const char* path) const = 0;
+ virtual double GetDouble(const char* path) const = 0;
+ virtual std::string GetString(const char* path) const = 0;
+ virtual FilePath GetFilePath(const char* path) const = 0;
+
+ // Returns the branch if it exists, or the registered default value otherwise.
+ // Note that |path| must point to a registered preference. In that case, these
+ // functions will never return NULL.
+ virtual const base::DictionaryValue* GetDictionary(
+ const char* path) const = 0;
+ virtual const base::ListValue* GetList(const char* path) const = 0;
+
+ // Removes a user pref and restores the pref to its default value.
+ virtual void ClearPref(const char* path) = 0;
+
+ // If the path is valid (i.e., registered), update the pref value in the user
+ // prefs.
+ // To set the value of dictionary or list values in the pref tree use
+ // Set(), but to modify the value of a dictionary or list use either
+ // ListPrefUpdate or DictionaryPrefUpdate from scoped_user_pref_update.h.
+ virtual void Set(const char* path, const base::Value& value) = 0;
+ virtual void SetBoolean(const char* path, bool value) = 0;
+ virtual void SetInteger(const char* path, int value) = 0;
+ virtual void SetDouble(const char* path, double value) = 0;
+ virtual void SetString(const char* path, const std::string& value) = 0;
+ virtual void SetFilePath(const char* path, const FilePath& value) = 0;
+
+ // Int64 helper methods that actually store the given value as a string.
+ // Note that if obtaining the named value via GetDictionary or GetList, the
+ // Value type will be TYPE_STRING.
+ virtual void SetInt64(const char* path, int64 value) = 0;
+ virtual int64 GetInt64(const char* path) const = 0;
+
+ // As above, but for unsigned values.
+ virtual void SetUint64(const char* path, uint64 value) = 0;
+ virtual uint64 GetUint64(const char* path) const = 0;
+
+ protected:
+ // Registration of pref change observers must be done using the
+ // PrefChangeRegistrar, which is declared as a friend here to grant it
+ // access to the otherwise protected members Add/RemovePrefObserver.
+ // PrefMember registers for preferences changes notification directly to
+ // avoid the storage overhead of the registrar, so its base class must be
+ // declared as a friend, too.
+ friend class PrefChangeRegistrar;
+ friend class subtle::PrefMemberBase;
+
+ // These are protected so they can only be accessed by the friend
+ // classes listed above.
+ //
+ // If the pref at the given path changes, we call the observer's
+ // OnPreferenceChanged method. Note that observers should not call
+ // these methods directly but rather use a PrefChangeRegistrar to
+ // make sure the observer gets cleaned up properly.
+ virtual void AddPrefObserver(const char* path, PrefObserver* obs) = 0;
+ virtual void RemovePrefObserver(const char* path, PrefObserver* obs) = 0;
+};
+
+#endif // BASE_PREFS_PUBLIC_PREF_SERVICE_BASE_H_
diff --git a/src/base/prefs/test/data/pref_service/invalid.json b/src/base/prefs/test/data/pref_service/invalid.json
new file mode 100644
index 0000000..43392a9
--- /dev/null
+++ b/src/base/prefs/test/data/pref_service/invalid.json
@@ -0,0 +1 @@
+!@#$%^&
\ No newline at end of file
diff --git a/src/base/prefs/test/data/pref_service/read.json b/src/base/prefs/test/data/pref_service/read.json
new file mode 100644
index 0000000..ea578a4
--- /dev/null
+++ b/src/base/prefs/test/data/pref_service/read.json
@@ -0,0 +1,8 @@
+{
+ "homepage": "http://www.cnn.com",
+ "some_directory": "/usr/local/",
+ "tabs": {
+ "new_windows_in_tabs": true,
+ "max_tabs": 20
+ }
+}
diff --git a/src/base/prefs/test/data/pref_service/read.need_empty_value.json b/src/base/prefs/test/data/pref_service/read.need_empty_value.json
new file mode 100644
index 0000000..48e1749
--- /dev/null
+++ b/src/base/prefs/test/data/pref_service/read.need_empty_value.json
@@ -0,0 +1,10 @@
+{
+ "list": [ 1 ],
+ "list_needs_empty_value": [ 2 ],
+ "dict": {
+ "dummy": true
+ },
+ "dict_needs_empty_value": {
+ "dummy": true
+ }
+}
diff --git a/src/base/prefs/test/data/pref_service/write.golden.json b/src/base/prefs/test/data/pref_service/write.golden.json
new file mode 100644
index 0000000..9a5523c
--- /dev/null
+++ b/src/base/prefs/test/data/pref_service/write.golden.json
@@ -0,0 +1,11 @@
+{
+ "homepage": "http://www.cnn.com",
+ "long_int": {
+ "pref": "214748364842"
+ },
+ "some_directory": "/usr/sbin/",
+ "tabs": {
+ "max_tabs": 10,
+ "new_windows_in_tabs": false
+ }
+}
diff --git a/src/base/prefs/test/data/pref_service/write.golden.need_empty_value.json b/src/base/prefs/test/data/pref_service/write.golden.need_empty_value.json
new file mode 100644
index 0000000..fa79590
--- /dev/null
+++ b/src/base/prefs/test/data/pref_service/write.golden.need_empty_value.json
@@ -0,0 +1,6 @@
+{
+ "dict_needs_empty_value": {
+
+ },
+ "list_needs_empty_value": [ ]
+}
diff --git a/src/base/prefs/testing_pref_store.cc b/src/base/prefs/testing_pref_store.cc
new file mode 100644
index 0000000..85f35bb
--- /dev/null
+++ b/src/base/prefs/testing_pref_store.cc
@@ -0,0 +1,135 @@
+// Copyright (c) 2012 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/prefs/testing_pref_store.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+
+TestingPrefStore::TestingPrefStore()
+ : read_only_(true),
+ init_complete_(false) {
+}
+
+bool TestingPrefStore::GetValue(const std::string& key,
+ const Value** value) const {
+ return prefs_.GetValue(key, value);
+}
+
+bool TestingPrefStore::GetMutableValue(const std::string& key,
+ Value** value) {
+ return prefs_.GetValue(key, value);
+}
+
+void TestingPrefStore::AddObserver(PrefStore::Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void TestingPrefStore::RemoveObserver(PrefStore::Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+size_t TestingPrefStore::NumberOfObservers() const {
+ return observers_.size();
+}
+
+bool TestingPrefStore::IsInitializationComplete() const {
+ return init_complete_;
+}
+
+void TestingPrefStore::SetValue(const std::string& key, Value* value) {
+ if (prefs_.SetValue(key, value))
+ NotifyPrefValueChanged(key);
+}
+
+void TestingPrefStore::SetValueSilently(const std::string& key, Value* value) {
+ prefs_.SetValue(key, value);
+}
+
+void TestingPrefStore::RemoveValue(const std::string& key) {
+ if (prefs_.RemoveValue(key))
+ NotifyPrefValueChanged(key);
+}
+
+void TestingPrefStore::MarkNeedsEmptyValue(const std::string& key) {
+}
+
+bool TestingPrefStore::ReadOnly() const {
+ return read_only_;
+}
+
+PersistentPrefStore::PrefReadError TestingPrefStore::GetReadError() const {
+ return PersistentPrefStore::PREF_READ_ERROR_NONE;
+}
+
+PersistentPrefStore::PrefReadError TestingPrefStore::ReadPrefs() {
+ NotifyInitializationCompleted();
+ return PersistentPrefStore::PREF_READ_ERROR_NONE;
+}
+
+void TestingPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate_raw) {
+ scoped_ptr<ReadErrorDelegate> error_delegate(error_delegate_raw);
+ NotifyInitializationCompleted();
+}
+
+void TestingPrefStore::SetInitializationCompleted() {
+ init_complete_ = true;
+ NotifyInitializationCompleted();
+}
+
+void TestingPrefStore::NotifyPrefValueChanged(const std::string& key) {
+ FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key));
+}
+
+void TestingPrefStore::NotifyInitializationCompleted() {
+ FOR_EACH_OBSERVER(Observer, observers_, OnInitializationCompleted(true));
+}
+
+void TestingPrefStore::ReportValueChanged(const std::string& key) {
+ FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key));
+}
+
+void TestingPrefStore::SetString(const std::string& key,
+ const std::string& value) {
+ SetValue(key, new base::StringValue(value));
+}
+
+void TestingPrefStore::SetInteger(const std::string& key, int value) {
+ SetValue(key, new base::FundamentalValue(value));
+}
+
+void TestingPrefStore::SetBoolean(const std::string& key, bool value) {
+ SetValue(key, new base::FundamentalValue(value));
+}
+
+bool TestingPrefStore::GetString(const std::string& key,
+ std::string* value) const {
+ const Value* stored_value;
+ if (!prefs_.GetValue(key, &stored_value) || !stored_value)
+ return false;
+
+ return stored_value->GetAsString(value);
+}
+
+bool TestingPrefStore::GetInteger(const std::string& key, int* value) const {
+ const Value* stored_value;
+ if (!prefs_.GetValue(key, &stored_value) || !stored_value)
+ return false;
+
+ return stored_value->GetAsInteger(value);
+}
+
+bool TestingPrefStore::GetBoolean(const std::string& key, bool* value) const {
+ const Value* stored_value;
+ if (!prefs_.GetValue(key, &stored_value) || !stored_value)
+ return false;
+
+ return stored_value->GetAsBoolean(value);
+}
+
+void TestingPrefStore::set_read_only(bool read_only) {
+ read_only_ = read_only;
+}
+
+TestingPrefStore::~TestingPrefStore() {}
diff --git a/src/base/prefs/testing_pref_store.h b/src/base/prefs/testing_pref_store.h
new file mode 100644
index 0000000..a9f1e92
--- /dev/null
+++ b/src/base/prefs/testing_pref_store.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2012 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_PREFS_TESTING_PREF_STORE_H_
+#define BASE_PREFS_TESTING_PREF_STORE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/observer_list.h"
+#include "base/prefs/persistent_pref_store.h"
+#include "base/prefs/pref_value_map.h"
+
+// |TestingPrefStore| is a preference store implementation that allows tests to
+// explicitly manipulate the contents of the store, triggering notifications
+// where appropriate.
+class TestingPrefStore : public PersistentPrefStore {
+ public:
+ TestingPrefStore();
+
+ // Overriden from PrefStore.
+ virtual bool GetValue(const std::string& key,
+ const base::Value** result) const OVERRIDE;
+ virtual void AddObserver(PrefStore::Observer* observer) OVERRIDE;
+ virtual void RemoveObserver(PrefStore::Observer* observer) OVERRIDE;
+ virtual size_t NumberOfObservers() const OVERRIDE;
+ virtual bool IsInitializationComplete() const OVERRIDE;
+
+ // PersistentPrefStore overrides:
+ virtual bool GetMutableValue(const std::string& key,
+ base::Value** result) OVERRIDE;
+ virtual void ReportValueChanged(const std::string& key) OVERRIDE;
+ virtual void SetValue(const std::string& key, base::Value* value) OVERRIDE;
+ virtual void SetValueSilently(const std::string& key,
+ base::Value* value) OVERRIDE;
+ virtual void RemoveValue(const std::string& key) OVERRIDE;
+ virtual void MarkNeedsEmptyValue(const std::string& key) OVERRIDE;
+ virtual bool ReadOnly() const OVERRIDE;
+ virtual PrefReadError GetReadError() const OVERRIDE;
+ virtual PersistentPrefStore::PrefReadError ReadPrefs() OVERRIDE;
+ virtual void ReadPrefsAsync(ReadErrorDelegate* error_delegate) OVERRIDE;
+ virtual void CommitPendingWrite() OVERRIDE {}
+
+ // Marks the store as having completed initialization.
+ void SetInitializationCompleted();
+
+ // Used for tests to trigger notifications explicitly.
+ void NotifyPrefValueChanged(const std::string& key);
+ void NotifyInitializationCompleted();
+
+ // Some convenience getters/setters.
+ void SetString(const std::string& key, const std::string& value);
+ void SetInteger(const std::string& key, int value);
+ void SetBoolean(const std::string& key, bool value);
+
+ bool GetString(const std::string& key, std::string* value) const;
+ bool GetInteger(const std::string& key, int* value) const;
+ bool GetBoolean(const std::string& key, bool* value) const;
+
+ // Getter and Setter methods for setting and getting the state of the
+ // |TestingPrefStore|.
+ virtual void set_read_only(bool read_only);
+
+ protected:
+ virtual ~TestingPrefStore();
+
+ private:
+ // Stores the preference values.
+ PrefValueMap prefs_;
+
+ // Flag that indicates if the PrefStore is read-only
+ bool read_only_;
+
+ // Whether initialization has been completed.
+ bool init_complete_;
+
+ ObserverList<PrefStore::Observer, true> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestingPrefStore);
+};
+
+#endif // BASE_PREFS_TESTING_PREF_STORE_H_
diff --git a/src/base/prefs/value_map_pref_store.cc b/src/base/prefs/value_map_pref_store.cc
new file mode 100644
index 0000000..1d58aa3
--- /dev/null
+++ b/src/base/prefs/value_map_pref_store.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 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/prefs/value_map_pref_store.h"
+
+#include <algorithm>
+
+#include "base/stl_util.h"
+#include "base/values.h"
+
+ValueMapPrefStore::ValueMapPrefStore() {}
+
+bool ValueMapPrefStore::GetValue(const std::string& key,
+ const Value** value) const {
+ return prefs_.GetValue(key, value);
+}
+
+void ValueMapPrefStore::AddObserver(PrefStore::Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void ValueMapPrefStore::RemoveObserver(PrefStore::Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+size_t ValueMapPrefStore::NumberOfObservers() const {
+ return observers_.size();
+}
+
+ValueMapPrefStore::~ValueMapPrefStore() {}
+
+void ValueMapPrefStore::SetValue(const std::string& key, Value* value) {
+ if (prefs_.SetValue(key, value))
+ FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key));
+}
+
+void ValueMapPrefStore::RemoveValue(const std::string& key) {
+ if (prefs_.RemoveValue(key))
+ FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key));
+}
+
+void ValueMapPrefStore::NotifyInitializationCompleted() {
+ FOR_EACH_OBSERVER(Observer, observers_, OnInitializationCompleted(true));
+}
diff --git a/src/base/prefs/value_map_pref_store.h b/src/base/prefs/value_map_pref_store.h
new file mode 100644
index 0000000..c9c9b1c
--- /dev/null
+++ b/src/base/prefs/value_map_pref_store.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2012 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_PREFS_VALUE_MAP_PREF_STORE_H_
+#define BASE_PREFS_VALUE_MAP_PREF_STORE_H_
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/observer_list.h"
+#include "base/prefs/base_prefs_export.h"
+#include "base/prefs/pref_store.h"
+#include "base/prefs/pref_value_map.h"
+
+// A basic PrefStore implementation that uses a simple name-value map for
+// storing the preference values.
+class BASE_PREFS_EXPORT ValueMapPrefStore : public PrefStore {
+ public:
+ ValueMapPrefStore();
+
+ // PrefStore overrides:
+ virtual bool GetValue(const std::string& key,
+ const base::Value** value) const OVERRIDE;
+ virtual void AddObserver(PrefStore::Observer* observer) OVERRIDE;
+ virtual void RemoveObserver(PrefStore::Observer* observer) OVERRIDE;
+ virtual size_t NumberOfObservers() const OVERRIDE;
+
+ protected:
+ virtual ~ValueMapPrefStore();
+
+ // Store a |value| for |key| in the store. Also generates an notification if
+ // the value changed. Assumes ownership of |value|, which must be non-NULL.
+ void SetValue(const std::string& key, base::Value* value);
+
+ // Remove the value for |key| from the store. Sends a notification if there
+ // was a value to be removed.
+ void RemoveValue(const std::string& key);
+
+ // Notify observers about the initialization completed event.
+ void NotifyInitializationCompleted();
+
+ private:
+ PrefValueMap prefs_;
+
+ ObserverList<PrefStore::Observer, true> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(ValueMapPrefStore);
+};
+
+#endif // BASE_PREFS_VALUE_MAP_PREF_STORE_H_
diff --git a/src/base/process.h b/src/base/process.h
new file mode 100644
index 0000000..4a4fe90
--- /dev/null
+++ b/src/base/process.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_PROCESS_H_
+#define BASE_PROCESS_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "build/build_config.h"
+
+#include <sys/types.h>
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_STARBOARD)
+#include "starboard/types.h"
+#endif
+
+namespace base {
+
+// ProcessHandle is a platform specific type which represents the underlying OS
+// handle to a process.
+// ProcessId is a number which identifies the process in the OS.
+#if defined(OS_WIN)
+typedef HANDLE ProcessHandle;
+typedef DWORD ProcessId;
+typedef HANDLE UserTokenHandle;
+const ProcessHandle kNullProcessHandle = NULL;
+const ProcessId kNullProcessId = 0;
+#elif defined(OS_POSIX)
+// On POSIX, our ProcessHandle will just be the PID.
+typedef pid_t ProcessHandle;
+typedef pid_t ProcessId;
+const ProcessHandle kNullProcessHandle = 0;
+const ProcessId kNullProcessId = 0;
+#elif defined(OS_STARBOARD)
+// We don't really support processes in Starboard.
+typedef int32_t ProcessHandle;
+typedef int32_t ProcessId;
+const ProcessHandle kNullProcessHandle = 0;
+const ProcessId kNullProcessId = 0;
+#endif // defined(OS_WIN)
+
+class BASE_EXPORT Process {
+ public:
+ Process() : process_(kNullProcessHandle) {
+ }
+
+ explicit Process(ProcessHandle handle) : process_(handle) {
+ }
+
+ // A handle to the current process.
+ static Process Current();
+
+ static bool CanBackgroundProcesses();
+
+ // Get/Set the handle for this process. The handle will be 0 if the process
+ // is no longer running.
+ ProcessHandle handle() const { return process_; }
+ void set_handle(ProcessHandle handle) {
+ process_ = handle;
+ }
+
+ // Get the PID for this process.
+ ProcessId pid() const;
+
+ // Is the this process the current process.
+ bool is_current() const;
+
+ // Close the process handle. This will not terminate the process.
+ void Close();
+
+ // Terminates the process with extreme prejudice. The given result code will
+ // be the exit code of the process. If the process has already exited, this
+ // will do nothing.
+ void Terminate(int result_code);
+
+ // A process is backgrounded when it's priority is lower than normal.
+ // Return true if this process is backgrounded, false otherwise.
+ bool IsProcessBackgrounded() const;
+
+ // Set a process as backgrounded. If value is true, the priority
+ // of the process will be lowered. If value is false, the priority
+ // of the process will be made "normal" - equivalent to default
+ // process priority.
+ // Returns true if the priority was changed, false otherwise.
+ bool SetProcessBackgrounded(bool value);
+
+ // Returns an integer representing the priority of a process. The meaning
+ // of this value is OS dependent.
+ int GetPriority() const;
+
+ private:
+ ProcessHandle process_;
+};
+
+} // namespace base
+
+#endif // BASE_PROCESS_H_
diff --git a/src/base/process_info.h b/src/base/process_info.h
new file mode 100644
index 0000000..429d4dc
--- /dev/null
+++ b/src/base/process_info.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2012 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_PROCESS_INFO_H_
+#define BASE_PROCESS_INFO_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+
+class Time;
+
+// Vends information about the current process.
+class BASE_EXPORT CurrentProcessInfo {
+ public:
+ // Returns the time at which the process was launched or NULL if an error
+ // occurred retrieving the information.
+ static const Time* CreationTime();
+};
+
+} // namespace base
+
+#endif // BASE_PROCESS_INFO_H_
diff --git a/src/base/process_info_mac.cc b/src/base/process_info_mac.cc
new file mode 100644
index 0000000..247a144
--- /dev/null
+++ b/src/base/process_info_mac.cc
@@ -0,0 +1,43 @@
+// Copyright (c) 2012 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/process_info.h"
+
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time.h"
+
+namespace {
+
+using base::Time;
+
+// Returns the process creation time, or NULL if an error occurred.
+Time* ProcessCreationTimeInternal() {
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };
+ size_t len = 0;
+ if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0)
+ return NULL;
+
+ scoped_ptr_malloc<struct kinfo_proc>
+ proc(static_cast<struct kinfo_proc*>(malloc(len)));
+ if (sysctl(mib, arraysize(mib), proc.get(), &len, NULL, 0) < 0)
+ return NULL;
+ return new Time(Time::FromTimeVal(proc->kp_proc.p_un.__p_starttime));
+}
+
+} // namespace
+
+namespace base {
+
+//static
+const Time* CurrentProcessInfo::CreationTime() {
+ static Time* process_creation_time = ProcessCreationTimeInternal();
+ return process_creation_time;
+}
+
+} // namespace base
diff --git a/src/base/process_info_win.cc b/src/base/process_info_win.cc
new file mode 100644
index 0000000..762961f
--- /dev/null
+++ b/src/base/process_info_win.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 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/process_info.h"
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "base/time.h"
+
+namespace {
+
+using base::Time;
+
+// Returns the process creation time, or NULL if an error occurred.
+Time* ProcessCreationTimeInternal() {
+ FILETIME creation_time = {};
+ FILETIME ignore = {};
+ if (::GetProcessTimes(::GetCurrentProcess(), &creation_time, &ignore,
+ &ignore, &ignore) == false)
+ return NULL;
+
+ return new Time(Time::FromFileTime(creation_time));
+}
+
+} // namespace
+
+namespace base {
+
+//static
+const Time* CurrentProcessInfo::CreationTime() {
+ static Time* process_creation_time = ProcessCreationTimeInternal();
+ return process_creation_time;
+}
+
+} // namespace base
diff --git a/src/base/process_linux.cc b/src/base/process_linux.cc
new file mode 100644
index 0000000..fcec287
--- /dev/null
+++ b/src/base/process_linux.cc
@@ -0,0 +1,135 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process.h"
+
+#include <errno.h>
+#include <sys/resource.h>
+
+#include "base/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "base/string_split.h"
+#include "base/synchronization/lock.h"
+
+namespace {
+const int kForegroundPriority = 0;
+
+#if defined(OS_CHROMEOS)
+// We are more aggressive in our lowering of background process priority
+// for chromeos as we have much more control over other processes running
+// on the machine.
+//
+// TODO(davemoore) Refactor this by adding support for higher levels to set
+// the foregrounding / backgrounding process so we don't have to keep
+// chrome / chromeos specific logic here.
+const int kBackgroundPriority = 19;
+const char kControlPath[] = "/sys/fs/cgroup/cpu%s/cgroup.procs";
+const char kForeground[] = "/chrome_renderers/foreground";
+const char kBackground[] = "/chrome_renderers/background";
+const char kProcPath[] = "/proc/%d/cgroup";
+
+struct CGroups {
+ // Check for cgroups files. ChromeOS supports these by default. It creates
+ // a cgroup mount in /sys/fs/cgroup and then configures two cpu task groups,
+ // one contains at most a single foreground renderer and the other contains
+ // all background renderers. This allows us to limit the impact of background
+ // renderers on foreground ones to a greater level than simple renicing.
+ bool enabled;
+ FilePath foreground_file;
+ FilePath background_file;
+
+ CGroups() {
+ foreground_file = FilePath(StringPrintf(kControlPath, kForeground));
+ background_file = FilePath(StringPrintf(kControlPath, kBackground));
+ file_util::FileSystemType foreground_type;
+ file_util::FileSystemType background_type;
+ enabled =
+ file_util::GetFileSystemType(foreground_file, &foreground_type) &&
+ file_util::GetFileSystemType(background_file, &background_type) &&
+ foreground_type == file_util::FILE_SYSTEM_CGROUP &&
+ background_type == file_util::FILE_SYSTEM_CGROUP;
+ }
+};
+
+base::LazyInstance<CGroups> cgroups = LAZY_INSTANCE_INITIALIZER;
+#else
+const int kBackgroundPriority = 5;
+#endif
+}
+
+namespace base {
+
+bool Process::IsProcessBackgrounded() const {
+ DCHECK(process_);
+
+#if defined(OS_CHROMEOS)
+ if (cgroups.Get().enabled) {
+ std::string proc;
+ if (file_util::ReadFileToString(
+ FilePath(StringPrintf(kProcPath, process_)),
+ &proc)) {
+ std::vector<std::string> proc_parts;
+ base::SplitString(proc, ':', &proc_parts);
+ DCHECK(proc_parts.size() == 3);
+ bool ret = proc_parts[2] == std::string(kBackground);
+ return ret;
+ } else {
+ return false;
+ }
+ }
+#endif
+ return GetPriority() == kBackgroundPriority;
+}
+
+bool Process::SetProcessBackgrounded(bool background) {
+ DCHECK(process_);
+
+#if defined(OS_CHROMEOS)
+ if (cgroups.Get().enabled) {
+ std::string pid = StringPrintf("%d", process_);
+ const FilePath file =
+ background ?
+ cgroups.Get().background_file : cgroups.Get().foreground_file;
+ return file_util::WriteFile(file, pid.c_str(), pid.size()) > 0;
+ }
+#endif // OS_CHROMEOS
+
+ if (!CanBackgroundProcesses())
+ return false;
+
+ int priority = background ? kBackgroundPriority : kForegroundPriority;
+ int result = setpriority(PRIO_PROCESS, process_, priority);
+ DPCHECK(result == 0);
+ return result == 0;
+}
+
+struct CheckForNicePermission {
+ CheckForNicePermission() : can_reraise_priority(false) {
+ // We won't be able to raise the priority if we don't have the right rlimit.
+ // The limit may be adjusted in /etc/security/limits.conf for PAM systems.
+ struct rlimit rlim;
+ if ((getrlimit(RLIMIT_NICE, &rlim) == 0) &&
+ (20 - kForegroundPriority) <= static_cast<int>(rlim.rlim_cur)) {
+ can_reraise_priority = true;
+ }
+ };
+
+ bool can_reraise_priority;
+};
+
+// static
+bool Process::CanBackgroundProcesses() {
+#if defined(OS_CHROMEOS)
+ if (cgroups.Get().enabled)
+ return true;
+#endif
+
+ static LazyInstance<CheckForNicePermission> check_for_nice_permission =
+ LAZY_INSTANCE_INITIALIZER;
+ return check_for_nice_permission.Get().can_reraise_priority;
+}
+
+} // namespace base
diff --git a/src/base/process_posix.cc b/src/base/process_posix.cc
new file mode 100644
index 0000000..20e623c
--- /dev/null
+++ b/src/base/process_posix.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process.h"
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "base/process_util.h"
+#include "base/logging.h"
+
+namespace base {
+
+// static
+Process Process::Current() {
+ return Process(GetCurrentProcessHandle());
+}
+
+ProcessId Process::pid() const {
+ if (process_ == 0)
+ return 0;
+
+ return GetProcId(process_);
+}
+
+bool Process::is_current() const {
+ return process_ == GetCurrentProcessHandle();
+}
+
+void Process::Close() {
+ process_ = 0;
+ // if the process wasn't terminated (so we waited) or the state
+ // wasn't already collected w/ a wait from process_utils, we're gonna
+ // end up w/ a zombie when it does finally exit.
+}
+
+void Process::Terminate(int result_code) {
+ // result_code isn't supportable.
+ if (!process_)
+ return;
+ // We don't wait here. It's the responsibility of other code to reap the
+ // child.
+ KillProcess(process_, result_code, false);
+}
+
+#if !defined(OS_LINUX)
+bool Process::IsProcessBackgrounded() const {
+ // See SetProcessBackgrounded().
+ return false;
+}
+
+bool Process::SetProcessBackgrounded(bool value) {
+ // POSIX only allows lowering the priority of a process, so if we
+ // were to lower it we wouldn't be able to raise it back to its initial
+ // priority.
+ return false;
+}
+
+// static
+bool Process::CanBackgroundProcesses() {
+ return false;
+}
+
+#endif
+
+int Process::GetPriority() const {
+ DCHECK(process_);
+ return getpriority(PRIO_PROCESS, process_);
+}
+
+} // namspace base
diff --git a/src/base/process_starboard.cc b/src/base/process_starboard.cc
new file mode 100644
index 0000000..e753b27
--- /dev/null
+++ b/src/base/process_starboard.cc
@@ -0,0 +1,61 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Starboard doesn't support the concept of processes, so this implementation
+// just minimally and trivially implements the functions of this interface that
+// are called, but that do not impact the functioning of the rest of the system.
+
+#include "base/process.h"
+
+#include "base/process_util.h"
+
+namespace base {
+
+// static
+Process Process::Current() {
+ return Process();
+}
+
+ProcessId Process::pid() const {
+ return kNullProcessId;
+}
+
+bool Process::is_current() const {
+ return true;
+}
+
+void Process::Close() {
+ process_ = kNullProcessHandle;
+}
+
+void Process::Terminate(int result_code) {}
+
+bool Process::IsProcessBackgrounded() const {
+ return false;
+}
+
+bool Process::SetProcessBackgrounded(bool value) {
+ return false;
+}
+
+// static
+bool Process::CanBackgroundProcesses() {
+ return false;
+}
+
+int Process::GetPriority() const {
+ return 0;
+}
+
+} // namespace base
diff --git a/src/base/process_util.cc b/src/base/process_util.cc
new file mode 100644
index 0000000..9979dfa
--- /dev/null
+++ b/src/base/process_util.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process_util.h"
+
+namespace base {
+
+#if defined(OS_POSIX)
+ProcessEntry::ProcessEntry() : pid_(0), ppid_(0), gid_(0) {}
+ProcessEntry::~ProcessEntry() {}
+#endif
+
+int GetProcessCount(const FilePath::StringType& executable_name,
+ const ProcessFilter* filter) {
+ int count = 0;
+ NamedProcessIterator iter(executable_name, filter);
+ while (iter.NextProcessEntry())
+ ++count;
+ return count;
+}
+
+bool KillProcesses(const FilePath::StringType& executable_name, int exit_code,
+ const ProcessFilter* filter) {
+ bool result = true;
+ NamedProcessIterator iter(executable_name, filter);
+ while (const ProcessEntry* entry = iter.NextProcessEntry()) {
+#if defined(OS_WIN)
+ result &= KillProcessById(entry->pid(), exit_code, true);
+#else
+ result &= KillProcess(entry->pid(), exit_code, true);
+#endif
+ }
+ return result;
+}
+
+const ProcessEntry* ProcessIterator::NextProcessEntry() {
+ bool result = false;
+ do {
+ result = CheckForNextProcess();
+ } while (result && !IncludeEntry());
+ if (result)
+ return &entry_;
+ return NULL;
+}
+
+ProcessIterator::ProcessEntries ProcessIterator::Snapshot() {
+ ProcessEntries found;
+ while (const ProcessEntry* process_entry = NextProcessEntry()) {
+ found.push_back(*process_entry);
+ }
+ return found;
+}
+
+bool ProcessIterator::IncludeEntry() {
+ return !filter_ || filter_->Includes(entry_);
+}
+
+NamedProcessIterator::NamedProcessIterator(
+ const FilePath::StringType& executable_name,
+ const ProcessFilter* filter) : ProcessIterator(filter),
+ executable_name_(executable_name) {
+#if defined(OS_ANDROID)
+ // On Android, the process name contains only the last 15 characters, which
+ // is in file /proc/<pid>/stat, the string between open parenthesis and close
+ // parenthesis. Please See ProcessIterator::CheckForNextProcess for details.
+ // Now if the length of input process name is greater than 15, only save the
+ // last 15 characters.
+ if (executable_name_.size() > 15) {
+ executable_name_ = FilePath::StringType(executable_name_,
+ executable_name_.size() - 15, 15);
+ }
+#endif
+}
+
+NamedProcessIterator::~NamedProcessIterator() {
+}
+
+} // namespace base
diff --git a/src/base/process_util.h b/src/base/process_util.h
new file mode 100644
index 0000000..38fbe1f
--- /dev/null
+++ b/src/base/process_util.h
@@ -0,0 +1,910 @@
+// Copyright (c) 2012 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/namespace contains utility functions for enumerating, ending and
+// computing statistics of processes.
+
+#ifndef BASE_PROCESS_UTIL_H_
+#define BASE_PROCESS_UTIL_H_
+
+#include "base/basictypes.h"
+#include "base/time.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include <tlhelp32.h>
+#elif defined(OS_MACOSX) || defined(OS_BSD)
+// kinfo_proc is defined in <sys/sysctl.h>, but this forward declaration
+// is sufficient for the vector<kinfo_proc> below.
+struct kinfo_proc;
+// malloc_zone_t is defined in <malloc/malloc.h>, but this forward declaration
+// is sufficient for GetPurgeableZone() below.
+typedef struct _malloc_zone_t malloc_zone_t;
+#if !defined(OS_BSD)
+#include <mach/mach.h>
+#endif
+#elif defined(OS_POSIX)
+#include <dirent.h>
+#include <limits.h>
+#include <sys/types.h>
+#endif
+
+#include <list>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/file_path.h"
+#include "base/process.h"
+
+#if defined(OS_POSIX)
+#include "base/posix/file_descriptor_shuffle.h"
+#endif
+
+class CommandLine;
+
+namespace base {
+
+#if defined(OS_WIN)
+struct ProcessEntry : public PROCESSENTRY32 {
+ ProcessId pid() const { return th32ProcessID; }
+ ProcessId parent_pid() const { return th32ParentProcessID; }
+ const wchar_t* exe_file() const { return szExeFile; }
+};
+
+struct IoCounters : public IO_COUNTERS {
+};
+
+// Process access masks. These constants provide platform-independent
+// definitions for the standard Windows access masks.
+// See http://msdn.microsoft.com/en-us/library/ms684880(VS.85).aspx for
+// the specific semantics of each mask value.
+const uint32 kProcessAccessTerminate = PROCESS_TERMINATE;
+const uint32 kProcessAccessCreateThread = PROCESS_CREATE_THREAD;
+const uint32 kProcessAccessSetSessionId = PROCESS_SET_SESSIONID;
+const uint32 kProcessAccessVMOperation = PROCESS_VM_OPERATION;
+const uint32 kProcessAccessVMRead = PROCESS_VM_READ;
+const uint32 kProcessAccessVMWrite = PROCESS_VM_WRITE;
+const uint32 kProcessAccessDuplicateHandle = PROCESS_DUP_HANDLE;
+const uint32 kProcessAccessCreateProcess = PROCESS_CREATE_PROCESS;
+const uint32 kProcessAccessSetQuota = PROCESS_SET_QUOTA;
+const uint32 kProcessAccessSetInformation = PROCESS_SET_INFORMATION;
+const uint32 kProcessAccessQueryInformation = PROCESS_QUERY_INFORMATION;
+const uint32 kProcessAccessSuspendResume = PROCESS_SUSPEND_RESUME;
+const uint32 kProcessAccessQueryLimitedInfomation =
+ PROCESS_QUERY_LIMITED_INFORMATION;
+const uint32 kProcessAccessWaitForTermination = SYNCHRONIZE;
+#elif defined(OS_POSIX)
+
+struct ProcessEntry {
+ ProcessEntry();
+ ~ProcessEntry();
+
+ ProcessId pid() const { return pid_; }
+ ProcessId parent_pid() const { return ppid_; }
+ ProcessId gid() const { return gid_; }
+ const char* exe_file() const { return exe_file_.c_str(); }
+ const std::vector<std::string>& cmd_line_args() const {
+ return cmd_line_args_;
+ }
+
+ ProcessId pid_;
+ ProcessId ppid_;
+ ProcessId gid_;
+ std::string exe_file_;
+ std::vector<std::string> cmd_line_args_;
+};
+
+struct IoCounters {
+ uint64_t ReadOperationCount;
+ uint64_t WriteOperationCount;
+ uint64_t OtherOperationCount;
+ uint64_t ReadTransferCount;
+ uint64_t WriteTransferCount;
+ uint64_t OtherTransferCount;
+};
+
+// Process access masks. They are not used on Posix because access checking
+// does not happen during handle creation.
+const uint32 kProcessAccessTerminate = 0;
+const uint32 kProcessAccessCreateThread = 0;
+const uint32 kProcessAccessSetSessionId = 0;
+const uint32 kProcessAccessVMOperation = 0;
+const uint32 kProcessAccessVMRead = 0;
+const uint32 kProcessAccessVMWrite = 0;
+const uint32 kProcessAccessDuplicateHandle = 0;
+const uint32 kProcessAccessCreateProcess = 0;
+const uint32 kProcessAccessSetQuota = 0;
+const uint32 kProcessAccessSetInformation = 0;
+const uint32 kProcessAccessQueryInformation = 0;
+const uint32 kProcessAccessSuspendResume = 0;
+const uint32 kProcessAccessQueryLimitedInfomation = 0;
+const uint32 kProcessAccessWaitForTermination = 0;
+#endif // defined(OS_POSIX)
+
+// Return status values from GetTerminationStatus. Don't use these as
+// exit code arguments to KillProcess*(), use platform/application
+// specific values instead.
+enum TerminationStatus {
+ TERMINATION_STATUS_NORMAL_TERMINATION, // zero exit status
+ TERMINATION_STATUS_ABNORMAL_TERMINATION, // non-zero exit status
+ TERMINATION_STATUS_PROCESS_WAS_KILLED, // e.g. SIGKILL or task manager kill
+ TERMINATION_STATUS_PROCESS_CRASHED, // e.g. Segmentation fault
+ TERMINATION_STATUS_STILL_RUNNING, // child hasn't exited yet
+ TERMINATION_STATUS_MAX_ENUM
+};
+
+#if defined(USE_LINUX_BREAKPAD)
+BASE_EXPORT extern size_t g_oom_size;
+#endif
+
+#if defined(OS_WIN)
+// Output multi-process printf, cout, cerr, etc to the cmd.exe console that ran
+// chrome. This is not thread-safe: only call from main thread.
+BASE_EXPORT void RouteStdioToConsole();
+#endif
+
+// Returns the id of the current process.
+BASE_EXPORT ProcessId GetCurrentProcId();
+
+// Returns the ProcessHandle of the current process.
+BASE_EXPORT ProcessHandle GetCurrentProcessHandle();
+
+#if defined(OS_WIN)
+// Returns the module handle to which an address belongs. The reference count
+// of the module is not incremented.
+BASE_EXPORT HMODULE GetModuleFromAddress(void* address);
+#endif
+
+#if !defined(OS_STARBOARD)
+// Converts a PID to a process handle. This handle must be closed by
+// CloseProcessHandle when you are done with it. Returns true on success.
+BASE_EXPORT bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle);
+
+// Converts a PID to a process handle. On Windows the handle is opened
+// with more access rights and must only be used by trusted code.
+// You have to close returned handle using CloseProcessHandle. Returns true
+// on success.
+// TODO(sanjeevr): Replace all calls to OpenPrivilegedProcessHandle with the
+// more specific OpenProcessHandleWithAccess method and delete this.
+BASE_EXPORT bool OpenPrivilegedProcessHandle(ProcessId pid,
+ ProcessHandle* handle);
+
+// Converts a PID to a process handle using the desired access flags. Use a
+// combination of the kProcessAccess* flags defined above for |access_flags|.
+BASE_EXPORT bool OpenProcessHandleWithAccess(ProcessId pid,
+ uint32 access_flags,
+ ProcessHandle* handle);
+
+// Closes the process handle opened by OpenProcessHandle.
+BASE_EXPORT void CloseProcessHandle(ProcessHandle process);
+
+// Returns the unique ID for the specified process. This is functionally the
+// same as Windows' GetProcessId(), but works on versions of Windows before
+// Win XP SP1 as well.
+BASE_EXPORT ProcessId GetProcId(ProcessHandle process);
+#endif // !defined(OS_STARBOARD)
+
+#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_BSD)
+// Returns the path to the executable of the given process.
+BASE_EXPORT FilePath GetProcessExecutablePath(ProcessHandle process);
+#endif
+
+#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(__LB_ANDROID__)
+// Parse the data found in /proc/<pid>/stat and return the sum of the
+// CPU-related ticks. Returns -1 on parse error.
+// Exposed for testing.
+BASE_EXPORT int ParseProcStatCPU(const std::string& input);
+
+// The maximum allowed value for the OOM score.
+const int kMaxOomScore = 1000;
+
+// This adjusts /proc/<pid>/oom_score_adj so the Linux OOM killer will
+// prefer to kill certain process types over others. The range for the
+// adjustment is [-1000, 1000], with [0, 1000] being user accessible.
+// If the Linux system doesn't support the newer oom_score_adj range
+// of [0, 1000], then we revert to using the older oom_adj, and
+// translate the given value into [0, 15]. Some aliasing of values
+// may occur in that case, of course.
+BASE_EXPORT bool AdjustOOMScore(ProcessId process, int score);
+
+// /proc/self/exe refers to the current executable.
+BASE_EXPORT extern const char kProcSelfExe[];
+#endif // defined(OS_LINUX) || defined(OS_ANDROID)
+
+#if defined(OS_POSIX)
+// Returns the ID for the parent of the given process.
+BASE_EXPORT ProcessId GetParentProcessId(ProcessHandle process);
+
+// Close all file descriptors, except those which are a destination in the
+// given multimap. Only call this function in a child process where you know
+// that there aren't any other threads.
+BASE_EXPORT void CloseSuperfluousFds(const InjectiveMultimap& saved_map);
+#endif // defined(OS_POSIX)
+
+typedef std::vector<std::pair<std::string, std::string> > EnvironmentVector;
+typedef std::vector<std::pair<int, int> > FileHandleMappingVector;
+
+#if defined(OS_MACOSX)
+// Used with LaunchOptions::synchronize and LaunchSynchronize, a
+// LaunchSynchronizationHandle is an opaque value that LaunchProcess will
+// create and set, and that LaunchSynchronize will consume and destroy.
+typedef int* LaunchSynchronizationHandle;
+#endif // defined(OS_MACOSX)
+
+// Options for launching a subprocess that are passed to LaunchProcess().
+// The default constructor constructs the object with default options.
+struct LaunchOptions {
+ LaunchOptions() : wait(false),
+#if defined(OS_WIN)
+ start_hidden(false), inherit_handles(false), as_user(NULL),
+ empty_desktop_name(false), job_handle(NULL),
+ force_breakaway_from_job_(false)
+#else
+ environ(NULL), fds_to_remap(NULL), maximize_rlimits(NULL),
+ new_process_group(false)
+#if defined(OS_LINUX)
+ , clone_flags(0)
+#endif // OS_LINUX
+#if defined(OS_CHROMEOS)
+ , ctrl_terminal_fd(-1)
+#endif // OS_CHROMEOS
+#if defined(OS_MACOSX)
+ , synchronize(NULL)
+#endif // defined(OS_MACOSX)
+#endif // !defined(OS_WIN)
+ {}
+
+ // If true, wait for the process to complete.
+ bool wait;
+
+#if defined(OS_WIN)
+ bool start_hidden;
+
+ // If true, the new process inherits handles from the parent.
+ bool inherit_handles;
+
+ // If non-NULL, runs as if the user represented by the token had launched it.
+ // Whether the application is visible on the interactive desktop depends on
+ // the token belonging to an interactive logon session.
+ //
+ // To avoid hard to diagnose problems, when specified this loads the
+ // environment variables associated with the user and if this operation fails
+ // the entire call fails as well.
+ UserTokenHandle as_user;
+
+ // If true, use an empty string for the desktop name.
+ bool empty_desktop_name;
+
+ // If non-NULL, launches the application in that job object. The process will
+ // be terminated immediately and LaunchProcess() will fail if assignment to
+ // the job object fails.
+ HANDLE job_handle;
+
+ // If set to true, ensures that the child process is launched with the
+ // CREATE_BREAKAWAY_FROM_JOB flag which allows it to breakout of the parent
+ // job if any.
+ bool force_breakaway_from_job_;
+#else
+ // If non-NULL, set/unset environment variables.
+ // See documentation of AlterEnvironment().
+ // This pointer is owned by the caller and must live through the
+ // call to LaunchProcess().
+ const EnvironmentVector* environ;
+
+ // If non-NULL, remap file descriptors according to the mapping of
+ // src fd->dest fd to propagate FDs into the child process.
+ // This pointer is owned by the caller and must live through the
+ // call to LaunchProcess().
+ const FileHandleMappingVector* fds_to_remap;
+
+ // Each element is an RLIMIT_* constant that should be raised to its
+ // rlim_max. This pointer is owned by the caller and must live through
+ // the call to LaunchProcess().
+ const std::set<int>* maximize_rlimits;
+
+ // If true, start the process in a new process group, instead of
+ // inheriting the parent's process group. The pgid of the child process
+ // will be the same as its pid.
+ bool new_process_group;
+
+#if defined(OS_LINUX)
+ // If non-zero, start the process using clone(), using flags as provided.
+ int clone_flags;
+#endif // defined(OS_LINUX)
+
+#if defined(OS_CHROMEOS)
+ // If non-negative, the specified file descriptor will be set as the launched
+ // process' controlling terminal.
+ int ctrl_terminal_fd;
+#endif // defined(OS_CHROMEOS)
+
+#if defined(OS_MACOSX)
+ // When non-NULL, a new LaunchSynchronizationHandle will be created and
+ // stored in *synchronize whenever LaunchProcess returns true in the parent
+ // process. The child process will have been created (with fork) but will
+ // be waiting (before exec) for the parent to call LaunchSynchronize with
+ // this handle. Only when LaunchSynchronize is called will the child be
+ // permitted to continue execution and call exec. LaunchSynchronize
+ // destroys the handle created by LaunchProcess.
+ //
+ // When synchronize is non-NULL, the parent must call LaunchSynchronize
+ // whenever LaunchProcess returns true. No exceptions.
+ //
+ // Synchronization is useful when the parent process needs to guarantee that
+ // it can take some action (such as recording the newly-forked child's
+ // process ID) before the child does something (such as using its process ID
+ // to communicate with its parent).
+ //
+ // |synchronize| and |wait| must not both be set simultaneously.
+ LaunchSynchronizationHandle* synchronize;
+#endif // defined(OS_MACOSX)
+
+#endif // !defined(OS_WIN)
+};
+
+#if !defined(OS_STARBOARD)
+// Launch a process via the command line |cmdline|.
+// See the documentation of LaunchOptions for details on |options|.
+//
+// Returns true upon success.
+//
+// Upon success, if |process_handle| is non-NULL, it will be filled in with the
+// handle of the launched process. NOTE: In this case, the caller is
+// responsible for closing the handle so that it doesn't leak!
+// Otherwise, the process handle will be implicitly closed.
+//
+// Unix-specific notes:
+// - All file descriptors open in the parent process will be closed in the
+// child process except for any preserved by options::fds_to_remap, and
+// stdin, stdout, and stderr. If not remapped by options::fds_to_remap,
+// stdin is reopened as /dev/null, and the child is allowed to inherit its
+// parent's stdout and stderr.
+// - If the first argument on the command line does not contain a slash,
+// PATH will be searched. (See man execvp.)
+BASE_EXPORT bool LaunchProcess(const CommandLine& cmdline,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle);
+#endif // !defined(OS_STARBOARD)
+
+#if defined(OS_WIN)
+
+enum IntegrityLevel {
+ INTEGRITY_UNKNOWN,
+ LOW_INTEGRITY,
+ MEDIUM_INTEGRITY,
+ HIGH_INTEGRITY,
+};
+// Determine the integrity level of the specified process. Returns false
+// if the system does not support integrity levels (pre-Vista) or in the case
+// of an underlying system failure.
+BASE_EXPORT bool GetProcessIntegrityLevel(ProcessHandle process,
+ IntegrityLevel* level);
+
+// Windows-specific LaunchProcess that takes the command line as a
+// string. Useful for situations where you need to control the
+// command line arguments directly, but prefer the CommandLine version
+// if launching Chrome itself.
+//
+// The first command line argument should be the path to the process,
+// and don't forget to quote it.
+//
+// Example (including literal quotes)
+// cmdline = "c:\windows\explorer.exe" -foo "c:\bar\"
+BASE_EXPORT bool LaunchProcess(const string16& cmdline,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle);
+
+#elif defined(OS_POSIX)
+// A POSIX-specific version of LaunchProcess that takes an argv array
+// instead of a CommandLine. Useful for situations where you need to
+// control the command line arguments directly, but prefer the
+// CommandLine version if launching Chrome itself.
+BASE_EXPORT bool LaunchProcess(const std::vector<std::string>& argv,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle);
+
+// AlterEnvironment returns a modified environment vector, constructed from the
+// given environment and the list of changes given in |changes|. Each key in
+// the environment is matched against the first element of the pairs. In the
+// event of a match, the value is replaced by the second of the pair, unless
+// the second is empty, in which case the key-value is removed.
+//
+// The returned array is allocated using new[] and must be freed by the caller.
+BASE_EXPORT char** AlterEnvironment(const EnvironmentVector& changes,
+ const char* const* const env);
+
+#if defined(OS_MACOSX)
+
+// After a successful call to LaunchProcess with LaunchOptions::synchronize
+// set, the parent process must call LaunchSynchronize to allow the child
+// process to proceed, and to destroy the LaunchSynchronizationHandle.
+BASE_EXPORT void LaunchSynchronize(LaunchSynchronizationHandle handle);
+
+#endif // defined(OS_MACOSX)
+#endif // defined(OS_POSIX)
+
+#if defined(OS_WIN)
+// Set JOBOBJECT_EXTENDED_LIMIT_INFORMATION to JobObject |job_object|.
+// As its limit_info.BasicLimitInformation.LimitFlags has
+// JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE.
+// When the provide JobObject |job_object| is closed, the binded process will
+// be terminated.
+BASE_EXPORT bool SetJobObjectAsKillOnJobClose(HANDLE job_object);
+#endif // defined(OS_WIN)
+
+#if !defined(OS_STARBOARD)
+// Executes the application specified by |cl| and wait for it to exit. Stores
+// the output (stdout) in |output|. Redirects stderr to /dev/null. Returns true
+// on success (application launched and exited cleanly, with exit code
+// indicating success).
+BASE_EXPORT bool GetAppOutput(const CommandLine& cl, std::string* output);
+#endif // !defined(OS_STARBOARD)
+
+#if defined(OS_POSIX)
+// A POSIX-specific version of GetAppOutput that takes an argv array
+// instead of a CommandLine. Useful for situations where you need to
+// control the command line arguments directly.
+BASE_EXPORT bool GetAppOutput(const std::vector<std::string>& argv,
+ std::string* output);
+
+// A restricted version of |GetAppOutput()| which (a) clears the environment,
+// and (b) stores at most |max_output| bytes; also, it doesn't search the path
+// for the command.
+BASE_EXPORT bool GetAppOutputRestricted(const CommandLine& cl,
+ std::string* output, size_t max_output);
+
+// A version of |GetAppOutput()| which also returns the exit code of the
+// executed command. Returns true if the application runs and exits cleanly. If
+// this is the case the exit code of the application is available in
+// |*exit_code|.
+BASE_EXPORT bool GetAppOutputWithExitCode(const CommandLine& cl,
+ std::string* output, int* exit_code);
+#endif // defined(OS_POSIX)
+
+#if !defined(OS_STARBOARD)
+// Used to filter processes by process ID.
+class ProcessFilter {
+ public:
+ // Returns true to indicate set-inclusion and false otherwise. This method
+ // should not have side-effects and should be idempotent.
+ virtual bool Includes(const ProcessEntry& entry) const = 0;
+
+ protected:
+ virtual ~ProcessFilter() {}
+};
+
+// Returns the number of processes on the machine that are running from the
+// given executable name. If filter is non-null, then only processes selected
+// by the filter will be counted.
+BASE_EXPORT int GetProcessCount(const FilePath::StringType& executable_name,
+ const ProcessFilter* filter);
+
+// Attempts to kill all the processes on the current machine that were launched
+// from the given executable name, ending them with the given exit code. If
+// filter is non-null, then only processes selected by the filter are killed.
+// Returns true if all processes were able to be killed off, false if at least
+// one couldn't be killed.
+BASE_EXPORT bool KillProcesses(const FilePath::StringType& executable_name,
+ int exit_code, const ProcessFilter* filter);
+
+// Attempts to kill the process identified by the given process
+// entry structure, giving it the specified exit code. If |wait| is true, wait
+// for the process to be actually terminated before returning.
+// Returns true if this is successful, false otherwise.
+BASE_EXPORT bool KillProcess(ProcessHandle process, int exit_code, bool wait);
+#endif // !defined(OS_STARBOARD)
+
+#if defined(OS_POSIX)
+// Attempts to kill the process group identified by |process_group_id|. Returns
+// true on success.
+BASE_EXPORT bool KillProcessGroup(ProcessHandle process_group_id);
+#endif // defined(OS_POSIX)
+
+#if defined(OS_WIN)
+BASE_EXPORT bool KillProcessById(ProcessId process_id, int exit_code,
+ bool wait);
+#endif // defined(OS_WIN)
+
+#if !defined(OS_STARBOARD)
+// Get the termination status of the process by interpreting the
+// circumstances of the child process' death. |exit_code| is set to
+// the status returned by waitpid() on POSIX, and from
+// GetExitCodeProcess() on Windows. |exit_code| may be NULL if the
+// caller is not interested in it. Note that on Linux, this function
+// will only return a useful result the first time it is called after
+// the child exits (because it will reap the child and the information
+// will no longer be available).
+BASE_EXPORT TerminationStatus GetTerminationStatus(ProcessHandle handle,
+ int* exit_code);
+#endif // !defined(OS_STARBOARD)
+
+#if defined(OS_POSIX)
+// Wait for the process to exit and get the termination status. See
+// GetTerminationStatus for more information. On POSIX systems, we can't call
+// WaitForExitCode and then GetTerminationStatus as the child will be reaped
+// when WaitForExitCode return and this information will be lost.
+BASE_EXPORT TerminationStatus WaitForTerminationStatus(ProcessHandle handle,
+ int* exit_code);
+#endif // defined(OS_POSIX)
+
+#if !defined(OS_STARBOARD)
+// Waits for process to exit. On POSIX systems, if the process hasn't been
+// signaled then puts the exit code in |exit_code|; otherwise it's considered
+// a failure. On Windows |exit_code| is always filled. Returns true on success,
+// and closes |handle| in any case.
+BASE_EXPORT bool WaitForExitCode(ProcessHandle handle, int* exit_code);
+
+// Waits for process to exit. If it did exit within |timeout_milliseconds|,
+// then puts the exit code in |exit_code|, and returns true.
+// In POSIX systems, if the process has been signaled then |exit_code| is set
+// to -1. Returns false on failure (the caller is then responsible for closing
+// |handle|).
+// The caller is always responsible for closing the |handle|.
+BASE_EXPORT bool WaitForExitCodeWithTimeout(ProcessHandle handle,
+ int* exit_code,
+ base::TimeDelta timeout);
+
+// Wait for all the processes based on the named executable to exit. If filter
+// is non-null, then only processes selected by the filter are waited on.
+// Returns after all processes have exited or wait_milliseconds have expired.
+// Returns true if all the processes exited, false otherwise.
+BASE_EXPORT bool WaitForProcessesToExit(
+ const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ const ProcessFilter* filter);
+
+// Wait for a single process to exit. Return true if it exited cleanly within
+// the given time limit. On Linux |handle| must be a child process, however
+// on Mac and Windows it can be any process.
+BASE_EXPORT bool WaitForSingleProcess(ProcessHandle handle,
+ base::TimeDelta wait);
+
+// Waits a certain amount of time (can be 0) for all the processes with a given
+// executable name to exit, then kills off any of them that are still around.
+// If filter is non-null, then only processes selected by the filter are waited
+// on. Killed processes are ended with the given exit code. Returns false if
+// any processes needed to be killed, true if they all exited cleanly within
+// the wait_milliseconds delay.
+BASE_EXPORT bool CleanupProcesses(const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ int exit_code,
+ const ProcessFilter* filter);
+
+// This method ensures that the specified process eventually terminates, and
+// then it closes the given process handle.
+//
+// It assumes that the process has already been signalled to exit, and it
+// begins by waiting a small amount of time for it to exit. If the process
+// does not appear to have exited, then this function starts to become
+// aggressive about ensuring that the process terminates.
+//
+// On Linux this method does not block the calling thread.
+// On OS X this method may block for up to 2 seconds.
+//
+// NOTE: The process handle must have been opened with the PROCESS_TERMINATE
+// and SYNCHRONIZE permissions.
+//
+BASE_EXPORT void EnsureProcessTerminated(ProcessHandle process_handle);
+#endif // !defined(OS_STARBOARD)
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+// The nicer version of EnsureProcessTerminated() that is patient and will
+// wait for |process_handle| to finish and then reap it.
+BASE_EXPORT void EnsureProcessGetsReaped(ProcessHandle process_handle);
+#endif
+
+#if !defined(OS_STARBOARD)
+// This class provides a way to iterate through a list of processes on the
+// current machine with a specified filter.
+// To use, create an instance and then call NextProcessEntry() until it returns
+// false.
+class BASE_EXPORT ProcessIterator {
+ public:
+ typedef std::list<ProcessEntry> ProcessEntries;
+
+ explicit ProcessIterator(const ProcessFilter* filter);
+ virtual ~ProcessIterator();
+
+ // If there's another process that matches the given executable name,
+ // returns a const pointer to the corresponding PROCESSENTRY32.
+ // If there are no more matching processes, returns NULL.
+ // The returned pointer will remain valid until NextProcessEntry()
+ // is called again or this NamedProcessIterator goes out of scope.
+ const ProcessEntry* NextProcessEntry();
+
+ // Takes a snapshot of all the ProcessEntry found.
+ ProcessEntries Snapshot();
+
+ protected:
+ virtual bool IncludeEntry();
+ const ProcessEntry& entry() { return entry_; }
+
+ private:
+ // Determines whether there's another process (regardless of executable)
+ // left in the list of all processes. Returns true and sets entry_ to
+ // that process's info if there is one, false otherwise.
+ bool CheckForNextProcess();
+
+ // Initializes a PROCESSENTRY32 data structure so that it's ready for
+ // use with Process32First/Process32Next.
+ void InitProcessEntry(ProcessEntry* entry);
+
+#if defined(OS_WIN)
+ HANDLE snapshot_;
+ bool started_iteration_;
+#elif defined(OS_MACOSX) || defined(OS_BSD)
+ std::vector<kinfo_proc> kinfo_procs_;
+ size_t index_of_kinfo_proc_;
+#elif defined(OS_POSIX)
+ DIR* procfs_dir_;
+#endif
+ ProcessEntry entry_;
+ const ProcessFilter* filter_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessIterator);
+};
+
+// This class provides a way to iterate through the list of processes
+// on the current machine that were started from the given executable
+// name. To use, create an instance and then call NextProcessEntry()
+// until it returns false.
+class BASE_EXPORT NamedProcessIterator : public ProcessIterator {
+ public:
+ NamedProcessIterator(const FilePath::StringType& executable_name,
+ const ProcessFilter* filter);
+ virtual ~NamedProcessIterator();
+
+ protected:
+ virtual bool IncludeEntry() OVERRIDE;
+
+ private:
+ FilePath::StringType executable_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(NamedProcessIterator);
+};
+
+// Working Set (resident) memory usage broken down by
+//
+// On Windows:
+// priv (private): These pages (kbytes) cannot be shared with any other process.
+// shareable: These pages (kbytes) can be shared with other processes under
+// the right circumstances.
+// shared : These pages (kbytes) are currently shared with at least one
+// other process.
+//
+// On Linux:
+// priv: Pages mapped only by this process
+// shared: PSS or 0 if the kernel doesn't support this
+// shareable: 0
+//
+// On OS X: TODO(thakis): Revise.
+// priv: Memory.
+// shared: 0
+// shareable: 0
+struct WorkingSetKBytes {
+ WorkingSetKBytes() : priv(0), shareable(0), shared(0) {}
+ size_t priv;
+ size_t shareable;
+ size_t shared;
+};
+
+// Committed (resident + paged) memory usage broken down by
+// private: These pages cannot be shared with any other process.
+// mapped: These pages are mapped into the view of a section (backed by
+// pagefile.sys)
+// image: These pages are mapped into the view of an image section (backed by
+// file system)
+struct CommittedKBytes {
+ CommittedKBytes() : priv(0), mapped(0), image(0) {}
+ size_t priv;
+ size_t mapped;
+ size_t image;
+};
+
+// Free memory (Megabytes marked as free) in the 2G process address space.
+// total : total amount in megabytes marked as free. Maximum value is 2048.
+// largest : size of the largest contiguous amount of memory found. It is
+// always smaller or equal to FreeMBytes::total.
+// largest_ptr: starting address of the largest memory block.
+struct FreeMBytes {
+ size_t total;
+ size_t largest;
+ void* largest_ptr;
+};
+
+// Convert a POSIX timeval to microseconds.
+BASE_EXPORT int64 TimeValToMicroseconds(const struct timeval& tv);
+
+// Provides performance metrics for a specified process (CPU usage, memory and
+// IO counters). To use it, invoke CreateProcessMetrics() to get an instance
+// for a specific process, then access the information with the different get
+// methods.
+class BASE_EXPORT ProcessMetrics {
+ public:
+ ~ProcessMetrics();
+
+ // Creates a ProcessMetrics for the specified process.
+ // The caller owns the returned object.
+#if !defined(OS_MACOSX) || defined(OS_IOS)
+ static ProcessMetrics* CreateProcessMetrics(ProcessHandle process);
+#else
+ class PortProvider {
+ public:
+ virtual ~PortProvider() {}
+
+ // Should return the mach task for |process| if possible, or else
+ // |MACH_PORT_NULL|. Only processes that this returns tasks for will have
+ // metrics on OS X (except for the current process, which always gets
+ // metrics).
+ virtual mach_port_t TaskForPid(ProcessHandle process) const = 0;
+ };
+
+ // The port provider needs to outlive the ProcessMetrics object returned by
+ // this function. If NULL is passed as provider, the returned object
+ // only returns valid metrics if |process| is the current process.
+ static ProcessMetrics* CreateProcessMetrics(ProcessHandle process,
+ PortProvider* port_provider);
+#endif // !defined(OS_MACOSX) || defined(OS_IOS)
+
+ // Returns the current space allocated for the pagefile, in bytes (these pages
+ // may or may not be in memory). On Linux, this returns the total virtual
+ // memory size.
+ size_t GetPagefileUsage() const;
+ // Returns the peak space allocated for the pagefile, in bytes.
+ size_t GetPeakPagefileUsage() const;
+ // Returns the current working set size, in bytes. On Linux, this returns
+ // the resident set size.
+ size_t GetWorkingSetSize() const;
+ // Returns the peak working set size, in bytes.
+ size_t GetPeakWorkingSetSize() const;
+ // Returns private and sharedusage, in bytes. Private bytes is the amount of
+ // memory currently allocated to a process that cannot be shared. Returns
+ // false on platform specific error conditions. Note: |private_bytes|
+ // returns 0 on unsupported OSes: prior to XP SP2.
+ bool GetMemoryBytes(size_t* private_bytes,
+ size_t* shared_bytes);
+ // Fills a CommittedKBytes with both resident and paged
+ // memory usage as per definition of CommittedBytes.
+ void GetCommittedKBytes(CommittedKBytes* usage) const;
+ // Fills a WorkingSetKBytes containing resident private and shared memory
+ // usage in bytes, as per definition of WorkingSetBytes.
+ bool GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const;
+
+ // Computes the current process available memory for allocation.
+ // It does a linear scan of the address space querying each memory region
+ // for its free (unallocated) status. It is useful for estimating the memory
+ // load and fragmentation.
+ bool CalculateFreeMemory(FreeMBytes* free) const;
+
+ // Returns the CPU usage in percent since the last time this method was
+ // called. The first time this method is called it returns 0 and will return
+ // the actual CPU info on subsequent calls.
+ // On Windows, the CPU usage value is for all CPUs. So if you have 2 CPUs and
+ // your process is using all the cycles of 1 CPU and not the other CPU, this
+ // method returns 50.
+ double GetCPUUsage();
+
+ // Retrieves accounting information for all I/O operations performed by the
+ // process.
+ // If IO information is retrieved successfully, the function returns true
+ // and fills in the IO_COUNTERS passed in. The function returns false
+ // otherwise.
+ bool GetIOCounters(IoCounters* io_counters) const;
+
+ private:
+#if !defined(OS_MACOSX) || defined(OS_IOS)
+ explicit ProcessMetrics(ProcessHandle process);
+#else
+ ProcessMetrics(ProcessHandle process, PortProvider* port_provider);
+#endif // !defined(OS_MACOSX) || defined(OS_IOS)
+
+ ProcessHandle process_;
+
+ int processor_count_;
+
+ // Used to store the previous times and CPU usage counts so we can
+ // compute the CPU usage between calls.
+ int64 last_time_;
+ int64 last_system_time_;
+
+#if !defined(OS_IOS)
+#if defined(OS_MACOSX)
+ // Queries the port provider if it's set.
+ mach_port_t TaskForPid(ProcessHandle process) const;
+
+ PortProvider* port_provider_;
+#elif defined(OS_POSIX)
+ // Jiffie count at the last_time_ we updated.
+ int last_cpu_;
+#endif // defined(OS_POSIX)
+#endif // !defined(OS_IOS)
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessMetrics);
+};
+#endif // !defined(OS_STARBOARD)
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+// Data from /proc/meminfo about system-wide memory consumption.
+// Values are in KB.
+struct BASE_EXPORT SystemMemoryInfoKB {
+ SystemMemoryInfoKB();
+
+ int total;
+ int free;
+ int buffers;
+ int cached;
+ int active_anon;
+ int inactive_anon;
+ int active_file;
+ int inactive_file;
+ int shmem;
+
+ // Gem data will be -1 if not supported.
+ int gem_objects;
+ long long gem_size;
+};
+// Retrieves data from /proc/meminfo about system-wide memory consumption.
+// Fills in the provided |meminfo| structure. Returns true on success.
+// Exposed for memory debugging widget.
+BASE_EXPORT bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo);
+#endif // defined(OS_LINUX) || defined(OS_ANDROID)
+
+#if !defined(OS_STARBOARD)
+// Returns the memory committed by the system in KBytes.
+// Returns 0 if it can't compute the commit charge.
+BASE_EXPORT size_t GetSystemCommitCharge();
+
+// Enables low fragmentation heap (LFH) for every heaps of this process. This
+// won't have any effect on heaps created after this function call. It will not
+// modify data allocated in the heaps before calling this function. So it is
+// better to call this function early in initialization and again before
+// entering the main loop.
+// Note: Returns true on Windows 2000 without doing anything.
+BASE_EXPORT bool EnableLowFragmentationHeap();
+#endif // !defined(OS_STARBOARD)
+
+// Enables 'terminate on heap corruption' flag. Helps protect against heap
+// overflow. Has no effect if the OS doesn't provide the necessary facility.
+BASE_EXPORT void EnableTerminationOnHeapCorruption();
+
+#if !defined(OS_STARBOARD)
+// Turns on process termination if memory runs out.
+BASE_EXPORT void EnableTerminationOnOutOfMemory();
+
+// If supported on the platform, and the user has sufficent rights, increase
+// the current process's scheduling priority to a high priority.
+BASE_EXPORT void RaiseProcessToHighPriority();
+#endif // !defined(OS_STARBOARD)
+
+#if defined(OS_MACOSX)
+// Restore the default exception handler, setting it to Apple Crash Reporter
+// (ReportCrash). When forking and execing a new process, the child will
+// inherit the parent's exception ports, which may be set to the Breakpad
+// instance running inside the parent. The parent's Breakpad instance should
+// not handle the child's exceptions. Calling RestoreDefaultExceptionHandler
+// in the child after forking will restore the standard exception handler.
+// See http://crbug.com/20371/ for more details.
+void RestoreDefaultExceptionHandler();
+#endif // defined(OS_MACOSX)
+
+#if defined(OS_MACOSX)
+// Very large images or svg canvases can cause huge mallocs. Skia
+// does tricks on tcmalloc-based systems to allow malloc to fail with
+// a NULL rather than hit the oom crasher. This replicates that for
+// OSX.
+//
+// IF YOU USE THIS WITHOUT CONSULTING YOUR FRIENDLY OSX DEVELOPER,
+// YOUR CODE IS LIKELY TO BE REVERTED. THANK YOU.
+//
+// TODO(shess): Weird place to put it, but this is where the OOM
+// killer currently lives.
+BASE_EXPORT void* UncheckedMalloc(size_t size);
+#endif // defined(OS_MACOSX)
+
+} // namespace base
+
+#endif // BASE_PROCESS_UTIL_H_
diff --git a/src/base/process_util_freebsd.cc b/src/base/process_util_freebsd.cc
new file mode 100644
index 0000000..1157fb2
--- /dev/null
+++ b/src/base/process_util_freebsd.cc
@@ -0,0 +1,293 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process_util.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
+#include "base/string_tokenizer.h"
+#include "base/string_util.h"
+#include "base/sys_info.h"
+
+namespace base {
+
+ProcessId GetParentProcessId(ProcessHandle process) {
+ struct kinfo_proc info;
+ size_t length;
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process };
+
+ if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
+ return -1;
+
+ return info.ki_ppid;
+}
+
+FilePath GetProcessExecutablePath(ProcessHandle process) {
+ char pathname[PATH_MAX];
+ size_t length;
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, process };
+
+ length = sizeof(pathname);
+
+ if (sysctl(mib, arraysize(mib), pathname, &length, NULL, 0) < 0 ||
+ length == 0) {
+ return FilePath();
+ }
+
+ return FilePath(std::string(pathname));
+}
+
+ProcessIterator::ProcessIterator(const ProcessFilter* filter)
+ : index_of_kinfo_proc_(),
+ filter_(filter) {
+
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, getuid() };
+
+ bool done = false;
+ int try_num = 1;
+ const int max_tries = 10;
+
+ do {
+ size_t len = 0;
+ if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) <0 ){
+ LOG(ERROR) << "failed to get the size needed for the process list";
+ kinfo_procs_.resize(0);
+ done = true;
+ } else {
+ size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
+ // Leave some spare room for process table growth (more could show up
+ // between when we check and now)
+ num_of_kinfo_proc += 16;
+ kinfo_procs_.resize(num_of_kinfo_proc);
+ len = num_of_kinfo_proc * sizeof(struct kinfo_proc);
+ if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) <0) {
+ // If we get a mem error, it just means we need a bigger buffer, so
+ // loop around again. Anything else is a real error and give up.
+ if (errno != ENOMEM) {
+ LOG(ERROR) << "failed to get the process list";
+ kinfo_procs_.resize(0);
+ done = true;
+ }
+ } else {
+ // Got the list, just make sure we're sized exactly right
+ size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
+ kinfo_procs_.resize(num_of_kinfo_proc);
+ done = true;
+ }
+ }
+ } while (!done && (try_num++ < max_tries));
+
+ if (!done) {
+ LOG(ERROR) << "failed to collect the process list in a few tries";
+ kinfo_procs_.resize(0);
+ }
+}
+
+ProcessIterator::~ProcessIterator() {
+}
+
+bool ProcessIterator::CheckForNextProcess() {
+ std::string data;
+
+ for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++ index_of_kinfo_proc_) {
+ size_t length;
+ struct kinfo_proc kinfo = kinfo_procs_[index_of_kinfo_proc_];
+ int mib[] = { CTL_KERN, KERN_PROC_ARGS, kinfo.ki_pid };
+
+ if ((kinfo.ki_pid > 0) && (kinfo.ki_stat == SZOMB))
+ continue;
+
+ length = 0;
+ if (sysctl(mib, arraysize(mib), NULL, &length, NULL, 0) < 0) {
+ LOG(ERROR) << "failed to figure out the buffer size for a command line";
+ continue;
+ }
+
+ data.resize(length);
+
+ if (sysctl(mib, arraysize(mib), &data[0], &length, NULL, 0) < 0) {
+ LOG(ERROR) << "failed to fetch a commandline";
+ continue;
+ }
+
+ std::string delimiters;
+ delimiters.push_back('\0');
+ Tokenize(data, delimiters, &entry_.cmd_line_args_);
+
+ size_t exec_name_end = data.find('\0');
+ if (exec_name_end == std::string::npos) {
+ LOG(ERROR) << "command line data didn't match expected format";
+ continue;
+ }
+
+ entry_.pid_ = kinfo.ki_pid;
+ entry_.ppid_ = kinfo.ki_ppid;
+ entry_.gid_ = kinfo.ki_pgid;
+
+ size_t last_slash = data.rfind('/', exec_name_end);
+ if (last_slash == std::string::npos) {
+ entry_.exe_file_.assign(data, 0, exec_name_end);
+ } else {
+ entry_.exe_file_.assign(data, last_slash + 1,
+ exec_name_end - last_slash - 1);
+ }
+
+ // Start w/ the next entry next time through
+ ++index_of_kinfo_proc_;
+
+ return true;
+ }
+ return false;
+}
+
+bool NamedProcessIterator::IncludeEntry() {
+ if(executable_name_ != entry().exe_file())
+ return false;
+
+ return ProcessIterator::IncludeEntry();
+}
+
+
+ProcessMetrics::ProcessMetrics(ProcessHandle process)
+ : process_(process),
+ last_time_(0),
+ last_system_time_(0),
+ last_cpu_(0) {
+ processor_count_ = base::SysInfo::NumberOfProcessors();
+}
+
+// static
+ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
+ return new ProcessMetrics(process);
+}
+
+size_t ProcessMetrics::GetPagefileUsage() const {
+ struct kinfo_proc info;
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_ };
+ size_t length = sizeof(info);
+
+ if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
+ return 0;
+
+ return info.ki_size;
+}
+
+size_t ProcessMetrics::GetPeakPagefileUsage() const {
+ return 0;
+}
+
+size_t ProcessMetrics::GetWorkingSetSize() const {
+ struct kinfo_proc info;
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_ };
+ size_t length = sizeof(info);
+
+ if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
+ return 0;
+
+ return info.ki_rssize * getpagesize();
+}
+
+size_t ProcessMetrics::GetPeakWorkingSetSize() const {
+ return 0;
+}
+
+bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
+ size_t* shared_bytes) {
+ WorkingSetKBytes ws_usage;
+ if (!GetWorkingSetKBytes(&ws_usage))
+ return false;
+
+ if (private_bytes)
+ *private_bytes = ws_usage.priv << 10;
+
+ if (shared_bytes)
+ *shared_bytes = ws_usage.shared * 1024;
+
+ return true;
+}
+
+bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
+// TODO(bapt) be sure we can't be precise
+ size_t priv = GetWorkingSetSize();
+ if (!priv)
+ return false;
+ ws_usage->priv = priv / 1024;
+ ws_usage->shareable = 0;
+ ws_usage->shared = 0;
+
+ return true;
+}
+
+bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
+ return false;
+}
+
+double ProcessMetrics::GetCPUUsage() {
+ struct kinfo_proc info;
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_ };
+ size_t length = sizeof(info);
+
+ struct timeval now;
+ int retval = gettimeofday(&now, NULL);
+ if (retval)
+ return 0;
+
+ if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
+ return 0;
+
+ return (info.ki_pctcpu / FSCALE) * 100.0;
+}
+
+size_t GetSystemCommitCharge() {
+ int mib[2], pagesize;
+ unsigned long mem_total, mem_free, mem_inactive;
+ size_t length = sizeof(mem_total);
+
+ if (sysctl(mib, arraysize(mib), &mem_total, &length, NULL, 0) < 0)
+ return 0;
+
+ length = sizeof(mem_free);
+ if (sysctlbyname("vm.stats.vm.v_free_count", &mem_free, &length, NULL, 0) < 0)
+ return 0;
+
+ length = sizeof(mem_inactive);
+ if (sysctlbyname("vm.stats.vm.v_inactive_count", &mem_inactive, &length,
+ NULL, 0) < 0) {
+ return 0;
+ }
+
+ pagesize = getpagesize();
+
+ return mem_total - (mem_free*pagesize) - (mem_inactive*pagesize);
+}
+
+void EnableTerminationOnOutOfMemory() {
+ DLOG(WARNING) << "Not feasible.";
+}
+
+void EnableTerminationOnHeapCorruption() {
+ // Nothing to do.
+}
+
+bool AdjustOOMScore(ProcessId process, int score) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+} // namespace base
diff --git a/src/base/process_util_ios.mm b/src/base/process_util_ios.mm
new file mode 100644
index 0000000..161d396
--- /dev/null
+++ b/src/base/process_util_ios.mm
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 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/process_util.h"
+
+#import <Foundation/Foundation.h>
+#include <mach/task.h>
+#include <stdio.h>
+
+#include "base/logging.h"
+
+// This is just enough of a shim to let the support needed by test_support
+// link. In general, process_util isn't valid on iOS.
+
+namespace base {
+
+namespace {
+
+bool GetTaskInfo(task_basic_info_64* task_info_data) {
+ mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT;
+ kern_return_t kr = task_info(mach_task_self(),
+ TASK_BASIC_INFO_64,
+ reinterpret_cast<task_info_t>(task_info_data),
+ &count);
+ return kr == KERN_SUCCESS;
+}
+
+} // namespace
+
+ProcessId GetCurrentProcId() {
+ return getpid();
+}
+
+ProcessHandle GetCurrentProcessHandle() {
+ return GetCurrentProcId();
+}
+
+void EnableTerminationOnHeapCorruption() {
+ // On iOS, there nothing to do AFAIK.
+}
+
+void EnableTerminationOnOutOfMemory() {
+ // iOS provides this for free!
+}
+
+void RaiseProcessToHighPriority() {
+ // Impossible on iOS. Do nothing.
+}
+
+ProcessMetrics::ProcessMetrics(ProcessHandle process) {}
+
+ProcessMetrics::~ProcessMetrics() {}
+
+// static
+ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
+ return new ProcessMetrics(process);
+}
+
+size_t ProcessMetrics::GetWorkingSetSize() const {
+ task_basic_info_64 task_info_data;
+ if (!GetTaskInfo(&task_info_data))
+ return 0;
+ return task_info_data.resident_size;
+}
+
+} // namespace base
diff --git a/src/base/process_util_linux.cc b/src/base/process_util_linux.cc
new file mode 100644
index 0000000..11937f0
--- /dev/null
+++ b/src/base/process_util_linux.cc
@@ -0,0 +1,886 @@
+// Copyright (c) 2012 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/process_util.h"
+
+#include <dirent.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
+#include "base/string_tokenizer.h"
+#include "base/string_util.h"
+#include "base/sys_info.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace {
+
+enum ParsingState {
+ KEY_NAME,
+ KEY_VALUE
+};
+
+const char kProcDir[] = "/proc";
+const char kStatFile[] = "stat";
+
+// Returns a FilePath to "/proc/pid".
+FilePath GetProcPidDir(pid_t pid) {
+ return FilePath(kProcDir).Append(base::IntToString(pid));
+}
+
+// Fields from /proc/<pid>/stat, 0-based. See man 5 proc.
+// If the ordering ever changes, carefully review functions that use these
+// values.
+enum ProcStatsFields {
+ VM_COMM = 1, // Filename of executable, without parentheses.
+ VM_STATE = 2, // Letter indicating the state of the process.
+ VM_PPID = 3, // PID of the parent.
+ VM_PGRP = 4, // Process group id.
+ VM_UTIME = 13, // Time scheduled in user mode in clock ticks.
+ VM_STIME = 14, // Time scheduled in kernel mode in clock ticks.
+ VM_VSIZE = 22, // Virtual memory size in bytes.
+ VM_RSS = 23, // Resident Set Size in pages.
+};
+
+// Reads /proc/<pid>/stat into |buffer|. Returns true if the file can be read
+// and is non-empty.
+bool ReadProcStats(pid_t pid, std::string* buffer) {
+ buffer->clear();
+ // Synchronously reading files in /proc is safe.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ FilePath stat_file = GetProcPidDir(pid).Append(kStatFile);
+ if (!file_util::ReadFileToString(stat_file, buffer)) {
+ DLOG(WARNING) << "Failed to get process stats.";
+ return false;
+ }
+ return !buffer->empty();
+}
+
+// Takes |stats_data| and populates |proc_stats| with the values split by
+// spaces. Taking into account the 2nd field may, in itself, contain spaces.
+// Returns true if successful.
+bool ParseProcStats(const std::string& stats_data,
+ std::vector<std::string>* proc_stats) {
+ // |stats_data| may be empty if the process disappeared somehow.
+ // e.g. http://crbug.com/145811
+ if (stats_data.empty())
+ return false;
+
+ // The stat file is formatted as:
+ // pid (process name) data1 data2 .... dataN
+ // Look for the closing paren by scanning backwards, to avoid being fooled by
+ // processes with ')' in the name.
+ size_t open_parens_idx = stats_data.find(" (");
+ size_t close_parens_idx = stats_data.rfind(") ");
+ if (open_parens_idx == std::string::npos ||
+ close_parens_idx == std::string::npos ||
+ open_parens_idx > close_parens_idx) {
+ DLOG(WARNING) << "Failed to find matched parens in '" << stats_data << "'";
+ NOTREACHED();
+ return false;
+ }
+ open_parens_idx++;
+
+ proc_stats->clear();
+ // PID.
+ proc_stats->push_back(stats_data.substr(0, open_parens_idx));
+ // Process name without parentheses.
+ proc_stats->push_back(
+ stats_data.substr(open_parens_idx + 1,
+ close_parens_idx - (open_parens_idx + 1)));
+
+ // Split the rest.
+ std::vector<std::string> other_stats;
+ base::SplitString(stats_data.substr(close_parens_idx + 2), ' ', &other_stats);
+ for (size_t i = 0; i < other_stats.size(); ++i)
+ proc_stats->push_back(other_stats[i]);
+ return true;
+}
+
+// Reads the |field_num|th field from |proc_stats|. Returns 0 on failure.
+// This version does not handle the first 3 values, since the first value is
+// simply |pid|, and the next two values are strings.
+int GetProcStatsFieldAsInt(const std::vector<std::string>& proc_stats,
+ ProcStatsFields field_num) {
+ DCHECK_GE(field_num, VM_PPID);
+ CHECK_LT(static_cast<size_t>(field_num), proc_stats.size());
+
+ int value;
+ return base::StringToInt(proc_stats[field_num], &value) ? value : 0;
+}
+
+// Same as GetProcStatsFieldAsInt(), but for size_t values.
+size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats,
+ ProcStatsFields field_num) {
+ DCHECK_GE(field_num, VM_PPID);
+ CHECK_LT(static_cast<size_t>(field_num), proc_stats.size());
+
+ size_t value;
+ return base::StringToSizeT(proc_stats[field_num], &value) ? value : 0;
+}
+
+// Convenience wrapper around GetProcStatsFieldAsInt(), ParseProcStats() and
+// ReadProcStats(). See GetProcStatsFieldAsInt() for details.
+int ReadProcStatsAndGetFieldAsInt(pid_t pid, ProcStatsFields field_num) {
+ std::string stats_data;
+ if (!ReadProcStats(pid, &stats_data))
+ return 0;
+ std::vector<std::string> proc_stats;
+ if (!ParseProcStats(stats_data, &proc_stats))
+ return 0;
+ return GetProcStatsFieldAsInt(proc_stats, field_num);
+}
+
+// Same as ReadProcStatsAndGetFieldAsInt() but for size_t values.
+size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid, ProcStatsFields field_num) {
+ std::string stats_data;
+ if (!ReadProcStats(pid, &stats_data))
+ return 0;
+ std::vector<std::string> proc_stats;
+ if (!ParseProcStats(stats_data, &proc_stats))
+ return 0;
+ return GetProcStatsFieldAsSizeT(proc_stats, field_num);
+}
+
+// Reads the |field_num|th field from |proc_stats|.
+// Returns an empty string on failure.
+// This version only handles VM_COMM and VM_STATE, which are the only fields
+// that are strings.
+std::string GetProcStatsFieldAsString(
+ const std::vector<std::string>& proc_stats,
+ ProcStatsFields field_num) {
+ if (field_num < VM_COMM || field_num > VM_STATE) {
+ NOTREACHED();
+ return "";
+ }
+
+ if (proc_stats.size() > static_cast<size_t>(field_num))
+ return proc_stats[field_num];
+
+ NOTREACHED();
+ return 0;
+}
+
+// Reads /proc/<pid>/cmdline and populates |proc_cmd_line_args| with the command
+// line arguments. Returns true if successful.
+// Note: /proc/<pid>/cmdline contains command line arguments separated by single
+// null characters. We tokenize it into a vector of strings using '\0' as a
+// delimiter.
+bool GetProcCmdline(pid_t pid, std::vector<std::string>* proc_cmd_line_args) {
+ // Synchronously reading files in /proc is safe.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ FilePath cmd_line_file = GetProcPidDir(pid).Append("cmdline");
+ std::string cmd_line;
+ if (!file_util::ReadFileToString(cmd_line_file, &cmd_line))
+ return false;
+ std::string delimiters;
+ delimiters.push_back('\0');
+ Tokenize(cmd_line, delimiters, proc_cmd_line_args);
+ return true;
+}
+
+// Take a /proc directory entry named |d_name|, and if it is the directory for
+// a process, convert it to a pid_t.
+// Returns 0 on failure.
+// e.g. /proc/self/ will return 0, whereas /proc/1234 will return 1234.
+pid_t ProcDirSlotToPid(const char* d_name) {
+ int i;
+ for (i = 0; i < NAME_MAX && d_name[i]; ++i) {
+ if (!IsAsciiDigit(d_name[i])) {
+ return 0;
+ }
+ }
+ if (i == NAME_MAX)
+ return 0;
+
+ // Read the process's command line.
+ pid_t pid;
+ std::string pid_string(d_name);
+ if (!base::StringToInt(pid_string, &pid)) {
+ NOTREACHED();
+ return 0;
+ }
+ return pid;
+}
+
+// Get the total CPU of a single process. Return value is number of jiffies
+// on success or -1 on error.
+int GetProcessCPU(pid_t pid) {
+ // Use /proc/<pid>/task to find all threads and parse their /stat file.
+ FilePath task_path = GetProcPidDir(pid).Append("task");
+
+ DIR* dir = opendir(task_path.value().c_str());
+ if (!dir) {
+ DPLOG(ERROR) << "opendir(" << task_path.value() << ")";
+ return -1;
+ }
+
+ int total_cpu = 0;
+ while (struct dirent* ent = readdir(dir)) {
+ pid_t tid = ProcDirSlotToPid(ent->d_name);
+ if (!tid)
+ continue;
+
+ // Synchronously reading files in /proc is safe.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ std::string stat;
+ FilePath stat_path = task_path.Append(ent->d_name).Append(kStatFile);
+ if (file_util::ReadFileToString(stat_path, &stat)) {
+ int cpu = base::ParseProcStatCPU(stat);
+ if (cpu > 0)
+ total_cpu += cpu;
+ }
+ }
+ closedir(dir);
+
+ return total_cpu;
+}
+
+// Read /proc/<pid>/status and returns the value for |field|, or 0 on failure.
+// Only works for fields in the form of "Field: value kB".
+size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) {
+ FilePath stat_file = GetProcPidDir(pid).Append("status");
+ std::string status;
+ {
+ // Synchronously reading files in /proc is safe.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+ if (!file_util::ReadFileToString(stat_file, &status))
+ return 0;
+ }
+
+ StringTokenizer tokenizer(status, ":\n");
+ ParsingState state = KEY_NAME;
+ base::StringPiece last_key_name;
+ while (tokenizer.GetNext()) {
+ switch (state) {
+ case KEY_NAME:
+ last_key_name = tokenizer.token_piece();
+ state = KEY_VALUE;
+ break;
+ case KEY_VALUE:
+ DCHECK(!last_key_name.empty());
+ if (last_key_name == field) {
+ std::string value_str;
+ tokenizer.token_piece().CopyToString(&value_str);
+ std::string value_str_trimmed;
+ TrimWhitespaceASCII(value_str, TRIM_ALL, &value_str_trimmed);
+ std::vector<std::string> split_value_str;
+ base::SplitString(value_str_trimmed, ' ', &split_value_str);
+ if (split_value_str.size() != 2 || split_value_str[1] != "kB") {
+ NOTREACHED();
+ return 0;
+ }
+ size_t value;
+ if (!base::StringToSizeT(split_value_str[0], &value)) {
+ NOTREACHED();
+ return 0;
+ }
+ return value;
+ }
+ state = KEY_NAME;
+ break;
+ }
+ }
+ NOTREACHED();
+ return 0;
+}
+
+} // namespace
+
+namespace base {
+
+#if defined(USE_LINUX_BREAKPAD)
+size_t g_oom_size = 0U;
+#endif
+
+const char kProcSelfExe[] = "/proc/self/exe";
+
+ProcessId GetParentProcessId(ProcessHandle process) {
+ ProcessId pid = ReadProcStatsAndGetFieldAsInt(process, VM_PPID);
+ if (pid)
+ return pid;
+ return -1;
+}
+
+FilePath GetProcessExecutablePath(ProcessHandle process) {
+ FilePath stat_file = GetProcPidDir(process).Append("exe");
+ FilePath exe_name;
+ if (!file_util::ReadSymbolicLink(stat_file, &exe_name)) {
+ // No such process. Happens frequently in e.g. TerminateAllChromeProcesses
+ return FilePath();
+ }
+ return exe_name;
+}
+
+ProcessIterator::ProcessIterator(const ProcessFilter* filter)
+ : filter_(filter) {
+ procfs_dir_ = opendir(kProcDir);
+}
+
+ProcessIterator::~ProcessIterator() {
+ if (procfs_dir_) {
+ closedir(procfs_dir_);
+ procfs_dir_ = NULL;
+ }
+}
+
+bool ProcessIterator::CheckForNextProcess() {
+ // TODO(port): skip processes owned by different UID
+
+ pid_t pid = kNullProcessId;
+ std::vector<std::string> cmd_line_args;
+ std::string stats_data;
+ std::vector<std::string> proc_stats;
+
+ // Arbitrarily guess that there will never be more than 200 non-process
+ // files in /proc. Hardy has 53 and Lucid has 61.
+ int skipped = 0;
+ const int kSkipLimit = 200;
+ while (skipped < kSkipLimit) {
+ dirent* slot = readdir(procfs_dir_);
+ // all done looking through /proc?
+ if (!slot)
+ return false;
+
+ // If not a process, keep looking for one.
+ pid = ProcDirSlotToPid(slot->d_name);
+ if (!pid) {
+ skipped++;
+ continue;
+ }
+
+ if (!GetProcCmdline(pid, &cmd_line_args))
+ continue;
+
+ if (!ReadProcStats(pid, &stats_data))
+ continue;
+ if (!ParseProcStats(stats_data, &proc_stats))
+ continue;
+
+ std::string runstate = GetProcStatsFieldAsString(proc_stats, VM_STATE);
+ if (runstate.size() != 1) {
+ NOTREACHED();
+ continue;
+ }
+
+ // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped?
+ // Allowed values: D R S T Z
+ if (runstate[0] != 'Z')
+ break;
+
+ // Nope, it's a zombie; somebody isn't cleaning up after their children.
+ // (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.)
+ // There could be a lot of zombies, can't really decrement i here.
+ }
+ if (skipped >= kSkipLimit) {
+ NOTREACHED();
+ return false;
+ }
+
+ entry_.pid_ = pid;
+ entry_.ppid_ = GetProcStatsFieldAsInt(proc_stats, VM_PPID);
+ entry_.gid_ = GetProcStatsFieldAsInt(proc_stats, VM_PGRP);
+ entry_.cmd_line_args_.assign(cmd_line_args.begin(), cmd_line_args.end());
+
+ // TODO(port): read pid's commandline's $0, like killall does. Using the
+ // short name between openparen and closeparen won't work for long names!
+ entry_.exe_file_ = GetProcStatsFieldAsString(proc_stats, VM_COMM);
+ return true;
+}
+
+bool NamedProcessIterator::IncludeEntry() {
+ if (executable_name_ != entry().exe_file())
+ return false;
+ return ProcessIterator::IncludeEntry();
+}
+
+
+// static
+ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
+ return new ProcessMetrics(process);
+}
+
+// On linux, we return vsize.
+size_t ProcessMetrics::GetPagefileUsage() const {
+ return ReadProcStatsAndGetFieldAsSizeT(process_, VM_VSIZE);
+}
+
+// On linux, we return the high water mark of vsize.
+size_t ProcessMetrics::GetPeakPagefileUsage() const {
+ return ReadProcStatusAndGetFieldAsSizeT(process_, "VmPeak") * 1024;
+}
+
+// On linux, we return RSS.
+size_t ProcessMetrics::GetWorkingSetSize() const {
+ return ReadProcStatsAndGetFieldAsSizeT(process_, VM_RSS) * getpagesize();
+}
+
+// On linux, we return the high water mark of RSS.
+size_t ProcessMetrics::GetPeakWorkingSetSize() const {
+ return ReadProcStatusAndGetFieldAsSizeT(process_, "VmHWM") * 1024;
+}
+
+bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
+ size_t* shared_bytes) {
+ WorkingSetKBytes ws_usage;
+ if (!GetWorkingSetKBytes(&ws_usage))
+ return false;
+
+ if (private_bytes)
+ *private_bytes = ws_usage.priv * 1024;
+
+ if (shared_bytes)
+ *shared_bytes = ws_usage.shared * 1024;
+
+ return true;
+}
+
+// Private and Shared working set sizes are obtained from /proc/<pid>/statm.
+bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
+ // Use statm instead of smaps because smaps is:
+ // a) Large and slow to parse.
+ // b) Unavailable in the SUID sandbox.
+
+ // First we need to get the page size, since everything is measured in pages.
+ // For details, see: man 5 proc.
+ const int page_size_kb = getpagesize() / 1024;
+ if (page_size_kb <= 0)
+ return false;
+
+ std::string statm;
+ {
+ FilePath statm_file = GetProcPidDir(process_).Append("statm");
+ // Synchronously reading files in /proc is safe.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+ bool ret = file_util::ReadFileToString(statm_file, &statm);
+ if (!ret || statm.length() == 0)
+ return false;
+ }
+
+ std::vector<std::string> statm_vec;
+ base::SplitString(statm, ' ', &statm_vec);
+ if (statm_vec.size() != 7)
+ return false; // Not the format we expect.
+
+ int statm_rss, statm_shared;
+ base::StringToInt(statm_vec[1], &statm_rss);
+ base::StringToInt(statm_vec[2], &statm_shared);
+
+ ws_usage->priv = (statm_rss - statm_shared) * page_size_kb;
+ ws_usage->shared = statm_shared * page_size_kb;
+
+ // Sharable is not calculated, as it does not provide interesting data.
+ ws_usage->shareable = 0;
+
+ return true;
+}
+
+double ProcessMetrics::GetCPUUsage() {
+ // This queries the /proc-specific scaling factor which is
+ // conceptually the system hertz. To dump this value on another
+ // system, try
+ // od -t dL /proc/self/auxv
+ // and look for the number after 17 in the output; mine is
+ // 0000040 17 100 3 134512692
+ // which means the answer is 100.
+ // It may be the case that this value is always 100.
+ static const int kHertz = sysconf(_SC_CLK_TCK);
+
+ struct timeval now;
+ int retval = gettimeofday(&now, NULL);
+ if (retval)
+ return 0;
+ int64 time = TimeValToMicroseconds(now);
+
+ if (last_time_ == 0) {
+ // First call, just set the last values.
+ last_time_ = time;
+ last_cpu_ = GetProcessCPU(process_);
+ return 0;
+ }
+
+ int64 time_delta = time - last_time_;
+ DCHECK_NE(time_delta, 0);
+ if (time_delta == 0)
+ return 0;
+
+ int cpu = GetProcessCPU(process_);
+
+ // We have the number of jiffies in the time period. Convert to percentage.
+ // Note this means we will go *over* 100 in the case where multiple threads
+ // are together adding to more than one CPU's worth.
+ int percentage = 100 * (cpu - last_cpu_) /
+ (kHertz * TimeDelta::FromMicroseconds(time_delta).InSecondsF());
+
+ last_time_ = time;
+ last_cpu_ = cpu;
+
+ return percentage;
+}
+
+// To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING
+// in your kernel configuration.
+bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
+ // Synchronously reading files in /proc is safe.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ std::string proc_io_contents;
+ FilePath io_file = GetProcPidDir(process_).Append("io");
+ if (!file_util::ReadFileToString(io_file, &proc_io_contents))
+ return false;
+
+ (*io_counters).OtherOperationCount = 0;
+ (*io_counters).OtherTransferCount = 0;
+
+ StringTokenizer tokenizer(proc_io_contents, ": \n");
+ ParsingState state = KEY_NAME;
+ StringPiece last_key_name;
+ while (tokenizer.GetNext()) {
+ switch (state) {
+ case KEY_NAME:
+ last_key_name = tokenizer.token_piece();
+ state = KEY_VALUE;
+ break;
+ case KEY_VALUE:
+ DCHECK(!last_key_name.empty());
+ if (last_key_name == "syscr") {
+ base::StringToInt64(tokenizer.token_piece(),
+ reinterpret_cast<int64*>(&(*io_counters).ReadOperationCount));
+ } else if (last_key_name == "syscw") {
+ base::StringToInt64(tokenizer.token_piece(),
+ reinterpret_cast<int64*>(&(*io_counters).WriteOperationCount));
+ } else if (last_key_name == "rchar") {
+ base::StringToInt64(tokenizer.token_piece(),
+ reinterpret_cast<int64*>(&(*io_counters).ReadTransferCount));
+ } else if (last_key_name == "wchar") {
+ base::StringToInt64(tokenizer.token_piece(),
+ reinterpret_cast<int64*>(&(*io_counters).WriteTransferCount));
+ }
+ state = KEY_NAME;
+ break;
+ }
+ }
+ return true;
+}
+
+ProcessMetrics::ProcessMetrics(ProcessHandle process)
+ : process_(process),
+ last_time_(0),
+ last_system_time_(0),
+ last_cpu_(0) {
+ processor_count_ = base::SysInfo::NumberOfProcessors();
+}
+
+
+// Exposed for testing.
+int ParseProcStatCPU(const std::string& input) {
+ std::vector<std::string> proc_stats;
+ if (!ParseProcStats(input, &proc_stats))
+ return -1;
+
+ if (proc_stats.size() <= VM_STIME)
+ return -1;
+ int utime = GetProcStatsFieldAsInt(proc_stats, VM_UTIME);
+ int stime = GetProcStatsFieldAsInt(proc_stats, VM_STIME);
+ return utime + stime;
+}
+
+namespace {
+
+// The format of /proc/meminfo is:
+//
+// MemTotal: 8235324 kB
+// MemFree: 1628304 kB
+// Buffers: 429596 kB
+// Cached: 4728232 kB
+// ...
+const size_t kMemTotalIndex = 1;
+const size_t kMemFreeIndex = 4;
+const size_t kMemBuffersIndex = 7;
+const size_t kMemCachedIndex = 10;
+const size_t kMemActiveAnonIndex = 22;
+const size_t kMemInactiveAnonIndex = 25;
+const size_t kMemActiveFileIndex = 28;
+const size_t kMemInactiveFileIndex = 31;
+
+} // namespace
+
+SystemMemoryInfoKB::SystemMemoryInfoKB()
+ : total(0),
+ free(0),
+ buffers(0),
+ cached(0),
+ active_anon(0),
+ inactive_anon(0),
+ active_file(0),
+ inactive_file(0),
+ shmem(0),
+ gem_objects(-1),
+ gem_size(-1) {
+}
+
+bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
+ // Synchronously reading files in /proc is safe.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ // Used memory is: total - free - buffers - caches
+ FilePath meminfo_file("/proc/meminfo");
+ std::string meminfo_data;
+ if (!file_util::ReadFileToString(meminfo_file, &meminfo_data)) {
+ DLOG(WARNING) << "Failed to open " << meminfo_file.value();
+ return false;
+ }
+ std::vector<std::string> meminfo_fields;
+ SplitStringAlongWhitespace(meminfo_data, &meminfo_fields);
+
+ if (meminfo_fields.size() < kMemCachedIndex) {
+ DLOG(WARNING) << "Failed to parse " << meminfo_file.value()
+ << ". Only found " << meminfo_fields.size() << " fields.";
+ return false;
+ }
+
+ DCHECK_EQ(meminfo_fields[kMemTotalIndex-1], "MemTotal:");
+ DCHECK_EQ(meminfo_fields[kMemFreeIndex-1], "MemFree:");
+ DCHECK_EQ(meminfo_fields[kMemBuffersIndex-1], "Buffers:");
+ DCHECK_EQ(meminfo_fields[kMemCachedIndex-1], "Cached:");
+ DCHECK_EQ(meminfo_fields[kMemActiveAnonIndex-1], "Active(anon):");
+ DCHECK_EQ(meminfo_fields[kMemInactiveAnonIndex-1], "Inactive(anon):");
+ DCHECK_EQ(meminfo_fields[kMemActiveFileIndex-1], "Active(file):");
+ DCHECK_EQ(meminfo_fields[kMemInactiveFileIndex-1], "Inactive(file):");
+
+ base::StringToInt(meminfo_fields[kMemTotalIndex], &meminfo->total);
+ base::StringToInt(meminfo_fields[kMemFreeIndex], &meminfo->free);
+ base::StringToInt(meminfo_fields[kMemBuffersIndex], &meminfo->buffers);
+ base::StringToInt(meminfo_fields[kMemCachedIndex], &meminfo->cached);
+ base::StringToInt(meminfo_fields[kMemActiveAnonIndex], &meminfo->active_anon);
+ base::StringToInt(meminfo_fields[kMemInactiveAnonIndex],
+ &meminfo->inactive_anon);
+ base::StringToInt(meminfo_fields[kMemActiveFileIndex], &meminfo->active_file);
+ base::StringToInt(meminfo_fields[kMemInactiveFileIndex],
+ &meminfo->inactive_file);
+#if defined(OS_CHROMEOS)
+ // Chrome OS has a tweaked kernel that allows us to query Shmem, which is
+ // usually video memory otherwise invisible to the OS. Unfortunately, the
+ // meminfo format varies on different hardware so we have to search for the
+ // string. It always appears after "Cached:".
+ for (size_t i = kMemCachedIndex+2; i < meminfo_fields.size(); i += 3) {
+ if (meminfo_fields[i] == "Shmem:") {
+ base::StringToInt(meminfo_fields[i+1], &meminfo->shmem);
+ break;
+ }
+ }
+#endif
+
+ // Check for graphics memory data and report if present. Synchronously
+ // reading files in /sys is fast.
+#if defined(ARCH_CPU_ARM_FAMILY)
+ FilePath geminfo_file("/sys/kernel/debug/dri/0/exynos_gem_objects");
+#else
+ FilePath geminfo_file("/sys/kernel/debug/dri/0/i915_gem_objects");
+#endif
+ std::string geminfo_data;
+ meminfo->gem_objects = -1;
+ meminfo->gem_size = -1;
+ if (file_util::ReadFileToString(geminfo_file, &geminfo_data)) {
+ int gem_objects = -1;
+ long long gem_size = -1;
+ int num_res = sscanf(geminfo_data.c_str(),
+ "%d objects, %lld bytes",
+ &gem_objects, &gem_size);
+ if (num_res == 2) {
+ meminfo->gem_objects = gem_objects;
+ meminfo->gem_size = gem_size;
+ }
+ }
+
+#if defined(ARCH_CPU_ARM_FAMILY)
+ // Incorporate Mali graphics memory if present.
+ FilePath mali_memory_file("/sys/devices/platform/mali.0/memory");
+ std::string mali_memory_data;
+ if (file_util::ReadFileToString(mali_memory_file, &mali_memory_data)) {
+ long long mali_size = -1;
+ int num_res = sscanf(mali_memory_data.c_str(), "%lld bytes", &mali_size);
+ if (num_res == 1)
+ meminfo->gem_size += mali_size;
+ }
+#endif // defined(ARCH_CPU_ARM_FAMILY)
+
+ return true;
+}
+
+size_t GetSystemCommitCharge() {
+ SystemMemoryInfoKB meminfo;
+ if (!GetSystemMemoryInfo(&meminfo))
+ return 0;
+ return meminfo.total - meminfo.free - meminfo.buffers - meminfo.cached;
+}
+
+namespace {
+
+void OnNoMemorySize(size_t size) {
+#if defined(USE_LINUX_BREAKPAD)
+ g_oom_size = size;
+#endif
+
+ if (size != 0)
+ LOG(FATAL) << "Out of memory, size = " << size;
+ LOG(FATAL) << "Out of memory.";
+}
+
+void OnNoMemory() {
+ OnNoMemorySize(0);
+}
+
+} // namespace
+
+extern "C" {
+#if !defined(USE_TCMALLOC) && !defined(ADDRESS_SANITIZER) && \
+ !defined(OS_ANDROID) && !defined(THREAD_SANITIZER)
+
+extern "C" {
+void* __libc_malloc(size_t size);
+void* __libc_realloc(void* ptr, size_t size);
+void* __libc_calloc(size_t nmemb, size_t size);
+void* __libc_valloc(size_t size);
+void* __libc_pvalloc(size_t size);
+void* __libc_memalign(size_t alignment, size_t size);
+} // extern "C"
+
+// Overriding the system memory allocation functions:
+//
+// For security reasons, we want malloc failures to be fatal. Too much code
+// doesn't check for a NULL return value from malloc and unconditionally uses
+// the resulting pointer. If the first offset that they try to access is
+// attacker controlled, then the attacker can direct the code to access any
+// part of memory.
+//
+// Thus, we define all the standard malloc functions here and mark them as
+// visibility 'default'. This means that they replace the malloc functions for
+// all Chromium code and also for all code in shared libraries. There are tests
+// for this in process_util_unittest.cc.
+//
+// If we are using tcmalloc, then the problem is moot since tcmalloc handles
+// this for us. Thus this code is in a !defined(USE_TCMALLOC) block.
+//
+// If we are testing the binary with AddressSanitizer, we should not
+// redefine malloc and let AddressSanitizer do it instead.
+//
+// We call the real libc functions in this code by using __libc_malloc etc.
+// Previously we tried using dlsym(RTLD_NEXT, ...) but that failed depending on
+// the link order. Since ld.so needs calloc during symbol resolution, it
+// defines its own versions of several of these functions in dl-minimal.c.
+// Depending on the runtime library order, dlsym ended up giving us those
+// functions and bad things happened. See crbug.com/31809
+//
+// This means that any code which calls __libc_* gets the raw libc versions of
+// these functions.
+
+#define DIE_ON_OOM_1(function_name) \
+ void* function_name(size_t) __attribute__ ((visibility("default"))); \
+ \
+ void* function_name(size_t size) { \
+ void* ret = __libc_##function_name(size); \
+ if (ret == NULL && size != 0) \
+ OnNoMemorySize(size); \
+ return ret; \
+ }
+
+#define DIE_ON_OOM_2(function_name, arg1_type) \
+ void* function_name(arg1_type, size_t) \
+ __attribute__ ((visibility("default"))); \
+ \
+ void* function_name(arg1_type arg1, size_t size) { \
+ void* ret = __libc_##function_name(arg1, size); \
+ if (ret == NULL && size != 0) \
+ OnNoMemorySize(size); \
+ return ret; \
+ }
+
+DIE_ON_OOM_1(malloc)
+DIE_ON_OOM_1(valloc)
+DIE_ON_OOM_1(pvalloc)
+
+DIE_ON_OOM_2(calloc, size_t)
+DIE_ON_OOM_2(realloc, void*)
+DIE_ON_OOM_2(memalign, size_t)
+
+// posix_memalign has a unique signature and doesn't have a __libc_ variant.
+int posix_memalign(void** ptr, size_t alignment, size_t size)
+ __attribute__ ((visibility("default")));
+
+int posix_memalign(void** ptr, size_t alignment, size_t size) {
+ // This will use the safe version of memalign, above.
+ *ptr = memalign(alignment, size);
+ return 0;
+}
+
+#endif // !defined(USE_TCMALLOC)
+} // extern C
+
+void EnableTerminationOnHeapCorruption() {
+ // On Linux, there nothing to do AFAIK.
+}
+
+void EnableTerminationOnOutOfMemory() {
+#if defined(OS_ANDROID)
+ // Android doesn't support setting a new handler.
+ DLOG(WARNING) << "Not feasible.";
+#else
+ // Set the new-out of memory handler.
+ std::set_new_handler(&OnNoMemory);
+ // If we're using glibc's allocator, the above functions will override
+ // malloc and friends and make them die on out of memory.
+#endif
+}
+
+// NOTE: This is not the only version of this function in the source:
+// the setuid sandbox (in process_util_linux.c, in the sandbox source)
+// also has its own C version.
+bool AdjustOOMScore(ProcessId process, int score) {
+ if (score < 0 || score > kMaxOomScore)
+ return false;
+
+ FilePath oom_path(GetProcPidDir(process));
+
+ // Attempt to write the newer oom_score_adj file first.
+ FilePath oom_file = oom_path.AppendASCII("oom_score_adj");
+ if (file_util::PathExists(oom_file)) {
+ std::string score_str = base::IntToString(score);
+ DVLOG(1) << "Adjusting oom_score_adj of " << process << " to "
+ << score_str;
+ int score_len = static_cast<int>(score_str.length());
+ return (score_len == file_util::WriteFile(oom_file,
+ score_str.c_str(),
+ score_len));
+ }
+
+ // If the oom_score_adj file doesn't exist, then we write the old
+ // style file and translate the oom_adj score to the range 0-15.
+ oom_file = oom_path.AppendASCII("oom_adj");
+ if (file_util::PathExists(oom_file)) {
+ // Max score for the old oom_adj range. Used for conversion of new
+ // values to old values.
+ const int kMaxOldOomScore = 15;
+
+ int converted_score = score * kMaxOldOomScore / kMaxOomScore;
+ std::string score_str = base::IntToString(converted_score);
+ DVLOG(1) << "Adjusting oom_adj of " << process << " to " << score_str;
+ int score_len = static_cast<int>(score_str.length());
+ return (score_len == file_util::WriteFile(oom_file,
+ score_str.c_str(),
+ score_len));
+ }
+
+ return false;
+}
+
+} // namespace base
diff --git a/src/base/process_util_mac.mm b/src/base/process_util_mac.mm
new file mode 100644
index 0000000..cf1e7a8
--- /dev/null
+++ b/src/base/process_util_mac.mm
@@ -0,0 +1,1265 @@
+// Copyright (c) 2012 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/process_util.h"
+
+#import <Cocoa/Cocoa.h>
+#include <crt_externs.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <mach/mach.h>
+#include <mach/mach_init.h>
+#include <mach/mach_vm.h>
+#include <mach/shared_region.h>
+#include <mach/task.h>
+#include <mach-o/nlist.h>
+#include <malloc/malloc.h>
+#import <objc/runtime.h>
+#include <signal.h>
+#include <spawn.h>
+#include <sys/event.h>
+#include <sys/mman.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <new>
+#include <string>
+
+#include "base/debug/debugger.h"
+#include "base/file_util.h"
+#include "base/hash_tables.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/string_util.h"
+#include "base/sys_info.h"
+#include "base/threading/thread_local.h"
+#include "third_party/apple_apsl/CFBase.h"
+#include "third_party/apple_apsl/malloc.h"
+#include "third_party/mach_override/mach_override.h"
+
+namespace base {
+
+void RestoreDefaultExceptionHandler() {
+ // This function is tailored to remove the Breakpad exception handler.
+ // exception_mask matches s_exception_mask in
+ // breakpad/src/client/mac/handler/exception_handler.cc
+ const exception_mask_t exception_mask = EXC_MASK_BAD_ACCESS |
+ EXC_MASK_BAD_INSTRUCTION |
+ EXC_MASK_ARITHMETIC |
+ EXC_MASK_BREAKPOINT;
+
+ // Setting the exception port to MACH_PORT_NULL may not be entirely
+ // kosher to restore the default exception handler, but in practice,
+ // it results in the exception port being set to Apple Crash Reporter,
+ // the desired behavior.
+ task_set_exception_ports(mach_task_self(), exception_mask, MACH_PORT_NULL,
+ EXCEPTION_DEFAULT, THREAD_STATE_NONE);
+}
+
+ProcessIterator::ProcessIterator(const ProcessFilter* filter)
+ : index_of_kinfo_proc_(0),
+ filter_(filter) {
+ // Get a snapshot of all of my processes (yes, as we loop it can go stale, but
+ // but trying to find where we were in a constantly changing list is basically
+ // impossible.
+
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, geteuid() };
+
+ // Since more processes could start between when we get the size and when
+ // we get the list, we do a loop to keep trying until we get it.
+ bool done = false;
+ int try_num = 1;
+ const int max_tries = 10;
+ do {
+ // Get the size of the buffer
+ size_t len = 0;
+ if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) {
+ DLOG(ERROR) << "failed to get the size needed for the process list";
+ kinfo_procs_.resize(0);
+ done = true;
+ } else {
+ size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
+ // Leave some spare room for process table growth (more could show up
+ // between when we check and now)
+ num_of_kinfo_proc += 16;
+ kinfo_procs_.resize(num_of_kinfo_proc);
+ len = num_of_kinfo_proc * sizeof(struct kinfo_proc);
+ // Load the list of processes
+ if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) {
+ // If we get a mem error, it just means we need a bigger buffer, so
+ // loop around again. Anything else is a real error and give up.
+ if (errno != ENOMEM) {
+ DLOG(ERROR) << "failed to get the process list";
+ kinfo_procs_.resize(0);
+ done = true;
+ }
+ } else {
+ // Got the list, just make sure we're sized exactly right
+ size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
+ kinfo_procs_.resize(num_of_kinfo_proc);
+ done = true;
+ }
+ }
+ } while (!done && (try_num++ < max_tries));
+
+ if (!done) {
+ DLOG(ERROR) << "failed to collect the process list in a few tries";
+ kinfo_procs_.resize(0);
+ }
+}
+
+ProcessIterator::~ProcessIterator() {
+}
+
+bool ProcessIterator::CheckForNextProcess() {
+ std::string data;
+ for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) {
+ kinfo_proc& kinfo = kinfo_procs_[index_of_kinfo_proc_];
+
+ // Skip processes just awaiting collection
+ if ((kinfo.kp_proc.p_pid > 0) && (kinfo.kp_proc.p_stat == SZOMB))
+ continue;
+
+ int mib[] = { CTL_KERN, KERN_PROCARGS, kinfo.kp_proc.p_pid };
+
+ // Find out what size buffer we need.
+ size_t data_len = 0;
+ if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) {
+ DVPLOG(1) << "failed to figure out the buffer size for a commandline";
+ continue;
+ }
+
+ data.resize(data_len);
+ if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) {
+ DVPLOG(1) << "failed to fetch a commandline";
+ continue;
+ }
+
+ // |data| contains all the command line parameters of the process, separated
+ // by blocks of one or more null characters. We tokenize |data| into a
+ // vector of strings using '\0' as a delimiter and populate
+ // |entry_.cmd_line_args_|.
+ std::string delimiters;
+ delimiters.push_back('\0');
+ Tokenize(data, delimiters, &entry_.cmd_line_args_);
+
+ // |data| starts with the full executable path followed by a null character.
+ // We search for the first instance of '\0' and extract everything before it
+ // to populate |entry_.exe_file_|.
+ size_t exec_name_end = data.find('\0');
+ if (exec_name_end == std::string::npos) {
+ DLOG(ERROR) << "command line data didn't match expected format";
+ continue;
+ }
+
+ entry_.pid_ = kinfo.kp_proc.p_pid;
+ entry_.ppid_ = kinfo.kp_eproc.e_ppid;
+ entry_.gid_ = kinfo.kp_eproc.e_pgid;
+ size_t last_slash = data.rfind('/', exec_name_end);
+ if (last_slash == std::string::npos)
+ entry_.exe_file_.assign(data, 0, exec_name_end);
+ else
+ entry_.exe_file_.assign(data, last_slash + 1,
+ exec_name_end - last_slash - 1);
+ // Start w/ the next entry next time through
+ ++index_of_kinfo_proc_;
+ // Done
+ return true;
+ }
+ return false;
+}
+
+bool NamedProcessIterator::IncludeEntry() {
+ return (executable_name_ == entry().exe_file() &&
+ ProcessIterator::IncludeEntry());
+}
+
+
+// ------------------------------------------------------------------------
+// NOTE: about ProcessMetrics
+//
+// Getting a mach task from a pid for another process requires permissions in
+// general, so there doesn't really seem to be a way to do these (and spinning
+// up ps to fetch each stats seems dangerous to put in a base api for anyone to
+// call). Child processes ipc their port, so return something if available,
+// otherwise return 0.
+//
+
+ProcessMetrics::ProcessMetrics(ProcessHandle process,
+ ProcessMetrics::PortProvider* port_provider)
+ : process_(process),
+ last_time_(0),
+ last_system_time_(0),
+ port_provider_(port_provider) {
+ processor_count_ = SysInfo::NumberOfProcessors();
+}
+
+// static
+ProcessMetrics* ProcessMetrics::CreateProcessMetrics(
+ ProcessHandle process,
+ ProcessMetrics::PortProvider* port_provider) {
+ return new ProcessMetrics(process, port_provider);
+}
+
+bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
+ return false;
+}
+
+static bool GetTaskInfo(mach_port_t task, task_basic_info_64* task_info_data) {
+ if (task == MACH_PORT_NULL)
+ return false;
+ mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT;
+ kern_return_t kr = task_info(task,
+ TASK_BASIC_INFO_64,
+ reinterpret_cast<task_info_t>(task_info_data),
+ &count);
+ // Most likely cause for failure: |task| is a zombie.
+ return kr == KERN_SUCCESS;
+}
+
+size_t ProcessMetrics::GetPagefileUsage() const {
+ task_basic_info_64 task_info_data;
+ if (!GetTaskInfo(TaskForPid(process_), &task_info_data))
+ return 0;
+ return task_info_data.virtual_size;
+}
+
+size_t ProcessMetrics::GetPeakPagefileUsage() const {
+ return 0;
+}
+
+size_t ProcessMetrics::GetWorkingSetSize() const {
+ task_basic_info_64 task_info_data;
+ if (!GetTaskInfo(TaskForPid(process_), &task_info_data))
+ return 0;
+ return task_info_data.resident_size;
+}
+
+size_t ProcessMetrics::GetPeakWorkingSetSize() const {
+ return 0;
+}
+
+static bool GetCPUTypeForProcess(pid_t pid, cpu_type_t* cpu_type) {
+ size_t len = sizeof(*cpu_type);
+ int result = sysctlbyname("sysctl.proc_cputype",
+ cpu_type,
+ &len,
+ NULL,
+ 0);
+ if (result != 0) {
+ DPLOG(ERROR) << "sysctlbyname(""sysctl.proc_cputype"")";
+ return false;
+ }
+
+ return true;
+}
+
+static bool IsAddressInSharedRegion(mach_vm_address_t addr, cpu_type_t type) {
+ if (type == CPU_TYPE_I386)
+ return addr >= SHARED_REGION_BASE_I386 &&
+ addr < (SHARED_REGION_BASE_I386 + SHARED_REGION_SIZE_I386);
+ else if (type == CPU_TYPE_X86_64)
+ return addr >= SHARED_REGION_BASE_X86_64 &&
+ addr < (SHARED_REGION_BASE_X86_64 + SHARED_REGION_SIZE_X86_64);
+ else
+ return false;
+}
+
+// This is a rough approximation of the algorithm that libtop uses.
+// private_bytes is the size of private resident memory.
+// shared_bytes is the size of shared resident memory.
+bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
+ size_t* shared_bytes) {
+ kern_return_t kr;
+ size_t private_pages_count = 0;
+ size_t shared_pages_count = 0;
+
+ if (!private_bytes && !shared_bytes)
+ return true;
+
+ mach_port_t task = TaskForPid(process_);
+ if (task == MACH_PORT_NULL) {
+ DLOG(ERROR) << "Invalid process";
+ return false;
+ }
+
+ cpu_type_t cpu_type;
+ if (!GetCPUTypeForProcess(process_, &cpu_type))
+ return false;
+
+ // The same region can be referenced multiple times. To avoid double counting
+ // we need to keep track of which regions we've already counted.
+ base::hash_set<int> seen_objects;
+
+ // We iterate through each VM region in the task's address map. For shared
+ // memory we add up all the pages that are marked as shared. Like libtop we
+ // try to avoid counting pages that are also referenced by other tasks. Since
+ // we don't have access to the VM regions of other tasks the only hint we have
+ // is if the address is in the shared region area.
+ //
+ // Private memory is much simpler. We simply count the pages that are marked
+ // as private or copy on write (COW).
+ //
+ // See libtop_update_vm_regions in
+ // http://www.opensource.apple.com/source/top/top-67/libtop.c
+ mach_vm_size_t size = 0;
+ for (mach_vm_address_t address = MACH_VM_MIN_ADDRESS;; address += size) {
+ vm_region_top_info_data_t info;
+ mach_msg_type_number_t info_count = VM_REGION_TOP_INFO_COUNT;
+ mach_port_t object_name;
+ kr = mach_vm_region(task,
+ &address,
+ &size,
+ VM_REGION_TOP_INFO,
+ (vm_region_info_t)&info,
+ &info_count,
+ &object_name);
+ if (kr == KERN_INVALID_ADDRESS) {
+ // We're at the end of the address space.
+ break;
+ } else if (kr != KERN_SUCCESS) {
+ DLOG(ERROR) << "Calling mach_vm_region failed with error: "
+ << mach_error_string(kr);
+ return false;
+ }
+
+ if (IsAddressInSharedRegion(address, cpu_type) &&
+ info.share_mode != SM_PRIVATE)
+ continue;
+
+ if (info.share_mode == SM_COW && info.ref_count == 1)
+ info.share_mode = SM_PRIVATE;
+
+ switch (info.share_mode) {
+ case SM_PRIVATE:
+ private_pages_count += info.private_pages_resident;
+ private_pages_count += info.shared_pages_resident;
+ break;
+ case SM_COW:
+ private_pages_count += info.private_pages_resident;
+ // Fall through
+ case SM_SHARED:
+ if (seen_objects.count(info.obj_id) == 0) {
+ // Only count the first reference to this region.
+ seen_objects.insert(info.obj_id);
+ shared_pages_count += info.shared_pages_resident;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ vm_size_t page_size;
+ kr = host_page_size(task, &page_size);
+ if (kr != KERN_SUCCESS) {
+ DLOG(ERROR) << "Failed to fetch host page size, error: "
+ << mach_error_string(kr);
+ return false;
+ }
+
+ if (private_bytes)
+ *private_bytes = private_pages_count * page_size;
+ if (shared_bytes)
+ *shared_bytes = shared_pages_count * page_size;
+
+ return true;
+}
+
+void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) const {
+}
+
+bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
+ size_t priv = GetWorkingSetSize();
+ if (!priv)
+ return false;
+ ws_usage->priv = priv / 1024;
+ ws_usage->shareable = 0;
+ ws_usage->shared = 0;
+ return true;
+}
+
+#define TIME_VALUE_TO_TIMEVAL(a, r) do { \
+ (r)->tv_sec = (a)->seconds; \
+ (r)->tv_usec = (a)->microseconds; \
+} while (0)
+
+double ProcessMetrics::GetCPUUsage() {
+ mach_port_t task = TaskForPid(process_);
+ if (task == MACH_PORT_NULL)
+ return 0;
+
+ kern_return_t kr;
+
+ // Libtop explicitly loops over the threads (libtop_pinfo_update_cpu_usage()
+ // in libtop.c), but this is more concise and gives the same results:
+ task_thread_times_info thread_info_data;
+ mach_msg_type_number_t thread_info_count = TASK_THREAD_TIMES_INFO_COUNT;
+ kr = task_info(task,
+ TASK_THREAD_TIMES_INFO,
+ reinterpret_cast<task_info_t>(&thread_info_data),
+ &thread_info_count);
+ if (kr != KERN_SUCCESS) {
+ // Most likely cause: |task| is a zombie.
+ return 0;
+ }
+
+ task_basic_info_64 task_info_data;
+ if (!GetTaskInfo(task, &task_info_data))
+ return 0;
+
+ /* Set total_time. */
+ // thread info contains live time...
+ struct timeval user_timeval, system_timeval, task_timeval;
+ TIME_VALUE_TO_TIMEVAL(&thread_info_data.user_time, &user_timeval);
+ TIME_VALUE_TO_TIMEVAL(&thread_info_data.system_time, &system_timeval);
+ timeradd(&user_timeval, &system_timeval, &task_timeval);
+
+ // ... task info contains terminated time.
+ TIME_VALUE_TO_TIMEVAL(&task_info_data.user_time, &user_timeval);
+ TIME_VALUE_TO_TIMEVAL(&task_info_data.system_time, &system_timeval);
+ timeradd(&user_timeval, &task_timeval, &task_timeval);
+ timeradd(&system_timeval, &task_timeval, &task_timeval);
+
+ struct timeval now;
+ int retval = gettimeofday(&now, NULL);
+ if (retval)
+ return 0;
+
+ int64 time = TimeValToMicroseconds(now);
+ int64 task_time = TimeValToMicroseconds(task_timeval);
+
+ if ((last_system_time_ == 0) || (last_time_ == 0)) {
+ // First call, just set the last values.
+ last_system_time_ = task_time;
+ last_time_ = time;
+ return 0;
+ }
+
+ int64 system_time_delta = task_time - last_system_time_;
+ int64 time_delta = time - last_time_;
+ DCHECK_NE(0U, time_delta);
+ if (time_delta == 0)
+ return 0;
+
+ // We add time_delta / 2 so the result is rounded.
+ double cpu = static_cast<double>((system_time_delta * 100.0) / time_delta);
+
+ last_system_time_ = task_time;
+ last_time_ = time;
+
+ return cpu;
+}
+
+mach_port_t ProcessMetrics::TaskForPid(ProcessHandle process) const {
+ mach_port_t task = MACH_PORT_NULL;
+ if (port_provider_)
+ task = port_provider_->TaskForPid(process_);
+ if (task == MACH_PORT_NULL && process_ == getpid())
+ task = mach_task_self();
+ return task;
+}
+
+// ------------------------------------------------------------------------
+
+// Bytes committed by the system.
+size_t GetSystemCommitCharge() {
+ base::mac::ScopedMachPort host(mach_host_self());
+ mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
+ vm_statistics_data_t data;
+ kern_return_t kr = host_statistics(host, HOST_VM_INFO,
+ reinterpret_cast<host_info_t>(&data),
+ &count);
+ if (kr) {
+ DLOG(WARNING) << "Failed to fetch host statistics.";
+ return 0;
+ }
+
+ vm_size_t page_size;
+ kr = host_page_size(host, &page_size);
+ if (kr) {
+ DLOG(ERROR) << "Failed to fetch host page size.";
+ return 0;
+ }
+
+ return (data.active_count * page_size) / 1024;
+}
+
+namespace {
+
+// Finds the library path for malloc() and thus the libC part of libSystem,
+// which in Lion is in a separate image.
+const char* LookUpLibCPath() {
+ const void* addr = reinterpret_cast<void*>(&malloc);
+
+ Dl_info info;
+ if (dladdr(addr, &info))
+ return info.dli_fname;
+
+ DLOG(WARNING) << "Could not find image path for malloc()";
+ return NULL;
+}
+
+typedef void(*malloc_error_break_t)(void);
+malloc_error_break_t g_original_malloc_error_break = NULL;
+
+// Returns the function pointer for malloc_error_break. This symbol is declared
+// as __private_extern__ and cannot be dlsym()ed. Instead, use nlist() to
+// get it.
+malloc_error_break_t LookUpMallocErrorBreak() {
+#if ARCH_CPU_32_BITS
+ const char* lib_c_path = LookUpLibCPath();
+ if (!lib_c_path)
+ return NULL;
+
+ // Only need to look up two symbols, but nlist() requires a NULL-terminated
+ // array and takes no count.
+ struct nlist nl[3];
+ bzero(&nl, sizeof(nl));
+
+ // The symbol to find.
+ nl[0].n_un.n_name = const_cast<char*>("_malloc_error_break");
+
+ // A reference symbol by which the address of the desired symbol will be
+ // calculated.
+ nl[1].n_un.n_name = const_cast<char*>("_malloc");
+
+ int rv = nlist(lib_c_path, nl);
+ if (rv != 0 || nl[0].n_type == N_UNDF || nl[1].n_type == N_UNDF) {
+ return NULL;
+ }
+
+ // nlist() returns addresses as offsets in the image, not the instruction
+ // pointer in memory. Use the known in-memory address of malloc()
+ // to compute the offset for malloc_error_break().
+ uintptr_t reference_addr = reinterpret_cast<uintptr_t>(&malloc);
+ reference_addr -= nl[1].n_value;
+ reference_addr += nl[0].n_value;
+
+ return reinterpret_cast<malloc_error_break_t>(reference_addr);
+#endif // ARCH_CPU_32_BITS
+
+ return NULL;
+}
+
+// Simple scoper that saves the current value of errno, resets it to 0, and on
+// destruction puts the old value back. This is so that CrMallocErrorBreak can
+// safely test errno free from the effects of other routines.
+class ScopedClearErrno {
+ public:
+ ScopedClearErrno() : old_errno_(errno) {
+ errno = 0;
+ }
+ ~ScopedClearErrno() {
+ if (errno == 0)
+ errno = old_errno_;
+ }
+
+ private:
+ int old_errno_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedClearErrno);
+};
+
+// Combines ThreadLocalBoolean with AutoReset. It would be convenient
+// to compose ThreadLocalPointer<bool> with base::AutoReset<bool>, but that
+// would require allocating some storage for the bool.
+class ThreadLocalBooleanAutoReset {
+ public:
+ ThreadLocalBooleanAutoReset(ThreadLocalBoolean* tlb, bool new_value)
+ : scoped_tlb_(tlb),
+ original_value_(tlb->Get()) {
+ scoped_tlb_->Set(new_value);
+ }
+ ~ThreadLocalBooleanAutoReset() {
+ scoped_tlb_->Set(original_value_);
+ }
+
+ private:
+ ThreadLocalBoolean* scoped_tlb_;
+ bool original_value_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadLocalBooleanAutoReset);
+};
+
+base::LazyInstance<ThreadLocalBoolean>::Leaky
+ g_unchecked_malloc = LAZY_INSTANCE_INITIALIZER;
+
+// NOTE(shess): This is called when the malloc library noticed that the heap
+// is fubar. Avoid calls which will re-enter the malloc library.
+void CrMallocErrorBreak() {
+ g_original_malloc_error_break();
+
+ // Out of memory is certainly not heap corruption, and not necessarily
+ // something for which the process should be terminated. Leave that decision
+ // to the OOM killer. The EBADF case comes up because the malloc library
+ // attempts to log to ASL (syslog) before calling this code, which fails
+ // accessing a Unix-domain socket because of sandboxing.
+ if (errno == ENOMEM || (errno == EBADF && g_unchecked_malloc.Get().Get()))
+ return;
+
+ // A unit test checks this error message, so it needs to be in release builds.
+ char buf[1024] =
+ "Terminating process due to a potential for future heap corruption: "
+ "errno=";
+ char errnobuf[] = {
+ '0' + ((errno / 100) % 10),
+ '0' + ((errno / 10) % 10),
+ '0' + (errno % 10),
+ '\000'
+ };
+ COMPILE_ASSERT(ELAST <= 999, errno_too_large_to_encode);
+ strlcat(buf, errnobuf, sizeof(buf));
+ RAW_LOG(ERROR, buf);
+
+ // Crash by writing to NULL+errno to allow analyzing errno from
+ // crash dump info (setting a breakpad key would re-enter the malloc
+ // library). Max documented errno in intro(2) is actually 102, but
+ // it really just needs to be "small" to stay on the right vm page.
+ const int kMaxErrno = 256;
+ char* volatile death_ptr = NULL;
+ death_ptr += std::min(errno, kMaxErrno);
+ *death_ptr = '!';
+}
+
+} // namespace
+
+void EnableTerminationOnHeapCorruption() {
+#ifdef ADDRESS_SANITIZER
+ // Don't do anything special on heap corruption, because it should be handled
+ // by AddressSanitizer.
+ return;
+#endif
+
+ // Only override once, otherwise CrMallocErrorBreak() will recurse
+ // to itself.
+ if (g_original_malloc_error_break)
+ return;
+
+ malloc_error_break_t malloc_error_break = LookUpMallocErrorBreak();
+ if (!malloc_error_break) {
+ DLOG(WARNING) << "Could not find malloc_error_break";
+ return;
+ }
+
+ mach_error_t err = mach_override_ptr(
+ (void*)malloc_error_break,
+ (void*)&CrMallocErrorBreak,
+ (void**)&g_original_malloc_error_break);
+
+ if (err != err_none)
+ DLOG(WARNING) << "Could not override malloc_error_break; error = " << err;
+}
+
+// ------------------------------------------------------------------------
+
+namespace {
+
+bool g_oom_killer_enabled;
+
+// === C malloc/calloc/valloc/realloc/posix_memalign ===
+
+typedef void* (*malloc_type)(struct _malloc_zone_t* zone,
+ size_t size);
+typedef void* (*calloc_type)(struct _malloc_zone_t* zone,
+ size_t num_items,
+ size_t size);
+typedef void* (*valloc_type)(struct _malloc_zone_t* zone,
+ size_t size);
+typedef void (*free_type)(struct _malloc_zone_t* zone,
+ void* ptr);
+typedef void* (*realloc_type)(struct _malloc_zone_t* zone,
+ void* ptr,
+ size_t size);
+typedef void* (*memalign_type)(struct _malloc_zone_t* zone,
+ size_t alignment,
+ size_t size);
+
+malloc_type g_old_malloc;
+calloc_type g_old_calloc;
+valloc_type g_old_valloc;
+free_type g_old_free;
+realloc_type g_old_realloc;
+memalign_type g_old_memalign;
+
+malloc_type g_old_malloc_purgeable;
+calloc_type g_old_calloc_purgeable;
+valloc_type g_old_valloc_purgeable;
+free_type g_old_free_purgeable;
+realloc_type g_old_realloc_purgeable;
+memalign_type g_old_memalign_purgeable;
+
+void* oom_killer_malloc(struct _malloc_zone_t* zone,
+ size_t size) {
+ ScopedClearErrno clear_errno;
+ void* result = g_old_malloc(zone, size);
+ if (!result && size)
+ debug::BreakDebugger();
+ return result;
+}
+
+void* oom_killer_calloc(struct _malloc_zone_t* zone,
+ size_t num_items,
+ size_t size) {
+ ScopedClearErrno clear_errno;
+ void* result = g_old_calloc(zone, num_items, size);
+ if (!result && num_items && size)
+ debug::BreakDebugger();
+ return result;
+}
+
+void* oom_killer_valloc(struct _malloc_zone_t* zone,
+ size_t size) {
+ ScopedClearErrno clear_errno;
+ void* result = g_old_valloc(zone, size);
+ if (!result && size)
+ debug::BreakDebugger();
+ return result;
+}
+
+void oom_killer_free(struct _malloc_zone_t* zone,
+ void* ptr) {
+ ScopedClearErrno clear_errno;
+ g_old_free(zone, ptr);
+}
+
+void* oom_killer_realloc(struct _malloc_zone_t* zone,
+ void* ptr,
+ size_t size) {
+ ScopedClearErrno clear_errno;
+ void* result = g_old_realloc(zone, ptr, size);
+ if (!result && size)
+ debug::BreakDebugger();
+ return result;
+}
+
+void* oom_killer_memalign(struct _malloc_zone_t* zone,
+ size_t alignment,
+ size_t size) {
+ ScopedClearErrno clear_errno;
+ void* result = g_old_memalign(zone, alignment, size);
+ // Only die if posix_memalign would have returned ENOMEM, since there are
+ // other reasons why NULL might be returned (see
+ // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c ).
+ if (!result && size && alignment >= sizeof(void*)
+ && (alignment & (alignment - 1)) == 0) {
+ debug::BreakDebugger();
+ }
+ return result;
+}
+
+void* oom_killer_malloc_purgeable(struct _malloc_zone_t* zone,
+ size_t size) {
+ ScopedClearErrno clear_errno;
+ void* result = g_old_malloc_purgeable(zone, size);
+ if (!result && size)
+ debug::BreakDebugger();
+ return result;
+}
+
+void* oom_killer_calloc_purgeable(struct _malloc_zone_t* zone,
+ size_t num_items,
+ size_t size) {
+ ScopedClearErrno clear_errno;
+ void* result = g_old_calloc_purgeable(zone, num_items, size);
+ if (!result && num_items && size)
+ debug::BreakDebugger();
+ return result;
+}
+
+void* oom_killer_valloc_purgeable(struct _malloc_zone_t* zone,
+ size_t size) {
+ ScopedClearErrno clear_errno;
+ void* result = g_old_valloc_purgeable(zone, size);
+ if (!result && size)
+ debug::BreakDebugger();
+ return result;
+}
+
+void oom_killer_free_purgeable(struct _malloc_zone_t* zone,
+ void* ptr) {
+ ScopedClearErrno clear_errno;
+ g_old_free_purgeable(zone, ptr);
+}
+
+void* oom_killer_realloc_purgeable(struct _malloc_zone_t* zone,
+ void* ptr,
+ size_t size) {
+ ScopedClearErrno clear_errno;
+ void* result = g_old_realloc_purgeable(zone, ptr, size);
+ if (!result && size)
+ debug::BreakDebugger();
+ return result;
+}
+
+void* oom_killer_memalign_purgeable(struct _malloc_zone_t* zone,
+ size_t alignment,
+ size_t size) {
+ ScopedClearErrno clear_errno;
+ void* result = g_old_memalign_purgeable(zone, alignment, size);
+ // Only die if posix_memalign would have returned ENOMEM, since there are
+ // other reasons why NULL might be returned (see
+ // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c ).
+ if (!result && size && alignment >= sizeof(void*)
+ && (alignment & (alignment - 1)) == 0) {
+ debug::BreakDebugger();
+ }
+ return result;
+}
+
+// === C++ operator new ===
+
+void oom_killer_new() {
+ debug::BreakDebugger();
+}
+
+// === Core Foundation CFAllocators ===
+
+bool CanGetContextForCFAllocator() {
+ return !base::mac::IsOSLaterThanMountainLion_DontCallThis();
+}
+
+CFAllocatorContext* ContextForCFAllocator(CFAllocatorRef allocator) {
+ if (base::mac::IsOSSnowLeopard()) {
+ ChromeCFAllocatorLeopards* our_allocator =
+ const_cast<ChromeCFAllocatorLeopards*>(
+ reinterpret_cast<const ChromeCFAllocatorLeopards*>(allocator));
+ return &our_allocator->_context;
+ } else if (base::mac::IsOSLion() || base::mac::IsOSMountainLion()) {
+ ChromeCFAllocatorLions* our_allocator =
+ const_cast<ChromeCFAllocatorLions*>(
+ reinterpret_cast<const ChromeCFAllocatorLions*>(allocator));
+ return &our_allocator->_context;
+ } else {
+ return NULL;
+ }
+}
+
+CFAllocatorAllocateCallBack g_old_cfallocator_system_default;
+CFAllocatorAllocateCallBack g_old_cfallocator_malloc;
+CFAllocatorAllocateCallBack g_old_cfallocator_malloc_zone;
+
+void* oom_killer_cfallocator_system_default(CFIndex alloc_size,
+ CFOptionFlags hint,
+ void* info) {
+ void* result = g_old_cfallocator_system_default(alloc_size, hint, info);
+ if (!result)
+ debug::BreakDebugger();
+ return result;
+}
+
+void* oom_killer_cfallocator_malloc(CFIndex alloc_size,
+ CFOptionFlags hint,
+ void* info) {
+ void* result = g_old_cfallocator_malloc(alloc_size, hint, info);
+ if (!result)
+ debug::BreakDebugger();
+ return result;
+}
+
+void* oom_killer_cfallocator_malloc_zone(CFIndex alloc_size,
+ CFOptionFlags hint,
+ void* info) {
+ void* result = g_old_cfallocator_malloc_zone(alloc_size, hint, info);
+ if (!result)
+ debug::BreakDebugger();
+ return result;
+}
+
+// === Cocoa NSObject allocation ===
+
+typedef id (*allocWithZone_t)(id, SEL, NSZone*);
+allocWithZone_t g_old_allocWithZone;
+
+id oom_killer_allocWithZone(id self, SEL _cmd, NSZone* zone)
+{
+ id result = g_old_allocWithZone(self, _cmd, zone);
+ if (!result)
+ debug::BreakDebugger();
+ return result;
+}
+
+} // namespace
+
+void* UncheckedMalloc(size_t size) {
+ if (g_old_malloc) {
+ ScopedClearErrno clear_errno;
+ ThreadLocalBooleanAutoReset flag(g_unchecked_malloc.Pointer(), true);
+ return g_old_malloc(malloc_default_zone(), size);
+ }
+ return malloc(size);
+}
+
+void EnableTerminationOnOutOfMemory() {
+ if (g_oom_killer_enabled)
+ return;
+
+ g_oom_killer_enabled = true;
+
+ // === C malloc/calloc/valloc/realloc/posix_memalign ===
+
+ // This approach is not perfect, as requests for amounts of memory larger than
+ // MALLOC_ABSOLUTE_MAX_SIZE (currently SIZE_T_MAX - (2 * PAGE_SIZE)) will
+ // still fail with a NULL rather than dying (see
+ // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c for details).
+ // Unfortunately, it's the best we can do. Also note that this does not affect
+ // allocations from non-default zones.
+
+ CHECK(!g_old_malloc && !g_old_calloc && !g_old_valloc && !g_old_realloc &&
+ !g_old_memalign) << "Old allocators unexpectedly non-null";
+
+ CHECK(!g_old_malloc_purgeable && !g_old_calloc_purgeable &&
+ !g_old_valloc_purgeable && !g_old_realloc_purgeable &&
+ !g_old_memalign_purgeable) << "Old allocators unexpectedly non-null";
+
+#if !defined(ADDRESS_SANITIZER)
+ // Don't do anything special on OOM for the malloc zones replaced by
+ // AddressSanitizer, as modifying or protecting them may not work correctly.
+
+ // See http://trac.webkit.org/changeset/53362/trunk/Tools/DumpRenderTree/mac
+ bool zone_allocators_protected = base::mac::IsOSLionOrLater();
+
+ ChromeMallocZone* default_zone =
+ reinterpret_cast<ChromeMallocZone*>(malloc_default_zone());
+ ChromeMallocZone* purgeable_zone =
+ reinterpret_cast<ChromeMallocZone*>(malloc_default_purgeable_zone());
+
+ vm_address_t page_start_default = 0;
+ vm_address_t page_start_purgeable = 0;
+ vm_size_t len_default = 0;
+ vm_size_t len_purgeable = 0;
+ if (zone_allocators_protected) {
+ page_start_default = reinterpret_cast<vm_address_t>(default_zone) &
+ static_cast<vm_size_t>(~(getpagesize() - 1));
+ len_default = reinterpret_cast<vm_address_t>(default_zone) -
+ page_start_default + sizeof(ChromeMallocZone);
+ mprotect(reinterpret_cast<void*>(page_start_default), len_default,
+ PROT_READ | PROT_WRITE);
+
+ if (purgeable_zone) {
+ page_start_purgeable = reinterpret_cast<vm_address_t>(purgeable_zone) &
+ static_cast<vm_size_t>(~(getpagesize() - 1));
+ len_purgeable = reinterpret_cast<vm_address_t>(purgeable_zone) -
+ page_start_purgeable + sizeof(ChromeMallocZone);
+ mprotect(reinterpret_cast<void*>(page_start_purgeable), len_purgeable,
+ PROT_READ | PROT_WRITE);
+ }
+ }
+
+ // Default zone
+
+ g_old_malloc = default_zone->malloc;
+ g_old_calloc = default_zone->calloc;
+ g_old_valloc = default_zone->valloc;
+ g_old_free = default_zone->free;
+ g_old_realloc = default_zone->realloc;
+ CHECK(g_old_malloc && g_old_calloc && g_old_valloc && g_old_free &&
+ g_old_realloc)
+ << "Failed to get system allocation functions.";
+
+ default_zone->malloc = oom_killer_malloc;
+ default_zone->calloc = oom_killer_calloc;
+ default_zone->valloc = oom_killer_valloc;
+ default_zone->free = oom_killer_free;
+ default_zone->realloc = oom_killer_realloc;
+
+ if (default_zone->version >= 5) {
+ g_old_memalign = default_zone->memalign;
+ if (g_old_memalign)
+ default_zone->memalign = oom_killer_memalign;
+ }
+
+ // Purgeable zone (if it exists)
+
+ if (purgeable_zone) {
+ g_old_malloc_purgeable = purgeable_zone->malloc;
+ g_old_calloc_purgeable = purgeable_zone->calloc;
+ g_old_valloc_purgeable = purgeable_zone->valloc;
+ g_old_free_purgeable = purgeable_zone->free;
+ g_old_realloc_purgeable = purgeable_zone->realloc;
+ CHECK(g_old_malloc_purgeable && g_old_calloc_purgeable &&
+ g_old_valloc_purgeable && g_old_free_purgeable &&
+ g_old_realloc_purgeable)
+ << "Failed to get system allocation functions.";
+
+ purgeable_zone->malloc = oom_killer_malloc_purgeable;
+ purgeable_zone->calloc = oom_killer_calloc_purgeable;
+ purgeable_zone->valloc = oom_killer_valloc_purgeable;
+ purgeable_zone->free = oom_killer_free_purgeable;
+ purgeable_zone->realloc = oom_killer_realloc_purgeable;
+
+ if (purgeable_zone->version >= 5) {
+ g_old_memalign_purgeable = purgeable_zone->memalign;
+ if (g_old_memalign_purgeable)
+ purgeable_zone->memalign = oom_killer_memalign_purgeable;
+ }
+ }
+
+ if (zone_allocators_protected) {
+ mprotect(reinterpret_cast<void*>(page_start_default), len_default,
+ PROT_READ);
+ if (purgeable_zone) {
+ mprotect(reinterpret_cast<void*>(page_start_purgeable), len_purgeable,
+ PROT_READ);
+ }
+ }
+#endif
+
+ // === C malloc_zone_batch_malloc ===
+
+ // batch_malloc is omitted because the default malloc zone's implementation
+ // only supports batch_malloc for "tiny" allocations from the free list. It
+ // will fail for allocations larger than "tiny", and will only allocate as
+ // many blocks as it's able to from the free list. These factors mean that it
+ // can return less than the requested memory even in a non-out-of-memory
+ // situation. There's no good way to detect whether a batch_malloc failure is
+ // due to these other factors, or due to genuine memory or address space
+ // exhaustion. The fact that it only allocates space from the "tiny" free list
+ // means that it's likely that a failure will not be due to memory exhaustion.
+ // Similarly, these constraints on batch_malloc mean that callers must always
+ // be expecting to receive less memory than was requested, even in situations
+ // where memory pressure is not a concern. Finally, the only public interface
+ // to batch_malloc is malloc_zone_batch_malloc, which is specific to the
+ // system's malloc implementation. It's unlikely that anyone's even heard of
+ // it.
+
+ // === C++ operator new ===
+
+ // Yes, operator new does call through to malloc, but this will catch failures
+ // that our imperfect handling of malloc cannot.
+
+ std::set_new_handler(oom_killer_new);
+
+#ifndef ADDRESS_SANITIZER
+ // === Core Foundation CFAllocators ===
+
+ // This will not catch allocation done by custom allocators, but will catch
+ // all allocation done by system-provided ones.
+
+ CHECK(!g_old_cfallocator_system_default && !g_old_cfallocator_malloc &&
+ !g_old_cfallocator_malloc_zone)
+ << "Old allocators unexpectedly non-null";
+
+ bool cf_allocator_internals_known = CanGetContextForCFAllocator();
+
+ if (cf_allocator_internals_known) {
+ CFAllocatorContext* context =
+ ContextForCFAllocator(kCFAllocatorSystemDefault);
+ CHECK(context) << "Failed to get context for kCFAllocatorSystemDefault.";
+ g_old_cfallocator_system_default = context->allocate;
+ CHECK(g_old_cfallocator_system_default)
+ << "Failed to get kCFAllocatorSystemDefault allocation function.";
+ context->allocate = oom_killer_cfallocator_system_default;
+
+ context = ContextForCFAllocator(kCFAllocatorMalloc);
+ CHECK(context) << "Failed to get context for kCFAllocatorMalloc.";
+ g_old_cfallocator_malloc = context->allocate;
+ CHECK(g_old_cfallocator_malloc)
+ << "Failed to get kCFAllocatorMalloc allocation function.";
+ context->allocate = oom_killer_cfallocator_malloc;
+
+ context = ContextForCFAllocator(kCFAllocatorMallocZone);
+ CHECK(context) << "Failed to get context for kCFAllocatorMallocZone.";
+ g_old_cfallocator_malloc_zone = context->allocate;
+ CHECK(g_old_cfallocator_malloc_zone)
+ << "Failed to get kCFAllocatorMallocZone allocation function.";
+ context->allocate = oom_killer_cfallocator_malloc_zone;
+ } else {
+ NSLog(@"Internals of CFAllocator not known; out-of-memory failures via "
+ "CFAllocator will not result in termination. http://crbug.com/45650");
+ }
+#endif
+
+ // === Cocoa NSObject allocation ===
+
+ // Note that both +[NSObject new] and +[NSObject alloc] call through to
+ // +[NSObject allocWithZone:].
+
+ CHECK(!g_old_allocWithZone)
+ << "Old allocator unexpectedly non-null";
+
+ Class nsobject_class = [NSObject class];
+ Method orig_method = class_getClassMethod(nsobject_class,
+ @selector(allocWithZone:));
+ g_old_allocWithZone = reinterpret_cast<allocWithZone_t>(
+ method_getImplementation(orig_method));
+ CHECK(g_old_allocWithZone)
+ << "Failed to get allocWithZone allocation function.";
+ method_setImplementation(orig_method,
+ reinterpret_cast<IMP>(oom_killer_allocWithZone));
+}
+
+ProcessId GetParentProcessId(ProcessHandle process) {
+ struct kinfo_proc info;
+ size_t length = sizeof(struct kinfo_proc);
+ int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process };
+ if (sysctl(mib, 4, &info, &length, NULL, 0) < 0) {
+ DPLOG(ERROR) << "sysctl";
+ return -1;
+ }
+ if (length == 0)
+ return -1;
+ return info.kp_eproc.e_ppid;
+}
+
+namespace {
+
+const int kWaitBeforeKillSeconds = 2;
+
+// Reap |child| process. This call blocks until completion.
+void BlockingReap(pid_t child) {
+ const pid_t result = HANDLE_EINTR(waitpid(child, NULL, 0));
+ if (result == -1) {
+ DPLOG(ERROR) << "waitpid(" << child << ", NULL, 0)";
+ }
+}
+
+// Waits for |timeout| seconds for the given |child| to exit and reap it. If
+// the child doesn't exit within the time specified, kills it.
+//
+// This function takes two approaches: first, it tries to use kqueue to
+// observe when the process exits. kevent can monitor a kqueue with a
+// timeout, so this method is preferred to wait for a specified period of
+// time. Once the kqueue indicates the process has exited, waitpid will reap
+// the exited child. If the kqueue doesn't provide an exit event notification,
+// before the timeout expires, or if the kqueue fails or misbehaves, the
+// process will be mercilessly killed and reaped.
+//
+// A child process passed to this function may be in one of several states:
+// running, terminated and not yet reaped, and (apparently, and unfortunately)
+// terminated and already reaped. Normally, a process will at least have been
+// asked to exit before this function is called, but this is not required.
+// If a process is terminating and unreaped, there may be a window between the
+// time that kqueue will no longer recognize it and when it becomes an actual
+// zombie that a non-blocking (WNOHANG) waitpid can reap. This condition is
+// detected when kqueue indicates that the process is not running and a
+// non-blocking waitpid fails to reap the process but indicates that it is
+// still running. In this event, a blocking attempt to reap the process
+// collects the known-dying child, preventing zombies from congregating.
+//
+// In the event that the kqueue misbehaves entirely, as it might under a
+// EMFILE condition ("too many open files", or out of file descriptors), this
+// function will forcibly kill and reap the child without delay. This
+// eliminates another potential zombie vector. (If you're out of file
+// descriptors, you're probably deep into something else, but that doesn't
+// mean that zombies be allowed to kick you while you're down.)
+//
+// The fact that this function seemingly can be called to wait on a child
+// that's not only already terminated but already reaped is a bit of a
+// problem: a reaped child's pid can be reclaimed and may refer to a distinct
+// process in that case. The fact that this function can seemingly be called
+// to wait on a process that's not even a child is also a problem: kqueue will
+// work in that case, but waitpid won't, and killing a non-child might not be
+// the best approach.
+void WaitForChildToDie(pid_t child, int timeout) {
+ DCHECK(child > 0);
+ DCHECK(timeout > 0);
+
+ // DON'T ADD ANY EARLY RETURNS TO THIS FUNCTION without ensuring that
+ // |child| has been reaped. Specifically, even if a kqueue, kevent, or other
+ // call fails, this function should fall back to the last resort of trying
+ // to kill and reap the process. Not observing this rule will resurrect
+ // zombies.
+
+ int result;
+
+ int kq = HANDLE_EINTR(kqueue());
+ if (kq == -1) {
+ DPLOG(ERROR) << "kqueue()";
+ } else {
+ file_util::ScopedFD auto_close_kq(&kq);
+
+ struct kevent change = {0};
+ EV_SET(&change, child, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
+ result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
+
+ if (result == -1) {
+ if (errno != ESRCH) {
+ DPLOG(ERROR) << "kevent (setup " << child << ")";
+ } else {
+ // At this point, one of the following has occurred:
+ // 1. The process has died but has not yet been reaped.
+ // 2. The process has died and has already been reaped.
+ // 3. The process is in the process of dying. It's no longer
+ // kqueueable, but it may not be waitable yet either. Mark calls
+ // this case the "zombie death race".
+
+ result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG));
+
+ if (result != 0) {
+ // A positive result indicates case 1. waitpid succeeded and reaped
+ // the child. A result of -1 indicates case 2. The child has already
+ // been reaped. In both of these cases, no further action is
+ // necessary.
+ return;
+ }
+
+ // |result| is 0, indicating case 3. The process will be waitable in
+ // short order. Fall back out of the kqueue code to kill it (for good
+ // measure) and reap it.
+ }
+ } else {
+ // Keep track of the elapsed time to be able to restart kevent if it's
+ // interrupted.
+ TimeDelta remaining_delta = TimeDelta::FromSeconds(timeout);
+ Time deadline = Time::Now() + remaining_delta;
+ result = -1;
+ struct kevent event = {0};
+ while (remaining_delta.InMilliseconds() > 0) {
+ const struct timespec remaining_timespec = remaining_delta.ToTimeSpec();
+ result = kevent(kq, NULL, 0, &event, 1, &remaining_timespec);
+ if (result == -1 && errno == EINTR) {
+ remaining_delta = deadline - Time::Now();
+ result = 0;
+ } else {
+ break;
+ }
+ }
+
+ if (result == -1) {
+ DPLOG(ERROR) << "kevent (wait " << child << ")";
+ } else if (result > 1) {
+ DLOG(ERROR) << "kevent (wait " << child << "): unexpected result "
+ << result;
+ } else if (result == 1) {
+ if ((event.fflags & NOTE_EXIT) &&
+ (event.ident == static_cast<uintptr_t>(child))) {
+ // The process is dead or dying. This won't block for long, if at
+ // all.
+ BlockingReap(child);
+ return;
+ } else {
+ DLOG(ERROR) << "kevent (wait " << child
+ << "): unexpected event: fflags=" << event.fflags
+ << ", ident=" << event.ident;
+ }
+ }
+ }
+ }
+
+ // The child is still alive, or is very freshly dead. Be sure by sending it
+ // a signal. This is safe even if it's freshly dead, because it will be a
+ // zombie (or on the way to zombiedom) and kill will return 0 even if the
+ // signal is not delivered to a live process.
+ result = kill(child, SIGKILL);
+ if (result == -1) {
+ DPLOG(ERROR) << "kill(" << child << ", SIGKILL)";
+ } else {
+ // The child is definitely on the way out now. BlockingReap won't need to
+ // wait for long, if at all.
+ BlockingReap(child);
+ }
+}
+
+} // namespace
+
+void EnsureProcessTerminated(ProcessHandle process) {
+ WaitForChildToDie(process, kWaitBeforeKillSeconds);
+}
+
+} // namespace base
diff --git a/src/base/process_util_openbsd.cc b/src/base/process_util_openbsd.cc
new file mode 100644
index 0000000..2b2bd25
--- /dev/null
+++ b/src/base/process_util_openbsd.cc
@@ -0,0 +1,346 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process_util.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
+#include "base/string_tokenizer.h"
+#include "base/string_util.h"
+#include "base/sys_info.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace base {
+
+ProcessId GetParentProcessId(ProcessHandle process) {
+ struct kinfo_proc info;
+ size_t length;
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process,
+ sizeof(struct kinfo_proc), 0 };
+
+ if (sysctl(mib, arraysize(mib), NULL, &length, NULL, 0) < 0)
+ return -1;
+
+ mib[5] = (length / sizeof(struct kinfo_proc));
+
+ if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
+ return -1;
+
+ return info.p_ppid;
+}
+
+FilePath GetProcessExecutablePath(ProcessHandle process) {
+ struct kinfo_proc kp;
+ size_t len;
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process,
+ sizeof(struct kinfo_proc), 0 };
+
+ if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) == -1)
+ return FilePath();
+ mib[5] = (len / sizeof(struct kinfo_proc));
+ if (sysctl(mib, arraysize(mib), &kp, &len, NULL, 0) < 0)
+ return FilePath();
+ if ((kp.p_flag & P_SYSTEM) != 0)
+ return FilePath();
+ if (strcmp(kp.p_comm, "chrome") == 0)
+ return FilePath(kp.p_comm);
+
+ return FilePath();
+}
+
+ProcessIterator::ProcessIterator(const ProcessFilter* filter)
+ : index_of_kinfo_proc_(),
+ filter_(filter) {
+
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, getuid(),
+ sizeof(struct kinfo_proc), 0 };
+
+ bool done = false;
+ int try_num = 1;
+ const int max_tries = 10;
+
+ do {
+ size_t len = 0;
+ if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) {
+ DLOG(ERROR) << "failed to get the size needed for the process list";
+ kinfo_procs_.resize(0);
+ done = true;
+ } else {
+ size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
+ // Leave some spare room for process table growth (more could show up
+ // between when we check and now)
+ num_of_kinfo_proc += 16;
+ kinfo_procs_.resize(num_of_kinfo_proc);
+ len = num_of_kinfo_proc * sizeof(struct kinfo_proc);
+ if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) {
+ // If we get a mem error, it just means we need a bigger buffer, so
+ // loop around again. Anything else is a real error and give up.
+ if (errno != ENOMEM) {
+ DLOG(ERROR) << "failed to get the process list";
+ kinfo_procs_.resize(0);
+ done = true;
+ }
+ } else {
+ // Got the list, just make sure we're sized exactly right
+ size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
+ kinfo_procs_.resize(num_of_kinfo_proc);
+ done = true;
+ }
+ }
+ } while (!done && (try_num++ < max_tries));
+
+ if (!done) {
+ DLOG(ERROR) << "failed to collect the process list in a few tries";
+ kinfo_procs_.resize(0);
+ }
+}
+
+ProcessIterator::~ProcessIterator() {
+}
+
+bool ProcessIterator::CheckForNextProcess() {
+ std::string data;
+ for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) {
+ kinfo_proc& kinfo = kinfo_procs_[index_of_kinfo_proc_];
+
+ // Skip processes just awaiting collection
+ if ((kinfo.p_pid > 0) && (kinfo.p_stat == SZOMB))
+ continue;
+
+ int mib[] = { CTL_KERN, KERN_PROC_ARGS, kinfo.p_pid };
+
+ // Find out what size buffer we need.
+ size_t data_len = 0;
+ if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) {
+ DVPLOG(1) << "failed to figure out the buffer size for a commandline";
+ continue;
+ }
+
+ data.resize(data_len);
+ if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) {
+ DVPLOG(1) << "failed to fetch a commandline";
+ continue;
+ }
+
+ // |data| contains all the command line parameters of the process, separated
+ // by blocks of one or more null characters. We tokenize |data| into a
+ // vector of strings using '\0' as a delimiter and populate
+ // |entry_.cmd_line_args_|.
+ std::string delimiters;
+ delimiters.push_back('\0');
+ Tokenize(data, delimiters, &entry_.cmd_line_args_);
+
+ // |data| starts with the full executable path followed by a null character.
+ // We search for the first instance of '\0' and extract everything before it
+ // to populate |entry_.exe_file_|.
+ size_t exec_name_end = data.find('\0');
+ if (exec_name_end == std::string::npos) {
+ DLOG(ERROR) << "command line data didn't match expected format";
+ continue;
+ }
+
+ entry_.pid_ = kinfo.p_pid;
+ entry_.ppid_ = kinfo.p_ppid;
+ entry_.gid_ = kinfo.p__pgid;
+ size_t last_slash = data.rfind('/', exec_name_end);
+ if (last_slash == std::string::npos)
+ entry_.exe_file_.assign(data, 0, exec_name_end);
+ else
+ entry_.exe_file_.assign(data, last_slash + 1,
+ exec_name_end - last_slash - 1);
+ // Start w/ the next entry next time through
+ ++index_of_kinfo_proc_;
+ // Done
+ return true;
+ }
+ return false;
+}
+
+bool NamedProcessIterator::IncludeEntry() {
+ return (executable_name_ == entry().exe_file() &&
+ ProcessIterator::IncludeEntry());
+}
+
+
+ProcessMetrics::ProcessMetrics(ProcessHandle process)
+ : process_(process),
+ last_time_(0),
+ last_system_time_(0),
+ last_cpu_(0) {
+
+ processor_count_ = base::SysInfo::NumberOfProcessors();
+}
+
+// static
+ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
+ return new ProcessMetrics(process);
+}
+
+size_t ProcessMetrics::GetPagefileUsage() const {
+ struct kinfo_proc info;
+ size_t length;
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_,
+ sizeof(struct kinfo_proc), 0 };
+
+ if (sysctl(mib, arraysize(mib), NULL, &length, NULL, 0) < 0)
+ return -1;
+
+ mib[5] = (length / sizeof(struct kinfo_proc));
+
+ if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
+ return -1;
+
+ return (info.p_vm_tsize + info.p_vm_dsize + info.p_vm_ssize);
+}
+
+size_t ProcessMetrics::GetPeakPagefileUsage() const {
+
+ return 0;
+}
+
+size_t ProcessMetrics::GetWorkingSetSize() const {
+ struct kinfo_proc info;
+ size_t length;
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_,
+ sizeof(struct kinfo_proc), 0 };
+
+ if (sysctl(mib, arraysize(mib), NULL, &length, NULL, 0) < 0)
+ return -1;
+
+ mib[5] = (length / sizeof(struct kinfo_proc));
+
+ if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
+ return -1;
+
+ return info.p_vm_rssize * getpagesize();
+}
+
+size_t ProcessMetrics::GetPeakWorkingSetSize() const {
+
+ return 0;
+}
+
+bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
+ size_t* shared_bytes) {
+ WorkingSetKBytes ws_usage;
+
+ if (!GetWorkingSetKBytes(&ws_usage))
+ return false;
+
+ if (private_bytes)
+ *private_bytes = ws_usage.priv << 10;
+
+ if (shared_bytes)
+ *shared_bytes = ws_usage.shared * 1024;
+
+ return true;
+}
+
+bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
+// TODO(bapt) be sure we can't be precise
+ size_t priv = GetWorkingSetSize();
+ if (!priv)
+ return false;
+ ws_usage->priv = priv / 1024;
+ ws_usage->shareable = 0;
+ ws_usage->shared = 0;
+
+ return true;
+}
+
+bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
+ return false;
+}
+
+static int GetProcessCPU(pid_t pid) {
+ struct kinfo_proc info;
+ size_t length;
+ int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid,
+ sizeof(struct kinfo_proc), 0 };
+
+ if (sysctl(mib, arraysize(mib), NULL, &length, NULL, 0) < 0)
+ return -1;
+
+ mib[5] = (length / sizeof(struct kinfo_proc));
+
+ if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
+ return 0;
+
+ return info.p_pctcpu;
+}
+
+double ProcessMetrics::GetCPUUsage() {
+ struct timeval now;
+
+ int retval = gettimeofday(&now, NULL);
+ if (retval)
+ return 0;
+
+ int64 time = TimeValToMicroseconds(now);
+
+ if (last_time_ == 0) {
+ // First call, just set the last values.
+ last_time_ = time;
+ last_cpu_ = GetProcessCPU(process_);
+ return 0;
+ }
+
+ int64 time_delta = time - last_time_;
+ DCHECK_NE(time_delta, 0);
+
+ if (time_delta == 0)
+ return 0;
+
+ int cpu = GetProcessCPU(process_);
+
+ last_time_ = time;
+ last_cpu_ = cpu;
+
+ double percentage = static_cast<double>((cpu * 100.0) / FSCALE);
+
+ return percentage;
+}
+
+size_t GetSystemCommitCharge() {
+ int mib[] = { CTL_VM, VM_METER };
+ int pagesize;
+ struct vmtotal vmtotal;
+ unsigned long mem_total, mem_free, mem_inactive;
+ size_t len = sizeof(vmtotal);
+
+ if (sysctl(mib, arraysize(mib), &vmtotal, &len, NULL, 0) < 0)
+ return 0;
+
+ mem_total = vmtotal.t_vm;
+ mem_free = vmtotal.t_free;
+ mem_inactive = vmtotal.t_vm - vmtotal.t_avm;
+
+ pagesize = getpagesize();
+
+ return mem_total - (mem_free*pagesize) - (mem_inactive*pagesize);
+}
+
+void EnableTerminationOnOutOfMemory() {
+}
+
+void EnableTerminationOnHeapCorruption() {
+}
+
+} // namespace base
diff --git a/src/base/process_util_posix.cc b/src/base/process_util_posix.cc
new file mode 100644
index 0000000..284d00f
--- /dev/null
+++ b/src/base/process_util_posix.cc
@@ -0,0 +1,1280 @@
+// Copyright (c) 2012 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 <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <iterator>
+#include <limits>
+#include <set>
+
+#include "base/allocator/type_profiler_control.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/debug/debugger.h"
+#include "base/debug/stack_trace.h"
+#include "base/file_util.h"
+#include "base/files/dir_reader_posix.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process_util.h"
+#include "base/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread_restrictions.h"
+
+#if defined(OS_CHROMEOS)
+#include <sys/ioctl.h>
+#endif
+
+#if defined(OS_FREEBSD)
+#include <sys/event.h>
+#include <sys/ucontext.h>
+#endif
+
+#if defined(OS_MACOSX)
+#include <crt_externs.h>
+#include <sys/event.h>
+#else
+extern char** environ;
+#endif
+
+namespace base {
+
+namespace {
+
+// Get the process's "environment" (i.e. the thing that setenv/getenv
+// work with).
+char** GetEnvironment() {
+#if defined(OS_MACOSX)
+ return *_NSGetEnviron();
+#else
+ return environ;
+#endif
+}
+
+// Set the process's "environment" (i.e. the thing that setenv/getenv
+// work with).
+void SetEnvironment(char** env) {
+#if defined(OS_MACOSX)
+ *_NSGetEnviron() = env;
+#else
+ environ = env;
+#endif
+}
+
+int WaitpidWithTimeout(ProcessHandle handle, int64 wait_milliseconds,
+ bool* success) {
+ // This POSIX version of this function only guarantees that we wait no less
+ // than |wait_milliseconds| for the process to exit. The child process may
+ // exit sometime before the timeout has ended but we may still block for up
+ // to 256 milliseconds after the fact.
+ //
+ // waitpid() has no direct support on POSIX for specifying a timeout, you can
+ // either ask it to block indefinitely or return immediately (WNOHANG).
+ // When a child process terminates a SIGCHLD signal is sent to the parent.
+ // Catching this signal would involve installing a signal handler which may
+ // affect other parts of the application and would be difficult to debug.
+ //
+ // Our strategy is to call waitpid() once up front to check if the process
+ // has already exited, otherwise to loop for wait_milliseconds, sleeping for
+ // at most 256 milliseconds each time using usleep() and then calling
+ // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and
+ // we double it every 4 sleep cycles.
+ //
+ // usleep() is speced to exit if a signal is received for which a handler
+ // has been installed. This means that when a SIGCHLD is sent, it will exit
+ // depending on behavior external to this function.
+ //
+ // This function is used primarily for unit tests, if we want to use it in
+ // the application itself it would probably be best to examine other routes.
+ int status = -1;
+ pid_t ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
+ static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds.
+ int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds.
+ int64 double_sleep_time = 0;
+
+ // If the process hasn't exited yet, then sleep and try again.
+ Time wakeup_time = Time::Now() +
+ TimeDelta::FromMilliseconds(wait_milliseconds);
+ while (ret_pid == 0) {
+ Time now = Time::Now();
+ if (now > wakeup_time)
+ break;
+ // Guaranteed to be non-negative!
+ int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds();
+ // Sleep for a bit while we wait for the process to finish.
+ if (sleep_time_usecs > max_sleep_time_usecs)
+ sleep_time_usecs = max_sleep_time_usecs;
+
+ // usleep() will return 0 and set errno to EINTR on receipt of a signal
+ // such as SIGCHLD.
+ usleep(sleep_time_usecs);
+ ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
+
+ if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) &&
+ (double_sleep_time++ % 4 == 0)) {
+ max_sleep_time_usecs *= 2;
+ }
+ }
+
+ if (success)
+ *success = (ret_pid != -1);
+
+ return status;
+}
+
+void ResetChildSignalHandlersToDefaults() {
+ // The previous signal handlers are likely to be meaningless in the child's
+ // context so we reset them to the defaults for now. http://crbug.com/44953
+ // These signal handlers are set up at least in browser_main_posix.cc:
+ // BrowserMainPartsPosix::PreEarlyInitialization and stack_trace_posix.cc:
+ // EnableInProcessStackDumping.
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGILL, SIG_DFL);
+ signal(SIGABRT, SIG_DFL);
+ signal(SIGFPE, SIG_DFL);
+ signal(SIGBUS, SIG_DFL);
+ signal(SIGSEGV, SIG_DFL);
+ signal(SIGSYS, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+}
+
+TerminationStatus GetTerminationStatusImpl(ProcessHandle handle,
+ bool can_block,
+ int* exit_code) {
+ int status = 0;
+ const pid_t result = HANDLE_EINTR(waitpid(handle, &status,
+ can_block ? 0 : WNOHANG));
+ if (result == -1) {
+ DPLOG(ERROR) << "waitpid(" << handle << ")";
+ if (exit_code)
+ *exit_code = 0;
+ return TERMINATION_STATUS_NORMAL_TERMINATION;
+ } else if (result == 0) {
+ // the child hasn't exited yet.
+ if (exit_code)
+ *exit_code = 0;
+ return TERMINATION_STATUS_STILL_RUNNING;
+ }
+
+ if (exit_code)
+ *exit_code = status;
+
+ if (WIFSIGNALED(status)) {
+ switch (WTERMSIG(status)) {
+ case SIGABRT:
+ case SIGBUS:
+ case SIGFPE:
+ case SIGILL:
+ case SIGSEGV:
+ return TERMINATION_STATUS_PROCESS_CRASHED;
+ case SIGINT:
+ case SIGKILL:
+ case SIGTERM:
+ return TERMINATION_STATUS_PROCESS_WAS_KILLED;
+ default:
+ break;
+ }
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
+ return TERMINATION_STATUS_ABNORMAL_TERMINATION;
+
+ return TERMINATION_STATUS_NORMAL_TERMINATION;
+}
+
+} // anonymous namespace
+
+ProcessId GetCurrentProcId() {
+ return getpid();
+}
+
+ProcessHandle GetCurrentProcessHandle() {
+ return GetCurrentProcId();
+}
+
+bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) {
+ // On Posix platforms, process handles are the same as PIDs, so we
+ // don't need to do anything.
+ *handle = pid;
+ return true;
+}
+
+bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) {
+ // On POSIX permissions are checked for each operation on process,
+ // not when opening a "handle".
+ return OpenProcessHandle(pid, handle);
+}
+
+bool OpenProcessHandleWithAccess(ProcessId pid,
+ uint32 access_flags,
+ ProcessHandle* handle) {
+ // On POSIX permissions are checked for each operation on process,
+ // not when opening a "handle".
+ return OpenProcessHandle(pid, handle);
+}
+
+void CloseProcessHandle(ProcessHandle process) {
+ // See OpenProcessHandle, nothing to do.
+ return;
+}
+
+ProcessId GetProcId(ProcessHandle process) {
+ return process;
+}
+
+// Attempts to kill the process identified by the given process
+// entry structure. Ignores specified exit_code; posix can't force that.
+// Returns true if this is successful, false otherwise.
+bool KillProcess(ProcessHandle process_id, int exit_code, bool wait) {
+ DCHECK_GT(process_id, 1) << " tried to kill invalid process_id";
+ if (process_id <= 1)
+ return false;
+ bool result = kill(process_id, SIGTERM) == 0;
+ if (result && wait) {
+ int tries = 60;
+
+ if (RunningOnValgrind()) {
+ // Wait for some extra time when running under Valgrind since the child
+ // processes may take some time doing leak checking.
+ tries *= 2;
+ }
+
+ unsigned sleep_ms = 4;
+
+ // The process may not end immediately due to pending I/O
+ bool exited = false;
+ while (tries-- > 0) {
+ pid_t pid = HANDLE_EINTR(waitpid(process_id, NULL, WNOHANG));
+ if (pid == process_id) {
+ exited = true;
+ break;
+ }
+ if (pid == -1) {
+ if (errno == ECHILD) {
+ // The wait may fail with ECHILD if another process also waited for
+ // the same pid, causing the process state to get cleaned up.
+ exited = true;
+ break;
+ }
+ DPLOG(ERROR) << "Error waiting for process " << process_id;
+ }
+
+ usleep(sleep_ms * 1000);
+ const unsigned kMaxSleepMs = 1000;
+ if (sleep_ms < kMaxSleepMs)
+ sleep_ms *= 2;
+ }
+
+ // If we're waiting and the child hasn't died by now, force it
+ // with a SIGKILL.
+ if (!exited)
+ result = kill(process_id, SIGKILL) == 0;
+ }
+
+ if (!result)
+ DPLOG(ERROR) << "Unable to terminate process " << process_id;
+
+ return result;
+}
+
+bool KillProcessGroup(ProcessHandle process_group_id) {
+ bool result = kill(-1 * process_group_id, SIGKILL) == 0;
+ if (!result)
+ DPLOG(ERROR) << "Unable to terminate process group " << process_group_id;
+ return result;
+}
+
+// A class to handle auto-closing of DIR*'s.
+class ScopedDIRClose {
+ public:
+ inline void operator()(DIR* x) const {
+ if (x) {
+ closedir(x);
+ }
+ }
+};
+typedef scoped_ptr_malloc<DIR, ScopedDIRClose> ScopedDIR;
+
+#if defined(OS_LINUX)
+ static const rlim_t kSystemDefaultMaxFds = 8192;
+ static const char kFDDir[] = "/proc/self/fd";
+#elif defined(OS_MACOSX)
+ static const rlim_t kSystemDefaultMaxFds = 256;
+ static const char kFDDir[] = "/dev/fd";
+#elif defined(OS_SOLARIS)
+ static const rlim_t kSystemDefaultMaxFds = 8192;
+ static const char kFDDir[] = "/dev/fd";
+#elif defined(OS_FREEBSD)
+ static const rlim_t kSystemDefaultMaxFds = 8192;
+ static const char kFDDir[] = "/dev/fd";
+#elif defined(OS_OPENBSD)
+ static const rlim_t kSystemDefaultMaxFds = 256;
+ static const char kFDDir[] = "/dev/fd";
+#elif defined(OS_ANDROID)
+ static const rlim_t kSystemDefaultMaxFds = 1024;
+ static const char kFDDir[] = "/proc/self/fd";
+#endif
+
+void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) {
+ // DANGER: no calls to malloc are allowed from now on:
+ // http://crbug.com/36678
+
+ // Get the maximum number of FDs possible.
+ struct rlimit nofile;
+ rlim_t max_fds;
+ if (getrlimit(RLIMIT_NOFILE, &nofile)) {
+ // getrlimit failed. Take a best guess.
+ max_fds = kSystemDefaultMaxFds;
+ RAW_LOG(ERROR, "getrlimit(RLIMIT_NOFILE) failed");
+ } else {
+ max_fds = nofile.rlim_cur;
+ }
+
+ if (max_fds > INT_MAX)
+ max_fds = INT_MAX;
+
+ DirReaderPosix fd_dir(kFDDir);
+
+ if (!fd_dir.IsValid()) {
+ // Fallback case: Try every possible fd.
+ for (rlim_t i = 0; i < max_fds; ++i) {
+ const int fd = static_cast<int>(i);
+ if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO)
+ continue;
+ InjectiveMultimap::const_iterator j;
+ for (j = saved_mapping.begin(); j != saved_mapping.end(); j++) {
+ if (fd == j->dest)
+ break;
+ }
+ if (j != saved_mapping.end())
+ continue;
+
+ // Since we're just trying to close anything we can find,
+ // ignore any error return values of close().
+ ignore_result(HANDLE_EINTR(close(fd)));
+ }
+ return;
+ }
+
+ const int dir_fd = fd_dir.fd();
+
+ for ( ; fd_dir.Next(); ) {
+ // Skip . and .. entries.
+ if (fd_dir.name()[0] == '.')
+ continue;
+
+ char *endptr;
+ errno = 0;
+ const long int fd = strtol(fd_dir.name(), &endptr, 10);
+ if (fd_dir.name()[0] == 0 || *endptr || fd < 0 || errno)
+ continue;
+ if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO)
+ continue;
+ InjectiveMultimap::const_iterator i;
+ for (i = saved_mapping.begin(); i != saved_mapping.end(); i++) {
+ if (fd == i->dest)
+ break;
+ }
+ if (i != saved_mapping.end())
+ continue;
+ if (fd == dir_fd)
+ continue;
+
+ // When running under Valgrind, Valgrind opens several FDs for its
+ // own use and will complain if we try to close them. All of
+ // these FDs are >= |max_fds|, so we can check against that here
+ // before closing. See https://bugs.kde.org/show_bug.cgi?id=191758
+ if (fd < static_cast<int>(max_fds)) {
+ int ret = HANDLE_EINTR(close(fd));
+ DPCHECK(ret == 0);
+ }
+ }
+}
+
+char** AlterEnvironment(const EnvironmentVector& changes,
+ const char* const* const env) {
+ unsigned count = 0;
+ unsigned size = 0;
+
+ // First assume that all of the current environment will be included.
+ for (unsigned i = 0; env[i]; i++) {
+ const char *const pair = env[i];
+ count++;
+ size += strlen(pair) + 1 /* terminating NUL */;
+ }
+
+ for (EnvironmentVector::const_iterator j = changes.begin();
+ j != changes.end();
+ ++j) {
+ bool found = false;
+ const char *pair;
+
+ for (unsigned i = 0; env[i]; i++) {
+ pair = env[i];
+ const char *const equals = strchr(pair, '=');
+ if (!equals)
+ continue;
+ const unsigned keylen = equals - pair;
+ if (keylen == j->first.size() &&
+ memcmp(pair, j->first.data(), keylen) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ // if found, we'll either be deleting or replacing this element.
+ if (found) {
+ count--;
+ size -= strlen(pair) + 1;
+ if (j->second.size())
+ found = false;
+ }
+
+ // if !found, then we have a new element to add.
+ if (!found && !j->second.empty()) {
+ count++;
+ size += j->first.size() + 1 /* '=' */ + j->second.size() + 1 /* NUL */;
+ }
+ }
+
+ count++; // for the final NULL
+ uint8_t *buffer = new uint8_t[sizeof(char*) * count + size];
+ char **const ret = reinterpret_cast<char**>(buffer);
+ unsigned k = 0;
+ char *scratch = reinterpret_cast<char*>(buffer + sizeof(char*) * count);
+
+ for (unsigned i = 0; env[i]; i++) {
+ const char *const pair = env[i];
+ const char *const equals = strchr(pair, '=');
+ if (!equals) {
+ const unsigned len = strlen(pair);
+ ret[k++] = scratch;
+ memcpy(scratch, pair, len + 1);
+ scratch += len + 1;
+ continue;
+ }
+ const unsigned keylen = equals - pair;
+ bool handled = false;
+ for (EnvironmentVector::const_iterator
+ j = changes.begin(); j != changes.end(); j++) {
+ if (j->first.size() == keylen &&
+ memcmp(j->first.data(), pair, keylen) == 0) {
+ if (!j->second.empty()) {
+ ret[k++] = scratch;
+ memcpy(scratch, pair, keylen + 1);
+ scratch += keylen + 1;
+ memcpy(scratch, j->second.c_str(), j->second.size() + 1);
+ scratch += j->second.size() + 1;
+ }
+ handled = true;
+ break;
+ }
+ }
+
+ if (!handled) {
+ const unsigned len = strlen(pair);
+ ret[k++] = scratch;
+ memcpy(scratch, pair, len + 1);
+ scratch += len + 1;
+ }
+ }
+
+ // Now handle new elements
+ for (EnvironmentVector::const_iterator
+ j = changes.begin(); j != changes.end(); j++) {
+ if (j->second.empty())
+ continue;
+
+ bool found = false;
+ for (unsigned i = 0; env[i]; i++) {
+ const char *const pair = env[i];
+ const char *const equals = strchr(pair, '=');
+ if (!equals)
+ continue;
+ const unsigned keylen = equals - pair;
+ if (keylen == j->first.size() &&
+ memcmp(pair, j->first.data(), keylen) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ ret[k++] = scratch;
+ memcpy(scratch, j->first.data(), j->first.size());
+ scratch += j->first.size();
+ *scratch++ = '=';
+ memcpy(scratch, j->second.c_str(), j->second.size() + 1);
+ scratch += j->second.size() + 1;
+ }
+ }
+
+ ret[k] = NULL;
+ return ret;
+}
+
+bool LaunchProcess(const std::vector<std::string>& argv,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle) {
+ size_t fd_shuffle_size = 0;
+ if (options.fds_to_remap) {
+ fd_shuffle_size = options.fds_to_remap->size();
+ }
+
+#if defined(OS_MACOSX)
+ if (options.synchronize) {
+ // When synchronizing, the "read" end of the synchronization pipe needs
+ // to make it to the child process. This is handled by mapping it back to
+ // itself.
+ ++fd_shuffle_size;
+ }
+#endif // defined(OS_MACOSX)
+
+ InjectiveMultimap fd_shuffle1;
+ InjectiveMultimap fd_shuffle2;
+ fd_shuffle1.reserve(fd_shuffle_size);
+ fd_shuffle2.reserve(fd_shuffle_size);
+
+ scoped_array<char*> argv_cstr(new char*[argv.size() + 1]);
+ scoped_array<char*> new_environ;
+ if (options.environ)
+ new_environ.reset(AlterEnvironment(*options.environ, GetEnvironment()));
+
+#if defined(OS_MACOSX)
+ int synchronization_pipe_fds[2];
+ file_util::ScopedFD synchronization_read_fd;
+ file_util::ScopedFD synchronization_write_fd;
+
+ if (options.synchronize) {
+ // wait means "don't return from LaunchProcess until the child exits", and
+ // synchronize means "return from LaunchProcess but don't let the child
+ // run until LaunchSynchronize is called". These two options are highly
+ // incompatible.
+ DCHECK(!options.wait);
+
+ // Create the pipe used for synchronization.
+ if (HANDLE_EINTR(pipe(synchronization_pipe_fds)) != 0) {
+ DPLOG(ERROR) << "pipe";
+ return false;
+ }
+
+ // The parent process will only use synchronization_write_fd as the write
+ // side of the pipe. It can close the read side as soon as the child
+ // process has forked off. The child process will only use
+ // synchronization_read_fd as the read side of the pipe. In that process,
+ // the write side can be closed as soon as it has forked.
+ synchronization_read_fd.reset(&synchronization_pipe_fds[0]);
+ synchronization_write_fd.reset(&synchronization_pipe_fds[1]);
+ }
+#endif // OS_MACOSX
+
+ pid_t pid;
+#if defined(OS_LINUX)
+ if (options.clone_flags) {
+ pid = syscall(__NR_clone, options.clone_flags, 0, 0, 0);
+ } else
+#endif
+ {
+ pid = fork();
+ }
+
+ if (pid < 0) {
+ DPLOG(ERROR) << "fork";
+ return false;
+ } else if (pid == 0) {
+ // Child process
+
+ // DANGER: fork() rule: in the child, if you don't end up doing exec*(),
+ // you call _exit() instead of exit(). This is because _exit() does not
+ // call any previously-registered (in the parent) exit handlers, which
+ // might do things like block waiting for threads that don't even exist
+ // in the child.
+
+ // If a child process uses the readline library, the process block forever.
+ // In BSD like OSes including OS X it is safe to assign /dev/null as stdin.
+ // See http://crbug.com/56596.
+ int null_fd = HANDLE_EINTR(open("/dev/null", O_RDONLY));
+ if (null_fd < 0) {
+ RAW_LOG(ERROR, "Failed to open /dev/null");
+ _exit(127);
+ }
+
+ file_util::ScopedFD null_fd_closer(&null_fd);
+ int new_fd = HANDLE_EINTR(dup2(null_fd, STDIN_FILENO));
+ if (new_fd != STDIN_FILENO) {
+ RAW_LOG(ERROR, "Failed to dup /dev/null for stdin");
+ _exit(127);
+ }
+
+ if (options.new_process_group) {
+ // Instead of inheriting the process group ID of the parent, the child
+ // starts off a new process group with pgid equal to its process ID.
+ if (setpgid(0, 0) < 0) {
+ RAW_LOG(ERROR, "setpgid failed");
+ _exit(127);
+ }
+ }
+
+ // Stop type-profiler.
+ // The profiler should be stopped between fork and exec since it inserts
+ // locks at new/delete expressions. See http://crbug.com/36678.
+ base::type_profiler::Controller::Stop();
+
+ if (options.maximize_rlimits) {
+ // Some resource limits need to be maximal in this child.
+ std::set<int>::const_iterator resource;
+ for (resource = options.maximize_rlimits->begin();
+ resource != options.maximize_rlimits->end();
+ ++resource) {
+ struct rlimit limit;
+ if (getrlimit(*resource, &limit) < 0) {
+ RAW_LOG(WARNING, "getrlimit failed");
+ } else if (limit.rlim_cur < limit.rlim_max) {
+ limit.rlim_cur = limit.rlim_max;
+ if (setrlimit(*resource, &limit) < 0) {
+ RAW_LOG(WARNING, "setrlimit failed");
+ }
+ }
+ }
+ }
+
+#if defined(OS_MACOSX)
+ RestoreDefaultExceptionHandler();
+#endif // defined(OS_MACOSX)
+
+ ResetChildSignalHandlersToDefaults();
+
+#if defined(OS_MACOSX)
+ if (options.synchronize) {
+ // The "write" side of the synchronization pipe belongs to the parent.
+ synchronization_write_fd.reset(); // closes synchronization_pipe_fds[1]
+ }
+#endif // defined(OS_MACOSX)
+
+#if 0
+ // When debugging it can be helpful to check that we really aren't making
+ // any hidden calls to malloc.
+ void *malloc_thunk =
+ reinterpret_cast<void*>(reinterpret_cast<intptr_t>(malloc) & ~4095);
+ mprotect(malloc_thunk, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
+ memset(reinterpret_cast<void*>(malloc), 0xff, 8);
+#endif // 0
+
+ // DANGER: no calls to malloc are allowed from now on:
+ // http://crbug.com/36678
+
+#if defined(OS_CHROMEOS)
+ if (options.ctrl_terminal_fd >= 0) {
+ // Set process' controlling terminal.
+ if (HANDLE_EINTR(setsid()) != -1) {
+ if (HANDLE_EINTR(
+ ioctl(options.ctrl_terminal_fd, TIOCSCTTY, NULL)) == -1) {
+ RAW_LOG(WARNING, "ioctl(TIOCSCTTY), ctrl terminal not set");
+ }
+ } else {
+ RAW_LOG(WARNING, "setsid failed, ctrl terminal not set");
+ }
+ }
+#endif // defined(OS_CHROMEOS)
+
+ if (options.fds_to_remap) {
+ for (FileHandleMappingVector::const_iterator
+ it = options.fds_to_remap->begin();
+ it != options.fds_to_remap->end(); ++it) {
+ fd_shuffle1.push_back(InjectionArc(it->first, it->second, false));
+ fd_shuffle2.push_back(InjectionArc(it->first, it->second, false));
+ }
+ }
+
+#if defined(OS_MACOSX)
+ if (options.synchronize) {
+ // Remap the read side of the synchronization pipe back onto itself,
+ // ensuring that it won't be closed by CloseSuperfluousFds.
+ int keep_fd = *synchronization_read_fd.get();
+ fd_shuffle1.push_back(InjectionArc(keep_fd, keep_fd, false));
+ fd_shuffle2.push_back(InjectionArc(keep_fd, keep_fd, false));
+ }
+#endif // defined(OS_MACOSX)
+
+ if (options.environ)
+ SetEnvironment(new_environ.get());
+
+ // fd_shuffle1 is mutated by this call because it cannot malloc.
+ if (!ShuffleFileDescriptors(&fd_shuffle1))
+ _exit(127);
+
+ CloseSuperfluousFds(fd_shuffle2);
+
+#if defined(OS_MACOSX)
+ if (options.synchronize) {
+ // Do a blocking read to wait until the parent says it's OK to proceed.
+ // The byte that's read here is written by LaunchSynchronize.
+ char read_char;
+ int read_result =
+ HANDLE_EINTR(read(*synchronization_read_fd.get(), &read_char, 1));
+ if (read_result != 1) {
+ RAW_LOG(ERROR, "LaunchProcess: synchronization read: error");
+ _exit(127);
+ }
+
+ // The pipe is no longer useful. Don't let it live on in the new process
+ // after exec.
+ synchronization_read_fd.reset(); // closes synchronization_pipe_fds[0]
+ }
+#endif // defined(OS_MACOSX)
+
+ for (size_t i = 0; i < argv.size(); i++)
+ argv_cstr[i] = const_cast<char*>(argv[i].c_str());
+ argv_cstr[argv.size()] = NULL;
+ execvp(argv_cstr[0], argv_cstr.get());
+
+ RAW_LOG(ERROR, "LaunchProcess: failed to execvp:");
+ RAW_LOG(ERROR, argv_cstr[0]);
+ _exit(127);
+ } else {
+ // Parent process
+ if (options.wait) {
+ // While this isn't strictly disk IO, waiting for another process to
+ // finish is the sort of thing ThreadRestrictions is trying to prevent.
+ base::ThreadRestrictions::AssertIOAllowed();
+ pid_t ret = HANDLE_EINTR(waitpid(pid, 0, 0));
+ DPCHECK(ret > 0);
+ }
+
+ if (process_handle)
+ *process_handle = pid;
+
+#if defined(OS_MACOSX)
+ if (options.synchronize) {
+ // The "read" side of the synchronization pipe belongs to the child.
+ synchronization_read_fd.reset(); // closes synchronization_pipe_fds[0]
+ *options.synchronize = new int(*synchronization_write_fd.release());
+ }
+#endif // defined(OS_MACOSX)
+ }
+
+ return true;
+}
+
+
+bool LaunchProcess(const CommandLine& cmdline,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle) {
+ return LaunchProcess(cmdline.argv(), options, process_handle);
+}
+
+#if defined(OS_MACOSX)
+void LaunchSynchronize(LaunchSynchronizationHandle handle) {
+ int synchronization_fd = *handle;
+ file_util::ScopedFD synchronization_fd_closer(&synchronization_fd);
+ delete handle;
+
+ // Write a '\0' character to the pipe.
+ if (HANDLE_EINTR(write(synchronization_fd, "", 1)) != 1) {
+ DPLOG(ERROR) << "write";
+ }
+}
+#endif // defined(OS_MACOSX)
+
+ProcessMetrics::~ProcessMetrics() { }
+
+void RaiseProcessToHighPriority() {
+ // On POSIX, we don't actually do anything here. We could try to nice() or
+ // setpriority() or sched_getscheduler, but these all require extra rights.
+}
+
+TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
+ return GetTerminationStatusImpl(handle, false /* can_block */, exit_code);
+}
+
+TerminationStatus WaitForTerminationStatus(ProcessHandle handle,
+ int* exit_code) {
+ return GetTerminationStatusImpl(handle, true /* can_block */, exit_code);
+}
+
+bool WaitForExitCode(ProcessHandle handle, int* exit_code) {
+ int status;
+ if (HANDLE_EINTR(waitpid(handle, &status, 0)) == -1) {
+ NOTREACHED();
+ return false;
+ }
+
+ if (WIFEXITED(status)) {
+ *exit_code = WEXITSTATUS(status);
+ return true;
+ }
+
+ // If it didn't exit cleanly, it must have been signaled.
+ DCHECK(WIFSIGNALED(status));
+ return false;
+}
+
+bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code,
+ base::TimeDelta timeout) {
+ bool waitpid_success = false;
+ int status = WaitpidWithTimeout(handle, timeout.InMilliseconds(),
+ &waitpid_success);
+ if (status == -1)
+ return false;
+ if (!waitpid_success)
+ return false;
+ if (WIFSIGNALED(status)) {
+ *exit_code = -1;
+ return true;
+ }
+ if (WIFEXITED(status)) {
+ *exit_code = WEXITSTATUS(status);
+ return true;
+ }
+ return false;
+}
+
+#if defined(OS_MACOSX)
+// Using kqueue on Mac so that we can wait on non-child processes.
+// We can't use kqueues on child processes because we need to reap
+// our own children using wait.
+static bool WaitForSingleNonChildProcess(ProcessHandle handle,
+ base::TimeDelta wait) {
+ DCHECK_GT(handle, 0);
+ DCHECK(wait.InMilliseconds() == base::kNoTimeout || wait > base::TimeDelta());
+
+ int kq = kqueue();
+ if (kq == -1) {
+ DPLOG(ERROR) << "kqueue";
+ return false;
+ }
+ file_util::ScopedFD kq_closer(&kq);
+
+ struct kevent change = {0};
+ EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
+ int result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
+ if (result == -1) {
+ if (errno == ESRCH) {
+ // If the process wasn't found, it must be dead.
+ return true;
+ }
+
+ DPLOG(ERROR) << "kevent (setup " << handle << ")";
+ return false;
+ }
+
+ // Keep track of the elapsed time to be able to restart kevent if it's
+ // interrupted.
+ bool wait_forever = wait.InMilliseconds() == base::kNoTimeout;
+ base::TimeDelta remaining_delta;
+ base::Time deadline;
+ if (!wait_forever) {
+ remaining_delta = wait;
+ deadline = base::Time::Now() + remaining_delta;
+ }
+
+ result = -1;
+ struct kevent event = {0};
+
+ while (wait_forever || remaining_delta > base::TimeDelta()) {
+ struct timespec remaining_timespec;
+ struct timespec* remaining_timespec_ptr;
+ if (wait_forever) {
+ remaining_timespec_ptr = NULL;
+ } else {
+ remaining_timespec = remaining_delta.ToTimeSpec();
+ remaining_timespec_ptr = &remaining_timespec;
+ }
+
+ result = kevent(kq, NULL, 0, &event, 1, remaining_timespec_ptr);
+
+ if (result == -1 && errno == EINTR) {
+ if (!wait_forever) {
+ remaining_delta = deadline - base::Time::Now();
+ }
+ result = 0;
+ } else {
+ break;
+ }
+ }
+
+ if (result < 0) {
+ DPLOG(ERROR) << "kevent (wait " << handle << ")";
+ return false;
+ } else if (result > 1) {
+ DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result "
+ << result;
+ return false;
+ } else if (result == 0) {
+ // Timed out.
+ return false;
+ }
+
+ DCHECK_EQ(result, 1);
+
+ if (event.filter != EVFILT_PROC ||
+ (event.fflags & NOTE_EXIT) == 0 ||
+ event.ident != static_cast<uintptr_t>(handle)) {
+ DLOG(ERROR) << "kevent (wait " << handle
+ << "): unexpected event: filter=" << event.filter
+ << ", fflags=" << event.fflags
+ << ", ident=" << event.ident;
+ return false;
+ }
+
+ return true;
+}
+#endif // OS_MACOSX
+
+bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) {
+ ProcessHandle parent_pid = GetParentProcessId(handle);
+ ProcessHandle our_pid = Process::Current().handle();
+ if (parent_pid != our_pid) {
+#if defined(OS_MACOSX)
+ // On Mac we can wait on non child processes.
+ return WaitForSingleNonChildProcess(handle, wait);
+#else
+ // Currently on Linux we can't handle non child processes.
+ NOTIMPLEMENTED();
+#endif // OS_MACOSX
+ }
+
+ bool waitpid_success;
+ int status = -1;
+ if (wait.InMilliseconds() == base::kNoTimeout) {
+ waitpid_success = (HANDLE_EINTR(waitpid(handle, &status, 0)) != -1);
+ } else {
+ status = WaitpidWithTimeout(
+ handle, wait.InMilliseconds(), &waitpid_success);
+ }
+
+ if (status != -1) {
+ DCHECK(waitpid_success);
+ return WIFEXITED(status);
+ } else {
+ return false;
+ }
+}
+
+int64 TimeValToMicroseconds(const struct timeval& tv) {
+ static const int kMicrosecondsPerSecond = 1000000;
+ int64 ret = tv.tv_sec; // Avoid (int * int) integer overflow.
+ ret *= kMicrosecondsPerSecond;
+ ret += tv.tv_usec;
+ return ret;
+}
+
+// Return value used by GetAppOutputInternal to encapsulate the various exit
+// scenarios from the function.
+enum GetAppOutputInternalResult {
+ EXECUTE_FAILURE,
+ EXECUTE_SUCCESS,
+ GOT_MAX_OUTPUT,
+};
+
+// Executes the application specified by |argv| and wait for it to exit. Stores
+// the output (stdout) in |output|. If |do_search_path| is set, it searches the
+// path for the application; in that case, |envp| must be null, and it will use
+// the current environment. If |do_search_path| is false, |argv[0]| should fully
+// specify the path of the application, and |envp| will be used as the
+// environment. Redirects stderr to /dev/null.
+// If we successfully start the application and get all requested output, we
+// return GOT_MAX_OUTPUT, or if there is a problem starting or exiting
+// the application we return RUN_FAILURE. Otherwise we return EXECUTE_SUCCESS.
+// The GOT_MAX_OUTPUT return value exists so a caller that asks for limited
+// output can treat this as a success, despite having an exit code of SIG_PIPE
+// due to us closing the output pipe.
+// In the case of EXECUTE_SUCCESS, the application exit code will be returned
+// in |*exit_code|, which should be checked to determine if the application
+// ran successfully.
+static GetAppOutputInternalResult GetAppOutputInternal(
+ const std::vector<std::string>& argv,
+ char* const envp[],
+ std::string* output,
+ size_t max_output,
+ bool do_search_path,
+ int* exit_code) {
+ // Doing a blocking wait for another command to finish counts as IO.
+ base::ThreadRestrictions::AssertIOAllowed();
+ // exit_code must be supplied so calling function can determine success.
+ DCHECK(exit_code);
+ *exit_code = EXIT_FAILURE;
+
+ int pipe_fd[2];
+ pid_t pid;
+ InjectiveMultimap fd_shuffle1, fd_shuffle2;
+ scoped_array<char*> argv_cstr(new char*[argv.size() + 1]);
+
+ fd_shuffle1.reserve(3);
+ fd_shuffle2.reserve(3);
+
+ // Either |do_search_path| should be false or |envp| should be null, but not
+ // both.
+ DCHECK(!do_search_path ^ !envp);
+
+ if (pipe(pipe_fd) < 0)
+ return EXECUTE_FAILURE;
+
+ switch (pid = fork()) {
+ case -1: // error
+ close(pipe_fd[0]);
+ close(pipe_fd[1]);
+ return EXECUTE_FAILURE;
+ case 0: // child
+ {
+#if defined(OS_MACOSX)
+ RestoreDefaultExceptionHandler();
+#endif
+ // DANGER: no calls to malloc are allowed from now on:
+ // http://crbug.com/36678
+
+ // Obscure fork() rule: in the child, if you don't end up doing exec*(),
+ // you call _exit() instead of exit(). This is because _exit() does not
+ // call any previously-registered (in the parent) exit handlers, which
+ // might do things like block waiting for threads that don't even exist
+ // in the child.
+ int dev_null = open("/dev/null", O_WRONLY);
+ if (dev_null < 0)
+ _exit(127);
+
+ // Stop type-profiler.
+ // The profiler should be stopped between fork and exec since it inserts
+ // locks at new/delete expressions. See http://crbug.com/36678.
+ base::type_profiler::Controller::Stop();
+
+ fd_shuffle1.push_back(InjectionArc(pipe_fd[1], STDOUT_FILENO, true));
+ fd_shuffle1.push_back(InjectionArc(dev_null, STDERR_FILENO, true));
+ fd_shuffle1.push_back(InjectionArc(dev_null, STDIN_FILENO, true));
+ // Adding another element here? Remeber to increase the argument to
+ // reserve(), above.
+
+ std::copy(fd_shuffle1.begin(), fd_shuffle1.end(),
+ std::back_inserter(fd_shuffle2));
+
+ if (!ShuffleFileDescriptors(&fd_shuffle1))
+ _exit(127);
+
+ CloseSuperfluousFds(fd_shuffle2);
+
+ for (size_t i = 0; i < argv.size(); i++)
+ argv_cstr[i] = const_cast<char*>(argv[i].c_str());
+ argv_cstr[argv.size()] = NULL;
+ if (do_search_path)
+ execvp(argv_cstr[0], argv_cstr.get());
+ else
+ execve(argv_cstr[0], argv_cstr.get(), envp);
+ _exit(127);
+ }
+ default: // parent
+ {
+ // Close our writing end of pipe now. Otherwise later read would not
+ // be able to detect end of child's output (in theory we could still
+ // write to the pipe).
+ close(pipe_fd[1]);
+
+ output->clear();
+ char buffer[256];
+ size_t output_buf_left = max_output;
+ ssize_t bytes_read = 1; // A lie to properly handle |max_output == 0|
+ // case in the logic below.
+
+ while (output_buf_left > 0) {
+ bytes_read = HANDLE_EINTR(read(pipe_fd[0], buffer,
+ std::min(output_buf_left, sizeof(buffer))));
+ if (bytes_read <= 0)
+ break;
+ output->append(buffer, bytes_read);
+ output_buf_left -= static_cast<size_t>(bytes_read);
+ }
+ close(pipe_fd[0]);
+
+ // Always wait for exit code (even if we know we'll declare
+ // GOT_MAX_OUTPUT).
+ bool success = WaitForExitCode(pid, exit_code);
+
+ // If we stopped because we read as much as we wanted, we return
+ // GOT_MAX_OUTPUT (because the child may exit due to |SIGPIPE|).
+ if (!output_buf_left && bytes_read > 0)
+ return GOT_MAX_OUTPUT;
+ else if (success)
+ return EXECUTE_SUCCESS;
+ return EXECUTE_FAILURE;
+ }
+ }
+}
+
+bool GetAppOutput(const CommandLine& cl, std::string* output) {
+ return GetAppOutput(cl.argv(), output);
+}
+
+bool GetAppOutput(const std::vector<std::string>& argv, std::string* output) {
+ // Run |execve()| with the current environment and store "unlimited" data.
+ int exit_code;
+ GetAppOutputInternalResult result = GetAppOutputInternal(
+ argv, NULL, output, std::numeric_limits<std::size_t>::max(), true,
+ &exit_code);
+ return result == EXECUTE_SUCCESS && exit_code == EXIT_SUCCESS;
+}
+
+// TODO(viettrungluu): Conceivably, we should have a timeout as well, so we
+// don't hang if what we're calling hangs.
+bool GetAppOutputRestricted(const CommandLine& cl,
+ std::string* output, size_t max_output) {
+ // Run |execve()| with the empty environment.
+ char* const empty_environ = NULL;
+ int exit_code;
+ GetAppOutputInternalResult result = GetAppOutputInternal(
+ cl.argv(), &empty_environ, output, max_output, false, &exit_code);
+ return result == GOT_MAX_OUTPUT || (result == EXECUTE_SUCCESS &&
+ exit_code == EXIT_SUCCESS);
+}
+
+bool GetAppOutputWithExitCode(const CommandLine& cl,
+ std::string* output,
+ int* exit_code) {
+ // Run |execve()| with the current environment and store "unlimited" data.
+ GetAppOutputInternalResult result = GetAppOutputInternal(
+ cl.argv(), NULL, output, std::numeric_limits<std::size_t>::max(), true,
+ exit_code);
+ return result == EXECUTE_SUCCESS;
+}
+
+bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ const ProcessFilter* filter) {
+ bool result = false;
+
+ // TODO(port): This is inefficient, but works if there are multiple procs.
+ // TODO(port): use waitpid to avoid leaving zombies around
+
+ base::Time end_time = base::Time::Now() + wait;
+ do {
+ NamedProcessIterator iter(executable_name, filter);
+ if (!iter.NextProcessEntry()) {
+ result = true;
+ break;
+ }
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
+ } while ((end_time - base::Time::Now()) > base::TimeDelta());
+
+ return result;
+}
+
+bool CleanupProcesses(const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ int exit_code,
+ const ProcessFilter* filter) {
+ bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter);
+ if (!exited_cleanly)
+ KillProcesses(executable_name, exit_code, filter);
+ return exited_cleanly;
+}
+
+#if !defined(OS_MACOSX)
+
+namespace {
+
+// Return true if the given child is dead. This will also reap the process.
+// Doesn't block.
+static bool IsChildDead(pid_t child) {
+ const pid_t result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG));
+ if (result == -1) {
+ DPLOG(ERROR) << "waitpid(" << child << ")";
+ NOTREACHED();
+ } else if (result > 0) {
+ // The child has died.
+ return true;
+ }
+
+ return false;
+}
+
+// A thread class which waits for the given child to exit and reaps it.
+// If the child doesn't exit within a couple of seconds, kill it.
+class BackgroundReaper : public PlatformThread::Delegate {
+ public:
+ BackgroundReaper(pid_t child, unsigned timeout)
+ : child_(child),
+ timeout_(timeout) {
+ }
+
+ // Overridden from PlatformThread::Delegate:
+ virtual void ThreadMain() OVERRIDE {
+ WaitForChildToDie();
+ delete this;
+ }
+
+ void WaitForChildToDie() {
+ // Wait forever case.
+ if (timeout_ == 0) {
+ pid_t r = HANDLE_EINTR(waitpid(child_, NULL, 0));
+ if (r != child_) {
+ DPLOG(ERROR) << "While waiting for " << child_
+ << " to terminate, we got the following result: " << r;
+ }
+ return;
+ }
+
+ // There's no good way to wait for a specific child to exit in a timed
+ // fashion. (No kqueue on Linux), so we just loop and sleep.
+
+ // Wait for 2 * timeout_ 500 milliseconds intervals.
+ for (unsigned i = 0; i < 2 * timeout_; ++i) {
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(500));
+ if (IsChildDead(child_))
+ return;
+ }
+
+ if (kill(child_, SIGKILL) == 0) {
+ // SIGKILL is uncatchable. Since the signal was delivered, we can
+ // just wait for the process to die now in a blocking manner.
+ if (HANDLE_EINTR(waitpid(child_, NULL, 0)) < 0)
+ DPLOG(WARNING) << "waitpid";
+ } else {
+ DLOG(ERROR) << "While waiting for " << child_ << " to terminate we"
+ << " failed to deliver a SIGKILL signal (" << errno << ").";
+ }
+ }
+
+ private:
+ const pid_t child_;
+ // Number of seconds to wait, if 0 then wait forever and do not attempt to
+ // kill |child_|.
+ const unsigned timeout_;
+
+ DISALLOW_COPY_AND_ASSIGN(BackgroundReaper);
+};
+
+} // namespace
+
+void EnsureProcessTerminated(ProcessHandle process) {
+ // If the child is already dead, then there's nothing to do.
+ if (IsChildDead(process))
+ return;
+
+ const unsigned timeout = 2; // seconds
+ BackgroundReaper* reaper = new BackgroundReaper(process, timeout);
+ PlatformThread::CreateNonJoinable(0, reaper);
+}
+
+void EnsureProcessGetsReaped(ProcessHandle process) {
+ // If the child is already dead, then there's nothing to do.
+ if (IsChildDead(process))
+ return;
+
+ BackgroundReaper* reaper = new BackgroundReaper(process, 0);
+ PlatformThread::CreateNonJoinable(0, reaper);
+}
+
+#endif // !defined(OS_MACOSX)
+
+} // namespace base
diff --git a/src/base/process_util_starboard.cc b/src/base/process_util_starboard.cc
new file mode 100644
index 0000000..0848acf
--- /dev/null
+++ b/src/base/process_util_starboard.cc
@@ -0,0 +1,35 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Starboard doesn't support the concept of processes, so this implementation
+// just minimally and trivially implements the functions of this interface that
+// are called, but that do not impact the functioning of the rest of the system.
+
+#include "base/process_util.h"
+
+#include "base/process.h"
+
+namespace base {
+
+ProcessHandle GetCurrentProcessHandle() {
+ return kNullProcessHandle;
+}
+
+ProcessId GetCurrentProcId() {
+ return kNullProcessId;
+}
+
+void EnableTerminationOnHeapCorruption() {}
+
+} // namespace base
diff --git a/src/base/process_util_unittest.cc b/src/base/process_util_unittest.cc
new file mode 100644
index 0000000..4c1eff4
--- /dev/null
+++ b/src/base/process_util_unittest.cc
@@ -0,0 +1,1220 @@
+// Copyright (c) 2012 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 _CRT_SECURE_NO_WARNINGS
+
+#include <limits>
+
+#include "base/command_line.h"
+#include "base/debug/alias.h"
+#include "base/debug/stack_trace.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/path_service.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process_util.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/test_timeouts.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/threading/platform_thread.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+#if defined(OS_LINUX)
+#include <malloc.h>
+#include <glib.h>
+#include <sched.h>
+#endif
+#if defined(OS_POSIX)
+#include <errno.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#endif
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+#if defined(OS_MACOSX)
+#include <mach/vm_param.h>
+#include <malloc/malloc.h>
+#include "base/process_util_unittest_mac.h"
+#endif
+
+namespace {
+
+#if defined(OS_WIN)
+const wchar_t kProcessName[] = L"base_unittests.exe";
+#else
+const wchar_t kProcessName[] = L"base_unittests";
+#endif // defined(OS_WIN)
+
+#if defined(OS_ANDROID)
+const char kShellPath[] = "/system/bin/sh";
+const char kPosixShell[] = "sh";
+#else
+const char kShellPath[] = "/bin/sh";
+const char kPosixShell[] = "bash";
+#endif
+
+const char kSignalFileSlow[] = "SlowChildProcess.die";
+const char kSignalFileCrash[] = "CrashingChildProcess.die";
+const char kSignalFileKill[] = "KilledChildProcess.die";
+
+#if defined(OS_WIN)
+const int kExpectedStillRunningExitCode = 0x102;
+const int kExpectedKilledExitCode = 1;
+#else
+const int kExpectedStillRunningExitCode = 0;
+#endif
+
+#if defined(OS_WIN)
+// HeapQueryInformation function pointer.
+typedef BOOL (WINAPI* HeapQueryFn) \
+ (HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T);
+#endif
+
+// Sleeps until file filename is created.
+void WaitToDie(const char* filename) {
+ FILE* fp;
+ do {
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
+ fp = fopen(filename, "r");
+ } while (!fp);
+ fclose(fp);
+}
+
+// Signals children they should die now.
+void SignalChildren(const char* filename) {
+ FILE* fp = fopen(filename, "w");
+ fclose(fp);
+}
+
+// Using a pipe to the child to wait for an event was considered, but
+// there were cases in the past where pipes caused problems (other
+// libraries closing the fds, child deadlocking). This is a simple
+// case, so it's not worth the risk. Using wait loops is discouraged
+// in most instances.
+base::TerminationStatus WaitForChildTermination(base::ProcessHandle handle,
+ int* exit_code) {
+ // Now we wait until the result is something other than STILL_RUNNING.
+ base::TerminationStatus status = base::TERMINATION_STATUS_STILL_RUNNING;
+ const base::TimeDelta kInterval = base::TimeDelta::FromMilliseconds(20);
+ base::TimeDelta waited;
+ do {
+ status = base::GetTerminationStatus(handle, exit_code);
+ base::PlatformThread::Sleep(kInterval);
+ waited += kInterval;
+ } while (status == base::TERMINATION_STATUS_STILL_RUNNING &&
+// Waiting for more time for process termination on android devices.
+#if defined(OS_ANDROID)
+ waited < TestTimeouts::large_test_timeout());
+#else
+ waited < TestTimeouts::action_max_timeout());
+#endif
+
+ return status;
+}
+
+} // namespace
+
+class ProcessUtilTest : public base::MultiProcessTest {
+ public:
+#if defined(OS_POSIX)
+ // Spawn a child process that counts how many file descriptors are open.
+ int CountOpenFDsInChild();
+#endif
+ // Converts the filename to a platform specific filepath.
+ // On Android files can not be created in arbitrary directories.
+ static std::string GetSignalFilePath(const char* filename);
+};
+
+std::string ProcessUtilTest::GetSignalFilePath(const char* filename) {
+#if !defined(OS_ANDROID)
+ return filename;
+#else
+ FilePath tmp_dir;
+ PathService::Get(base::DIR_CACHE, &tmp_dir);
+ tmp_dir = tmp_dir.Append(filename);
+ return tmp_dir.value();
+#endif
+}
+
+MULTIPROCESS_TEST_MAIN(SimpleChildProcess) {
+ return 0;
+}
+
+TEST_F(ProcessUtilTest, SpawnChild) {
+ base::ProcessHandle handle = this->SpawnChild("SimpleChildProcess", false);
+ ASSERT_NE(base::kNullProcessHandle, handle);
+ EXPECT_TRUE(base::WaitForSingleProcess(
+ handle, TestTimeouts::action_max_timeout()));
+ base::CloseProcessHandle(handle);
+}
+
+MULTIPROCESS_TEST_MAIN(SlowChildProcess) {
+ WaitToDie(ProcessUtilTest::GetSignalFilePath(kSignalFileSlow).c_str());
+ return 0;
+}
+
+TEST_F(ProcessUtilTest, KillSlowChild) {
+ const std::string signal_file =
+ ProcessUtilTest::GetSignalFilePath(kSignalFileSlow);
+ remove(signal_file.c_str());
+ base::ProcessHandle handle = this->SpawnChild("SlowChildProcess", false);
+ ASSERT_NE(base::kNullProcessHandle, handle);
+ SignalChildren(signal_file.c_str());
+ EXPECT_TRUE(base::WaitForSingleProcess(
+ handle, TestTimeouts::action_max_timeout()));
+ base::CloseProcessHandle(handle);
+ remove(signal_file.c_str());
+}
+
+// Times out on Linux and Win, flakes on other platforms, http://crbug.com/95058
+TEST_F(ProcessUtilTest, DISABLED_GetTerminationStatusExit) {
+ const std::string signal_file =
+ ProcessUtilTest::GetSignalFilePath(kSignalFileSlow);
+ remove(signal_file.c_str());
+ base::ProcessHandle handle = this->SpawnChild("SlowChildProcess", false);
+ ASSERT_NE(base::kNullProcessHandle, handle);
+
+ int exit_code = 42;
+ EXPECT_EQ(base::TERMINATION_STATUS_STILL_RUNNING,
+ base::GetTerminationStatus(handle, &exit_code));
+ EXPECT_EQ(kExpectedStillRunningExitCode, exit_code);
+
+ SignalChildren(signal_file.c_str());
+ exit_code = 42;
+ base::TerminationStatus status =
+ WaitForChildTermination(handle, &exit_code);
+ EXPECT_EQ(base::TERMINATION_STATUS_NORMAL_TERMINATION, status);
+ EXPECT_EQ(0, exit_code);
+ base::CloseProcessHandle(handle);
+ remove(signal_file.c_str());
+}
+
+#if defined(OS_WIN)
+// TODO(cpu): figure out how to test this in other platforms.
+TEST_F(ProcessUtilTest, GetProcId) {
+ base::ProcessId id1 = base::GetProcId(GetCurrentProcess());
+ EXPECT_NE(0ul, id1);
+ base::ProcessHandle handle = this->SpawnChild("SimpleChildProcess", false);
+ ASSERT_NE(base::kNullProcessHandle, handle);
+ base::ProcessId id2 = base::GetProcId(handle);
+ EXPECT_NE(0ul, id2);
+ EXPECT_NE(id1, id2);
+ base::CloseProcessHandle(handle);
+}
+
+TEST_F(ProcessUtilTest, GetModuleFromAddress) {
+ // Since the unit tests are their own EXE, this should be
+ // equivalent to the EXE's HINSTANCE.
+ //
+ // kExpectedKilledExitCode is a constant in this file and
+ // therefore within the unit test EXE.
+ EXPECT_EQ(::GetModuleHandle(NULL),
+ base::GetModuleFromAddress(
+ const_cast<int*>(&kExpectedKilledExitCode)));
+
+ // Any address within the kernel32 module should return
+ // kernel32's HMODULE. Our only assumption here is that
+ // kernel32 is larger than 4 bytes.
+ HMODULE kernel32 = ::GetModuleHandle(L"kernel32.dll");
+ HMODULE kernel32_from_address =
+ base::GetModuleFromAddress(reinterpret_cast<DWORD*>(kernel32) + 1);
+ EXPECT_EQ(kernel32, kernel32_from_address);
+}
+#endif
+
+#if !defined(OS_MACOSX)
+// This test is disabled on Mac, since it's flaky due to ReportCrash
+// taking a variable amount of time to parse and load the debug and
+// symbol data for this unit test's executable before firing the
+// signal handler.
+//
+// TODO(gspencer): turn this test process into a very small program
+// with no symbols (instead of using the multiprocess testing
+// framework) to reduce the ReportCrash overhead.
+
+MULTIPROCESS_TEST_MAIN(CrashingChildProcess) {
+ WaitToDie(ProcessUtilTest::GetSignalFilePath(kSignalFileCrash).c_str());
+#if defined(OS_POSIX)
+ // Have to disable to signal handler for segv so we can get a crash
+ // instead of an abnormal termination through the crash dump handler.
+ ::signal(SIGSEGV, SIG_DFL);
+#endif
+ // Make this process have a segmentation fault.
+ volatile int* oops = NULL;
+ *oops = 0xDEAD;
+ return 1;
+}
+
+// This test intentionally crashes, so we don't need to run it under
+// AddressSanitizer.
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_GetTerminationStatusCrash DISABLED_GetTerminationStatusCrash
+#else
+#define MAYBE_GetTerminationStatusCrash GetTerminationStatusCrash
+#endif
+TEST_F(ProcessUtilTest, MAYBE_GetTerminationStatusCrash) {
+ const std::string signal_file =
+ ProcessUtilTest::GetSignalFilePath(kSignalFileCrash);
+ remove(signal_file.c_str());
+ base::ProcessHandle handle = this->SpawnChild("CrashingChildProcess",
+ false);
+ ASSERT_NE(base::kNullProcessHandle, handle);
+
+ int exit_code = 42;
+ EXPECT_EQ(base::TERMINATION_STATUS_STILL_RUNNING,
+ base::GetTerminationStatus(handle, &exit_code));
+ EXPECT_EQ(kExpectedStillRunningExitCode, exit_code);
+
+ SignalChildren(signal_file.c_str());
+ exit_code = 42;
+ base::TerminationStatus status =
+ WaitForChildTermination(handle, &exit_code);
+ EXPECT_EQ(base::TERMINATION_STATUS_PROCESS_CRASHED, status);
+
+#if defined(OS_WIN)
+ EXPECT_EQ(0xc0000005, exit_code);
+#elif defined(OS_POSIX)
+ int signaled = WIFSIGNALED(exit_code);
+ EXPECT_NE(0, signaled);
+ int signal = WTERMSIG(exit_code);
+ EXPECT_EQ(SIGSEGV, signal);
+#endif
+ base::CloseProcessHandle(handle);
+
+ // Reset signal handlers back to "normal".
+ base::debug::EnableInProcessStackDumping();
+ remove(signal_file.c_str());
+}
+#endif // !defined(OS_MACOSX)
+
+MULTIPROCESS_TEST_MAIN(KilledChildProcess) {
+ WaitToDie(ProcessUtilTest::GetSignalFilePath(kSignalFileKill).c_str());
+#if defined(OS_WIN)
+ // Kill ourselves.
+ HANDLE handle = ::OpenProcess(PROCESS_ALL_ACCESS, 0, ::GetCurrentProcessId());
+ ::TerminateProcess(handle, kExpectedKilledExitCode);
+#elif defined(OS_POSIX)
+ // Send a SIGKILL to this process, just like the OOM killer would.
+ ::kill(getpid(), SIGKILL);
+#endif
+ return 1;
+}
+
+TEST_F(ProcessUtilTest, GetTerminationStatusKill) {
+ const std::string signal_file =
+ ProcessUtilTest::GetSignalFilePath(kSignalFileKill);
+ remove(signal_file.c_str());
+ base::ProcessHandle handle = this->SpawnChild("KilledChildProcess",
+ false);
+ ASSERT_NE(base::kNullProcessHandle, handle);
+
+ int exit_code = 42;
+ EXPECT_EQ(base::TERMINATION_STATUS_STILL_RUNNING,
+ base::GetTerminationStatus(handle, &exit_code));
+ EXPECT_EQ(kExpectedStillRunningExitCode, exit_code);
+
+ SignalChildren(signal_file.c_str());
+ exit_code = 42;
+ base::TerminationStatus status =
+ WaitForChildTermination(handle, &exit_code);
+ EXPECT_EQ(base::TERMINATION_STATUS_PROCESS_WAS_KILLED, status);
+#if defined(OS_WIN)
+ EXPECT_EQ(kExpectedKilledExitCode, exit_code);
+#elif defined(OS_POSIX)
+ int signaled = WIFSIGNALED(exit_code);
+ EXPECT_NE(0, signaled);
+ int signal = WTERMSIG(exit_code);
+ EXPECT_EQ(SIGKILL, signal);
+#endif
+ base::CloseProcessHandle(handle);
+ remove(signal_file.c_str());
+}
+
+// Ensure that the priority of a process is restored correctly after
+// backgrounding and restoring.
+// Note: a platform may not be willing or able to lower the priority of
+// a process. The calls to SetProcessBackground should be noops then.
+TEST_F(ProcessUtilTest, SetProcessBackgrounded) {
+ base::ProcessHandle handle = this->SpawnChild("SimpleChildProcess", false);
+ base::Process process(handle);
+ int old_priority = process.GetPriority();
+#if defined(OS_WIN)
+ EXPECT_TRUE(process.SetProcessBackgrounded(true));
+ EXPECT_TRUE(process.IsProcessBackgrounded());
+ EXPECT_TRUE(process.SetProcessBackgrounded(false));
+ EXPECT_FALSE(process.IsProcessBackgrounded());
+#else
+ process.SetProcessBackgrounded(true);
+ process.SetProcessBackgrounded(false);
+#endif
+ int new_priority = process.GetPriority();
+ EXPECT_EQ(old_priority, new_priority);
+}
+
+// Same as SetProcessBackgrounded but to this very process. It uses
+// a different code path at least for Windows.
+TEST_F(ProcessUtilTest, SetProcessBackgroundedSelf) {
+ base::Process process(base::Process::Current().handle());
+ int old_priority = process.GetPriority();
+#if defined(OS_WIN)
+ EXPECT_TRUE(process.SetProcessBackgrounded(true));
+ EXPECT_TRUE(process.IsProcessBackgrounded());
+ EXPECT_TRUE(process.SetProcessBackgrounded(false));
+ EXPECT_FALSE(process.IsProcessBackgrounded());
+#else
+ process.SetProcessBackgrounded(true);
+ process.SetProcessBackgrounded(false);
+#endif
+ int new_priority = process.GetPriority();
+ EXPECT_EQ(old_priority, new_priority);
+}
+
+// TODO(estade): if possible, port these 2 tests.
+#if defined(OS_WIN)
+TEST_F(ProcessUtilTest, EnableLFH) {
+ ASSERT_TRUE(base::EnableLowFragmentationHeap());
+ if (IsDebuggerPresent()) {
+ // Under these conditions, LFH can't be enabled. There's no point to test
+ // anything.
+ const char* no_debug_env = getenv("_NO_DEBUG_HEAP");
+ if (!no_debug_env || strcmp(no_debug_env, "1"))
+ return;
+ }
+ HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
+ ASSERT_TRUE(kernel32 != NULL);
+ HeapQueryFn heap_query = reinterpret_cast<HeapQueryFn>(GetProcAddress(
+ kernel32,
+ "HeapQueryInformation"));
+
+ // On Windows 2000, the function is not exported. This is not a reason to
+ // fail but we won't be able to retrieves information about the heap, so we
+ // should stop here.
+ if (heap_query == NULL)
+ return;
+
+ HANDLE heaps[1024] = { 0 };
+ unsigned number_heaps = GetProcessHeaps(1024, heaps);
+ EXPECT_GT(number_heaps, 0u);
+ for (unsigned i = 0; i < number_heaps; ++i) {
+ ULONG flag = 0;
+ SIZE_T length;
+ ASSERT_NE(0, heap_query(heaps[i],
+ HeapCompatibilityInformation,
+ &flag,
+ sizeof(flag),
+ &length));
+ // If flag is 0, the heap is a standard heap that does not support
+ // look-asides. If flag is 1, the heap supports look-asides. If flag is 2,
+ // the heap is a low-fragmentation heap (LFH). Note that look-asides are not
+ // supported on the LFH.
+
+ // We don't have any documented way of querying the HEAP_NO_SERIALIZE flag.
+ EXPECT_LE(flag, 2u);
+ EXPECT_NE(flag, 1u);
+ }
+}
+
+TEST_F(ProcessUtilTest, CalcFreeMemory) {
+ scoped_ptr<base::ProcessMetrics> metrics(
+ base::ProcessMetrics::CreateProcessMetrics(::GetCurrentProcess()));
+ ASSERT_TRUE(NULL != metrics.get());
+
+ // Typical values here is ~1900 for total and ~1000 for largest. Obviously
+ // it depends in what other tests have done to this process.
+ base::FreeMBytes free_mem1 = {0};
+ EXPECT_TRUE(metrics->CalculateFreeMemory(&free_mem1));
+ EXPECT_LT(10u, free_mem1.total);
+ EXPECT_LT(10u, free_mem1.largest);
+ EXPECT_GT(2048u, free_mem1.total);
+ EXPECT_GT(2048u, free_mem1.largest);
+ EXPECT_GE(free_mem1.total, free_mem1.largest);
+ EXPECT_TRUE(NULL != free_mem1.largest_ptr);
+
+ // Allocate 20M and check again. It should have gone down.
+ const int kAllocMB = 20;
+ scoped_array<char> alloc(new char[kAllocMB * 1024 * 1024]);
+ size_t expected_total = free_mem1.total - kAllocMB;
+ size_t expected_largest = free_mem1.largest;
+
+ base::FreeMBytes free_mem2 = {0};
+ EXPECT_TRUE(metrics->CalculateFreeMemory(&free_mem2));
+ EXPECT_GE(free_mem2.total, free_mem2.largest);
+ EXPECT_GE(expected_total, free_mem2.total);
+ EXPECT_GE(expected_largest, free_mem2.largest);
+ EXPECT_TRUE(NULL != free_mem2.largest_ptr);
+}
+
+TEST_F(ProcessUtilTest, GetAppOutput) {
+ // Let's create a decently long message.
+ std::string message;
+ for (int i = 0; i < 1025; i++) { // 1025 so it does not end on a kilo-byte
+ // boundary.
+ message += "Hello!";
+ }
+ // cmd.exe's echo always adds a \r\n to its output.
+ std::string expected(message);
+ expected += "\r\n";
+
+ FilePath cmd(L"cmd.exe");
+ CommandLine cmd_line(cmd);
+ cmd_line.AppendArg("/c");
+ cmd_line.AppendArg("echo " + message + "");
+ std::string output;
+ ASSERT_TRUE(base::GetAppOutput(cmd_line, &output));
+ EXPECT_EQ(expected, output);
+
+ // Let's make sure stderr is ignored.
+ CommandLine other_cmd_line(cmd);
+ other_cmd_line.AppendArg("/c");
+ // http://msdn.microsoft.com/library/cc772622.aspx
+ cmd_line.AppendArg("echo " + message + " >&2");
+ output.clear();
+ ASSERT_TRUE(base::GetAppOutput(other_cmd_line, &output));
+ EXPECT_EQ("", output);
+}
+
+TEST_F(ProcessUtilTest, LaunchAsUser) {
+ base::UserTokenHandle token;
+ ASSERT_TRUE(OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token));
+ std::wstring cmdline =
+ this->MakeCmdLine("SimpleChildProcess", false).GetCommandLineString();
+ base::LaunchOptions options;
+ options.as_user = token;
+ EXPECT_TRUE(base::LaunchProcess(cmdline, options, NULL));
+}
+
+#endif // defined(OS_WIN)
+
+#if defined(OS_MACOSX)
+
+// For the following Mac tests:
+// Note that base::EnableTerminationOnHeapCorruption() is called as part of
+// test suite setup and does not need to be done again, else mach_override
+// will fail.
+
+#if !defined(ADDRESS_SANITIZER)
+// The following code tests the system implementation of malloc() thus no need
+// to test it under AddressSanitizer.
+TEST_F(ProcessUtilTest, MacMallocFailureDoesNotTerminate) {
+ // Install the OOM killer.
+ base::EnableTerminationOnOutOfMemory();
+
+ // Test that ENOMEM doesn't crash via CrMallocErrorBreak two ways: the exit
+ // code and lack of the error string. The number of bytes is one less than
+ // MALLOC_ABSOLUTE_MAX_SIZE, more than which the system early-returns NULL and
+ // does not call through malloc_error_break(). See the comment at
+ // EnableTerminationOnOutOfMemory() for more information.
+ void* buf = NULL;
+ ASSERT_EXIT(
+ buf = malloc(std::numeric_limits<size_t>::max() - (2 * PAGE_SIZE) - 1),
+ testing::KilledBySignal(SIGTRAP),
+ "\\*\\*\\* error: can't allocate region.*"
+ "(Terminating process due to a potential for future heap "
+ "corruption){0}");
+
+ base::debug::Alias(buf);
+}
+#endif // !defined(ADDRESS_SANITIZER)
+
+TEST_F(ProcessUtilTest, MacTerminateOnHeapCorruption) {
+ // Assert that freeing an unallocated pointer will crash the process.
+ char buf[3];
+ asm("" : "=r" (buf)); // Prevent clang from being too smart.
+#if !defined(ADDRESS_SANITIZER)
+ ASSERT_DEATH(free(buf), "being freed.*"
+ "\\*\\*\\* set a breakpoint in malloc_error_break to debug.*"
+ "Terminating process due to a potential for future heap corruption");
+#else
+ // AddressSanitizer replaces malloc() and prints a different error message on
+ // heap corruption.
+ ASSERT_DEATH(free(buf), "attempting free on address which "
+ "was not malloc\\(\\)-ed");
+#endif // !defined(ADDRESS_SANITIZER)
+}
+
+#endif // defined(OS_MACOSX)
+
+#if defined(OS_POSIX)
+
+namespace {
+
+// Returns the maximum number of files that a process can have open.
+// Returns 0 on error.
+int GetMaxFilesOpenInProcess() {
+ struct rlimit rlim;
+ if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
+ return 0;
+ }
+
+ // rlim_t is a uint64 - clip to maxint. We do this since FD #s are ints
+ // which are all 32 bits on the supported platforms.
+ rlim_t max_int = static_cast<rlim_t>(std::numeric_limits<int32>::max());
+ if (rlim.rlim_cur > max_int) {
+ return max_int;
+ }
+
+ return rlim.rlim_cur;
+}
+
+const int kChildPipe = 20; // FD # for write end of pipe in child process.
+
+} // namespace
+
+MULTIPROCESS_TEST_MAIN(ProcessUtilsLeakFDChildProcess) {
+ // This child process counts the number of open FDs, it then writes that
+ // number out to a pipe connected to the parent.
+ int num_open_files = 0;
+ int write_pipe = kChildPipe;
+ int max_files = GetMaxFilesOpenInProcess();
+ for (int i = STDERR_FILENO + 1; i < max_files; i++) {
+ if (i != kChildPipe) {
+ int fd;
+ if ((fd = HANDLE_EINTR(dup(i))) != -1) {
+ close(fd);
+ num_open_files += 1;
+ }
+ }
+ }
+
+ int written = HANDLE_EINTR(write(write_pipe, &num_open_files,
+ sizeof(num_open_files)));
+ DCHECK_EQ(static_cast<size_t>(written), sizeof(num_open_files));
+ int ret = HANDLE_EINTR(close(write_pipe));
+ DPCHECK(ret == 0);
+
+ return 0;
+}
+
+int ProcessUtilTest::CountOpenFDsInChild() {
+ int fds[2];
+ if (pipe(fds) < 0)
+ NOTREACHED();
+
+ base::FileHandleMappingVector fd_mapping_vec;
+ fd_mapping_vec.push_back(std::pair<int, int>(fds[1], kChildPipe));
+ base::ProcessHandle handle = this->SpawnChild(
+ "ProcessUtilsLeakFDChildProcess", fd_mapping_vec, false);
+ CHECK(handle);
+ int ret = HANDLE_EINTR(close(fds[1]));
+ DPCHECK(ret == 0);
+
+ // Read number of open files in client process from pipe;
+ int num_open_files = -1;
+ ssize_t bytes_read =
+ HANDLE_EINTR(read(fds[0], &num_open_files, sizeof(num_open_files)));
+ CHECK_EQ(bytes_read, static_cast<ssize_t>(sizeof(num_open_files)));
+
+#if defined(THREAD_SANITIZER)
+ // Compiler-based ThreadSanitizer makes this test slow.
+ CHECK(base::WaitForSingleProcess(handle, base::TimeDelta::FromSeconds(3)));
+#else
+ CHECK(base::WaitForSingleProcess(handle, base::TimeDelta::FromSeconds(1)));
+#endif
+ base::CloseProcessHandle(handle);
+ ret = HANDLE_EINTR(close(fds[0]));
+ DPCHECK(ret == 0);
+
+ return num_open_files;
+}
+
+TEST_F(ProcessUtilTest, FDRemapping) {
+ int fds_before = CountOpenFDsInChild();
+
+ // open some dummy fds to make sure they don't propagate over to the
+ // child process.
+ int dev_null = open("/dev/null", O_RDONLY);
+ int sockets[2];
+ socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
+
+ int fds_after = CountOpenFDsInChild();
+
+ ASSERT_EQ(fds_after, fds_before);
+
+ int ret;
+ ret = HANDLE_EINTR(close(sockets[0]));
+ DPCHECK(ret == 0);
+ ret = HANDLE_EINTR(close(sockets[1]));
+ DPCHECK(ret == 0);
+ ret = HANDLE_EINTR(close(dev_null));
+ DPCHECK(ret == 0);
+}
+
+namespace {
+
+std::string TestLaunchProcess(const base::EnvironmentVector& env_changes,
+ const int clone_flags) {
+ std::vector<std::string> args;
+ base::FileHandleMappingVector fds_to_remap;
+
+ args.push_back(kPosixShell);
+ args.push_back("-c");
+ args.push_back("echo $BASE_TEST");
+
+ int fds[2];
+ PCHECK(pipe(fds) == 0);
+
+ fds_to_remap.push_back(std::make_pair(fds[1], 1));
+ base::LaunchOptions options;
+ options.wait = true;
+ options.environ = &env_changes;
+ options.fds_to_remap = &fds_to_remap;
+#if defined(OS_LINUX)
+ options.clone_flags = clone_flags;
+#else
+ CHECK_EQ(0, clone_flags);
+#endif // OS_LINUX
+ EXPECT_TRUE(base::LaunchProcess(args, options, NULL));
+ PCHECK(HANDLE_EINTR(close(fds[1])) == 0);
+
+ char buf[512];
+ const ssize_t n = HANDLE_EINTR(read(fds[0], buf, sizeof(buf)));
+ PCHECK(n > 0);
+
+ PCHECK(HANDLE_EINTR(close(fds[0])) == 0);
+
+ return std::string(buf, n);
+}
+
+const char kLargeString[] =
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789";
+
+} // namespace
+
+TEST_F(ProcessUtilTest, LaunchProcess) {
+ base::EnvironmentVector env_changes;
+ const int no_clone_flags = 0;
+
+ env_changes.push_back(std::make_pair(std::string("BASE_TEST"),
+ std::string("bar")));
+ EXPECT_EQ("bar\n", TestLaunchProcess(env_changes, no_clone_flags));
+ env_changes.clear();
+
+ EXPECT_EQ(0, setenv("BASE_TEST", "testing", 1 /* override */));
+ EXPECT_EQ("testing\n", TestLaunchProcess(env_changes, no_clone_flags));
+
+ env_changes.push_back(std::make_pair(std::string("BASE_TEST"),
+ std::string("")));
+ EXPECT_EQ("\n", TestLaunchProcess(env_changes, no_clone_flags));
+
+ env_changes[0].second = "foo";
+ EXPECT_EQ("foo\n", TestLaunchProcess(env_changes, no_clone_flags));
+
+ env_changes.clear();
+ EXPECT_EQ(0, setenv("BASE_TEST", kLargeString, 1 /* override */));
+ EXPECT_EQ(std::string(kLargeString) + "\n",
+ TestLaunchProcess(env_changes, no_clone_flags));
+
+ env_changes.push_back(std::make_pair(std::string("BASE_TEST"),
+ std::string("wibble")));
+ EXPECT_EQ("wibble\n", TestLaunchProcess(env_changes, no_clone_flags));
+
+#if defined(OS_LINUX)
+ // Test a non-trival value for clone_flags.
+ // Don't test on Valgrind as it has limited support for clone().
+ if (!RunningOnValgrind()) {
+ EXPECT_EQ("wibble\n", TestLaunchProcess(env_changes, CLONE_FS | SIGCHLD));
+ }
+#endif
+}
+
+TEST_F(ProcessUtilTest, AlterEnvironment) {
+ const char* const empty[] = { NULL };
+ const char* const a2[] = { "A=2", NULL };
+ base::EnvironmentVector changes;
+ char** e;
+
+ e = base::AlterEnvironment(changes, empty);
+ EXPECT_TRUE(e[0] == NULL);
+ delete[] e;
+
+ changes.push_back(std::make_pair(std::string("A"), std::string("1")));
+ e = base::AlterEnvironment(changes, empty);
+ EXPECT_EQ(std::string("A=1"), e[0]);
+ EXPECT_TRUE(e[1] == NULL);
+ delete[] e;
+
+ changes.clear();
+ changes.push_back(std::make_pair(std::string("A"), std::string("")));
+ e = base::AlterEnvironment(changes, empty);
+ EXPECT_TRUE(e[0] == NULL);
+ delete[] e;
+
+ changes.clear();
+ e = base::AlterEnvironment(changes, a2);
+ EXPECT_EQ(std::string("A=2"), e[0]);
+ EXPECT_TRUE(e[1] == NULL);
+ delete[] e;
+
+ changes.clear();
+ changes.push_back(std::make_pair(std::string("A"), std::string("1")));
+ e = base::AlterEnvironment(changes, a2);
+ EXPECT_EQ(std::string("A=1"), e[0]);
+ EXPECT_TRUE(e[1] == NULL);
+ delete[] e;
+
+ changes.clear();
+ changes.push_back(std::make_pair(std::string("A"), std::string("")));
+ e = base::AlterEnvironment(changes, a2);
+ EXPECT_TRUE(e[0] == NULL);
+ delete[] e;
+}
+
+TEST_F(ProcessUtilTest, GetAppOutput) {
+ std::string output;
+
+#if defined(OS_ANDROID)
+ std::vector<std::string> argv;
+ argv.push_back("sh"); // Instead of /bin/sh, force path search to find it.
+ argv.push_back("-c");
+
+ argv.push_back("exit 0");
+ EXPECT_TRUE(base::GetAppOutput(CommandLine(argv), &output));
+ EXPECT_STREQ("", output.c_str());
+
+ argv[2] = "exit 1";
+ EXPECT_FALSE(base::GetAppOutput(CommandLine(argv), &output));
+ EXPECT_STREQ("", output.c_str());
+
+ argv[2] = "echo foobar42";
+ EXPECT_TRUE(base::GetAppOutput(CommandLine(argv), &output));
+ EXPECT_STREQ("foobar42\n", output.c_str());
+#else
+ EXPECT_TRUE(base::GetAppOutput(CommandLine(FilePath("true")), &output));
+ EXPECT_STREQ("", output.c_str());
+
+ EXPECT_FALSE(base::GetAppOutput(CommandLine(FilePath("false")), &output));
+
+ std::vector<std::string> argv;
+ argv.push_back("/bin/echo");
+ argv.push_back("-n");
+ argv.push_back("foobar42");
+ EXPECT_TRUE(base::GetAppOutput(CommandLine(argv), &output));
+ EXPECT_STREQ("foobar42", output.c_str());
+#endif // defined(OS_ANDROID)
+}
+
+TEST_F(ProcessUtilTest, GetAppOutputRestricted) {
+ // Unfortunately, since we can't rely on the path, we need to know where
+ // everything is. So let's use /bin/sh, which is on every POSIX system, and
+ // its built-ins.
+ std::vector<std::string> argv;
+ argv.push_back(std::string(kShellPath)); // argv[0]
+ argv.push_back("-c"); // argv[1]
+
+ // On success, should set |output|. We use |/bin/sh -c 'exit 0'| instead of
+ // |true| since the location of the latter may be |/bin| or |/usr/bin| (and we
+ // need absolute paths).
+ argv.push_back("exit 0"); // argv[2]; equivalent to "true"
+ std::string output = "abc";
+ EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 100));
+ EXPECT_STREQ("", output.c_str());
+
+ argv[2] = "exit 1"; // equivalent to "false"
+ output = "before";
+ EXPECT_FALSE(base::GetAppOutputRestricted(CommandLine(argv),
+ &output, 100));
+ EXPECT_STREQ("", output.c_str());
+
+ // Amount of output exactly equal to space allowed.
+ argv[2] = "echo 123456789"; // (the sh built-in doesn't take "-n")
+ output.clear();
+ EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 10));
+ EXPECT_STREQ("123456789\n", output.c_str());
+
+ // Amount of output greater than space allowed.
+ output.clear();
+ EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 5));
+ EXPECT_STREQ("12345", output.c_str());
+
+ // Amount of output less than space allowed.
+ output.clear();
+ EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 15));
+ EXPECT_STREQ("123456789\n", output.c_str());
+
+ // Zero space allowed.
+ output = "abc";
+ EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 0));
+ EXPECT_STREQ("", output.c_str());
+}
+
+#if !defined(OS_MACOSX) && !defined(OS_OPENBSD)
+// TODO(benwells): GetAppOutputRestricted should terminate applications
+// with SIGPIPE when we have enough output. http://crbug.com/88502
+TEST_F(ProcessUtilTest, GetAppOutputRestrictedSIGPIPE) {
+ std::vector<std::string> argv;
+ std::string output;
+
+ argv.push_back(std::string(kShellPath)); // argv[0]
+ argv.push_back("-c");
+#if defined(OS_ANDROID)
+ argv.push_back("while echo 12345678901234567890; do :; done");
+ EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 10));
+ EXPECT_STREQ("1234567890", output.c_str());
+#else
+ argv.push_back("yes");
+ EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 10));
+ EXPECT_STREQ("y\ny\ny\ny\ny\n", output.c_str());
+#endif
+}
+#endif
+
+TEST_F(ProcessUtilTest, GetAppOutputRestrictedNoZombies) {
+ std::vector<std::string> argv;
+
+ argv.push_back(std::string(kShellPath)); // argv[0]
+ argv.push_back("-c"); // argv[1]
+ argv.push_back("echo 123456789012345678901234567890"); // argv[2]
+
+ // Run |GetAppOutputRestricted()| 300 (> default per-user processes on Mac OS
+ // 10.5) times with an output buffer big enough to capture all output.
+ for (int i = 0; i < 300; i++) {
+ std::string output;
+ EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 100));
+ EXPECT_STREQ("123456789012345678901234567890\n", output.c_str());
+ }
+
+ // Ditto, but with an output buffer too small to capture all output.
+ for (int i = 0; i < 300; i++) {
+ std::string output;
+ EXPECT_TRUE(base::GetAppOutputRestricted(CommandLine(argv), &output, 10));
+ EXPECT_STREQ("1234567890", output.c_str());
+ }
+}
+
+TEST_F(ProcessUtilTest, GetAppOutputWithExitCode) {
+ // Test getting output from a successful application.
+ std::vector<std::string> argv;
+ std::string output;
+ int exit_code;
+ argv.push_back(std::string(kShellPath)); // argv[0]
+ argv.push_back("-c"); // argv[1]
+ argv.push_back("echo foo"); // argv[2];
+ EXPECT_TRUE(base::GetAppOutputWithExitCode(CommandLine(argv), &output,
+ &exit_code));
+ EXPECT_STREQ("foo\n", output.c_str());
+ EXPECT_EQ(exit_code, 0);
+
+ // Test getting output from an application which fails with a specific exit
+ // code.
+ output.clear();
+ argv[2] = "echo foo; exit 2";
+ EXPECT_TRUE(base::GetAppOutputWithExitCode(CommandLine(argv), &output,
+ &exit_code));
+ EXPECT_STREQ("foo\n", output.c_str());
+ EXPECT_EQ(exit_code, 2);
+}
+
+TEST_F(ProcessUtilTest, GetParentProcessId) {
+ base::ProcessId ppid = base::GetParentProcessId(base::GetCurrentProcId());
+ EXPECT_EQ(ppid, getppid());
+}
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+TEST_F(ProcessUtilTest, ParseProcStatCPU) {
+ // /proc/self/stat for a process running "top".
+ const char kTopStat[] = "960 (top) S 16230 960 16230 34818 960 "
+ "4202496 471 0 0 0 "
+ "12 16 0 0 " // <- These are the goods.
+ "20 0 1 0 121946157 15077376 314 18446744073709551615 4194304 "
+ "4246868 140733983044336 18446744073709551615 140244213071219 "
+ "0 0 0 138047495 0 0 0 17 1 0 0 0 0 0";
+ EXPECT_EQ(12 + 16, base::ParseProcStatCPU(kTopStat));
+
+ // cat /proc/self/stat on a random other machine I have.
+ const char kSelfStat[] = "5364 (cat) R 5354 5364 5354 34819 5364 "
+ "0 142 0 0 0 "
+ "0 0 0 0 " // <- No CPU, apparently.
+ "16 0 1 0 1676099790 2957312 114 4294967295 134512640 134528148 "
+ "3221224832 3221224344 3086339742 0 0 0 0 0 0 0 17 0 0 0";
+
+ EXPECT_EQ(0, base::ParseProcStatCPU(kSelfStat));
+}
+#endif // defined(OS_LINUX) || defined(OS_ANDROID)
+
+// TODO(port): port those unit tests.
+bool IsProcessDead(base::ProcessHandle child) {
+ // waitpid() will actually reap the process which is exactly NOT what we
+ // want to test for. The good thing is that if it can't find the process
+ // we'll get a nice value for errno which we can test for.
+ const pid_t result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG));
+ return result == -1 && errno == ECHILD;
+}
+
+TEST_F(ProcessUtilTest, DelayedTermination) {
+ base::ProcessHandle child_process =
+ SpawnChild("process_util_test_never_die", false);
+ ASSERT_TRUE(child_process);
+ base::EnsureProcessTerminated(child_process);
+ base::WaitForSingleProcess(child_process, base::TimeDelta::FromSeconds(5));
+
+ // Check that process was really killed.
+ EXPECT_TRUE(IsProcessDead(child_process));
+ base::CloseProcessHandle(child_process);
+}
+
+MULTIPROCESS_TEST_MAIN(process_util_test_never_die) {
+ while (1) {
+ sleep(500);
+ }
+ return 0;
+}
+
+TEST_F(ProcessUtilTest, ImmediateTermination) {
+ base::ProcessHandle child_process =
+ SpawnChild("process_util_test_die_immediately", false);
+ ASSERT_TRUE(child_process);
+ // Give it time to die.
+ sleep(2);
+ base::EnsureProcessTerminated(child_process);
+
+ // Check that process was really killed.
+ EXPECT_TRUE(IsProcessDead(child_process));
+ base::CloseProcessHandle(child_process);
+}
+
+MULTIPROCESS_TEST_MAIN(process_util_test_die_immediately) {
+ return 0;
+}
+
+#endif // defined(OS_POSIX)
+
+// Android doesn't implement set_new_handler, so we can't use the
+// OutOfMemoryTest cases.
+// OpenBSD does not support these tests either.
+// AddressSanitizer defines the malloc()/free()/etc. functions so that they
+// don't crash if the program is out of memory, so the OOM tests aren't supposed
+// to work.
+// TODO(vandebo) make this work on Windows too.
+#if !defined(OS_ANDROID) && !defined(OS_OPENBSD) && \
+ !defined(OS_WIN) && !defined(ADDRESS_SANITIZER)
+
+#if defined(USE_TCMALLOC)
+extern "C" {
+int tc_set_new_mode(int mode);
+}
+#endif // defined(USE_TCMALLOC)
+
+class OutOfMemoryDeathTest : public testing::Test {
+ public:
+ OutOfMemoryDeathTest()
+ : value_(NULL),
+ // Make test size as large as possible minus a few pages so
+ // that alignment or other rounding doesn't make it wrap.
+ test_size_(std::numeric_limits<std::size_t>::max() - 12 * 1024),
+ signed_test_size_(std::numeric_limits<ssize_t>::max()) {
+ }
+
+#if defined(USE_TCMALLOC)
+ virtual void SetUp() OVERRIDE {
+ tc_set_new_mode(1);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ tc_set_new_mode(0);
+ }
+#endif // defined(USE_TCMALLOC)
+
+ void SetUpInDeathAssert() {
+ // Must call EnableTerminationOnOutOfMemory() because that is called from
+ // chrome's main function and therefore hasn't been called yet.
+ // Since this call may result in another thread being created and death
+ // tests shouldn't be started in a multithread environment, this call
+ // should be done inside of the ASSERT_DEATH.
+ base::EnableTerminationOnOutOfMemory();
+ }
+
+ void* value_;
+ size_t test_size_;
+ ssize_t signed_test_size_;
+};
+
+TEST_F(OutOfMemoryDeathTest, New) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = operator new(test_size_);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, NewArray) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = new char[test_size_];
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, Malloc) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = malloc(test_size_);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, Realloc) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = realloc(NULL, test_size_);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, Calloc) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = calloc(1024, test_size_ / 1024L);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, Valloc) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = valloc(test_size_);
+ }, "");
+}
+
+#if defined(OS_LINUX)
+TEST_F(OutOfMemoryDeathTest, Pvalloc) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = pvalloc(test_size_);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, Memalign) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = memalign(4, test_size_);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, ViaSharedLibraries) {
+ // g_try_malloc is documented to return NULL on failure. (g_malloc is the
+ // 'safe' default that crashes if allocation fails). However, since we have
+ // hopefully overridden malloc, even g_try_malloc should fail. This tests
+ // that the run-time symbol resolution is overriding malloc for shared
+ // libraries as well as for our code.
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = g_try_malloc(test_size_);
+ }, "");
+}
+#endif // OS_LINUX
+
+// Android doesn't implement posix_memalign().
+#if defined(OS_POSIX) && !defined(OS_ANDROID)
+TEST_F(OutOfMemoryDeathTest, Posix_memalign) {
+ // Grab the return value of posix_memalign to silence a compiler warning
+ // about unused return values. We don't actually care about the return
+ // value, since we're asserting death.
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ EXPECT_EQ(ENOMEM, posix_memalign(&value_, 8, test_size_));
+ }, "");
+}
+#endif // defined(OS_POSIX) && !defined(OS_ANDROID)
+
+#if defined(OS_MACOSX)
+
+// Purgeable zone tests
+
+TEST_F(OutOfMemoryDeathTest, MallocPurgeable) {
+ malloc_zone_t* zone = malloc_default_purgeable_zone();
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = malloc_zone_malloc(zone, test_size_);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, ReallocPurgeable) {
+ malloc_zone_t* zone = malloc_default_purgeable_zone();
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = malloc_zone_realloc(zone, NULL, test_size_);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, CallocPurgeable) {
+ malloc_zone_t* zone = malloc_default_purgeable_zone();
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = malloc_zone_calloc(zone, 1024, test_size_ / 1024L);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, VallocPurgeable) {
+ malloc_zone_t* zone = malloc_default_purgeable_zone();
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = malloc_zone_valloc(zone, test_size_);
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, PosixMemalignPurgeable) {
+ malloc_zone_t* zone = malloc_default_purgeable_zone();
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ value_ = malloc_zone_memalign(zone, 8, test_size_);
+ }, "");
+}
+
+// Since these allocation functions take a signed size, it's possible that
+// calling them just once won't be enough to exhaust memory. In the 32-bit
+// environment, it's likely that these allocation attempts will fail because
+// not enough contiguous address space is available. In the 64-bit environment,
+// it's likely that they'll fail because they would require a preposterous
+// amount of (virtual) memory.
+
+TEST_F(OutOfMemoryDeathTest, CFAllocatorSystemDefault) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ while ((value_ =
+ base::AllocateViaCFAllocatorSystemDefault(signed_test_size_))) {}
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, CFAllocatorMalloc) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ while ((value_ =
+ base::AllocateViaCFAllocatorMalloc(signed_test_size_))) {}
+ }, "");
+}
+
+TEST_F(OutOfMemoryDeathTest, CFAllocatorMallocZone) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ while ((value_ =
+ base::AllocateViaCFAllocatorMallocZone(signed_test_size_))) {}
+ }, "");
+}
+
+#if !defined(ARCH_CPU_64_BITS)
+
+// See process_util_unittest_mac.mm for an explanation of why this test isn't
+// run in the 64-bit environment.
+
+TEST_F(OutOfMemoryDeathTest, PsychoticallyBigObjCObject) {
+ ASSERT_DEATH({
+ SetUpInDeathAssert();
+ while ((value_ = base::AllocatePsychoticallyBigObjCObject())) {}
+ }, "");
+}
+
+#endif // !ARCH_CPU_64_BITS
+#endif // OS_MACOSX
+
+#endif // !defined(OS_ANDROID) && !defined(OS_OPENBSD) &&
+ // !defined(OS_WIN) && !defined(ADDRESS_SANITIZER)
diff --git a/src/base/process_util_unittest_ios.cc b/src/base/process_util_unittest_ios.cc
new file mode 100644
index 0000000..c82535d
--- /dev/null
+++ b/src/base/process_util_unittest_ios.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2012 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/memory/scoped_ptr.h"
+#include "base/process_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(ProcessUtilTestIos, Memory) {
+ scoped_ptr<base::ProcessMetrics> process_metrics(
+ base::ProcessMetrics::CreateProcessMetrics(
+ base::GetCurrentProcessHandle()));
+
+ ASSERT_NE(0u, process_metrics->GetWorkingSetSize());
+}
diff --git a/src/base/process_util_unittest_mac.h b/src/base/process_util_unittest_mac.h
new file mode 100644
index 0000000..7b4fe1c
--- /dev/null
+++ b/src/base/process_util_unittest_mac.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains helpers for the process_util_unittest to allow it to fully
+// test the Mac code.
+
+#ifndef BASE_PROCESS_UTIL_UNITTEST_MAC_H_
+#define BASE_PROCESS_UTIL_UNITTEST_MAC_H_
+
+#include "base/basictypes.h"
+
+namespace base {
+
+// Allocates memory via system allocators. Alas, they take a _signed_ size for
+// allocation.
+void* AllocateViaCFAllocatorSystemDefault(ssize_t size);
+void* AllocateViaCFAllocatorMalloc(ssize_t size);
+void* AllocateViaCFAllocatorMallocZone(ssize_t size);
+
+#if !defined(ARCH_CPU_64_BITS)
+// See process_util_unittest_mac.mm for an explanation of why this function
+// isn't implemented for the 64-bit environment.
+
+// Allocates a huge Objective C object.
+void* AllocatePsychoticallyBigObjCObject();
+
+#endif // !ARCH_CPU_64_BITS
+
+} // namespace base
+
+#endif // BASE_PROCESS_UTIL_UNITTEST_MAC_H_
diff --git a/src/base/process_util_unittest_mac.mm b/src/base/process_util_unittest_mac.mm
new file mode 100644
index 0000000..2ef1868
--- /dev/null
+++ b/src/base/process_util_unittest_mac.mm
@@ -0,0 +1,59 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process_util_unittest_mac.h"
+
+#import <Foundation/Foundation.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+#if !defined(ARCH_CPU_64_BITS)
+
+// In the 64-bit environment, the Objective-C 2.0 Runtime Reference states
+// that sizeof(anInstance) is constrained to 32 bits. That's not necessarily
+// "psychotically big" and in fact a 64-bit program is expected to be able to
+// successfully allocate an object that large, likely reserving a good deal of
+// swap space. The only way to test the behavior of memory exhaustion for
+// Objective-C allocation in this environment would be to loop over allocation
+// of these large objects, but that would slowly consume all available memory
+// and cause swap file proliferation. That's bad, so this behavior isn't
+// tested in the 64-bit environment.
+
+@interface PsychoticallyBigObjCObject : NSObject
+{
+ // In the 32-bit environment, the compiler limits Objective-C objects to
+ // < 2GB in size.
+ int justUnder2Gigs_[(2U * 1024 * 1024 * 1024 - 1) / sizeof(int)];
+}
+
+@end
+
+@implementation PsychoticallyBigObjCObject
+
+@end
+
+namespace base {
+
+void* AllocatePsychoticallyBigObjCObject() {
+ return [[PsychoticallyBigObjCObject alloc] init];
+}
+
+} // namespace base
+
+#endif // ARCH_CPU_64_BITS
+
+namespace base {
+
+void* AllocateViaCFAllocatorSystemDefault(ssize_t size) {
+ return CFAllocatorAllocate(kCFAllocatorSystemDefault, size, 0);
+}
+
+void* AllocateViaCFAllocatorMalloc(ssize_t size) {
+ return CFAllocatorAllocate(kCFAllocatorMalloc, size, 0);
+}
+
+void* AllocateViaCFAllocatorMallocZone(ssize_t size) {
+ return CFAllocatorAllocate(kCFAllocatorMallocZone, size, 0);
+}
+
+} // namespace base
diff --git a/src/base/process_util_win.cc b/src/base/process_util_win.cc
new file mode 100644
index 0000000..a1a55ea
--- /dev/null
+++ b/src/base/process_util_win.cc
@@ -0,0 +1,982 @@
+// Copyright (c) 2012 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/process_util.h"
+
+#include <fcntl.h>
+#include <io.h>
+#include <windows.h>
+#include <userenv.h>
+#include <psapi.h>
+
+#include <ios>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/debug/stack_trace.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/metrics/histogram.h"
+#include "base/sys_info.h"
+#include "base/win/object_watcher.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/windows_version.h"
+
+// userenv.dll is required for CreateEnvironmentBlock().
+#pragma comment(lib, "userenv.lib")
+
+namespace base {
+
+namespace {
+
+// System pagesize. This value remains constant on x86/64 architectures.
+const int PAGESIZE_KB = 4;
+
+// Exit codes with special meanings on Windows.
+const DWORD kNormalTerminationExitCode = 0;
+const DWORD kDebuggerInactiveExitCode = 0xC0000354;
+const DWORD kKeyboardInterruptExitCode = 0xC000013A;
+const DWORD kDebuggerTerminatedExitCode = 0x40010004;
+
+// Maximum amount of time (in milliseconds) to wait for the process to exit.
+static const int kWaitInterval = 2000;
+
+// This exit code is used by the Windows task manager when it kills a
+// process. It's value is obviously not that unique, and it's
+// surprising to me that the task manager uses this value, but it
+// seems to be common practice on Windows to test for it as an
+// indication that the task manager has killed something if the
+// process goes away.
+const DWORD kProcessKilledExitCode = 1;
+
+// HeapSetInformation function pointer.
+typedef BOOL (WINAPI* HeapSetFn)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T);
+
+void OnNoMemory() {
+ // Kill the process. This is important for security, since WebKit doesn't
+ // NULL-check many memory allocations. If a malloc fails, returns NULL, and
+ // the buffer is then used, it provides a handy mapping of memory starting at
+ // address 0 for an attacker to utilize.
+ __debugbreak();
+ _exit(1);
+}
+
+class TimerExpiredTask : public win::ObjectWatcher::Delegate {
+ public:
+ explicit TimerExpiredTask(ProcessHandle process);
+ ~TimerExpiredTask();
+
+ void TimedOut();
+
+ // MessageLoop::Watcher -----------------------------------------------------
+ virtual void OnObjectSignaled(HANDLE object);
+
+ private:
+ void KillProcess();
+
+ // The process that we are watching.
+ ProcessHandle process_;
+
+ win::ObjectWatcher watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(TimerExpiredTask);
+};
+
+TimerExpiredTask::TimerExpiredTask(ProcessHandle process) : process_(process) {
+ watcher_.StartWatching(process_, this);
+}
+
+TimerExpiredTask::~TimerExpiredTask() {
+ TimedOut();
+ DCHECK(!process_) << "Make sure to close the handle.";
+}
+
+void TimerExpiredTask::TimedOut() {
+ if (process_)
+ KillProcess();
+}
+
+void TimerExpiredTask::OnObjectSignaled(HANDLE object) {
+ CloseHandle(process_);
+ process_ = NULL;
+}
+
+void TimerExpiredTask::KillProcess() {
+ // Stop watching the process handle since we're killing it.
+ watcher_.StopWatching();
+
+ // OK, time to get frisky. We don't actually care when the process
+ // terminates. We just care that it eventually terminates, and that's what
+ // TerminateProcess should do for us. Don't check for the result code since
+ // it fails quite often. This should be investigated eventually.
+ base::KillProcess(process_, kProcessKilledExitCode, false);
+
+ // Now, just cleanup as if the process exited normally.
+ OnObjectSignaled(process_);
+}
+
+} // namespace
+
+void RouteStdioToConsole() {
+ if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
+ unsigned int result = GetLastError();
+ // Was probably already attached.
+ if (result == ERROR_ACCESS_DENIED)
+ return;
+ // Don't bother creating a new console for each child process if the
+ // parent process is invalid (eg: crashed).
+ if (result == ERROR_GEN_FAILURE)
+ return;
+ // Make a new console if attaching to parent fails with any other error.
+ // It should be ERROR_INVALID_HANDLE at this point, which means the browser
+ // was likely not started from a console.
+ AllocConsole();
+ }
+
+ // Arbitrary byte count to use when buffering output lines. More
+ // means potential waste, less means more risk of interleaved
+ // log-lines in output.
+ enum { kOutputBufferSize = 64 * 1024 };
+
+ if (freopen("CONOUT$", "w", stdout))
+ setvbuf(stdout, NULL, _IOLBF, kOutputBufferSize);
+ if (freopen("CONOUT$", "w", stderr))
+ setvbuf(stderr, NULL, _IOLBF, kOutputBufferSize);
+
+ // Fix all cout, wcout, cin, wcin, cerr, wcerr, clog and wclog.
+ std::ios::sync_with_stdio();
+}
+
+ProcessId GetCurrentProcId() {
+ return ::GetCurrentProcessId();
+}
+
+ProcessHandle GetCurrentProcessHandle() {
+ return ::GetCurrentProcess();
+}
+
+HMODULE GetModuleFromAddress(void* address) {
+ HMODULE instance = NULL;
+ if (!::GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ static_cast<char*>(address),
+ &instance)) {
+ NOTREACHED();
+ }
+ return instance;
+}
+
+bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) {
+ // We try to limit privileges granted to the handle. If you need this
+ // for test code, consider using OpenPrivilegedProcessHandle instead of
+ // adding more privileges here.
+ ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_TERMINATE,
+ FALSE, pid);
+
+ if (result == NULL)
+ return false;
+
+ *handle = result;
+ return true;
+}
+
+bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) {
+ ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE |
+ PROCESS_TERMINATE |
+ PROCESS_QUERY_INFORMATION |
+ PROCESS_VM_READ |
+ SYNCHRONIZE,
+ FALSE, pid);
+
+ if (result == NULL)
+ return false;
+
+ *handle = result;
+ return true;
+}
+
+bool OpenProcessHandleWithAccess(ProcessId pid,
+ uint32 access_flags,
+ ProcessHandle* handle) {
+ ProcessHandle result = OpenProcess(access_flags, FALSE, pid);
+
+ if (result == NULL)
+ return false;
+
+ *handle = result;
+ return true;
+}
+
+void CloseProcessHandle(ProcessHandle process) {
+ CloseHandle(process);
+}
+
+ProcessId GetProcId(ProcessHandle process) {
+ // Get a handle to |process| that has PROCESS_QUERY_INFORMATION rights.
+ HANDLE current_process = GetCurrentProcess();
+ HANDLE process_with_query_rights;
+ if (DuplicateHandle(current_process, process, current_process,
+ &process_with_query_rights, PROCESS_QUERY_INFORMATION,
+ false, 0)) {
+ DWORD id = GetProcessId(process_with_query_rights);
+ CloseHandle(process_with_query_rights);
+ return id;
+ }
+
+ // We're screwed.
+ NOTREACHED();
+ return 0;
+}
+
+bool GetProcessIntegrityLevel(ProcessHandle process, IntegrityLevel *level) {
+ if (!level)
+ return false;
+
+ if (win::GetVersion() < base::win::VERSION_VISTA)
+ return false;
+
+ HANDLE process_token;
+ if (!OpenProcessToken(process, TOKEN_QUERY | TOKEN_QUERY_SOURCE,
+ &process_token))
+ return false;
+
+ win::ScopedHandle scoped_process_token(process_token);
+
+ DWORD token_info_length = 0;
+ if (GetTokenInformation(process_token, TokenIntegrityLevel, NULL, 0,
+ &token_info_length) ||
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ return false;
+
+ scoped_array<char> token_label_bytes(new char[token_info_length]);
+ if (!token_label_bytes.get())
+ return false;
+
+ TOKEN_MANDATORY_LABEL* token_label =
+ reinterpret_cast<TOKEN_MANDATORY_LABEL*>(token_label_bytes.get());
+ if (!token_label)
+ return false;
+
+ if (!GetTokenInformation(process_token, TokenIntegrityLevel, token_label,
+ token_info_length, &token_info_length))
+ return false;
+
+ DWORD integrity_level = *GetSidSubAuthority(token_label->Label.Sid,
+ (DWORD)(UCHAR)(*GetSidSubAuthorityCount(token_label->Label.Sid)-1));
+
+ if (integrity_level < SECURITY_MANDATORY_MEDIUM_RID) {
+ *level = LOW_INTEGRITY;
+ } else if (integrity_level >= SECURITY_MANDATORY_MEDIUM_RID &&
+ integrity_level < SECURITY_MANDATORY_HIGH_RID) {
+ *level = MEDIUM_INTEGRITY;
+ } else if (integrity_level >= SECURITY_MANDATORY_HIGH_RID) {
+ *level = HIGH_INTEGRITY;
+ } else {
+ NOTREACHED();
+ return false;
+ }
+
+ return true;
+}
+
+bool LaunchProcess(const string16& cmdline,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle) {
+ STARTUPINFO startup_info = {};
+ startup_info.cb = sizeof(startup_info);
+ if (options.empty_desktop_name)
+ startup_info.lpDesktop = L"";
+ startup_info.dwFlags = STARTF_USESHOWWINDOW;
+ startup_info.wShowWindow = options.start_hidden ? SW_HIDE : SW_SHOW;
+
+ DWORD flags = 0;
+
+ if (options.job_handle) {
+ flags |= CREATE_SUSPENDED;
+
+ // If this code is run under a debugger, the launched process is
+ // automatically associated with a job object created by the debugger.
+ // The CREATE_BREAKAWAY_FROM_JOB flag is used to prevent this.
+ flags |= CREATE_BREAKAWAY_FROM_JOB;
+ }
+
+ if (options.force_breakaway_from_job_)
+ flags |= CREATE_BREAKAWAY_FROM_JOB;
+
+ base::win::ScopedProcessInformation process_info;
+
+ if (options.as_user) {
+ flags |= CREATE_UNICODE_ENVIRONMENT;
+ void* enviroment_block = NULL;
+
+ if (!CreateEnvironmentBlock(&enviroment_block, options.as_user, FALSE))
+ return false;
+
+ BOOL launched =
+ CreateProcessAsUser(options.as_user, NULL,
+ const_cast<wchar_t*>(cmdline.c_str()),
+ NULL, NULL, options.inherit_handles, flags,
+ enviroment_block, NULL, &startup_info,
+ process_info.Receive());
+ DestroyEnvironmentBlock(enviroment_block);
+ if (!launched)
+ return false;
+ } else {
+ if (!CreateProcess(NULL,
+ const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL,
+ options.inherit_handles, flags, NULL, NULL,
+ &startup_info, process_info.Receive())) {
+ return false;
+ }
+ }
+
+ if (options.job_handle) {
+ if (0 == AssignProcessToJobObject(options.job_handle,
+ process_info.process_handle())) {
+ DLOG(ERROR) << "Could not AssignProcessToObject.";
+ KillProcess(process_info.process_handle(), kProcessKilledExitCode, true);
+ return false;
+ }
+
+ ResumeThread(process_info.thread_handle());
+ }
+
+ if (options.wait)
+ WaitForSingleObject(process_info.process_handle(), INFINITE);
+
+ // If the caller wants the process handle, we won't close it.
+ if (process_handle)
+ *process_handle = process_info.TakeProcessHandle();
+
+ return true;
+}
+
+bool LaunchProcess(const CommandLine& cmdline,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle) {
+ return LaunchProcess(cmdline.GetCommandLineString(), options, process_handle);
+}
+
+bool SetJobObjectAsKillOnJobClose(HANDLE job_object) {
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {0};
+ limit_info.BasicLimitInformation.LimitFlags =
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+ return 0 != SetInformationJobObject(
+ job_object,
+ JobObjectExtendedLimitInformation,
+ &limit_info,
+ sizeof(limit_info));
+}
+
+// Attempts to kill the process identified by the given process
+// entry structure, giving it the specified exit code.
+// Returns true if this is successful, false otherwise.
+bool KillProcessById(ProcessId process_id, int exit_code, bool wait) {
+ HANDLE process = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE,
+ FALSE, // Don't inherit handle
+ process_id);
+ if (!process) {
+ DLOG(ERROR) << "Unable to open process " << process_id << " : "
+ << GetLastError();
+ return false;
+ }
+ bool ret = KillProcess(process, exit_code, wait);
+ CloseHandle(process);
+ return ret;
+}
+
+bool GetAppOutput(const CommandLine& cl, std::string* output) {
+ HANDLE out_read = NULL;
+ HANDLE out_write = NULL;
+
+ SECURITY_ATTRIBUTES sa_attr;
+ // Set the bInheritHandle flag so pipe handles are inherited.
+ sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa_attr.bInheritHandle = TRUE;
+ sa_attr.lpSecurityDescriptor = NULL;
+
+ // Create the pipe for the child process's STDOUT.
+ if (!CreatePipe(&out_read, &out_write, &sa_attr, 0)) {
+ NOTREACHED() << "Failed to create pipe";
+ return false;
+ }
+
+ // Ensure we don't leak the handles.
+ win::ScopedHandle scoped_out_read(out_read);
+ win::ScopedHandle scoped_out_write(out_write);
+
+ // Ensure the read handle to the pipe for STDOUT is not inherited.
+ if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
+ NOTREACHED() << "Failed to disabled pipe inheritance";
+ return false;
+ }
+
+ FilePath::StringType writable_command_line_string(cl.GetCommandLineString());
+
+ base::win::ScopedProcessInformation proc_info;
+ STARTUPINFO start_info = { 0 };
+
+ start_info.cb = sizeof(STARTUPINFO);
+ start_info.hStdOutput = out_write;
+ // Keep the normal stdin and stderr.
+ start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ start_info.dwFlags |= STARTF_USESTDHANDLES;
+
+ // Create the child process.
+ if (!CreateProcess(NULL,
+ &writable_command_line_string[0],
+ NULL, NULL,
+ TRUE, // Handles are inherited.
+ 0, NULL, NULL, &start_info, proc_info.Receive())) {
+ NOTREACHED() << "Failed to start process";
+ return false;
+ }
+
+ // Close our writing end of pipe now. Otherwise later read would not be able
+ // to detect end of child's output.
+ scoped_out_write.Close();
+
+ // Read output from the child process's pipe for STDOUT
+ const int kBufferSize = 1024;
+ char buffer[kBufferSize];
+
+ for (;;) {
+ DWORD bytes_read = 0;
+ BOOL success = ReadFile(out_read, buffer, kBufferSize, &bytes_read, NULL);
+ if (!success || bytes_read == 0)
+ break;
+ output->append(buffer, bytes_read);
+ }
+
+ // Let's wait for the process to finish.
+ WaitForSingleObject(proc_info.process_handle(), INFINITE);
+
+ return true;
+}
+
+bool KillProcess(ProcessHandle process, int exit_code, bool wait) {
+ bool result = (TerminateProcess(process, exit_code) != FALSE);
+ if (result && wait) {
+ // The process may not end immediately due to pending I/O
+ if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000))
+ DLOG(ERROR) << "Error waiting for process exit: " << GetLastError();
+ } else if (!result) {
+ DLOG(ERROR) << "Unable to terminate process: " << GetLastError();
+ }
+ return result;
+}
+
+TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
+ DWORD tmp_exit_code = 0;
+
+ if (!::GetExitCodeProcess(handle, &tmp_exit_code)) {
+ NOTREACHED();
+ if (exit_code) {
+ // This really is a random number. We haven't received any
+ // information about the exit code, presumably because this
+ // process doesn't have permission to get the exit code, or
+ // because of some other cause for GetExitCodeProcess to fail
+ // (MSDN docs don't give the possible failure error codes for
+ // this function, so it could be anything). But we don't want
+ // to leave exit_code uninitialized, since that could cause
+ // random interpretations of the exit code. So we assume it
+ // terminated "normally" in this case.
+ *exit_code = kNormalTerminationExitCode;
+ }
+ // Assume the child has exited normally if we can't get the exit
+ // code.
+ return TERMINATION_STATUS_NORMAL_TERMINATION;
+ }
+ if (tmp_exit_code == STILL_ACTIVE) {
+ DWORD wait_result = WaitForSingleObject(handle, 0);
+ if (wait_result == WAIT_TIMEOUT) {
+ if (exit_code)
+ *exit_code = wait_result;
+ return TERMINATION_STATUS_STILL_RUNNING;
+ }
+
+ DCHECK_EQ(WAIT_OBJECT_0, wait_result);
+
+ // Strange, the process used 0x103 (STILL_ACTIVE) as exit code.
+ NOTREACHED();
+
+ return TERMINATION_STATUS_ABNORMAL_TERMINATION;
+ }
+
+ if (exit_code)
+ *exit_code = tmp_exit_code;
+
+ switch (tmp_exit_code) {
+ case kNormalTerminationExitCode:
+ return TERMINATION_STATUS_NORMAL_TERMINATION;
+ case kDebuggerInactiveExitCode: // STATUS_DEBUGGER_INACTIVE.
+ case kKeyboardInterruptExitCode: // Control-C/end session.
+ case kDebuggerTerminatedExitCode: // Debugger terminated process.
+ case kProcessKilledExitCode: // Task manager kill.
+ return TERMINATION_STATUS_PROCESS_WAS_KILLED;
+ default:
+ // All other exit codes indicate crashes.
+ return TERMINATION_STATUS_PROCESS_CRASHED;
+ }
+}
+
+bool WaitForExitCode(ProcessHandle handle, int* exit_code) {
+ bool success = WaitForExitCodeWithTimeout(
+ handle, exit_code, base::TimeDelta::FromMilliseconds(INFINITE));
+ CloseProcessHandle(handle);
+ return success;
+}
+
+bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code,
+ base::TimeDelta timeout) {
+ if (::WaitForSingleObject(handle, timeout.InMilliseconds()) != WAIT_OBJECT_0)
+ return false;
+ DWORD temp_code; // Don't clobber out-parameters in case of failure.
+ if (!::GetExitCodeProcess(handle, &temp_code))
+ return false;
+
+ *exit_code = temp_code;
+ return true;
+}
+
+ProcessIterator::ProcessIterator(const ProcessFilter* filter)
+ : started_iteration_(false),
+ filter_(filter) {
+ snapshot_ = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+}
+
+ProcessIterator::~ProcessIterator() {
+ CloseHandle(snapshot_);
+}
+
+bool ProcessIterator::CheckForNextProcess() {
+ InitProcessEntry(&entry_);
+
+ if (!started_iteration_) {
+ started_iteration_ = true;
+ return !!Process32First(snapshot_, &entry_);
+ }
+
+ return !!Process32Next(snapshot_, &entry_);
+}
+
+void ProcessIterator::InitProcessEntry(ProcessEntry* entry) {
+ memset(entry, 0, sizeof(*entry));
+ entry->dwSize = sizeof(*entry);
+}
+
+bool NamedProcessIterator::IncludeEntry() {
+ // Case insensitive.
+ return _wcsicmp(executable_name_.c_str(), entry().exe_file()) == 0 &&
+ ProcessIterator::IncludeEntry();
+}
+
+bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ const ProcessFilter* filter) {
+ const ProcessEntry* entry;
+ bool result = true;
+ DWORD start_time = GetTickCount();
+
+ NamedProcessIterator iter(executable_name, filter);
+ while ((entry = iter.NextProcessEntry())) {
+ DWORD remaining_wait = std::max<int64>(
+ 0, wait.InMilliseconds() - (GetTickCount() - start_time));
+ HANDLE process = OpenProcess(SYNCHRONIZE,
+ FALSE,
+ entry->th32ProcessID);
+ DWORD wait_result = WaitForSingleObject(process, remaining_wait);
+ CloseHandle(process);
+ result = result && (wait_result == WAIT_OBJECT_0);
+ }
+
+ return result;
+}
+
+bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) {
+ int exit_code;
+ if (!WaitForExitCodeWithTimeout(handle, &exit_code, wait))
+ return false;
+ return exit_code == 0;
+}
+
+bool CleanupProcesses(const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ int exit_code,
+ const ProcessFilter* filter) {
+ bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter);
+ if (!exited_cleanly)
+ KillProcesses(executable_name, exit_code, filter);
+ return exited_cleanly;
+}
+
+void EnsureProcessTerminated(ProcessHandle process) {
+ DCHECK(process != GetCurrentProcess());
+
+ // If already signaled, then we are done!
+ if (WaitForSingleObject(process, 0) == WAIT_OBJECT_0) {
+ CloseHandle(process);
+ return;
+ }
+
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&TimerExpiredTask::TimedOut,
+ base::Owned(new TimerExpiredTask(process))),
+ base::TimeDelta::FromMilliseconds(kWaitInterval));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// ProcesMetrics
+
+ProcessMetrics::ProcessMetrics(ProcessHandle process)
+ : process_(process),
+ processor_count_(base::SysInfo::NumberOfProcessors()),
+ last_time_(0),
+ last_system_time_(0) {
+}
+
+// static
+ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
+ return new ProcessMetrics(process);
+}
+
+ProcessMetrics::~ProcessMetrics() { }
+
+size_t ProcessMetrics::GetPagefileUsage() const {
+ PROCESS_MEMORY_COUNTERS pmc;
+ if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
+ return pmc.PagefileUsage;
+ }
+ return 0;
+}
+
+// Returns the peak space allocated for the pagefile, in bytes.
+size_t ProcessMetrics::GetPeakPagefileUsage() const {
+ PROCESS_MEMORY_COUNTERS pmc;
+ if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
+ return pmc.PeakPagefileUsage;
+ }
+ return 0;
+}
+
+// Returns the current working set size, in bytes.
+size_t ProcessMetrics::GetWorkingSetSize() const {
+ PROCESS_MEMORY_COUNTERS pmc;
+ if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
+ return pmc.WorkingSetSize;
+ }
+ return 0;
+}
+
+// Returns the peak working set size, in bytes.
+size_t ProcessMetrics::GetPeakWorkingSetSize() const {
+ PROCESS_MEMORY_COUNTERS pmc;
+ if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
+ return pmc.PeakWorkingSetSize;
+ }
+ return 0;
+}
+
+bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
+ size_t* shared_bytes) {
+ // PROCESS_MEMORY_COUNTERS_EX is not supported until XP SP2.
+ // GetProcessMemoryInfo() will simply fail on prior OS. So the requested
+ // information is simply not available. Hence, we will return 0 on unsupported
+ // OSes. Unlike most Win32 API, we don't need to initialize the "cb" member.
+ PROCESS_MEMORY_COUNTERS_EX pmcx;
+ if (private_bytes &&
+ GetProcessMemoryInfo(process_,
+ reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmcx),
+ sizeof(pmcx))) {
+ *private_bytes = pmcx.PrivateUsage;
+ }
+
+ if (shared_bytes) {
+ WorkingSetKBytes ws_usage;
+ if (!GetWorkingSetKBytes(&ws_usage))
+ return false;
+
+ *shared_bytes = ws_usage.shared * 1024;
+ }
+
+ return true;
+}
+
+void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) const {
+ MEMORY_BASIC_INFORMATION mbi = {0};
+ size_t committed_private = 0;
+ size_t committed_mapped = 0;
+ size_t committed_image = 0;
+ void* base_address = NULL;
+ while (VirtualQueryEx(process_, base_address, &mbi, sizeof(mbi)) ==
+ sizeof(mbi)) {
+ if (mbi.State == MEM_COMMIT) {
+ if (mbi.Type == MEM_PRIVATE) {
+ committed_private += mbi.RegionSize;
+ } else if (mbi.Type == MEM_MAPPED) {
+ committed_mapped += mbi.RegionSize;
+ } else if (mbi.Type == MEM_IMAGE) {
+ committed_image += mbi.RegionSize;
+ } else {
+ NOTREACHED();
+ }
+ }
+ void* new_base = (static_cast<BYTE*>(mbi.BaseAddress)) + mbi.RegionSize;
+ // Avoid infinite loop by weird MEMORY_BASIC_INFORMATION.
+ // If we query 64bit processes in a 32bit process, VirtualQueryEx()
+ // returns such data.
+ if (new_base <= base_address) {
+ usage->image = 0;
+ usage->mapped = 0;
+ usage->priv = 0;
+ return;
+ }
+ base_address = new_base;
+ }
+ usage->image = committed_image / 1024;
+ usage->mapped = committed_mapped / 1024;
+ usage->priv = committed_private / 1024;
+}
+
+bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
+ size_t ws_private = 0;
+ size_t ws_shareable = 0;
+ size_t ws_shared = 0;
+
+ DCHECK(ws_usage);
+ memset(ws_usage, 0, sizeof(*ws_usage));
+
+ DWORD number_of_entries = 4096; // Just a guess.
+ PSAPI_WORKING_SET_INFORMATION* buffer = NULL;
+ int retries = 5;
+ for (;;) {
+ DWORD buffer_size = sizeof(PSAPI_WORKING_SET_INFORMATION) +
+ (number_of_entries * sizeof(PSAPI_WORKING_SET_BLOCK));
+
+ // if we can't expand the buffer, don't leak the previous
+ // contents or pass a NULL pointer to QueryWorkingSet
+ PSAPI_WORKING_SET_INFORMATION* new_buffer =
+ reinterpret_cast<PSAPI_WORKING_SET_INFORMATION*>(
+ realloc(buffer, buffer_size));
+ if (!new_buffer) {
+ free(buffer);
+ return false;
+ }
+ buffer = new_buffer;
+
+ // Call the function once to get number of items
+ if (QueryWorkingSet(process_, buffer, buffer_size))
+ break; // Success
+
+ if (GetLastError() != ERROR_BAD_LENGTH) {
+ free(buffer);
+ return false;
+ }
+
+ number_of_entries = static_cast<DWORD>(buffer->NumberOfEntries);
+
+ // Maybe some entries are being added right now. Increase the buffer to
+ // take that into account.
+ number_of_entries = static_cast<DWORD>(number_of_entries * 1.25);
+
+ if (--retries == 0) {
+ free(buffer); // If we're looping, eventually fail.
+ return false;
+ }
+ }
+
+ // On windows 2000 the function returns 1 even when the buffer is too small.
+ // The number of entries that we are going to parse is the minimum between the
+ // size we allocated and the real number of entries.
+ number_of_entries =
+ std::min(number_of_entries, static_cast<DWORD>(buffer->NumberOfEntries));
+ for (unsigned int i = 0; i < number_of_entries; i++) {
+ if (buffer->WorkingSetInfo[i].Shared) {
+ ws_shareable++;
+ if (buffer->WorkingSetInfo[i].ShareCount > 1)
+ ws_shared++;
+ } else {
+ ws_private++;
+ }
+ }
+
+ ws_usage->priv = ws_private * PAGESIZE_KB;
+ ws_usage->shareable = ws_shareable * PAGESIZE_KB;
+ ws_usage->shared = ws_shared * PAGESIZE_KB;
+ free(buffer);
+ return true;
+}
+
+static uint64 FileTimeToUTC(const FILETIME& ftime) {
+ LARGE_INTEGER li;
+ li.LowPart = ftime.dwLowDateTime;
+ li.HighPart = ftime.dwHighDateTime;
+ return li.QuadPart;
+}
+
+double ProcessMetrics::GetCPUUsage() {
+ FILETIME now;
+ FILETIME creation_time;
+ FILETIME exit_time;
+ FILETIME kernel_time;
+ FILETIME user_time;
+
+ GetSystemTimeAsFileTime(&now);
+
+ if (!GetProcessTimes(process_, &creation_time, &exit_time,
+ &kernel_time, &user_time)) {
+ // We don't assert here because in some cases (such as in the Task Manager)
+ // we may call this function on a process that has just exited but we have
+ // not yet received the notification.
+ return 0;
+ }
+ int64 system_time = (FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time)) /
+ processor_count_;
+ int64 time = FileTimeToUTC(now);
+
+ if ((last_system_time_ == 0) || (last_time_ == 0)) {
+ // First call, just set the last values.
+ last_system_time_ = system_time;
+ last_time_ = time;
+ return 0;
+ }
+
+ int64 system_time_delta = system_time - last_system_time_;
+ int64 time_delta = time - last_time_;
+ DCHECK_NE(0U, time_delta);
+ if (time_delta == 0)
+ return 0;
+
+ // We add time_delta / 2 so the result is rounded.
+ int cpu = static_cast<int>((system_time_delta * 100 + time_delta / 2) /
+ time_delta);
+
+ last_system_time_ = system_time;
+ last_time_ = time;
+
+ return cpu;
+}
+
+bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
+ return GetProcessIoCounters(process_, io_counters) != FALSE;
+}
+
+bool ProcessMetrics::CalculateFreeMemory(FreeMBytes* free) const {
+ const SIZE_T kTopAddress = 0x7F000000;
+ const SIZE_T kMegabyte = 1024 * 1024;
+ SIZE_T accumulated = 0;
+
+ MEMORY_BASIC_INFORMATION largest = {0};
+ UINT_PTR scan = 0;
+ while (scan < kTopAddress) {
+ MEMORY_BASIC_INFORMATION info;
+ if (!::VirtualQueryEx(process_, reinterpret_cast<void*>(scan),
+ &info, sizeof(info)))
+ return false;
+ if (info.State == MEM_FREE) {
+ accumulated += info.RegionSize;
+ if (info.RegionSize > largest.RegionSize)
+ largest = info;
+ }
+ scan += info.RegionSize;
+ }
+ free->largest = largest.RegionSize / kMegabyte;
+ free->largest_ptr = largest.BaseAddress;
+ free->total = accumulated / kMegabyte;
+ return true;
+}
+
+bool EnableLowFragmentationHeap() {
+ HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
+ HeapSetFn heap_set = reinterpret_cast<HeapSetFn>(GetProcAddress(
+ kernel32,
+ "HeapSetInformation"));
+
+ // On Windows 2000, the function is not exported. This is not a reason to
+ // fail.
+ if (!heap_set)
+ return true;
+
+ unsigned number_heaps = GetProcessHeaps(0, NULL);
+ if (!number_heaps)
+ return false;
+
+ // Gives us some extra space in the array in case a thread is creating heaps
+ // at the same time we're querying them.
+ static const int MARGIN = 8;
+ scoped_array<HANDLE> heaps(new HANDLE[number_heaps + MARGIN]);
+ number_heaps = GetProcessHeaps(number_heaps + MARGIN, heaps.get());
+ if (!number_heaps)
+ return false;
+
+ for (unsigned i = 0; i < number_heaps; ++i) {
+ ULONG lfh_flag = 2;
+ // Don't bother with the result code. It may fails on heaps that have the
+ // HEAP_NO_SERIALIZE flag. This is expected and not a problem at all.
+ heap_set(heaps[i],
+ HeapCompatibilityInformation,
+ &lfh_flag,
+ sizeof(lfh_flag));
+ }
+ return true;
+}
+
+void EnableTerminationOnHeapCorruption() {
+ // Ignore the result code. Supported on XP SP3 and Vista.
+ HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
+}
+
+void EnableTerminationOnOutOfMemory() {
+ std::set_new_handler(&OnNoMemory);
+}
+
+void RaiseProcessToHighPriority() {
+ SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
+}
+
+// GetPerformanceInfo is not available on WIN2K. So we'll
+// load it on-the-fly.
+const wchar_t kPsapiDllName[] = L"psapi.dll";
+typedef BOOL (WINAPI *GetPerformanceInfoFunction) (
+ PPERFORMANCE_INFORMATION pPerformanceInformation,
+ DWORD cb);
+
+// Beware of races if called concurrently from multiple threads.
+static BOOL InternalGetPerformanceInfo(
+ PPERFORMANCE_INFORMATION pPerformanceInformation, DWORD cb) {
+ static GetPerformanceInfoFunction GetPerformanceInfo_func = NULL;
+ if (!GetPerformanceInfo_func) {
+ HMODULE psapi_dll = ::GetModuleHandle(kPsapiDllName);
+ if (psapi_dll)
+ GetPerformanceInfo_func = reinterpret_cast<GetPerformanceInfoFunction>(
+ GetProcAddress(psapi_dll, "GetPerformanceInfo"));
+
+ if (!GetPerformanceInfo_func) {
+ // The function could be loaded!
+ memset(pPerformanceInformation, 0, cb);
+ return FALSE;
+ }
+ }
+ return GetPerformanceInfo_func(pPerformanceInformation, cb);
+}
+
+size_t GetSystemCommitCharge() {
+ // Get the System Page Size.
+ SYSTEM_INFO system_info;
+ GetSystemInfo(&system_info);
+
+ PERFORMANCE_INFORMATION info;
+ if (!InternalGetPerformanceInfo(&info, sizeof(info))) {
+ DLOG(ERROR) << "Failed to fetch internal performance info.";
+ return 0;
+ }
+ return (info.CommitTotal * system_info.dwPageSize) / 1024;
+}
+
+} // namespace base
diff --git a/src/base/process_win.cc b/src/base/process_win.cc
new file mode 100644
index 0000000..3f683ab
--- /dev/null
+++ b/src/base/process_win.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/process_util.h"
+#include "base/win/windows_version.h"
+
+namespace base {
+
+void Process::Close() {
+ if (!process_)
+ return;
+
+ // Don't call CloseHandle on a pseudo-handle.
+ if (process_ != ::GetCurrentProcess())
+ ::CloseHandle(process_);
+
+ process_ = NULL;
+}
+
+void Process::Terminate(int result_code) {
+ if (!process_)
+ return;
+
+ // Call NtTerminateProcess directly, without going through the import table,
+ // which might have been hooked with a buggy replacement by third party
+ // software. http://crbug.com/81449.
+ HMODULE module = GetModuleHandle(L"ntdll.dll");
+ typedef UINT (WINAPI *TerminateProcessPtr)(HANDLE handle, UINT code);
+ TerminateProcessPtr terminate_process = reinterpret_cast<TerminateProcessPtr>(
+ GetProcAddress(module, "NtTerminateProcess"));
+ terminate_process(process_, result_code);
+}
+
+bool Process::IsProcessBackgrounded() const {
+ if (!process_)
+ return false; // Failure case.
+ DWORD priority = GetPriority();
+ if (priority == 0)
+ return false; // Failure case.
+ return ((priority == BELOW_NORMAL_PRIORITY_CLASS) ||
+ (priority == IDLE_PRIORITY_CLASS));
+}
+
+bool Process::SetProcessBackgrounded(bool value) {
+ if (!process_)
+ return false;
+ // Vista and above introduce a real background mode, which not only
+ // sets the priority class on the threads but also on the IO generated
+ // by it. Unfortunately it can only be set for the calling process.
+ DWORD priority;
+ if ((base::win::GetVersion() >= base::win::VERSION_VISTA) &&
+ (process_ == ::GetCurrentProcess())) {
+ priority = value ? PROCESS_MODE_BACKGROUND_BEGIN :
+ PROCESS_MODE_BACKGROUND_END;
+ } else {
+ priority = value ? BELOW_NORMAL_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS;
+ }
+
+ return (::SetPriorityClass(process_, priority) != 0);
+}
+
+ProcessId Process::pid() const {
+ if (process_ == 0)
+ return 0;
+
+ return GetProcId(process_);
+}
+
+bool Process::is_current() const {
+ return process_ == GetCurrentProcess();
+}
+
+// static
+Process Process::Current() {
+ return Process(::GetCurrentProcess());
+}
+
+// static
+bool Process::CanBackgroundProcesses() {
+ return true;
+}
+
+int Process::GetPriority() const {
+ DCHECK(process_);
+ return ::GetPriorityClass(process_);
+}
+
+} // namespace base
diff --git a/src/base/profiler/alternate_timer.cc b/src/base/profiler/alternate_timer.cc
new file mode 100644
index 0000000..4eba89c
--- /dev/null
+++ b/src/base/profiler/alternate_timer.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 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/profiler/alternate_timer.h"
+
+#include "base/logging.h"
+
+namespace {
+
+tracked_objects::NowFunction* g_time_function = NULL;
+tracked_objects::TimeSourceType g_time_source_type =
+ tracked_objects::TIME_SOURCE_TYPE_WALL_TIME;
+
+} // anonymous namespace
+
+namespace tracked_objects {
+
+const char kAlternateProfilerTime[] = "CHROME_PROFILER_TIME";
+
+// Set an alternate timer function to replace the OS time function when
+// profiling.
+void SetAlternateTimeSource(NowFunction* now_function, TimeSourceType type) {
+ DCHECK_EQ(reinterpret_cast<NowFunction*>(NULL), g_time_function);
+ g_time_function = now_function;
+ g_time_source_type = type;
+}
+
+NowFunction* GetAlternateTimeSource() {
+ return g_time_function;
+}
+
+TimeSourceType GetTimeSourceType() {
+ return g_time_source_type;
+}
+
+} // namespace tracked_objects
diff --git a/src/base/profiler/alternate_timer.h b/src/base/profiler/alternate_timer.h
new file mode 100644
index 0000000..fdc75dc
--- /dev/null
+++ b/src/base/profiler/alternate_timer.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 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 glue file, which allows third party code to call into our profiler
+// without having to include most any functions from base.
+
+#ifndef BASE_PROFILER_ALTERNATE_TIMER_H_
+#define BASE_PROFILER_ALTERNATE_TIMER_H_
+
+#include "base/base_export.h"
+
+namespace tracked_objects {
+
+enum TimeSourceType {
+ TIME_SOURCE_TYPE_WALL_TIME,
+ TIME_SOURCE_TYPE_TCMALLOC
+};
+
+// Provide type for an alternate timer function.
+typedef unsigned int NowFunction();
+
+// Environment variable name that is used to activate alternate timer profiling
+// (such as using TCMalloc allocations to provide a pseudo-timer) for tasks
+// instead of wall clock profiling.
+BASE_EXPORT extern const char kAlternateProfilerTime[];
+
+// Set an alternate timer function to replace the OS time function when
+// profiling. Typically this is called by an allocator that is providing a
+// function that indicates how much memory has been allocated on any given
+// thread.
+BASE_EXPORT void SetAlternateTimeSource(NowFunction* now_function,
+ TimeSourceType type);
+
+// Gets the pointer to a function that was set via SetAlternateTimeSource().
+// Returns NULL if no set was done prior to calling GetAlternateTimeSource.
+NowFunction* GetAlternateTimeSource();
+
+// Returns the type of the currently set time source.
+BASE_EXPORT TimeSourceType GetTimeSourceType();
+
+} // namespace tracked_objects
+
+#endif // BASE_PROFILER_ALTERNATE_TIMER_H_
diff --git a/src/base/profiler/scoped_profile.cc b/src/base/profiler/scoped_profile.cc
new file mode 100644
index 0000000..93c86e9
--- /dev/null
+++ b/src/base/profiler/scoped_profile.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/profiler/scoped_profile.h"
+
+#include "base/location.h"
+#include "base/tracked_objects.h"
+
+
+namespace tracked_objects {
+
+
+ScopedProfile::ScopedProfile(const Location& location)
+ : birth_(ThreadData::TallyABirthIfActive(location)),
+ start_of_run_(ThreadData::NowForStartOfRun(birth_)) {
+}
+
+ScopedProfile::~ScopedProfile() {
+ StopClockAndTally();
+}
+
+void ScopedProfile::StopClockAndTally() {
+ if (!birth_)
+ return;
+ ThreadData::TallyRunInAScopedRegionIfTracking(birth_, start_of_run_,
+ ThreadData::NowForEndOfRun());
+ birth_ = NULL;
+}
+
+} // namespace tracked_objects
diff --git a/src/base/profiler/scoped_profile.h b/src/base/profiler/scoped_profile.h
new file mode 100644
index 0000000..8b77d6d
--- /dev/null
+++ b/src/base/profiler/scoped_profile.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#ifndef BASE_PROFILER_SCOPED_PROFILE_H_
+#define BASE_PROFILER_SCOPED_PROFILE_H_
+
+//------------------------------------------------------------------------------
+// ScopedProfile provides basic helper functions for profiling a short
+// region of code within a scope. It is separate from the related ThreadData
+// class so that it can be included without much other cruft, and provide the
+// macros listed below.
+
+#include "base/base_export.h"
+#include "base/location.h"
+#include "base/profiler/tracked_time.h"
+
+#if defined(GOOGLE_CHROME_BUILD)
+
+// We don't ship these profiled regions. This is for developer builds only.
+// It allows developers to do some profiling of their code, and see results on
+// their about:profiler page.
+#define TRACK_RUN_IN_THIS_SCOPED_REGION_FOR_DEVELOPER_BUILDS(scope_name) \
+ ((void)0)
+
+#else
+
+#define TRACK_RUN_IN_THIS_SCOPED_REGION_FOR_DEVELOPER_BUILDS(scope_name) \
+ ::tracked_objects::ScopedProfile LINE_BASED_VARIABLE_NAME_FOR_PROFILING( \
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(#scope_name))
+
+#endif
+
+
+
+#define PASTE_LINE_NUMBER_ON_NAME(name, line) name##line
+
+#define LINE_BASED_VARIABLE_NAME_FOR_PROFILING \
+ PASTE_LINE_NUMBER_ON_NAME(some_profiler_variable_, __LINE__)
+
+#define TRACK_RUN_IN_IPC_HANDLER(dispatch_function_name) \
+ ::tracked_objects::ScopedProfile some_tracking_variable_name( \
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(#dispatch_function_name))
+
+
+namespace tracked_objects {
+class Births;
+
+class BASE_EXPORT ScopedProfile {
+ public:
+ explicit ScopedProfile(const Location& location);
+ ~ScopedProfile();
+
+ // Stop tracing prior to the end destruction of the instance.
+ void StopClockAndTally();
+
+ private:
+ Births* birth_; // Place in code where tracking started.
+ const TrackedTime start_of_run_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedProfile);
+};
+
+} // namespace tracked_objects
+
+#endif // BASE_PROFILER_SCOPED_PROFILE_H_
diff --git a/src/base/profiler/tracked_time.cc b/src/base/profiler/tracked_time.cc
new file mode 100644
index 0000000..f1edd6d
--- /dev/null
+++ b/src/base/profiler/tracked_time.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2012 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/profiler/tracked_time.h"
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <mmsystem.h> // Declare timeGetTime()... after including build_config.
+#endif
+
+namespace tracked_objects {
+
+Duration::Duration() : ms_(0) {}
+Duration::Duration(int32 duration) : ms_(duration) {}
+
+Duration& Duration::operator+=(const Duration& other) {
+ ms_ += other.ms_;
+ return *this;
+}
+
+Duration Duration::operator+(const Duration& other) const {
+ return Duration(ms_ + other.ms_);
+}
+
+bool Duration::operator==(const Duration& other) const {
+ return ms_ == other.ms_;
+}
+
+bool Duration::operator!=(const Duration& other) const {
+ return ms_ != other.ms_;
+}
+
+bool Duration::operator>(const Duration& other) const {
+ return ms_ > other.ms_;
+}
+
+// static
+Duration Duration::FromMilliseconds(int ms) { return Duration(ms); }
+
+int32 Duration::InMilliseconds() const { return ms_; }
+
+//------------------------------------------------------------------------------
+
+TrackedTime::TrackedTime() : ms_(0) {}
+TrackedTime::TrackedTime(int32 ms) : ms_(ms) {}
+TrackedTime::TrackedTime(const base::TimeTicks& time)
+ : ms_((time - base::TimeTicks()).InMilliseconds()) {
+}
+
+// static
+TrackedTime TrackedTime::Now() {
+#if defined(OS_WIN)
+ // Use lock-free accessor to 32 bit time.
+ // Note that TimeTicks::Now() is built on this, so we have "compatible"
+ // times when we down-convert a TimeTicks sample.
+ // TODO(jar): Surface this interface via something in base/time.h.
+ return TrackedTime(static_cast<int32>(timeGetTime()));
+#else
+ // Posix has nice cheap 64 bit times, so we just down-convert it.
+ return TrackedTime(base::TimeTicks::Now());
+#endif // OS_WIN
+}
+
+Duration TrackedTime::operator-(const TrackedTime& other) const {
+ return Duration(ms_ - other.ms_);
+}
+
+TrackedTime TrackedTime::operator+(const Duration& other) const {
+ return TrackedTime(ms_ + other.ms_);
+}
+
+bool TrackedTime::is_null() const { return ms_ == 0; }
+
+} // namespace tracked_objects
diff --git a/src/base/profiler/tracked_time.h b/src/base/profiler/tracked_time.h
new file mode 100644
index 0000000..372c753
--- /dev/null
+++ b/src/base/profiler/tracked_time.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2012 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_PROFILER_TRACKED_TIME_H_
+#define BASE_PROFILER_TRACKED_TIME_H_
+
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/time.h"
+
+namespace tracked_objects {
+
+//------------------------------------------------------------------------------
+
+// TimeTicks maintains a wasteful 64 bits of data (we need less than 32), and on
+// windows, a 64 bit timer is expensive to even obtain. We use a simple
+// millisecond counter for most of our time values, as well as millisecond units
+// of duration between those values. This means we can only handle durations
+// up to 49 days (range), or 24 days (non-negative time durations).
+// We only define enough methods to service the needs of the tracking classes,
+// and our interfaces are modeled after what TimeTicks and TimeDelta use (so we
+// can swap them into place if we want to use the "real" classes).
+
+class BASE_EXPORT Duration { // Similar to base::TimeDelta.
+ public:
+ Duration();
+
+ Duration& operator+=(const Duration& other);
+ Duration operator+(const Duration& other) const;
+
+ bool operator==(const Duration& other) const;
+ bool operator!=(const Duration& other) const;
+ bool operator>(const Duration& other) const;
+
+ static Duration FromMilliseconds(int ms);
+
+ int32 InMilliseconds() const;
+
+ private:
+ friend class TrackedTime;
+ explicit Duration(int32 duration);
+
+ // Internal time is stored directly in milliseconds.
+ int32 ms_;
+};
+
+class BASE_EXPORT TrackedTime { // Similar to base::TimeTicks.
+ public:
+ TrackedTime();
+ explicit TrackedTime(const base::TimeTicks& time);
+
+ static TrackedTime Now();
+ Duration operator-(const TrackedTime& other) const;
+ TrackedTime operator+(const Duration& other) const;
+ bool is_null() const;
+
+ static TrackedTime FromMilliseconds(int32 ms) { return TrackedTime(ms); }
+
+ private:
+ friend class Duration;
+ explicit TrackedTime(int32 ms);
+
+ // Internal duration is stored directly in milliseconds.
+ uint32 ms_;
+};
+
+} // namespace tracked_objects
+
+#endif // BASE_PROFILER_TRACKED_TIME_H_
diff --git a/src/base/profiler/tracked_time_unittest.cc b/src/base/profiler/tracked_time_unittest.cc
new file mode 100644
index 0000000..a36a7ed
--- /dev/null
+++ b/src/base/profiler/tracked_time_unittest.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2012 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.
+
+// Test of classes in tracked_time.cc
+
+#include "base/profiler/tracked_time.h"
+#include "base/time.h"
+#include "base/tracked_objects.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace tracked_objects {
+
+class TrackedTimeTest : public testing::Test {
+ protected:
+ TrackedTimeTest() {
+ // On entry, leak any database structures in case they are still in use by
+ // prior threads.
+ ThreadData::ShutdownSingleThreadedCleanup(true);
+ }
+
+ virtual ~TrackedTimeTest() {
+ // We should not need to leak any structures we create, since we are
+ // single threaded, and carefully accounting for items.
+ ThreadData::ShutdownSingleThreadedCleanup(false);
+ }
+};
+
+TEST_F(TrackedTimeTest, TrackedTimerMilliseconds) {
+ // First make sure we basicallly transfer simple milliseconds values as
+ // expected. Most critically, things should not become null.
+ int32 kSomeMilliseconds = 243; // Some example times.
+ int64 kReallyBigMilliseconds = (1LL << 35) + kSomeMilliseconds;
+
+ TrackedTime some = TrackedTime() +
+ Duration::FromMilliseconds(kSomeMilliseconds);
+ EXPECT_EQ(kSomeMilliseconds, (some - TrackedTime()).InMilliseconds());
+ EXPECT_FALSE(some.is_null());
+
+ // Now create a big time, to check that it is wrapped modulo 2^32.
+ base::TimeTicks big = base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(kReallyBigMilliseconds);
+ EXPECT_EQ(kReallyBigMilliseconds, (big - base::TimeTicks()).InMilliseconds());
+
+ TrackedTime wrapped_big(big);
+ // Expect wrapping at 32 bits.
+ EXPECT_EQ(kSomeMilliseconds, (wrapped_big - TrackedTime()).InMilliseconds());
+}
+
+TEST_F(TrackedTimeTest, TrackedTimerDuration) {
+ int kFirstMilliseconds = 793;
+ int kSecondMilliseconds = 14889;
+
+ Duration first = Duration::FromMilliseconds(kFirstMilliseconds);
+ Duration second = Duration::FromMilliseconds(kSecondMilliseconds);
+
+ EXPECT_EQ(kFirstMilliseconds, first.InMilliseconds());
+ EXPECT_EQ(kSecondMilliseconds, second.InMilliseconds());
+
+ Duration sum = first + second;
+ EXPECT_EQ(kFirstMilliseconds + kSecondMilliseconds, sum.InMilliseconds());
+}
+
+TEST_F(TrackedTimeTest, TrackedTimerVsTimeTicks) {
+ // Make sure that our 32 bit timer is aligned with the TimeTicks() timer.
+
+ // First get a 64 bit timer (which should not be null).
+ base::TimeTicks ticks_before = base::TimeTicks::Now();
+ EXPECT_FALSE(ticks_before.is_null());
+
+ // Then get a 32 bit timer that can be be null when it wraps.
+ TrackedTime now = TrackedTime::Now();
+
+ // Then get a bracketing time.
+ base::TimeTicks ticks_after = base::TimeTicks::Now();
+ EXPECT_FALSE(ticks_after.is_null());
+
+ // Now make sure that we bracketed our tracked time nicely.
+ Duration before = now - TrackedTime(ticks_before);
+ EXPECT_LE(0, before.InMilliseconds());
+ Duration after = now - TrackedTime(ticks_after);
+ EXPECT_GE(0, after.InMilliseconds());
+}
+
+TEST_F(TrackedTimeTest, TrackedTimerDisabled) {
+ // Check to be sure disabling the collection of data induces a null time
+ // (which we know will return much faster).
+ if (!ThreadData::InitializeAndSetTrackingStatus(ThreadData::DEACTIVATED))
+ return;
+ // Since we disabled tracking, we should get a null response.
+ TrackedTime track_now = ThreadData::Now();
+ EXPECT_TRUE(track_now.is_null());
+ track_now = ThreadData::NowForStartOfRun(NULL);
+ EXPECT_TRUE(track_now.is_null());
+ track_now = ThreadData::NowForEndOfRun();
+ EXPECT_TRUE(track_now.is_null());
+}
+
+TEST_F(TrackedTimeTest, TrackedTimerEnabled) {
+ if (!ThreadData::InitializeAndSetTrackingStatus(
+ ThreadData::PROFILING_CHILDREN_ACTIVE))
+ return;
+ // Make sure that when we enable tracking, we get a real timer result.
+
+ // First get a 64 bit timer (which should not be null).
+ base::TimeTicks ticks_before = base::TimeTicks::Now();
+ EXPECT_FALSE(ticks_before.is_null());
+
+ // Then get a 32 bit timer that can be null when it wraps.
+ // Crtical difference from the TrackedTimerVsTimeTicks test, is that we use
+ // ThreadData::Now(). It can sometimes return the null time.
+ TrackedTime now = ThreadData::Now();
+
+ // Then get a bracketing time.
+ base::TimeTicks ticks_after = base::TimeTicks::Now();
+ EXPECT_FALSE(ticks_after.is_null());
+
+ // Now make sure that we bracketed our tracked time nicely.
+ Duration before = now - TrackedTime(ticks_before);
+ EXPECT_LE(0, before.InMilliseconds());
+ Duration after = now - TrackedTime(ticks_after);
+ EXPECT_GE(0, after.InMilliseconds());
+}
+
+} // namespace tracked_objects
diff --git a/src/base/rand_util.cc b/src/base/rand_util.cc
new file mode 100644
index 0000000..a9bc961
--- /dev/null
+++ b/src/base/rand_util.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/rand_util.h"
+
+#include <math.h>
+
+#include <limits>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+
+namespace base {
+
+int RandInt(int min, int max) {
+ DCHECK_LE(min, max);
+
+ uint64 range = static_cast<uint64>(max) - min + 1;
+ int result = min + static_cast<int>(base::RandGenerator(range));
+ DCHECK_GE(result, min);
+ DCHECK_LE(result, max);
+ return result;
+}
+
+double RandDouble() {
+ return BitsToOpenEndedUnitInterval(base::RandUint64());
+}
+
+double BitsToOpenEndedUnitInterval(uint64 bits) {
+ // We try to get maximum precision by masking out as many bits as will fit
+ // in the target type's mantissa, and raising it to an appropriate power to
+ // produce output in the range [0, 1). For IEEE 754 doubles, the mantissa
+ // is expected to accommodate 53 bits.
+
+ COMPILE_ASSERT(std::numeric_limits<double>::radix == 2, otherwise_use_scalbn);
+ static const int kBits = std::numeric_limits<double>::digits;
+ uint64 random_bits = bits & ((GG_UINT64_C(1) << kBits) - 1);
+ double result = ldexp(static_cast<double>(random_bits), -1 * kBits);
+ DCHECK_GE(result, 0.0);
+ DCHECK_LT(result, 1.0);
+ return result;
+}
+
+uint64 RandGenerator(uint64 range) {
+ DCHECK_GT(range, 0u);
+ // We must discard random results above this number, as they would
+ // make the random generator non-uniform (consider e.g. if
+ // MAX_UINT64 was 7 and |range| was 5, then a result of 1 would be twice
+ // as likely as a result of 3 or 4).
+ uint64 max_acceptable_value =
+ (std::numeric_limits<uint64>::max() / range) * range - 1;
+
+ uint64 value;
+ do {
+ value = base::RandUint64();
+ } while (value > max_acceptable_value);
+
+ return value % range;
+}
+
+void RandBytes(void* output, size_t output_length) {
+ uint64 random_int;
+ size_t random_int_size = sizeof(random_int);
+ for (size_t i = 0; i < output_length; i += random_int_size) {
+ random_int = base::RandUint64();
+ size_t copy_count = std::min(output_length - i, random_int_size);
+ memcpy(((uint8*)output) + i, &random_int, copy_count);
+ }
+}
+
+std::string RandBytesAsString(size_t length) {
+ DCHECK_GT(length, 0u);
+ std::string result;
+ RandBytes(WriteInto(&result, length + 1), length);
+ return result;
+}
+
+} // namespace base
diff --git a/src/base/rand_util.h b/src/base/rand_util.h
new file mode 100644
index 0000000..4f4765b
--- /dev/null
+++ b/src/base/rand_util.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 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_RAND_UTIL_H_
+#define BASE_RAND_UTIL_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+
+// Returns a random number in range [0, kuint64max]. Thread-safe.
+BASE_EXPORT uint64 RandUint64();
+
+// Returns a random number between min and max (inclusive). Thread-safe.
+BASE_EXPORT int RandInt(int min, int max);
+
+// Returns a random number in range [0, range). Thread-safe.
+//
+// Note that this can be used as an adapter for std::random_shuffle():
+// Given a pre-populated |std::vector<int> myvector|, shuffle it as
+// std::random_shuffle(myvector.begin(), myvector.end(), base::RandGenerator);
+BASE_EXPORT uint64 RandGenerator(uint64 range);
+
+// Returns a random double in range [0, 1). Thread-safe.
+BASE_EXPORT double RandDouble();
+
+// Given input |bits|, convert with maximum precision to a double in
+// the range [0, 1). Thread-safe.
+BASE_EXPORT double BitsToOpenEndedUnitInterval(uint64 bits);
+
+// Fills |output_length| bytes of |output| with random data.
+//
+// WARNING:
+// Do not use for security-sensitive purposes.
+// See crypto/ for cryptographically secure random number generation APIs.
+BASE_EXPORT void RandBytes(void* output, size_t output_length);
+
+// Fills a string of length |length| with with random data and returns it.
+// |length| should be nonzero.
+//
+// Note that this is a variation of |RandBytes| with a different return type.
+//
+// WARNING:
+// Do not use for security-sensitive purposes.
+// See crypto/ for cryptographically secure random number generation APIs.
+BASE_EXPORT std::string RandBytesAsString(size_t length);
+
+#ifdef OS_POSIX
+BASE_EXPORT int GetUrandomFD();
+#endif
+
+} // namespace base
+
+#endif // BASE_RAND_UTIL_H_
diff --git a/src/base/rand_util_nacl.cc b/src/base/rand_util_nacl.cc
new file mode 100644
index 0000000..47450aa
--- /dev/null
+++ b/src/base/rand_util_nacl.cc
@@ -0,0 +1,53 @@
+// Copyright (c) 2012 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/rand_util.h"
+
+#include "base/basictypes.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "native_client/src/untrusted/irt/irt.h"
+
+namespace {
+
+class NaclRandom {
+ public:
+ NaclRandom() {
+ size_t result = nacl_interface_query(NACL_IRT_RANDOM_v0_1,
+ &random_, sizeof(random_));
+ CHECK_EQ(result, sizeof(random_));
+ }
+
+ ~NaclRandom() {
+ }
+
+ void GetRandomBytes(char* buffer, uint32_t num_bytes) {
+ while (num_bytes > 0) {
+ size_t nread;
+ int error = random_.get_random_bytes(buffer, num_bytes, &nread);
+ CHECK_EQ(error, 0);
+ CHECK_LE(nread, num_bytes);
+ buffer += nread;
+ num_bytes -= nread;
+ }
+ }
+
+ private:
+ nacl_irt_random random_;
+};
+
+base::LazyInstance<NaclRandom>::Leaky g_nacl_random = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+namespace base {
+
+uint64 RandUint64() {
+ uint64 result;
+ g_nacl_random.Pointer()->GetRandomBytes(
+ reinterpret_cast<char*>(&result), sizeof(result));
+ return result;
+}
+
+} // namespace base
diff --git a/src/base/rand_util_posix.cc b/src/base/rand_util_posix.cc
new file mode 100644
index 0000000..d65ddae
--- /dev/null
+++ b/src/base/rand_util_posix.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 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/rand_util.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "base/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+
+namespace {
+
+// We keep the file descriptor for /dev/urandom around so we don't need to
+// reopen it (which is expensive), and since we may not even be able to reopen
+// it if we are later put in a sandbox. This class wraps the file descriptor so
+// we can use LazyInstance to handle opening it on the first access.
+class URandomFd {
+ public:
+ URandomFd() {
+ fd_ = open("/dev/urandom", O_RDONLY);
+ DCHECK_GE(fd_, 0) << "Cannot open /dev/urandom: " << errno;
+ }
+
+ ~URandomFd() {
+ close(fd_);
+ }
+
+ int fd() const { return fd_; }
+
+ private:
+ int fd_;
+};
+
+base::LazyInstance<URandomFd> g_urandom_fd = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+namespace base {
+
+// NOTE: This function must be cryptographically secure. http://crbug.com/140076
+uint64 RandUint64() {
+ uint64 number;
+
+ int urandom_fd = g_urandom_fd.Pointer()->fd();
+ bool success = file_util::ReadFromFD(urandom_fd,
+ reinterpret_cast<char*>(&number),
+ sizeof(number));
+ CHECK(success);
+
+ return number;
+}
+
+int GetUrandomFD(void) {
+ return g_urandom_fd.Pointer()->fd();
+}
+
+} // namespace base
diff --git a/src/base/rand_util_starboard.cc b/src/base/rand_util_starboard.cc
new file mode 100644
index 0000000..1c1c586
--- /dev/null
+++ b/src/base/rand_util_starboard.cc
@@ -0,0 +1,26 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/rand_util.h"
+
+#include "starboard/system.h"
+
+namespace base {
+
+// NOTE: This function must be cryptographically secure. http://crbug.com/140076
+uint64 RandUint64() {
+ return SbSystemGetRandomUInt64();
+}
+
+} // namespace base
diff --git a/src/base/rand_util_unittest.cc b/src/base/rand_util_unittest.cc
new file mode 100644
index 0000000..e0e85ec
--- /dev/null
+++ b/src/base/rand_util_unittest.cc
@@ -0,0 +1,122 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/rand_util.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const int kIntMin = std::numeric_limits<int>::min();
+const int kIntMax = std::numeric_limits<int>::max();
+
+} // namespace
+
+TEST(RandUtilTest, SameMinAndMax) {
+ EXPECT_EQ(base::RandInt(0, 0), 0);
+ EXPECT_EQ(base::RandInt(kIntMin, kIntMin), kIntMin);
+ EXPECT_EQ(base::RandInt(kIntMax, kIntMax), kIntMax);
+}
+
+TEST(RandUtilTest, RandDouble) {
+ // Force 64-bit precision, making sure we're not in a 80-bit FPU register.
+ volatile double number = base::RandDouble();
+ EXPECT_GT(1.0, number);
+ EXPECT_LE(0.0, number);
+}
+
+TEST(RandUtilTest, RandBytes) {
+ const size_t buffer_size = 50;
+ char buffer[buffer_size];
+ memset(buffer, 0, buffer_size);
+ base::RandBytes(buffer, buffer_size);
+ std::sort(buffer, buffer + buffer_size);
+ // Probability of occurrence of less than 25 unique bytes in 50 random bytes
+ // is below 10^-25.
+ EXPECT_GT(std::unique(buffer, buffer + buffer_size) - buffer, 25);
+}
+
+TEST(RandUtilTest, RandBytesAsString) {
+ std::string random_string = base::RandBytesAsString(1);
+ EXPECT_EQ(1U, random_string.size());
+ random_string = base::RandBytesAsString(145);
+ EXPECT_EQ(145U, random_string.size());
+ char accumulator = 0;
+ for (size_t i = 0; i < random_string.size(); ++i)
+ accumulator |= random_string[i];
+ // In theory this test can fail, but it won't before the universe dies of
+ // heat death.
+ EXPECT_NE(0, accumulator);
+}
+
+// Make sure that it is still appropriate to use RandGenerator in conjunction
+// with std::random_shuffle().
+TEST(RandUtilTest, RandGeneratorForRandomShuffle) {
+ EXPECT_EQ(base::RandGenerator(1), 0U);
+ EXPECT_LE(std::numeric_limits<ptrdiff_t>::max(),
+ std::numeric_limits<int64>::max());
+}
+
+TEST(RandUtilTest, RandGeneratorIsUniform) {
+ // Verify that RandGenerator has a uniform distribution. This is a
+ // regression test that consistently failed when RandGenerator was
+ // implemented this way:
+ //
+ // return base::RandUint64() % max;
+ //
+ // A degenerate case for such an implementation is e.g. a top of
+ // range that is 2/3rds of the way to MAX_UINT64, in which case the
+ // bottom half of the range would be twice as likely to occur as the
+ // top half. A bit of calculus care of jar@ shows that the largest
+ // measurable delta is when the top of the range is 3/4ths of the
+ // way, so that's what we use in the test.
+ const uint64 kTopOfRange = (std::numeric_limits<uint64>::max() / 4ULL) * 3ULL;
+ const uint64 kExpectedAverage = kTopOfRange / 2ULL;
+ const uint64 kAllowedVariance = kExpectedAverage / 50ULL; // +/- 2%
+ const int kMinAttempts = 1000;
+ const int kMaxAttempts = 1000000;
+
+ double cumulative_average = 0.0;
+ int count = 0;
+ while (count < kMaxAttempts) {
+ uint64 value = base::RandGenerator(kTopOfRange);
+ cumulative_average = (count * cumulative_average + value) / (count + 1);
+
+ // Don't quit too quickly for things to start converging, or we may have
+ // a false positive.
+ if (count > kMinAttempts &&
+ kExpectedAverage - kAllowedVariance < cumulative_average &&
+ cumulative_average < kExpectedAverage + kAllowedVariance) {
+ break;
+ }
+
+ ++count;
+ }
+
+ ASSERT_LT(count, kMaxAttempts) << "Expected average was " <<
+ kExpectedAverage << ", average ended at " << cumulative_average;
+}
+
+TEST(RandUtilTest, RandUint64ProducesBothValuesOfAllBits) {
+ // This tests to see that our underlying random generator is good
+ // enough, for some value of good enough.
+ uint64 kAllZeros = 0ULL;
+ uint64 kAllOnes = ~kAllZeros;
+ uint64 found_ones = kAllZeros;
+ uint64 found_zeros = kAllOnes;
+
+ for (size_t i = 0; i < 1000; ++i) {
+ uint64 value = base::RandUint64();
+ found_ones |= value;
+ found_zeros &= value;
+
+ if (found_zeros == kAllZeros && found_ones == kAllOnes)
+ return;
+ }
+
+ FAIL() << "Didn't achieve all bit values in maximum number of tries.";
+}
diff --git a/src/base/rand_util_win.cc b/src/base/rand_util_win.cc
new file mode 100644
index 0000000..391fe5b
--- /dev/null
+++ b/src/base/rand_util_win.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2012 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/rand_util.h"
+
+#include <stdlib.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace {
+
+uint32 RandUint32() {
+ uint32 number;
+ CHECK_EQ(rand_s(&number), 0);
+ return number;
+}
+
+} // namespace
+
+namespace base {
+
+// NOTE: This function must be cryptographically secure. http://crbug.com/140076
+uint64 RandUint64() {
+ uint32 first_half = RandUint32();
+ uint32 second_half = RandUint32();
+ return (static_cast<uint64>(first_half) << 32) + second_half;
+}
+
+} // namespace base
diff --git a/src/base/reuse_memory.h b/src/base/reuse_memory.h
new file mode 100644
index 0000000..9ef0268
--- /dev/null
+++ b/src/base/reuse_memory.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2012 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This class uses a static free list to reuse memory block. Note, this
+// class is not multi thread safe.
+
+#ifndef BASE_REUSE_MEMORY_H_
+#define BASE_REUSE_MEMORY_H_
+
+namespace base {
+
+// For base class if there is one.
+#define DECLARE_REUSE_MEMORY_BASE_FREE_FUNCTION() \
+ virtual bool AddToFreeList() { return false; }
+
+// Declear free function
+#define DECLARE_REUSE_MEMORY_FREE_FUNCTION() \
+ virtual bool AddToFreeList() { AddObjectToFreeList(this); return true; }
+
+// Utility macros
+#define TryRecycleAdoptPtrAndReturn(type, ...) \
+ void* data = AllocateFromFreeList(); \
+ if (data) \
+ return adoptPtr(new (data) type(__VA_ARGS__));
+
+// Free a vector<OwnPtr>. Delete the objects which are not handled by
+// AddToFreeList()
+#define FreeOwnPtrVector(type, vector_var) \
+ for (int _free_i = vector_var.size() - 1; _free_i >= 0; --_free_i) { \
+ type* free_data = vector_var[_free_i].leakPtr(); \
+ if (!free_data->AddToFreeList()) delete free_data; \
+ }
+
+// For reusable class. Note: this class should be the last one in base
+// class list.
+template<typename T>
+class ReuseMemory {
+ public:
+ ReuseMemory() : next_(NULL) {}
+ virtual ~ReuseMemory() {}
+
+ protected:
+ // Allocate object from free list
+ static void* AllocateFromFreeList() {
+ if (free_list_) { // List is valid and not empty
+ T* data = free_list_;
+ free_list_ = free_list_->next_;
+ return data;
+ }
+ return NULL;
+ }
+ // Add object to free list
+ static void AddObjectToFreeList(T* obj) {
+ obj->next_ = free_list_;
+ free_list_ = obj;
+ }
+
+ // Release all objects in free list
+ static void FreeAll() {
+ while (free_list_) {
+ T* data = free_list_;
+ free_list_ = free_list_->next_;
+ delete data;
+ }
+ }
+
+ private:
+ T* next_;
+ static T* free_list_;
+};
+
+} // namespace base
+
+#endif // BASE_REUSE_MEMORY_H_
diff --git a/src/base/run_loop.cc b/src/base/run_loop.cc
new file mode 100644
index 0000000..ebf0c27
--- /dev/null
+++ b/src/base/run_loop.cc
@@ -0,0 +1,96 @@
+// Copyright (c) 2012 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/run_loop.h"
+
+#include "base/bind.h"
+
+namespace base {
+
+RunLoop::RunLoop()
+ : loop_(MessageLoop::current()),
+ weak_factory_(this),
+ previous_run_loop_(NULL),
+ run_depth_(0),
+ run_called_(false),
+ quit_called_(false),
+ running_(false),
+ quit_when_idle_received_(false) {
+#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && !defined(__LB_ANDROID__) && \
+ !defined(OS_STARBOARD)
+ dispatcher_ = NULL;
+#endif
+}
+
+#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && !defined(__LB_ANDROID__) && \
+ !defined(OS_STARBOARD)
+RunLoop::RunLoop(MessageLoop::Dispatcher* dispatcher)
+ : loop_(MessageLoop::current()),
+ weak_factory_(this),
+ previous_run_loop_(NULL),
+ dispatcher_(dispatcher),
+ run_depth_(0),
+ run_called_(false),
+ quit_called_(false),
+ running_(false),
+ quit_when_idle_received_(false) {
+}
+#endif
+
+RunLoop::~RunLoop() {
+}
+
+void RunLoop::Run() {
+ if (!BeforeRun())
+ return;
+ loop_->RunHandler();
+ AfterRun();
+}
+
+void RunLoop::RunUntilIdle() {
+ quit_when_idle_received_ = true;
+ Run();
+}
+
+void RunLoop::Quit() {
+ quit_called_ = true;
+ if (running_ && loop_->run_loop_ == this) {
+ // This is the inner-most RunLoop, so quit now.
+ loop_->QuitNow();
+ }
+}
+
+base::Closure RunLoop::QuitClosure() {
+ return base::Bind(&RunLoop::Quit, weak_factory_.GetWeakPtr());
+}
+
+bool RunLoop::BeforeRun() {
+ DCHECK(!run_called_);
+ run_called_ = true;
+
+ // Allow Quit to be called before Run.
+ if (quit_called_)
+ return false;
+
+ // Push RunLoop stack:
+ previous_run_loop_ = loop_->run_loop_;
+ run_depth_ = previous_run_loop_? previous_run_loop_->run_depth_ + 1 : 1;
+ loop_->run_loop_ = this;
+
+ running_ = true;
+ return true;
+}
+
+void RunLoop::AfterRun() {
+ running_ = false;
+
+ // Pop RunLoop stack:
+ loop_->run_loop_ = previous_run_loop_;
+
+ // Execute deferred QuitNow, if any:
+ if (previous_run_loop_ && previous_run_loop_->quit_called_)
+ loop_->QuitNow();
+}
+
+} // namespace base
diff --git a/src/base/run_loop.h b/src/base/run_loop.h
new file mode 100644
index 0000000..59f5a41
--- /dev/null
+++ b/src/base/run_loop.h
@@ -0,0 +1,135 @@
+// Copyright (c) 2012 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_RUN_LOOP_H_
+#define BASE_RUN_LOOP_H_
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop.h"
+
+namespace base {
+#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
+class MessagePumpForUI;
+#endif
+
+#if defined(OS_STARBOARD)
+class MessagePumpUIStarboard;
+#endif
+
+#if defined(OS_IOS)
+class MessagePumpUIApplication;
+#endif
+
+// Helper class to Run a nested MessageLoop. Please do not use nested
+// MessageLoops in production code! If you must, use this class instead of
+// calling MessageLoop::Run/Quit directly. RunLoop::Run can only be called once
+// per RunLoop lifetime. Create a RunLoop on the stack and call Run/Quit to run
+// a nested MessageLoop.
+class BASE_EXPORT RunLoop {
+ public:
+ RunLoop();
+#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && !defined(__LB_ANDROID__) && \
+ !defined(OS_STARBOARD)
+ explicit RunLoop(MessageLoop::Dispatcher* dispatcher);
+#endif
+ ~RunLoop();
+
+#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && !defined(__LB_ANDROID__) && \
+ !defined(OS_STARBOARD)
+ void set_dispatcher(MessageLoop::Dispatcher* dispatcher) {
+ dispatcher_ = dispatcher;
+ }
+#endif
+
+ // Run the current MessageLoop. This blocks until Quit is called. Before
+ // calling Run, be sure to grab an AsWeakPtr or the QuitClosure in order to
+ // stop the MessageLoop asynchronously. MessageLoop::Quit and QuitNow will
+ // also trigger a return from Run, but those are deprecated.
+ void Run();
+
+ // Run the current MessageLoop until it doesn't find any tasks or messages in
+ // the queue (it goes idle). WARNING: This may never return! Only use this
+ // when repeating tasks such as animated web pages have been shut down.
+ void RunUntilIdle();
+
+ bool running() const { return running_; }
+
+ // Quit an earlier call to Run(). There can be other nested RunLoops servicing
+ // the same task queue (MessageLoop); Quitting one RunLoop has no bearing on
+ // the others. Quit can be called before, during or after Run. If called
+ // before Run, Run will return immediately when called. Calling Quit after the
+ // RunLoop has already finished running has no effect.
+ //
+ // WARNING: You must NEVER assume that a call to Quit will terminate the
+ // targetted message loop. If a nested message loop continues running, the
+ // target may NEVER terminate. It is very easy to livelock (run forever) in
+ // such a case.
+ void Quit();
+
+ // Convenience method to get a closure that safely calls Quit (has no effect
+ // if the RunLoop instance is gone).
+ //
+ // Example:
+ // RunLoop run_loop;
+ // PostTask(run_loop.QuitClosure());
+ // run_loop.Run();
+ base::Closure QuitClosure();
+
+ private:
+ friend class ::MessageLoop;
+#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
+ // Android doesn't support the blocking MessageLoop::Run, so it calls
+ // BeforeRun and AfterRun directly.
+ friend class base::MessagePumpForUI;
+#endif
+
+#if defined(OS_STARBOARD)
+ // Similar to Android (because it may be Android), Starboard also doesn't
+ // support the blocking MessageLoop::Run, so it calls BeforeRun and AfterRun
+ // directly.
+ friend class base::MessagePumpUIStarboard;
+#endif
+
+#if defined(OS_IOS)
+ // iOS doesn't support the blocking MessageLoop::Run, so it calls
+ // BeforeRun directly.
+ friend class base::MessagePumpUIApplication;
+#endif
+
+ // Return false to abort the Run.
+ bool BeforeRun();
+ void AfterRun();
+
+ MessageLoop* loop_;
+
+ // WeakPtrFactory for QuitClosure safety.
+ base::WeakPtrFactory<RunLoop> weak_factory_;
+
+ // Parent RunLoop or NULL if this is the top-most RunLoop.
+ RunLoop* previous_run_loop_;
+
+#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && !defined(__LB_ANDROID__) && \
+ !defined(OS_STARBOARD)
+ MessageLoop::Dispatcher* dispatcher_;
+#endif
+
+ // Used to count how many nested Run() invocations are on the stack.
+ int run_depth_;
+
+ bool run_called_;
+ bool quit_called_;
+ bool running_;
+
+ // Used to record that QuitWhenIdle() was called on the MessageLoop, meaning
+ // that we should quit Run once it becomes idle.
+ bool quit_when_idle_received_;
+
+ DISALLOW_COPY_AND_ASSIGN(RunLoop);
+};
+
+} // namespace base
+
+#endif // BASE_RUN_LOOP_H_
diff --git a/src/base/safe_strerror_posix.cc b/src/base/safe_strerror_posix.cc
new file mode 100644
index 0000000..012e7be
--- /dev/null
+++ b/src/base/safe_strerror_posix.cc
@@ -0,0 +1,119 @@
+// 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/safe_strerror_posix.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#if (defined(__GLIBC__) || defined(OS_NACL))
+#define USE_HISTORICAL_STRERRO_R 1
+#else
+#define USE_HISTORICAL_STRERRO_R 0
+#endif
+
+#if USE_HISTORICAL_STRERRO_R && defined(__GNUC__)
+// GCC will complain about the unused second wrap function unless we tell it
+// that we meant for them to be potentially unused, which is exactly what this
+// attribute is for.
+#define POSSIBLY_UNUSED __attribute__((unused))
+#else
+#define POSSIBLY_UNUSED
+#endif
+
+#if USE_HISTORICAL_STRERRO_R
+// glibc has two strerror_r functions: a historical GNU-specific one that
+// returns type char *, and a POSIX.1-2001 compliant one available since 2.3.4
+// that returns int. This wraps the GNU-specific one.
+static void POSSIBLY_UNUSED wrap_posix_strerror_r(
+ char *(*strerror_r_ptr)(int, char *, size_t),
+ int err,
+ char *buf,
+ size_t len) {
+ // GNU version.
+ char *rc = (*strerror_r_ptr)(err, buf, len);
+ if (rc != buf) {
+ // glibc did not use buf and returned a static string instead. Copy it
+ // into buf.
+ buf[0] = '\0';
+ strncat(buf, rc, len - 1);
+ }
+ // The GNU version never fails. Unknown errors get an "unknown error" message.
+ // The result is always null terminated.
+}
+#endif // USE_HISTORICAL_STRERRO_R
+
+// Wrapper for strerror_r functions that implement the POSIX interface. POSIX
+// does not define the behaviour for some of the edge cases, so we wrap it to
+// guarantee that they are handled. This is compiled on all POSIX platforms, but
+// it will only be used on Linux if the POSIX strerror_r implementation is
+// being used (see below).
+static void POSSIBLY_UNUSED wrap_posix_strerror_r(
+ int (*strerror_r_ptr)(int, char *, size_t),
+ int err,
+ char *buf,
+ size_t len) {
+ int old_errno = errno;
+ // Have to cast since otherwise we get an error if this is the GNU version
+ // (but in such a scenario this function is never called). Sadly we can't use
+ // C++-style casts because the appropriate one is reinterpret_cast but it's
+ // considered illegal to reinterpret_cast a type to itself, so we get an
+ // error in the opposite case.
+ int result = (*strerror_r_ptr)(err, buf, len);
+ if (result == 0) {
+ // POSIX is vague about whether the string will be terminated, although
+ // it indirectly implies that typically ERANGE will be returned, instead
+ // of truncating the string. We play it safe by always terminating the
+ // string explicitly.
+ buf[len - 1] = '\0';
+ } else {
+ // Error. POSIX is vague about whether the return value is itself a system
+ // error code or something else. On Linux currently it is -1 and errno is
+ // set. On BSD-derived systems it is a system error and errno is unchanged.
+ // We try and detect which case it is so as to put as much useful info as
+ // we can into our message.
+ int strerror_error; // The error encountered in strerror
+ int new_errno = errno;
+ if (new_errno != old_errno) {
+ // errno was changed, so probably the return value is just -1 or something
+ // else that doesn't provide any info, and errno is the error.
+ strerror_error = new_errno;
+ } else {
+ // Either the error from strerror_r was the same as the previous value, or
+ // errno wasn't used. Assume the latter.
+ strerror_error = result;
+ }
+ // snprintf truncates and always null-terminates.
+ snprintf(buf,
+ len,
+ "Error %d while retrieving error %d",
+ strerror_error,
+ err);
+ }
+ errno = old_errno;
+}
+
+void safe_strerror_r(int err, char *buf, size_t len) {
+ if (buf == NULL || len <= 0) {
+ return;
+ }
+ // If using glibc (i.e., Linux), the compiler will automatically select the
+ // appropriate overloaded function based on the function type of strerror_r.
+ // The other one will be elided from the translation unit since both are
+ // static.
+#if defined(__LB_XB1__) || defined(__LB_XB360__) || defined(__STDC_LIB_EXT1__)
+ strerror_s(buf, len, err);
+#else
+ wrap_posix_strerror_r(&strerror_r, err, buf, len);
+#endif
+}
+
+std::string safe_strerror(int err) {
+ const int buffer_size = 256;
+ char buf[buffer_size];
+ safe_strerror_r(err, buf, sizeof(buf));
+ return std::string(buf);
+}
diff --git a/src/base/safe_strerror_posix.h b/src/base/safe_strerror_posix.h
new file mode 100644
index 0000000..2f77d84
--- /dev/null
+++ b/src/base/safe_strerror_posix.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SAFE_STRERROR_POSIX_H_
+#define BASE_SAFE_STRERROR_POSIX_H_
+
+#include <string>
+
+#include "base/base_export.h"
+
+// BEFORE using anything from this file, first look at PLOG and friends in
+// logging.h and use them instead if applicable.
+//
+// This file declares safe, portable alternatives to the POSIX strerror()
+// function. strerror() is inherently unsafe in multi-threaded apps and should
+// never be used. Doing so can cause crashes. Additionally, the thread-safe
+// alternative strerror_r varies in semantics across platforms. Use these
+// functions instead.
+
+// Thread-safe strerror function with dependable semantics that never fails.
+// It will write the string form of error "err" to buffer buf of length len.
+// If there is an error calling the OS's strerror_r() function then a message to
+// that effect will be printed into buf, truncating if necessary. The final
+// result is always null-terminated. The value of errno is never changed.
+//
+// Use this instead of strerror_r().
+BASE_EXPORT void safe_strerror_r(int err, char *buf, size_t len);
+
+// Calls safe_strerror_r with a buffer of suitable size and returns the result
+// in a C++ string.
+//
+// Use this instead of strerror(). Note though that safe_strerror_r will be
+// more robust in the case of heap corruption errors, since it doesn't need to
+// allocate a string.
+BASE_EXPORT std::string safe_strerror(int err);
+
+#endif // BASE_SAFE_STRERROR_POSIX_H_
diff --git a/src/base/safe_strerror_shell.cc b/src/base/safe_strerror_shell.cc
new file mode 100644
index 0000000..1f1a8ea
--- /dev/null
+++ b/src/base/safe_strerror_shell.cc
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2012 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "safe_strerror_posix.cc"
+
diff --git a/src/base/scoped_native_library.cc b/src/base/scoped_native_library.cc
new file mode 100644
index 0000000..7290d29
--- /dev/null
+++ b/src/base/scoped_native_library.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/scoped_native_library.h"
+
+namespace base {
+
+ScopedNativeLibrary::ScopedNativeLibrary() : library_(NULL) {
+}
+
+ScopedNativeLibrary::ScopedNativeLibrary(NativeLibrary library)
+ : library_(library) {
+}
+
+ScopedNativeLibrary::ScopedNativeLibrary(const FilePath& library_path) {
+ library_ = base::LoadNativeLibrary(library_path, NULL);
+}
+
+ScopedNativeLibrary::~ScopedNativeLibrary() {
+ if (library_)
+ base::UnloadNativeLibrary(library_);
+}
+
+void* ScopedNativeLibrary::GetFunctionPointer(
+ const char* function_name) const {
+ if (!library_)
+ return NULL;
+ return base::GetFunctionPointerFromNativeLibrary(library_, function_name);
+}
+
+void ScopedNativeLibrary::Reset(NativeLibrary library) {
+ if (library_)
+ base::UnloadNativeLibrary(library_);
+ library_ = library;
+}
+
+NativeLibrary ScopedNativeLibrary::Release() {
+ NativeLibrary result = library_;
+ library_ = NULL;
+ return result;
+}
+
+} // namespace base
diff --git a/src/base/scoped_native_library.h b/src/base/scoped_native_library.h
new file mode 100644
index 0000000..94380ba
--- /dev/null
+++ b/src/base/scoped_native_library.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SCOPED_NATIVE_LIBRARY_H_
+#define BASE_SCOPED_NATIVE_LIBRARY_H_
+
+#include "base/base_export.h"
+#include "base/native_library.h"
+
+class FilePath;
+
+namespace base {
+
+// A class which encapsulates a base::NativeLibrary object available only in a
+// scope.
+// This class automatically unloads the loaded library in its destructor.
+class BASE_EXPORT ScopedNativeLibrary {
+ public:
+ // Initializes with a NULL library.
+ ScopedNativeLibrary();
+
+ // Takes ownership of the given library handle.
+ explicit ScopedNativeLibrary(NativeLibrary library);
+
+ // Opens the given library and manages its lifetime.
+ explicit ScopedNativeLibrary(const FilePath& library_path);
+
+ ~ScopedNativeLibrary();
+
+ // Returns true if there's a valid library loaded.
+ bool is_valid() const { return !!library_; }
+
+ void* GetFunctionPointer(const char* function_name) const;
+
+ // Takes ownership of the given library handle. Any existing handle will
+ // be freed.
+ void Reset(NativeLibrary library);
+
+ // Returns the native library handle and removes it from this object. The
+ // caller must manage the lifetime of the handle.
+ NativeLibrary Release();
+
+ private:
+ NativeLibrary library_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedNativeLibrary);
+};
+
+} // namespace base
+
+#endif // BASE_MEMORY_NATIVE_LIBRARY_H_
diff --git a/src/base/scoped_native_library_unittest.cc b/src/base/scoped_native_library_unittest.cc
new file mode 100644
index 0000000..7efffdd
--- /dev/null
+++ b/src/base/scoped_native_library_unittest.cc
@@ -0,0 +1,33 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/scoped_native_library.h"
+#if defined(OS_WIN)
+#include "base/file_path.h"
+#endif
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Tests whether or not a function pointer retrieved via ScopedNativeLibrary
+// is available only in a scope.
+TEST(ScopedNativeLibrary, Basic) {
+#if defined(OS_WIN)
+ // Get the pointer to DirectDrawCreate() from "ddraw.dll" and verify it
+ // is valid only in this scope.
+ // FreeLibrary() doesn't actually unload a DLL until its reference count
+ // becomes zero, i.e. this function pointer is still valid if the DLL used
+ // in this test is also used by another part of this executable.
+ // So, this test uses "ddraw.dll", which is not used by Chrome at all but
+ // installed on all versions of Windows.
+ FARPROC test_function;
+ {
+ FilePath path(base::GetNativeLibraryName(L"ddraw"));
+ base::ScopedNativeLibrary library(path);
+ test_function = reinterpret_cast<FARPROC>(
+ library.GetFunctionPointer("DirectDrawCreate"));
+ EXPECT_EQ(0, IsBadCodePtr(test_function));
+ }
+ EXPECT_NE(0, IsBadCodePtr(test_function));
+#endif
+}
diff --git a/src/base/scoped_observer.h b/src/base/scoped_observer.h
new file mode 100644
index 0000000..eae6367
--- /dev/null
+++ b/src/base/scoped_observer.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2012 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_SCOPED_OBSERVER_H_
+#define BASE_SCOPED_OBSERVER_H_
+
+#include <algorithm>
+#include <vector>
+
+#include "base/basictypes.h"
+
+// ScopedObserver is used to keep track of the set of sources an object has
+// attached itself to as an observer. When ScopedObserver is destroyed it
+// removes the object as an observer from all sources it has been added to.
+template <class Source, class Observer>
+class ScopedObserver {
+ public:
+ explicit ScopedObserver(Observer* observer) : observer_(observer) {}
+
+ ~ScopedObserver() {
+ for (size_t i = 0; i < sources_.size(); ++i)
+ sources_[i]->RemoveObserver(observer_);
+ }
+
+ // Adds the object passed to the constructor as an observer on |source|.
+ void Add(Source* source) {
+ sources_.push_back(source);
+ source->AddObserver(observer_);
+ }
+
+ // Remove the object passed to the constructor as an observer from |source|.
+ void Remove(Source* source) {
+ sources_.erase(std::find(sources_.begin(), sources_.end(), source));
+ source->RemoveObserver(observer_);
+ }
+
+ bool IsObserving(Source* source) const {
+ for (size_t i = 0; i < sources_.size(); ++i) {
+ if (sources_[i] == source)
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ Observer* observer_;
+
+ std::vector<Source*> sources_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedObserver);
+};
+
+#endif // BASE_SCOPED_OBSERVER_H_
diff --git a/src/base/sequenced_task_runner.cc b/src/base/sequenced_task_runner.cc
new file mode 100644
index 0000000..00d4048
--- /dev/null
+++ b/src/base/sequenced_task_runner.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2012 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/sequenced_task_runner.h"
+
+#include "base/bind.h"
+
+namespace base {
+
+bool SequencedTaskRunner::PostNonNestableTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task) {
+ return PostNonNestableDelayedTask(from_here, task, base::TimeDelta());
+}
+
+bool SequencedTaskRunner::DeleteSoonInternal(
+ const tracked_objects::Location& from_here,
+ void(*deleter)(const void*),
+ const void* object) {
+ return PostNonNestableTask(from_here, Bind(deleter, object));
+}
+
+bool SequencedTaskRunner::ReleaseSoonInternal(
+ const tracked_objects::Location& from_here,
+ void(*releaser)(const void*),
+ const void* object) {
+ return PostNonNestableTask(from_here, Bind(releaser, object));
+}
+
+} // namespace base
diff --git a/src/base/sequenced_task_runner.h b/src/base/sequenced_task_runner.h
new file mode 100644
index 0000000..f6ca646
--- /dev/null
+++ b/src/base/sequenced_task_runner.h
@@ -0,0 +1,159 @@
+// Copyright (c) 2012 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_SEQUENCED_TASKRUNNER_H_
+#define BASE_SEQUENCED_TASKRUNNER_H_
+
+#include "base/base_export.h"
+#include "base/sequenced_task_runner_helpers.h"
+#include "base/task_runner.h"
+
+namespace base {
+
+// A SequencedTaskRunner is a subclass of TaskRunner that provides
+// additional guarantees on the order that tasks are started, as well
+// as guarantees on when tasks are in sequence, i.e. one task finishes
+// before the other one starts.
+//
+// Summary
+// -------
+// Non-nested tasks with the same delay will run one by one in FIFO
+// order.
+//
+// Detailed guarantees
+// -------------------
+//
+// SequencedTaskRunner also adds additional methods for posting
+// non-nestable tasks. In general, an implementation of TaskRunner
+// may expose task-running methods which are themselves callable from
+// within tasks. A non-nestable task is one that is guaranteed to not
+// be run from within an already-running task. Conversely, a nestable
+// task (the default) is a task that can be run from within an
+// already-running task.
+//
+// The guarantees of SequencedTaskRunner are as follows:
+//
+// - Given two tasks T2 and T1, T2 will start after T1 starts if:
+//
+// * T2 is posted after T1; and
+// * T2 has equal or higher delay than T1; and
+// * T2 is non-nestable or T1 is nestable.
+//
+// - If T2 will start after T1 starts by the above guarantee, then
+// T2 will start after T1 finishes and is destroyed if:
+//
+// * T2 is non-nestable, or
+// * T1 doesn't call any task-running methods.
+//
+// - If T2 will start after T1 finishes by the above guarantee, then
+// all memory changes in T1 and T1's destruction will be visible
+// to T2.
+//
+// - If T2 runs nested within T1 via a call to the task-running
+// method M, then all memory changes in T1 up to the call to M
+// will be visible to T2, and all memory changes in T2 will be
+// visible to T1 from the return from M.
+//
+// Note that SequencedTaskRunner does not guarantee that tasks are run
+// on a single dedicated thread, although the above guarantees provide
+// most (but not all) of the same guarantees. If you do need to
+// guarantee that tasks are run on a single dedicated thread, see
+// SingleThreadTaskRunner (in single_thread_task_runner.h).
+//
+// Some corollaries to the above guarantees, assuming the tasks in
+// question don't call any task-running methods:
+//
+// - Tasks posted via PostTask are run in FIFO order.
+//
+// - Tasks posted via PostNonNestableTask are run in FIFO order.
+//
+// - Tasks posted with the same delay and the same nestable state
+// are run in FIFO order.
+//
+// - A list of tasks with the same nestable state posted in order of
+// non-decreasing delay is run in FIFO order.
+//
+// - A list of tasks posted in order of non-decreasing delay with at
+// most a single change in nestable state from nestable to
+// non-nestable is run in FIFO order. (This is equivalent to the
+// statement of the first guarantee above.)
+//
+// Some theoretical implementations of SequencedTaskRunner:
+//
+// - A SequencedTaskRunner that wraps a regular TaskRunner but makes
+// sure that only one task at a time is posted to the TaskRunner,
+// with appropriate memory barriers in between tasks.
+//
+// - A SequencedTaskRunner that, for each task, spawns a joinable
+// thread to run that task and immediately quit, and then
+// immediately joins that thread.
+//
+// - A SequencedTaskRunner that stores the list of posted tasks and
+// has a method Run() that runs each runnable task in FIFO order
+// that can be called from any thread, but only if another
+// (non-nested) Run() call isn't already happening.
+class BASE_EXPORT SequencedTaskRunner : public TaskRunner {
+ public:
+ // The two PostNonNestable*Task methods below are like their
+ // nestable equivalents in TaskRunner, but they guarantee that the
+ // posted task will not run nested within an already-running task.
+ //
+ // A simple corollary is that posting a task as non-nestable can
+ // only delay when the task gets run. That is, posting a task as
+ // non-nestable may not affect when the task gets run, or it could
+ // make it run later than it normally would, but it won't make it
+ // run earlier than it normally would.
+
+ // TODO(akalin): Get rid of the boolean return value for the methods
+ // below.
+
+ bool PostNonNestableTask(const tracked_objects::Location& from_here,
+ const Closure& task);
+
+ virtual bool PostNonNestableDelayedTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ base::TimeDelta delay) = 0;
+
+ // Submits a non-nestable task to delete the given object. Returns
+ // true if the object may be deleted at some point in the future,
+ // and false if the object definitely will not be deleted.
+ template <class T>
+ bool DeleteSoon(const tracked_objects::Location& from_here,
+ const T* object) {
+ return
+ subtle::DeleteHelperInternal<T, bool>::DeleteViaSequencedTaskRunner(
+ this, from_here, object);
+ }
+
+ // Submits a non-nestable task to release the given object. Returns
+ // true if the object may be released at some point in the future,
+ // and false if the object definitely will not be released.
+ template <class T>
+ bool ReleaseSoon(const tracked_objects::Location& from_here,
+ T* object) {
+ return
+ subtle::ReleaseHelperInternal<T, bool>::ReleaseViaSequencedTaskRunner(
+ this, from_here, object);
+ }
+
+ protected:
+ virtual ~SequencedTaskRunner() {}
+
+ private:
+ template <class T, class R> friend class subtle::DeleteHelperInternal;
+ template <class T, class R> friend class subtle::ReleaseHelperInternal;
+
+ bool DeleteSoonInternal(const tracked_objects::Location& from_here,
+ void(*deleter)(const void*),
+ const void* object);
+
+ bool ReleaseSoonInternal(const tracked_objects::Location& from_here,
+ void(*releaser)(const void*),
+ const void* object);
+};
+
+} // namespace base
+
+#endif // BASE_SEQUENCED_TASKRUNNER_H_
diff --git a/src/base/sequenced_task_runner_helpers.h b/src/base/sequenced_task_runner_helpers.h
new file mode 100644
index 0000000..2d0d493
--- /dev/null
+++ b/src/base/sequenced_task_runner_helpers.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2012 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_SEQUENCED_TASK_RUNNER_HELPERS_H_
+#define BASE_SEQUENCED_TASK_RUNNER_HELPERS_H_
+
+#include "base/basictypes.h"
+
+// TODO(akalin): Investigate whether it's possible to just have
+// SequencedTaskRunner use these helpers (instead of MessageLoop).
+// Then we can just move these to sequenced_task_runner.h.
+
+namespace tracked_objects {
+class Location;
+}
+
+namespace base {
+
+namespace subtle {
+template <class T, class R> class DeleteHelperInternal;
+template <class T, class R> class ReleaseHelperInternal;
+}
+
+// Template helpers which use function indirection to erase T from the
+// function signature while still remembering it so we can call the
+// correct destructor/release function.
+//
+// We use this trick so we don't need to include bind.h in a header
+// file like sequenced_task_runner.h. We also wrap the helpers in a
+// templated class to make it easier for users of DeleteSoon to
+// declare the helper as a friend.
+template <class T>
+class DeleteHelper {
+ private:
+ template <class T2, class R> friend class subtle::DeleteHelperInternal;
+
+ static void DoDelete(const void* object) {
+ delete reinterpret_cast<const T*>(object);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(DeleteHelper);
+};
+
+template <class T>
+class ReleaseHelper {
+ private:
+ template <class T2, class R> friend class subtle::ReleaseHelperInternal;
+
+ static void DoRelease(const void* object) {
+ reinterpret_cast<const T*>(object)->Release();
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ReleaseHelper);
+};
+
+namespace subtle {
+
+// An internal SequencedTaskRunner-like class helper for DeleteHelper
+// and ReleaseHelper. We don't want to expose the Do*() functions
+// directly directly since the void* argument makes it possible to
+// pass/ an object of the wrong type to delete. Instead, we force
+// callers to go through these internal helpers for type
+// safety. SequencedTaskRunner-like classes which expose DeleteSoon or
+// ReleaseSoon methods should friend the appropriate helper and
+// implement a corresponding *Internal method with the following
+// signature:
+//
+// bool(const tracked_objects::Location&,
+// void(*function)(const void*),
+// void* object)
+//
+// An implementation of this function should simply create a
+// base::Closure from (function, object) and return the result of
+// posting the task.
+template <class T, class ReturnType>
+class DeleteHelperInternal {
+ public:
+ template <class SequencedTaskRunnerType>
+ static ReturnType DeleteViaSequencedTaskRunner(
+ SequencedTaskRunnerType* sequenced_task_runner,
+ const tracked_objects::Location& from_here,
+ const T* object) {
+ return sequenced_task_runner->DeleteSoonInternal(
+ from_here, &DeleteHelper<T>::DoDelete, object);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DeleteHelperInternal);
+};
+
+template <class T, class ReturnType>
+class ReleaseHelperInternal {
+ public:
+ template <class SequencedTaskRunnerType>
+ static ReturnType ReleaseViaSequencedTaskRunner(
+ SequencedTaskRunnerType* sequenced_task_runner,
+ const tracked_objects::Location& from_here,
+ const T* object) {
+ return sequenced_task_runner->ReleaseSoonInternal(
+ from_here, &ReleaseHelper<T>::DoRelease, object);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ReleaseHelperInternal);
+};
+
+} // namespace subtle
+
+} // namespace base
+
+#endif // BASE_SEQUENCED_TASK_RUNNER_HELPERS_H_
diff --git a/src/base/sha1.h b/src/base/sha1.h
new file mode 100644
index 0000000..998cccb
--- /dev/null
+++ b/src/base/sha1.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SHA1_H_
+#define BASE_SHA1_H_
+
+#include <string>
+
+#include "base/base_export.h"
+
+namespace base {
+
+// These functions perform SHA-1 operations.
+
+static const size_t kSHA1Length = 20; // Length in bytes of a SHA-1 hash.
+
+// Computes the SHA-1 hash of the input string |str| and returns the full
+// hash.
+BASE_EXPORT std::string SHA1HashString(const std::string& str);
+
+// Computes the SHA-1 hash of the |len| bytes in |data| and puts the hash
+// in |hash|. |hash| must be kSHA1Length bytes long.
+BASE_EXPORT void SHA1HashBytes(const unsigned char* data, size_t len,
+ unsigned char* hash);
+
+} // namespace base
+
+#endif // BASE_SHA1_H_
diff --git a/src/base/sha1_portable.cc b/src/base/sha1_portable.cc
new file mode 100644
index 0000000..0e38f25
--- /dev/null
+++ b/src/base/sha1_portable.cc
@@ -0,0 +1,217 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/sha1.h"
+
+#include <string.h>
+
+#include "base/basictypes.h"
+
+namespace base {
+
+// Implementation of SHA-1. Only handles data in byte-sized blocks,
+// which simplifies the code a fair bit.
+
+// Identifier names follow notation in FIPS PUB 180-3, where you'll
+// also find a description of the algorithm:
+// http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf
+
+// Usage example:
+//
+// SecureHashAlgorithm sha;
+// while(there is data to hash)
+// sha.Update(moredata, size of data);
+// sha.Final();
+// memcpy(somewhere, sha.Digest(), 20);
+//
+// to reuse the instance of sha, call sha.Init();
+
+// TODO(jhawkins): Replace this implementation with a per-platform
+// implementation using each platform's crypto library. See
+// http://crbug.com/47218
+
+class SecureHashAlgorithm {
+ public:
+ SecureHashAlgorithm() { Init(); }
+
+ static const int kDigestSizeBytes;
+
+ void Init();
+ void Update(const void* data, size_t nbytes);
+ void Final();
+
+ // 20 bytes of message digest.
+ const unsigned char* Digest() const {
+ return reinterpret_cast<const unsigned char*>(H);
+ }
+
+ private:
+ void Pad();
+ void Process();
+
+ uint32 A, B, C, D, E;
+
+ uint32 H[5];
+
+ union {
+ uint32 W[80];
+ uint8 M[64];
+ };
+
+ uint32 cursor;
+ uint32 l;
+};
+
+static inline uint32 f(uint32 t, uint32 B, uint32 C, uint32 D) {
+ if (t < 20) {
+ return (B & C) | ((~B) & D);
+ } else if (t < 40) {
+ return B ^ C ^ D;
+ } else if (t < 60) {
+ return (B & C) | (B & D) | (C & D);
+ } else {
+ return B ^ C ^ D;
+ }
+}
+
+static inline uint32 S(uint32 n, uint32 X) {
+ return (X << n) | (X >> (32-n));
+}
+
+static inline uint32 K(uint32 t) {
+ if (t < 20) {
+ return 0x5a827999;
+ } else if (t < 40) {
+ return 0x6ed9eba1;
+ } else if (t < 60) {
+ return 0x8f1bbcdc;
+ } else {
+ return 0xca62c1d6;
+ }
+}
+
+static inline void swapends(uint32* t) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ *t = ((*t & 0xff000000) >> 24) |
+ ((*t & 0xff0000) >> 8) |
+ ((*t & 0xff00) << 8) |
+ ((*t & 0xff) << 24);
+#endif
+}
+
+const int SecureHashAlgorithm::kDigestSizeBytes = 20;
+
+void SecureHashAlgorithm::Init() {
+ A = 0;
+ B = 0;
+ C = 0;
+ D = 0;
+ E = 0;
+ cursor = 0;
+ l = 0;
+ H[0] = 0x67452301;
+ H[1] = 0xefcdab89;
+ H[2] = 0x98badcfe;
+ H[3] = 0x10325476;
+ H[4] = 0xc3d2e1f0;
+}
+
+void SecureHashAlgorithm::Final() {
+ Pad();
+ Process();
+
+ for (int t = 0; t < 5; ++t)
+ swapends(&H[t]);
+}
+
+void SecureHashAlgorithm::Update(const void* data, size_t nbytes) {
+ const uint8* d = reinterpret_cast<const uint8*>(data);
+ while (nbytes--) {
+ M[cursor++] = *d++;
+ if (cursor >= 64)
+ Process();
+ l += 8;
+ }
+}
+
+void SecureHashAlgorithm::Pad() {
+ M[cursor++] = 0x80;
+
+ if (cursor > 64-8) {
+ // pad out to next block
+ while (cursor < 64)
+ M[cursor++] = 0;
+
+ Process();
+ }
+
+ while (cursor < 64-4)
+ M[cursor++] = 0;
+
+ M[64-4] = (l & 0xff000000) >> 24;
+ M[64-3] = (l & 0xff0000) >> 16;
+ M[64-2] = (l & 0xff00) >> 8;
+ M[64-1] = (l & 0xff);
+}
+
+void SecureHashAlgorithm::Process() {
+ uint32 t;
+
+ // Each a...e corresponds to a section in the FIPS 180-3 algorithm.
+
+ // a.
+ //
+ // W and M are in a union, so no need to memcpy.
+ // memcpy(W, M, sizeof(M));
+ for (t = 0; t < 16; ++t)
+ swapends(&W[t]);
+
+ // b.
+ for (t = 16; t < 80; ++t)
+ W[t] = S(1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]);
+
+ // c.
+ A = H[0];
+ B = H[1];
+ C = H[2];
+ D = H[3];
+ E = H[4];
+
+ // d.
+ for (t = 0; t < 80; ++t) {
+ uint32 TEMP = S(5, A) + f(t, B, C, D) + E + W[t] + K(t);
+ E = D;
+ D = C;
+ C = S(30, B);
+ B = A;
+ A = TEMP;
+ }
+
+ // e.
+ H[0] += A;
+ H[1] += B;
+ H[2] += C;
+ H[3] += D;
+ H[4] += E;
+
+ cursor = 0;
+}
+
+std::string SHA1HashString(const std::string& str) {
+ char hash[SecureHashAlgorithm::kDigestSizeBytes];
+ SHA1HashBytes(reinterpret_cast<const unsigned char*>(str.c_str()),
+ str.length(), reinterpret_cast<unsigned char*>(hash));
+ return std::string(hash, SecureHashAlgorithm::kDigestSizeBytes);
+}
+
+void SHA1HashBytes(const unsigned char* data, size_t len,
+ unsigned char* hash) {
+ SecureHashAlgorithm sha;
+ sha.Update(data, len);
+ sha.Final();
+
+ memcpy(hash, sha.Digest(), SecureHashAlgorithm::kDigestSizeBytes);
+}
+
+} // namespace base
diff --git a/src/base/sha1_unittest.cc b/src/base/sha1_unittest.cc
new file mode 100644
index 0000000..b29fe46
--- /dev/null
+++ b/src/base/sha1_unittest.cc
@@ -0,0 +1,108 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/sha1.h"
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(SHA1Test, Test1) {
+ // Example A.1 from FIPS 180-2: one-block message.
+ std::string input = "abc";
+
+ int expected[] = { 0xa9, 0x99, 0x3e, 0x36,
+ 0x47, 0x06, 0x81, 0x6a,
+ 0xba, 0x3e, 0x25, 0x71,
+ 0x78, 0x50, 0xc2, 0x6c,
+ 0x9c, 0xd0, 0xd8, 0x9d };
+
+ std::string output = base::SHA1HashString(input);
+ for (size_t i = 0; i < base::kSHA1Length; i++)
+ EXPECT_EQ(expected[i], output[i] & 0xFF);
+}
+
+TEST(SHA1Test, Test2) {
+ // Example A.2 from FIPS 180-2: multi-block message.
+ std::string input =
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+
+ int expected[] = { 0x84, 0x98, 0x3e, 0x44,
+ 0x1c, 0x3b, 0xd2, 0x6e,
+ 0xba, 0xae, 0x4a, 0xa1,
+ 0xf9, 0x51, 0x29, 0xe5,
+ 0xe5, 0x46, 0x70, 0xf1 };
+
+ std::string output = base::SHA1HashString(input);
+ for (size_t i = 0; i < base::kSHA1Length; i++)
+ EXPECT_EQ(expected[i], output[i] & 0xFF);
+}
+
+TEST(SHA1Test, Test3) {
+ // Example A.3 from FIPS 180-2: long message.
+ std::string input(1000000, 'a');
+
+ int expected[] = { 0x34, 0xaa, 0x97, 0x3c,
+ 0xd4, 0xc4, 0xda, 0xa4,
+ 0xf6, 0x1e, 0xeb, 0x2b,
+ 0xdb, 0xad, 0x27, 0x31,
+ 0x65, 0x34, 0x01, 0x6f };
+
+ std::string output = base::SHA1HashString(input);
+ for (size_t i = 0; i < base::kSHA1Length; i++)
+ EXPECT_EQ(expected[i], output[i] & 0xFF);
+}
+
+TEST(SHA1Test, Test1Bytes) {
+ // Example A.1 from FIPS 180-2: one-block message.
+ std::string input = "abc";
+ unsigned char output[base::kSHA1Length];
+
+ unsigned char expected[] = { 0xa9, 0x99, 0x3e, 0x36,
+ 0x47, 0x06, 0x81, 0x6a,
+ 0xba, 0x3e, 0x25, 0x71,
+ 0x78, 0x50, 0xc2, 0x6c,
+ 0x9c, 0xd0, 0xd8, 0x9d };
+
+ base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()),
+ input.length(), output);
+ for (size_t i = 0; i < base::kSHA1Length; i++)
+ EXPECT_EQ(expected[i], output[i]);
+}
+
+TEST(SHA1Test, Test2Bytes) {
+ // Example A.2 from FIPS 180-2: multi-block message.
+ std::string input =
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+ unsigned char output[base::kSHA1Length];
+
+ unsigned char expected[] = { 0x84, 0x98, 0x3e, 0x44,
+ 0x1c, 0x3b, 0xd2, 0x6e,
+ 0xba, 0xae, 0x4a, 0xa1,
+ 0xf9, 0x51, 0x29, 0xe5,
+ 0xe5, 0x46, 0x70, 0xf1 };
+
+ base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()),
+ input.length(), output);
+ for (size_t i = 0; i < base::kSHA1Length; i++)
+ EXPECT_EQ(expected[i], output[i]);
+}
+
+TEST(SHA1Test, Test3Bytes) {
+ // Example A.3 from FIPS 180-2: long message.
+ std::string input(1000000, 'a');
+ unsigned char output[base::kSHA1Length];
+
+ unsigned char expected[] = { 0x34, 0xaa, 0x97, 0x3c,
+ 0xd4, 0xc4, 0xda, 0xa4,
+ 0xf6, 0x1e, 0xeb, 0x2b,
+ 0xdb, 0xad, 0x27, 0x31,
+ 0x65, 0x34, 0x01, 0x6f };
+
+ base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()),
+ input.length(), output);
+ for (size_t i = 0; i < base::kSHA1Length; i++)
+ EXPECT_EQ(expected[i], output[i]);
+}
diff --git a/src/base/sha1_win.cc b/src/base/sha1_win.cc
new file mode 100644
index 0000000..626f41b
--- /dev/null
+++ b/src/base/sha1_win.cc
@@ -0,0 +1,69 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/sha1.h"
+
+#include <windows.h>
+#include <wincrypt.h>
+
+// This file is not being compiled at the moment (see bug 47218). If we keep
+// sha1 inside base, we cannot depend on src/crypto.
+// #include "crypto/scoped_capi_types.h"
+#include "base/logging.h"
+
+namespace base {
+
+std::string SHA1HashString(const std::string& str) {
+ ScopedHCRYPTPROV provider;
+ if (!CryptAcquireContext(provider.receive(), NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT)) {
+ DLOG(ERROR) << "CryptAcquireContext failed: " << GetLastError();
+ return std::string(kSHA1Length, '\0');
+ }
+
+ {
+ ScopedHCRYPTHASH hash;
+ if (!CryptCreateHash(provider, CALG_SHA1, 0, 0, hash.receive())) {
+ DLOG(ERROR) << "CryptCreateHash failed: " << GetLastError();
+ return std::string(kSHA1Length, '\0');
+ }
+
+ if (!CryptHashData(hash, reinterpret_cast<CONST BYTE*>(str.data()),
+ static_cast<DWORD>(str.length()), 0)) {
+ DLOG(ERROR) << "CryptHashData failed: " << GetLastError();
+ return std::string(kSHA1Length, '\0');
+ }
+
+ DWORD hash_len = 0;
+ DWORD buffer_size = sizeof hash_len;
+ if (!CryptGetHashParam(hash, HP_HASHSIZE,
+ reinterpret_cast<unsigned char*>(&hash_len),
+ &buffer_size, 0)) {
+ DLOG(ERROR) << "CryptGetHashParam(HP_HASHSIZE) failed: "
+ << GetLastError();
+ return std::string(kSHA1Length, '\0');
+ }
+
+ std::string result;
+ if (!CryptGetHashParam(hash, HP_HASHVAL,
+ // We need the + 1 here not because the call will write a trailing \0,
+ // but so that result.length() is correctly set to |hash_len|.
+ reinterpret_cast<BYTE*>(WriteInto(&result, hash_len + 1)), &hash_len,
+ 0))) {
+ DLOG(ERROR) << "CryptGetHashParam(HP_HASHVAL) failed: "
+ << GetLastError();
+ return std::string(kSHA1Length, '\0');
+ }
+
+ if (hash_len != kSHA1Length) {
+ DLOG(ERROR) << "Returned hash value is wrong length: " << hash_len
+ << " should be " << kSHA1Length;
+ return std::string(kSHA1Length, '\0');
+ }
+
+ return result;
+ }
+}
+
+} // namespace base
diff --git a/src/base/shared_memory.h b/src/base/shared_memory.h
new file mode 100644
index 0000000..bf77788
--- /dev/null
+++ b/src/base/shared_memory.h
@@ -0,0 +1,319 @@
+// Copyright (c) 2012 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_SHARED_MEMORY_H_
+#define BASE_SHARED_MEMORY_H_
+
+#include "build/build_config.h"
+
+#include <string>
+
+#if defined(OS_POSIX) && !defined(__LB_SHELL__)
+#include <stdio.h>
+#include <sys/types.h>
+#include <semaphore.h>
+#endif
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/process.h"
+
+#if defined(__LB_SHELL__)
+#include <stdlib.h>
+#elif defined(OS_STARBOARD)
+#include "starboard/memory.h"
+#endif
+
+#if defined(__LB_SHELL__) || defined(OS_STARBOARD)
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#endif
+
+#if defined(OS_POSIX) && !defined(__LB_SHELL__)
+#include "base/file_descriptor_posix.h"
+#endif
+
+class FilePath;
+
+namespace base {
+
+// SharedMemoryHandle is a platform specific type which represents
+// the underlying OS handle to a shared memory segment.
+#if defined(OS_WIN)
+typedef HANDLE SharedMemoryHandle;
+typedef HANDLE SharedMemoryLock;
+#elif defined(OS_POSIX) && !defined(__LB_SHELL__)
+// A SharedMemoryId is sufficient to identify a given shared memory segment on a
+// system, but insufficient to map it.
+typedef FileDescriptor SharedMemoryHandle;
+typedef ino_t SharedMemoryId;
+// On POSIX, the lock is implemented as a lockf() on the mapped file,
+// so no additional member (or definition of SharedMemoryLock) is
+// needed.
+#elif defined(__LB_SHELL__) || defined(OS_STARBOARD)
+
+// For LB_SHELL, and Starboard, we will only ever have one process, and so we
+// use standard heap memory for this.
+class RefCountedMem : public base::RefCountedThreadSafe<RefCountedMem> {
+ public:
+ RefCountedMem(size_t size) {
+ size_ = size;
+#if defined(OS_STARBOARD)
+ memory_ = SbMemoryAllocate(size);
+#else
+ memory_ = malloc(size);
+#endif
+ }
+ ~RefCountedMem() {
+#if defined(OS_STARBOARD)
+ SbMemoryFree(memory_);
+#else
+ free(memory_);
+#endif
+ }
+
+ void* GetMemory() const { return memory_; }
+ size_t GetSize() const { return size_; }
+ Lock& lock() { return lock_; }
+private:
+ size_t size_;
+ void* memory_;
+ Lock lock_;
+};
+typedef scoped_refptr<RefCountedMem> SharedMemoryHandle;
+#endif
+
+// Options for creating a shared memory object.
+struct SharedMemoryCreateOptions {
+ SharedMemoryCreateOptions() : name(NULL), size(0), open_existing(false),
+ executable(false) {}
+
+ // If NULL, the object is anonymous. This pointer is owned by the caller
+ // and must live through the call to Create().
+ const std::string* name;
+
+ // Size of the shared memory object to be created.
+ // When opening an existing object, this has no effect.
+ size_t size;
+
+ // If true, and the shared memory already exists, Create() will open the
+ // existing shared memory and ignore the size parameter. If false,
+ // shared memory must not exist. This flag is meaningless unless name is
+ // non-NULL.
+ bool open_existing;
+
+ // If true, mappings might need to be made executable later.
+ bool executable;
+};
+
+// Platform abstraction for shared memory. Provides a C++ wrapper
+// around the OS primitive for a memory mapped file.
+class BASE_EXPORT SharedMemory {
+ public:
+ SharedMemory();
+
+#if defined(OS_WIN)
+ // Similar to the default constructor, except that this allows for
+ // calling Lock() to acquire the named mutex before either Create or Open
+ // are called on Windows.
+ explicit SharedMemory(const std::wstring& name);
+#endif
+
+ // Create a new SharedMemory object from an existing, open
+ // shared memory file.
+ SharedMemory(SharedMemoryHandle handle, bool read_only);
+
+ // Create a new SharedMemory object from an existing, open
+ // shared memory file that was created by a remote process and not shared
+ // to the current process.
+ SharedMemory(SharedMemoryHandle handle, bool read_only,
+ ProcessHandle process);
+
+ // Closes any open files.
+ ~SharedMemory();
+
+ // Return true iff the given handle is valid (i.e. not the distingished
+ // invalid value; NULL for a HANDLE and -1 for a file descriptor)
+ static bool IsHandleValid(const SharedMemoryHandle& handle);
+
+ // Returns invalid handle (see comment above for exact definition).
+ static SharedMemoryHandle NULLHandle();
+
+ // Closes a shared memory handle.
+ static void CloseHandle(const SharedMemoryHandle& handle);
+
+ // Creates a shared memory object as described by the options struct.
+ // Returns true on success and false on failure.
+ bool Create(const SharedMemoryCreateOptions& options);
+
+ // Creates and maps an anonymous shared memory segment of size size.
+ // Returns true on success and false on failure.
+ bool CreateAndMapAnonymous(size_t size);
+
+ // Creates an anonymous shared memory segment of size size.
+ // Returns true on success and false on failure.
+ bool CreateAnonymous(size_t size) {
+ SharedMemoryCreateOptions options;
+ options.size = size;
+ return Create(options);
+ }
+
+ // Creates or opens a shared memory segment based on a name.
+ // If open_existing is true, and the shared memory already exists,
+ // opens the existing shared memory and ignores the size parameter.
+ // If open_existing is false, shared memory must not exist.
+ // size is the size of the block to be created.
+ // Returns true on success, false on failure.
+ bool CreateNamed(const std::string& name, bool open_existing, size_t size) {
+ SharedMemoryCreateOptions options;
+ options.name = &name;
+ options.open_existing = open_existing;
+ options.size = size;
+ return Create(options);
+ }
+
+ // Deletes resources associated with a shared memory segment based on name.
+ // Not all platforms require this call.
+ bool Delete(const std::string& name);
+
+ // Opens a shared memory segment based on a name.
+ // If read_only is true, opens for read-only access.
+ // Returns true on success, false on failure.
+ bool Open(const std::string& name, bool read_only);
+
+ // Maps the shared memory into the caller's address space.
+ // Returns true on success, false otherwise. The memory address
+ // is accessed via the memory() accessor. The mapped address is guaranteed to
+ // have an alignment of at least MAP_MINIMUM_ALIGNMENT.
+ bool Map(size_t bytes);
+ enum { MAP_MINIMUM_ALIGNMENT = 32 };
+
+ // Unmaps the shared memory from the caller's address space.
+ // Returns true if successful; returns false on error or if the
+ // memory is not mapped.
+ bool Unmap();
+
+ // Get the size of the shared memory backing file.
+ // Note: This size is only available to the creator of the
+ // shared memory, and not to those that opened shared memory
+ // created externally.
+ // Returns 0 if not created or unknown.
+ // Deprecated method, please keep track of the size yourself if you created
+ // it.
+ // http://crbug.com/60821
+ size_t created_size() const { return created_size_; }
+
+ // Gets a pointer to the opened memory space if it has been
+ // Mapped via Map(). Returns NULL if it is not mapped.
+ void *memory() const { return memory_; }
+
+ // Returns the underlying OS handle for this segment.
+ // Use of this handle for anything other than an opaque
+ // identifier is not portable.
+ SharedMemoryHandle handle() const;
+
+#if defined(OS_POSIX) && !defined(OS_NACL) && !defined(__LB_SHELL__)
+ // Returns a unique identifier for this shared memory segment. Inode numbers
+ // are technically only unique to a single filesystem. However, we always
+ // allocate shared memory backing files from the same directory, so will end
+ // up on the same filesystem.
+ SharedMemoryId id() const { return inode_; }
+#endif
+
+ // Closes the open shared memory segment.
+ // It is safe to call Close repeatedly.
+ void Close();
+
+ // Shares the shared memory to another process. Attempts
+ // to create a platform-specific new_handle which can be
+ // used in a remote process to access the shared memory
+ // file. new_handle is an ouput parameter to receive
+ // the handle for use in the remote process.
+ // Returns true on success, false otherwise.
+ bool ShareToProcess(ProcessHandle process,
+ SharedMemoryHandle* new_handle) {
+ return ShareToProcessCommon(process, new_handle, false);
+ }
+
+ // Logically equivalent to:
+ // bool ok = ShareToProcess(process, new_handle);
+ // Close();
+ // return ok;
+ // Note that the memory is unmapped by calling this method, regardless of the
+ // return value.
+ bool GiveToProcess(ProcessHandle process,
+ SharedMemoryHandle* new_handle) {
+ return ShareToProcessCommon(process, new_handle, true);
+ }
+
+ // Locks the shared memory.
+ //
+ // WARNING: on POSIX the memory locking primitive only works across
+ // processes, not across threads. The Lock method is not currently
+ // used in inner loops, so we protect against multiple threads in a
+ // critical section using a class global lock.
+ void Lock();
+
+#if defined(OS_WIN)
+ // A Lock() implementation with a timeout that also allows setting
+ // security attributes on the mutex. sec_attr may be NULL.
+ // Returns true if the Lock() has been acquired, false if the timeout was
+ // reached.
+ bool Lock(uint32 timeout_ms, SECURITY_ATTRIBUTES* sec_attr);
+#endif
+
+ // Releases the shared memory lock.
+ void Unlock();
+
+ private:
+#if defined(OS_POSIX) && !defined(OS_NACL) && !defined(__LB_SHELL__)
+ bool PrepareMapFile(FILE *fp);
+ bool FilePathForMemoryName(const std::string& mem_name, FilePath* path);
+ void LockOrUnlockCommon(int function);
+#endif
+ bool ShareToProcessCommon(ProcessHandle process,
+ SharedMemoryHandle* new_handle,
+ bool close_self);
+
+#if defined(OS_WIN)
+ std::wstring name_;
+ HANDLE mapped_file_;
+#elif defined(OS_POSIX) && !defined(__LB_SHELL__)
+ int mapped_file_;
+ size_t mapped_size_;
+ ino_t inode_;
+#elif defined(__LB_SHELL__)
+ scoped_refptr<RefCountedMem> handle_;
+#endif
+ void* memory_;
+ bool read_only_;
+ size_t created_size_;
+#if !defined(OS_POSIX) && !defined(OS_STARBOARD)
+ SharedMemoryLock lock_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(SharedMemory);
+};
+
+// A helper class that acquires the shared memory lock while
+// the SharedMemoryAutoLock is in scope.
+class SharedMemoryAutoLock {
+ public:
+ explicit SharedMemoryAutoLock(SharedMemory* shared_memory)
+ : shared_memory_(shared_memory) {
+ shared_memory_->Lock();
+ }
+
+ ~SharedMemoryAutoLock() {
+ shared_memory_->Unlock();
+ }
+
+ private:
+ SharedMemory* shared_memory_;
+ DISALLOW_COPY_AND_ASSIGN(SharedMemoryAutoLock);
+};
+
+} // namespace base
+
+#endif // BASE_SHARED_MEMORY_H_
diff --git a/src/base/shared_memory_android.cc b/src/base/shared_memory_android.cc
new file mode 100644
index 0000000..e2c683c
--- /dev/null
+++ b/src/base/shared_memory_android.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/shared_memory.h"
+
+#include <sys/mman.h>
+
+#include "base/logging.h"
+#include "third_party/ashmem/ashmem.h"
+
+namespace base {
+
+// For Android, we use ashmem to implement SharedMemory. ashmem_create_region
+// will automatically pin the region. We never explicitly call pin/unpin. When
+// all the file descriptors from different processes associated with the region
+// are closed, the memory buffer will go away.
+
+bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
+ DCHECK_EQ(-1, mapped_file_ );
+
+ if (options.size > static_cast<size_t>(std::numeric_limits<int>::max()))
+ return false;
+
+ // "name" is just a label in ashmem. It is visible in /proc/pid/maps.
+ mapped_file_ = ashmem_create_region(
+ options.name == NULL ? "" : options.name->c_str(),
+ options.size);
+ if (-1 == mapped_file_) {
+ DLOG(ERROR) << "Shared memory creation failed";
+ return false;
+ }
+
+ int err = ashmem_set_prot_region(mapped_file_,
+ PROT_READ | PROT_WRITE | PROT_EXEC);
+ if (err < 0) {
+ DLOG(ERROR) << "Error " << err << " when setting protection of ashmem";
+ return false;
+ }
+ created_size_ = options.size;
+
+ return true;
+}
+
+bool SharedMemory::Delete(const std::string& name) {
+ // Like on Windows, this is intentionally returning true as ashmem will
+ // automatically releases the resource when all FDs on it are closed.
+ return true;
+}
+
+bool SharedMemory::Open(const std::string& name, bool read_only) {
+ // ashmem doesn't support name mapping
+ NOTIMPLEMENTED();
+ return false;
+}
+
+} // namespace base
diff --git a/src/base/shared_memory_nacl.cc b/src/base/shared_memory_nacl.cc
new file mode 100644
index 0000000..a86578b
--- /dev/null
+++ b/src/base/shared_memory_nacl.cc
@@ -0,0 +1,148 @@
+// Copyright (c) 2012 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/shared_memory.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <limits>
+
+#include "base/logging.h"
+
+namespace base {
+
+SharedMemory::SharedMemory()
+ : mapped_file_(-1),
+ mapped_size_(0),
+ inode_(0),
+ memory_(NULL),
+ read_only_(false),
+ created_size_(0) {
+}
+
+SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only)
+ : mapped_file_(handle.fd),
+ mapped_size_(0),
+ inode_(0),
+ memory_(NULL),
+ read_only_(read_only),
+ created_size_(0) {
+}
+
+SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only,
+ ProcessHandle process)
+ : mapped_file_(handle.fd),
+ mapped_size_(0),
+ inode_(0),
+ memory_(NULL),
+ read_only_(read_only),
+ created_size_(0) {
+ NOTREACHED();
+}
+
+SharedMemory::~SharedMemory() {
+ Close();
+}
+
+// static
+bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
+ return handle.fd >= 0;
+}
+
+// static
+SharedMemoryHandle SharedMemory::NULLHandle() {
+ return SharedMemoryHandle();
+}
+
+// static
+void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
+ DCHECK_GE(handle.fd, 0);
+ if (close(handle.fd) < 0)
+ DPLOG(ERROR) << "close";
+}
+
+bool SharedMemory::CreateAndMapAnonymous(size_t size) {
+ // Untrusted code can't create descriptors or handles.
+ return false;
+}
+
+bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
+ // Untrusted code can't create descriptors or handles.
+ return false;
+}
+
+bool SharedMemory::Delete(const std::string& name) {
+ return false;
+}
+
+bool SharedMemory::Open(const std::string& name, bool read_only) {
+ return false;
+}
+
+bool SharedMemory::Map(size_t bytes) {
+ if (mapped_file_ == -1)
+ return false;
+
+ if (bytes > static_cast<size_t>(std::numeric_limits<int>::max()))
+ return false;
+
+ memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE),
+ MAP_SHARED, mapped_file_, 0);
+
+ bool mmap_succeeded = memory_ != MAP_FAILED && memory_ != NULL;
+ if (mmap_succeeded) {
+ mapped_size_ = bytes;
+ DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(memory_) &
+ (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1));
+ } else {
+ memory_ = NULL;
+ }
+
+ return mmap_succeeded;
+}
+
+bool SharedMemory::Unmap() {
+ if (memory_ == NULL)
+ return false;
+
+ if (munmap(memory_, mapped_size_) < 0)
+ DPLOG(ERROR) << "munmap";
+ memory_ = NULL;
+ mapped_size_ = 0;
+ return true;
+}
+
+SharedMemoryHandle SharedMemory::handle() const {
+ return FileDescriptor(mapped_file_, false);
+}
+
+void SharedMemory::Close() {
+ Unmap();
+
+ if (mapped_file_ > 0) {
+ if (close(mapped_file_) < 0)
+ DPLOG(ERROR) << "close";
+ mapped_file_ = -1;
+ }
+}
+
+void SharedMemory::Lock() {
+ NOTIMPLEMENTED();
+}
+
+void SharedMemory::Unlock() {
+ NOTIMPLEMENTED();
+}
+
+bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
+ SharedMemoryHandle *new_handle,
+ bool close_self) {
+ return false;
+}
+
+} // namespace base
diff --git a/src/base/shared_memory_posix.cc b/src/base/shared_memory_posix.cc
new file mode 100644
index 0000000..7862f42
--- /dev/null
+++ b/src/base/shared_memory_posix.cc
@@ -0,0 +1,389 @@
+// Copyright (c) 2012 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/shared_memory.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "base/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/platform_thread.h"
+#include "base/safe_strerror_posix.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/utf_string_conversions.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/foundation_util.h"
+#endif // OS_MACOSX
+
+#if defined(OS_ANDROID)
+#include "base/os_compat_android.h"
+#include "third_party/ashmem/ashmem.h"
+#endif
+
+namespace base {
+
+namespace {
+
+// Paranoia. Semaphores and shared memory segments should live in different
+// namespaces, but who knows what's out there.
+const char kSemaphoreSuffix[] = "-sem";
+
+LazyInstance<Lock>::Leaky g_thread_lock_ = LAZY_INSTANCE_INITIALIZER;
+
+}
+
+SharedMemory::SharedMemory()
+ : mapped_file_(-1),
+ mapped_size_(0),
+ inode_(0),
+ memory_(NULL),
+ read_only_(false),
+ created_size_(0) {
+}
+
+SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only)
+ : mapped_file_(handle.fd),
+ mapped_size_(0),
+ inode_(0),
+ memory_(NULL),
+ read_only_(read_only),
+ created_size_(0) {
+ struct stat st;
+ if (fstat(handle.fd, &st) == 0) {
+ // If fstat fails, then the file descriptor is invalid and we'll learn this
+ // fact when Map() fails.
+ inode_ = st.st_ino;
+ }
+}
+
+SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only,
+ ProcessHandle process)
+ : mapped_file_(handle.fd),
+ mapped_size_(0),
+ inode_(0),
+ memory_(NULL),
+ read_only_(read_only),
+ created_size_(0) {
+ // We don't handle this case yet (note the ignored parameter); let's die if
+ // someone comes calling.
+ NOTREACHED();
+}
+
+SharedMemory::~SharedMemory() {
+ Close();
+}
+
+// static
+bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
+ return handle.fd >= 0;
+}
+
+// static
+SharedMemoryHandle SharedMemory::NULLHandle() {
+ return SharedMemoryHandle();
+}
+
+// static
+void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
+ DCHECK_GE(handle.fd, 0);
+ if (HANDLE_EINTR(close(handle.fd)) < 0)
+ DPLOG(ERROR) << "close";
+}
+
+bool SharedMemory::CreateAndMapAnonymous(size_t size) {
+ return CreateAnonymous(size) && Map(size);
+}
+
+#if !defined(OS_ANDROID)
+// Chromium mostly only uses the unique/private shmem as specified by
+// "name == L"". The exception is in the StatsTable.
+// TODO(jrg): there is no way to "clean up" all unused named shmem if
+// we restart from a crash. (That isn't a new problem, but it is a problem.)
+// In case we want to delete it later, it may be useful to save the value
+// of mem_filename after FilePathForMemoryName().
+bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
+ DCHECK_EQ(-1, mapped_file_);
+ if (options.size == 0) return false;
+
+ if (options.size > static_cast<size_t>(std::numeric_limits<int>::max()))
+ return false;
+
+ // This function theoretically can block on the disk, but realistically
+ // the temporary files we create will just go into the buffer cache
+ // and be deleted before they ever make it out to disk.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ FILE *fp;
+ bool fix_size = true;
+
+ FilePath path;
+ if (options.name == NULL || options.name->empty()) {
+ // It doesn't make sense to have a open-existing private piece of shmem
+ DCHECK(!options.open_existing);
+ // Q: Why not use the shm_open() etc. APIs?
+ // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU
+ fp = file_util::CreateAndOpenTemporaryShmemFile(&path, options.executable);
+
+ // Deleting the file prevents anyone else from mapping it in
+ // (making it private), and prevents the need for cleanup (once
+ // the last fd is closed, it is truly freed).
+ if (fp)
+ file_util::Delete(path, false);
+
+ } else {
+ if (!FilePathForMemoryName(*options.name, &path))
+ return false;
+
+ fp = file_util::OpenFile(path, "w+x");
+ if (fp == NULL && options.open_existing) {
+ // "w+" will truncate if it already exists.
+ fp = file_util::OpenFile(path, "a+");
+ fix_size = false;
+ }
+ }
+ if (fp && fix_size) {
+ // Get current size.
+ struct stat stat;
+ if (fstat(fileno(fp), &stat) != 0) {
+ file_util::CloseFile(fp);
+ return false;
+ }
+ const size_t current_size = stat.st_size;
+ if (current_size != options.size) {
+ if (HANDLE_EINTR(ftruncate(fileno(fp), options.size)) != 0) {
+ file_util::CloseFile(fp);
+ return false;
+ }
+ if (fseeko(fp, options.size, SEEK_SET) != 0) {
+ file_util::CloseFile(fp);
+ return false;
+ }
+ }
+ created_size_ = options.size;
+ }
+ if (fp == NULL) {
+#if !defined(OS_MACOSX)
+ PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
+ FilePath dir = path.DirName();
+ if (access(dir.value().c_str(), W_OK | X_OK) < 0) {
+ PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value();
+ if (dir.value() == "/dev/shm") {
+ LOG(FATAL) << "This is frequently caused by incorrect permissions on "
+ << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix.";
+ }
+ }
+#else
+ PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
+#endif
+ return false;
+ }
+
+ return PrepareMapFile(fp);
+}
+
+// Our current implementation of shmem is with mmap()ing of files.
+// These files need to be deleted explicitly.
+// In practice this call is only needed for unit tests.
+bool SharedMemory::Delete(const std::string& name) {
+ FilePath path;
+ if (!FilePathForMemoryName(name, &path))
+ return false;
+
+ if (file_util::PathExists(path)) {
+ return file_util::Delete(path, false);
+ }
+
+ // Doesn't exist, so success.
+ return true;
+}
+
+bool SharedMemory::Open(const std::string& name, bool read_only) {
+ FilePath path;
+ if (!FilePathForMemoryName(name, &path))
+ return false;
+
+ read_only_ = read_only;
+
+ const char *mode = read_only ? "r" : "r+";
+ FILE *fp = file_util::OpenFile(path, mode);
+ return PrepareMapFile(fp);
+}
+
+#endif // !defined(OS_ANDROID)
+
+bool SharedMemory::Map(size_t bytes) {
+ if (mapped_file_ == -1)
+ return false;
+
+ if (bytes > static_cast<size_t>(std::numeric_limits<int>::max()))
+ return false;
+
+#if defined(OS_ANDROID)
+ if (bytes == 0) {
+ int ashmem_bytes = ashmem_get_size_region(mapped_file_);
+ if (ashmem_bytes < 0)
+ return false;
+
+ DCHECK_GE(static_cast<uint32>(ashmem_bytes), bytes);
+ // The caller wants to determine the map region size from ashmem.
+ bytes = ashmem_bytes;
+ // TODO(port): we set the created size here so that it is available in
+ // transport_dib_android.cc. We should use ashmem_get_size_region()
+ // in transport_dib_android.cc.
+ created_size_ = bytes;
+ }
+#endif
+
+ memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE),
+ MAP_SHARED, mapped_file_, 0);
+
+ bool mmap_succeeded = memory_ != (void*)-1 && memory_ != NULL;
+ if (mmap_succeeded) {
+ mapped_size_ = bytes;
+ DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(memory_) &
+ (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1));
+ } else {
+ memory_ = NULL;
+ }
+
+ return mmap_succeeded;
+}
+
+bool SharedMemory::Unmap() {
+ if (memory_ == NULL)
+ return false;
+
+ munmap(memory_, mapped_size_);
+ memory_ = NULL;
+ mapped_size_ = 0;
+ return true;
+}
+
+SharedMemoryHandle SharedMemory::handle() const {
+ return FileDescriptor(mapped_file_, false);
+}
+
+void SharedMemory::Close() {
+ Unmap();
+
+ if (mapped_file_ > 0) {
+ if (HANDLE_EINTR(close(mapped_file_)) < 0)
+ PLOG(ERROR) << "close";
+ mapped_file_ = -1;
+ }
+}
+
+void SharedMemory::Lock() {
+ g_thread_lock_.Get().Acquire();
+ LockOrUnlockCommon(F_LOCK);
+}
+
+void SharedMemory::Unlock() {
+ LockOrUnlockCommon(F_ULOCK);
+ g_thread_lock_.Get().Release();
+}
+
+#if !defined(OS_ANDROID)
+bool SharedMemory::PrepareMapFile(FILE *fp) {
+ DCHECK_EQ(-1, mapped_file_);
+ if (fp == NULL) return false;
+
+ // This function theoretically can block on the disk, but realistically
+ // the temporary files we create will just go into the buffer cache
+ // and be deleted before they ever make it out to disk.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ file_util::ScopedFILE file_closer(fp);
+
+ mapped_file_ = dup(fileno(fp));
+ if (mapped_file_ == -1) {
+ if (errno == EMFILE) {
+ LOG(WARNING) << "Shared memory creation failed; out of file descriptors";
+ return false;
+ } else {
+ NOTREACHED() << "Call to dup failed, errno=" << errno;
+ }
+ }
+
+ struct stat st;
+ if (fstat(mapped_file_, &st))
+ NOTREACHED();
+ inode_ = st.st_ino;
+
+ return true;
+}
+#endif
+
+// For the given shmem named |mem_name|, return a filename to mmap()
+// (and possibly create). Modifies |filename|. Return false on
+// error, or true of we are happy.
+bool SharedMemory::FilePathForMemoryName(const std::string& mem_name,
+ FilePath* path) {
+ // mem_name will be used for a filename; make sure it doesn't
+ // contain anything which will confuse us.
+ DCHECK_EQ(std::string::npos, mem_name.find('/'));
+ DCHECK_EQ(std::string::npos, mem_name.find('\0'));
+
+ FilePath temp_dir;
+ if (!file_util::GetShmemTempDir(&temp_dir, false))
+ return false;
+
+#if !defined(OS_MACOSX)
+#if defined(GOOGLE_CHROME_BUILD)
+ std::string name_base = std::string("com.google.Chrome");
+#else
+ std::string name_base = std::string("org.chromium.Chromium");
+#endif
+#else // OS_MACOSX
+ std::string name_base = std::string(base::mac::BaseBundleID());
+#endif // OS_MACOSX
+ *path = temp_dir.AppendASCII(name_base + ".shmem." + mem_name);
+ return true;
+}
+
+void SharedMemory::LockOrUnlockCommon(int function) {
+ DCHECK_GE(mapped_file_, 0);
+ while (lockf(mapped_file_, function, 0) < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else if (errno == ENOLCK) {
+ // temporary kernel resource exaustion
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(500));
+ continue;
+ } else {
+ NOTREACHED() << "lockf() failed."
+ << " function:" << function
+ << " fd:" << mapped_file_
+ << " errno:" << errno
+ << " msg:" << safe_strerror(errno);
+ }
+ }
+}
+
+bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
+ SharedMemoryHandle *new_handle,
+ bool close_self) {
+ const int new_fd = dup(mapped_file_);
+ if (new_fd < 0) {
+ DPLOG(ERROR) << "dup() failed.";
+ return false;
+ }
+
+ new_handle->fd = new_fd;
+ new_handle->auto_close = true;
+
+ if (close_self)
+ Close();
+
+ return true;
+}
+
+} // namespace base
diff --git a/src/base/shared_memory_shell.cc b/src/base/shared_memory_shell.cc
new file mode 100644
index 0000000..c5d5cae
--- /dev/null
+++ b/src/base/shared_memory_shell.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2012 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/shared_memory.h"
+
+#include "base/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/platform_thread.h"
+#include "base/safe_strerror_posix.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/utf_string_conversions.h"
+
+namespace base {
+
+SharedMemory::SharedMemory()
+ : memory_(NULL),
+ read_only_(false),
+ created_size_(0) {
+}
+
+SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only)
+ : handle_(handle),
+ memory_(NULL),
+ read_only_(read_only),
+ created_size_(0) {
+}
+
+SharedMemory::~SharedMemory() {
+ Close();
+}
+
+// static
+bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
+ return handle.get() != NULL;
+}
+
+// static
+SharedMemoryHandle SharedMemory::NULLHandle() {
+ return SharedMemoryHandle();
+}
+
+// static
+void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
+ // Nothing to worry about here since shared memory are refcounted
+}
+
+bool SharedMemory::CreateAndMapAnonymous(size_t size) {
+ return CreateAnonymous(size) && Map(size);
+}
+
+bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
+ handle_ = new RefCountedMem(options.size);
+
+ return true;
+}
+
+bool SharedMemory::Delete(const std::string& name) {
+ NOTREACHED();
+ return false;
+}
+
+bool SharedMemory::Open(const std::string& name, bool read_only) {
+ NOTREACHED();
+ return false;
+}
+
+bool SharedMemory::Map(size_t bytes) {
+ DCHECK(bytes <= handle_->GetSize());
+ memory_ = handle_->GetMemory();
+ return true;
+}
+
+bool SharedMemory::Unmap() {
+ memory_ = NULL;
+ return true;
+}
+
+SharedMemoryHandle SharedMemory::handle() const {
+ return handle_.get();
+}
+
+void SharedMemory::Close() {
+ handle_ = NULL;
+}
+
+void SharedMemory::Lock() {
+ handle_->lock().Acquire();
+}
+
+void SharedMemory::Unlock() {
+ handle_->lock().Release();
+}
+
+bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
+ SharedMemoryHandle *new_handle,
+ bool close_self) {
+ // close_self doesn't matter because the shared memory instances are
+ // ref counted
+ *new_handle = handle_;
+
+ return true;
+}
+
+} // namespace base
diff --git a/src/base/shared_memory_unittest.cc b/src/base/shared_memory_unittest.cc
new file mode 100644
index 0000000..e7f1d86
--- /dev/null
+++ b/src/base/shared_memory_unittest.cc
@@ -0,0 +1,450 @@
+// Copyright (c) 2012 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/basictypes.h"
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_nsautorelease_pool.h"
+#endif
+#include "base/memory/scoped_ptr.h"
+#include "base/shared_memory.h"
+#include "base/test/multiprocess_test.h"
+#include "base/threading/platform_thread.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_nsautorelease_pool.h"
+#endif
+
+#if defined(OS_POSIX)
+#include <sys/mman.h>
+#endif
+
+static const int kNumThreads = 5;
+static const int kNumTasks = 5;
+
+namespace base {
+
+namespace {
+
+// Each thread will open the shared memory. Each thread will take a different 4
+// byte int pointer, and keep changing it, with some small pauses in between.
+// Verify that each thread's value in the shared memory is always correct.
+class MultipleThreadMain : public PlatformThread::Delegate {
+ public:
+ explicit MultipleThreadMain(int16 id) : id_(id) {}
+ virtual ~MultipleThreadMain() {}
+
+ static void CleanUp() {
+ SharedMemory memory;
+ memory.Delete(s_test_name_);
+ }
+
+ // PlatformThread::Delegate interface.
+ virtual void ThreadMain() OVERRIDE {
+#if defined(OS_MACOSX)
+ mac::ScopedNSAutoreleasePool pool;
+#endif
+ const uint32 kDataSize = 1024;
+ SharedMemory memory;
+ bool rv = memory.CreateNamed(s_test_name_, true, kDataSize);
+ EXPECT_TRUE(rv);
+ rv = memory.Map(kDataSize);
+ EXPECT_TRUE(rv);
+ int *ptr = static_cast<int*>(memory.memory()) + id_;
+ EXPECT_EQ(0, *ptr);
+
+ for (int idx = 0; idx < 100; idx++) {
+ *ptr = idx;
+ PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
+ EXPECT_EQ(*ptr, idx);
+ }
+ // Reset back to 0 for the next test that uses the same name.
+ *ptr = 0;
+
+ memory.Close();
+ }
+
+ private:
+ int16 id_;
+
+ static const char* const s_test_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(MultipleThreadMain);
+};
+
+const char* const MultipleThreadMain::s_test_name_ =
+ "SharedMemoryOpenThreadTest";
+
+// TODO(port):
+// This test requires the ability to pass file descriptors between processes.
+// We haven't done that yet in Chrome for POSIX.
+#if defined(OS_WIN)
+// Each thread will open the shared memory. Each thread will take the memory,
+// and keep changing it while trying to lock it, with some small pauses in
+// between. Verify that each thread's value in the shared memory is always
+// correct.
+class MultipleLockThread : public PlatformThread::Delegate {
+ public:
+ explicit MultipleLockThread(int id) : id_(id) {}
+ virtual ~MultipleLockThread() {}
+
+ // PlatformThread::Delegate interface.
+ virtual void ThreadMain() OVERRIDE {
+ const uint32 kDataSize = sizeof(int);
+ SharedMemoryHandle handle = NULL;
+ {
+ SharedMemory memory1;
+ EXPECT_TRUE(memory1.CreateNamed("SharedMemoryMultipleLockThreadTest",
+ true, kDataSize));
+ EXPECT_TRUE(memory1.ShareToProcess(GetCurrentProcess(), &handle));
+ // TODO(paulg): Implement this once we have a posix version of
+ // SharedMemory::ShareToProcess.
+ EXPECT_TRUE(true);
+ }
+
+ SharedMemory memory2(handle, false);
+ EXPECT_TRUE(memory2.Map(kDataSize));
+ volatile int* const ptr = static_cast<int*>(memory2.memory());
+
+ for (int idx = 0; idx < 20; idx++) {
+ memory2.Lock();
+ int i = (id_ << 16) + idx;
+ *ptr = i;
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(1));
+ EXPECT_EQ(*ptr, i);
+ memory2.Unlock();
+ }
+
+ memory2.Close();
+ }
+
+ private:
+ int id_;
+
+ DISALLOW_COPY_AND_ASSIGN(MultipleLockThread);
+};
+#endif
+
+} // namespace
+
+// Android doesn't support SharedMemory::Open/Delete/
+// CreateNamed(openExisting=true)
+#if !defined(OS_ANDROID)
+TEST(SharedMemoryTest, OpenClose) {
+ const uint32 kDataSize = 1024;
+ std::string test_name = "SharedMemoryOpenCloseTest";
+
+ // Open two handles to a memory segment, confirm that they are mapped
+ // separately yet point to the same space.
+ SharedMemory memory1;
+ bool rv = memory1.Delete(test_name);
+ EXPECT_TRUE(rv);
+ rv = memory1.Delete(test_name);
+ EXPECT_TRUE(rv);
+ rv = memory1.Open(test_name, false);
+ EXPECT_FALSE(rv);
+ rv = memory1.CreateNamed(test_name, false, kDataSize);
+ EXPECT_TRUE(rv);
+ rv = memory1.Map(kDataSize);
+ EXPECT_TRUE(rv);
+ SharedMemory memory2;
+ rv = memory2.Open(test_name, false);
+ EXPECT_TRUE(rv);
+ rv = memory2.Map(kDataSize);
+ EXPECT_TRUE(rv);
+ EXPECT_NE(memory1.memory(), memory2.memory()); // Compare the pointers.
+
+ // Make sure we don't segfault. (it actually happened!)
+ ASSERT_NE(memory1.memory(), static_cast<void*>(NULL));
+ ASSERT_NE(memory2.memory(), static_cast<void*>(NULL));
+
+ // Write data to the first memory segment, verify contents of second.
+ memset(memory1.memory(), '1', kDataSize);
+ EXPECT_EQ(memcmp(memory1.memory(), memory2.memory(), kDataSize), 0);
+
+ // Close the first memory segment, and verify the second has the right data.
+ memory1.Close();
+ char *start_ptr = static_cast<char *>(memory2.memory());
+ char *end_ptr = start_ptr + kDataSize;
+ for (char* ptr = start_ptr; ptr < end_ptr; ptr++)
+ EXPECT_EQ(*ptr, '1');
+
+ // Close the second memory segment.
+ memory2.Close();
+
+ rv = memory1.Delete(test_name);
+ EXPECT_TRUE(rv);
+ rv = memory2.Delete(test_name);
+ EXPECT_TRUE(rv);
+}
+
+TEST(SharedMemoryTest, OpenExclusive) {
+ const uint32 kDataSize = 1024;
+ const uint32 kDataSize2 = 2048;
+ std::ostringstream test_name_stream;
+ test_name_stream << "SharedMemoryOpenExclusiveTest."
+ << Time::Now().ToDoubleT();
+ std::string test_name = test_name_stream.str();
+
+ // Open two handles to a memory segment and check that open_existing works
+ // as expected.
+ SharedMemory memory1;
+ bool rv = memory1.CreateNamed(test_name, false, kDataSize);
+ EXPECT_TRUE(rv);
+
+ // Memory1 knows it's size because it created it.
+ EXPECT_EQ(memory1.created_size(), kDataSize);
+
+ rv = memory1.Map(kDataSize);
+ EXPECT_TRUE(rv);
+
+ memset(memory1.memory(), 'G', kDataSize);
+
+ SharedMemory memory2;
+ // Should not be able to create if openExisting is false.
+ rv = memory2.CreateNamed(test_name, false, kDataSize2);
+ EXPECT_FALSE(rv);
+
+ // Should be able to create with openExisting true.
+ rv = memory2.CreateNamed(test_name, true, kDataSize2);
+ EXPECT_TRUE(rv);
+
+ // Memory2 shouldn't know the size because we didn't create it.
+ EXPECT_EQ(memory2.created_size(), 0U);
+
+ // We should be able to map the original size.
+ rv = memory2.Map(kDataSize);
+ EXPECT_TRUE(rv);
+
+ // Verify that opening memory2 didn't truncate or delete memory 1.
+ char *start_ptr = static_cast<char *>(memory2.memory());
+ char *end_ptr = start_ptr + kDataSize;
+ for (char* ptr = start_ptr; ptr < end_ptr; ptr++) {
+ EXPECT_EQ(*ptr, 'G');
+ }
+
+ memory1.Close();
+ memory2.Close();
+
+ rv = memory1.Delete(test_name);
+ EXPECT_TRUE(rv);
+}
+#endif
+
+// Create a set of N threads to each open a shared memory segment and write to
+// it. Verify that they are always reading/writing consistent data.
+TEST(SharedMemoryTest, MultipleThreads) {
+ MultipleThreadMain::CleanUp();
+ // On POSIX we have a problem when 2 threads try to create the shmem
+ // (a file) at exactly the same time, since create both creates the
+ // file and zerofills it. We solve the problem for this unit test
+ // (make it not flaky) by starting with 1 thread, then
+ // intentionally don't clean up its shmem before running with
+ // kNumThreads.
+
+ int threadcounts[] = { 1, kNumThreads };
+ for (size_t i = 0; i < arraysize(threadcounts); i++) {
+ int numthreads = threadcounts[i];
+ scoped_array<PlatformThreadHandle> thread_handles;
+ scoped_array<MultipleThreadMain*> thread_delegates;
+
+ thread_handles.reset(new PlatformThreadHandle[numthreads]);
+ thread_delegates.reset(new MultipleThreadMain*[numthreads]);
+
+ // Spawn the threads.
+ for (int16 index = 0; index < numthreads; index++) {
+ PlatformThreadHandle pth;
+ thread_delegates[index] = new MultipleThreadMain(index);
+ EXPECT_TRUE(PlatformThread::Create(0, thread_delegates[index], &pth));
+ thread_handles[index] = pth;
+ }
+
+ // Wait for the threads to finish.
+ for (int index = 0; index < numthreads; index++) {
+ PlatformThread::Join(thread_handles[index]);
+ delete thread_delegates[index];
+ }
+ }
+ MultipleThreadMain::CleanUp();
+}
+
+// TODO(port): this test requires the MultipleLockThread class
+// (defined above), which requires the ability to pass file
+// descriptors between processes. We haven't done that yet in Chrome
+// for POSIX.
+#if defined(OS_WIN)
+// Create a set of threads to each open a shared memory segment and write to it
+// with the lock held. Verify that they are always reading/writing consistent
+// data.
+TEST(SharedMemoryTest, Lock) {
+ PlatformThreadHandle thread_handles[kNumThreads];
+ MultipleLockThread* thread_delegates[kNumThreads];
+
+ // Spawn the threads.
+ for (int index = 0; index < kNumThreads; ++index) {
+ PlatformThreadHandle pth;
+ thread_delegates[index] = new MultipleLockThread(index);
+ EXPECT_TRUE(PlatformThread::Create(0, thread_delegates[index], &pth));
+ thread_handles[index] = pth;
+ }
+
+ // Wait for the threads to finish.
+ for (int index = 0; index < kNumThreads; ++index) {
+ PlatformThread::Join(thread_handles[index]);
+ delete thread_delegates[index];
+ }
+}
+#endif
+
+// Allocate private (unique) shared memory with an empty string for a
+// name. Make sure several of them don't point to the same thing as
+// we might expect if the names are equal.
+TEST(SharedMemoryTest, AnonymousPrivate) {
+ int i, j;
+ int count = 4;
+ bool rv;
+ const uint32 kDataSize = 8192;
+
+ scoped_array<SharedMemory> memories(new SharedMemory[count]);
+ scoped_array<int*> pointers(new int*[count]);
+ ASSERT_TRUE(memories.get());
+ ASSERT_TRUE(pointers.get());
+
+ for (i = 0; i < count; i++) {
+ rv = memories[i].CreateAndMapAnonymous(kDataSize);
+ EXPECT_TRUE(rv);
+ int *ptr = static_cast<int*>(memories[i].memory());
+ EXPECT_TRUE(ptr);
+ pointers[i] = ptr;
+ }
+
+ for (i = 0; i < count; i++) {
+ // zero out the first int in each except for i; for that one, make it 100.
+ for (j = 0; j < count; j++) {
+ if (i == j)
+ pointers[j][0] = 100;
+ else
+ pointers[j][0] = 0;
+ }
+ // make sure there is no bleeding of the 100 into the other pointers
+ for (j = 0; j < count; j++) {
+ if (i == j)
+ EXPECT_EQ(100, pointers[j][0]);
+ else
+ EXPECT_EQ(0, pointers[j][0]);
+ }
+ }
+
+ for (int i = 0; i < count; i++) {
+ memories[i].Close();
+ }
+}
+
+#if defined(OS_POSIX) && !defined(__LB_XB1__) && !defined(__LB_XB360__)
+// Create a shared memory object, mmap it, and mprotect it to PROT_EXEC.
+TEST(SharedMemoryTest, AnonymousExecutable) {
+ const uint32 kTestSize = 1 << 16;
+
+ SharedMemory shared_memory;
+ SharedMemoryCreateOptions options;
+ options.size = kTestSize;
+ options.executable = true;
+
+ EXPECT_TRUE(shared_memory.Create(options));
+ EXPECT_TRUE(shared_memory.Map(shared_memory.created_size()));
+
+ EXPECT_EQ(0, mprotect(shared_memory.memory(), shared_memory.created_size(),
+ PROT_READ | PROT_EXEC));
+}
+#endif
+
+// Map() will return addresses which are aligned to the platform page size, this
+// varies from platform to platform though. Since we'd like to advertise a
+// minimum alignment that callers can count on, test for it here.
+TEST(SharedMemoryTest, MapMinimumAlignment) {
+ static const int kDataSize = 8192;
+
+ SharedMemory shared_memory;
+ ASSERT_TRUE(shared_memory.CreateAndMapAnonymous(kDataSize));
+ EXPECT_EQ(0U, reinterpret_cast<uintptr_t>(
+ shared_memory.memory()) & (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1));
+ shared_memory.Close();
+}
+
+#if !defined(OS_IOS) // iOS does not allow multiple processes.
+
+// On POSIX it is especially important we test shmem across processes,
+// not just across threads. But the test is enabled on all platforms.
+class SharedMemoryProcessTest : public MultiProcessTest {
+ public:
+
+ static void CleanUp() {
+ SharedMemory memory;
+ memory.Delete(s_test_name_);
+ }
+
+ static int TaskTestMain() {
+ int errors = 0;
+#if defined(OS_MACOSX)
+ mac::ScopedNSAutoreleasePool pool;
+#endif
+ const uint32 kDataSize = 1024;
+ SharedMemory memory;
+ bool rv = memory.CreateNamed(s_test_name_, true, kDataSize);
+ EXPECT_TRUE(rv);
+ if (rv != true)
+ errors++;
+ rv = memory.Map(kDataSize);
+ EXPECT_TRUE(rv);
+ if (rv != true)
+ errors++;
+ int *ptr = static_cast<int*>(memory.memory());
+
+ for (int idx = 0; idx < 20; idx++) {
+ memory.Lock();
+ int i = (1 << 16) + idx;
+ *ptr = i;
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(10));
+ if (*ptr != i)
+ errors++;
+ memory.Unlock();
+ }
+
+ memory.Close();
+ return errors;
+ }
+
+ private:
+ static const char* const s_test_name_;
+};
+
+const char* const SharedMemoryProcessTest::s_test_name_ = "MPMem";
+
+TEST_F(SharedMemoryProcessTest, Tasks) {
+ SharedMemoryProcessTest::CleanUp();
+
+ ProcessHandle handles[kNumTasks];
+ for (int index = 0; index < kNumTasks; ++index) {
+ handles[index] = SpawnChild("SharedMemoryTestMain", false);
+ ASSERT_TRUE(handles[index]);
+ }
+
+ int exit_code = 0;
+ for (int index = 0; index < kNumTasks; ++index) {
+ EXPECT_TRUE(WaitForExitCode(handles[index], &exit_code));
+ EXPECT_EQ(0, exit_code);
+ }
+
+ SharedMemoryProcessTest::CleanUp();
+}
+
+MULTIPROCESS_TEST_MAIN(SharedMemoryTestMain) {
+ return SharedMemoryProcessTest::TaskTestMain();
+}
+
+#endif // !OS_IOS
+
+} // namespace base
diff --git a/src/base/shared_memory_win.cc b/src/base/shared_memory_win.cc
new file mode 100644
index 0000000..877ccd7
--- /dev/null
+++ b/src/base/shared_memory_win.cc
@@ -0,0 +1,234 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/shared_memory.h"
+
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+
+namespace base {
+
+SharedMemory::SharedMemory()
+ : mapped_file_(NULL),
+ memory_(NULL),
+ read_only_(false),
+ created_size_(0),
+ lock_(NULL) {
+}
+
+SharedMemory::SharedMemory(const std::wstring& name)
+ : mapped_file_(NULL),
+ memory_(NULL),
+ read_only_(false),
+ created_size_(0),
+ lock_(NULL),
+ name_(name) {
+}
+
+SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only)
+ : mapped_file_(handle),
+ memory_(NULL),
+ read_only_(read_only),
+ created_size_(0),
+ lock_(NULL) {
+}
+
+SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only,
+ ProcessHandle process)
+ : mapped_file_(NULL),
+ memory_(NULL),
+ read_only_(read_only),
+ created_size_(0),
+ lock_(NULL) {
+ ::DuplicateHandle(process, handle,
+ GetCurrentProcess(), &mapped_file_,
+ STANDARD_RIGHTS_REQUIRED |
+ (read_only_ ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS),
+ FALSE, 0);
+}
+
+SharedMemory::~SharedMemory() {
+ Close();
+ if (lock_ != NULL)
+ CloseHandle(lock_);
+}
+
+// static
+bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
+ return handle != NULL;
+}
+
+// static
+SharedMemoryHandle SharedMemory::NULLHandle() {
+ return NULL;
+}
+
+// static
+void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
+ DCHECK(handle != NULL);
+ ::CloseHandle(handle);
+}
+
+bool SharedMemory::CreateAndMapAnonymous(size_t size) {
+ return CreateAnonymous(size) && Map(size);
+}
+
+bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
+ DCHECK(!options.executable);
+ DCHECK(!mapped_file_);
+ if (options.size == 0)
+ return false;
+
+ if (options.size > static_cast<size_t>(std::numeric_limits<int>::max()))
+ return false;
+
+ // NaCl's memory allocator requires 0mod64K alignment and size for
+ // shared memory objects. To allow passing shared memory to NaCl,
+ // therefore we round the size actually created to the nearest 64K unit.
+ // To avoid client impact, we continue to retain the size as the
+ // actual requested size.
+ uint32 rounded_size = (options.size + 0xffff) & ~0xffff;
+ if (rounded_size < options.size)
+ return false;
+ name_ = ASCIIToWide(options.name == NULL ? "" : *options.name);
+ mapped_file_ = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
+ PAGE_READWRITE, 0, static_cast<DWORD>(rounded_size),
+ name_.empty() ? NULL : name_.c_str());
+ if (!mapped_file_)
+ return false;
+
+ created_size_ = options.size;
+
+ // Check if the shared memory pre-exists.
+ if (GetLastError() == ERROR_ALREADY_EXISTS) {
+ // If the file already existed, set created_size_ to 0 to show that
+ // we don't know the size.
+ created_size_ = 0;
+ if (!options.open_existing) {
+ Close();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool SharedMemory::Delete(const std::string& name) {
+ // intentionally empty -- there is nothing for us to do on Windows.
+ return true;
+}
+
+bool SharedMemory::Open(const std::string& name, bool read_only) {
+ DCHECK(!mapped_file_);
+
+ name_ = ASCIIToWide(name);
+ read_only_ = read_only;
+ mapped_file_ = OpenFileMapping(
+ read_only_ ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, false,
+ name_.empty() ? NULL : name_.c_str());
+ if (mapped_file_ != NULL) {
+ // Note: size_ is not set in this case.
+ return true;
+ }
+ return false;
+}
+
+bool SharedMemory::Map(size_t bytes) {
+ if (mapped_file_ == NULL)
+ return false;
+
+ if (bytes > static_cast<size_t>(std::numeric_limits<int>::max()))
+ return false;
+
+ memory_ = MapViewOfFile(mapped_file_,
+ read_only_ ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, 0, 0, bytes);
+ if (memory_ != NULL) {
+ DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(memory_) &
+ (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1));
+ return true;
+ }
+ return false;
+}
+
+bool SharedMemory::Unmap() {
+ if (memory_ == NULL)
+ return false;
+
+ UnmapViewOfFile(memory_);
+ memory_ = NULL;
+ return true;
+}
+
+bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
+ SharedMemoryHandle *new_handle,
+ bool close_self) {
+ *new_handle = 0;
+ DWORD access = STANDARD_RIGHTS_REQUIRED | FILE_MAP_READ;
+ DWORD options = 0;
+ HANDLE mapped_file = mapped_file_;
+ HANDLE result;
+ if (!read_only_)
+ access |= FILE_MAP_WRITE;
+ if (close_self) {
+ // DUPLICATE_CLOSE_SOURCE causes DuplicateHandle to close mapped_file.
+ options = DUPLICATE_CLOSE_SOURCE;
+ mapped_file_ = NULL;
+ Unmap();
+ }
+
+ if (process == GetCurrentProcess() && close_self) {
+ *new_handle = mapped_file;
+ return true;
+ }
+
+ if (!DuplicateHandle(GetCurrentProcess(), mapped_file, process,
+ &result, access, FALSE, options))
+ return false;
+ *new_handle = result;
+ return true;
+}
+
+
+void SharedMemory::Close() {
+ if (memory_ != NULL) {
+ UnmapViewOfFile(memory_);
+ memory_ = NULL;
+ }
+
+ if (mapped_file_ != NULL) {
+ CloseHandle(mapped_file_);
+ mapped_file_ = NULL;
+ }
+}
+
+void SharedMemory::Lock() {
+ Lock(INFINITE, NULL);
+}
+
+bool SharedMemory::Lock(uint32 timeout_ms, SECURITY_ATTRIBUTES* sec_attr) {
+ if (lock_ == NULL) {
+ std::wstring name = name_;
+ name.append(L"lock");
+ lock_ = CreateMutex(sec_attr, FALSE, name.c_str());
+ if (lock_ == NULL) {
+ DPLOG(ERROR) << "Could not create mutex.";
+ return false; // there is nothing good we can do here.
+ }
+ }
+ DWORD result = WaitForSingleObject(lock_, timeout_ms);
+
+ // Return false for WAIT_ABANDONED, WAIT_TIMEOUT or WAIT_FAILED.
+ return (result == WAIT_OBJECT_0);
+}
+
+void SharedMemory::Unlock() {
+ DCHECK(lock_ != NULL);
+ ReleaseMutex(lock_);
+}
+
+SharedMemoryHandle SharedMemory::handle() const {
+ return mapped_file_;
+}
+
+} // namespace base
diff --git a/src/base/single_thread_task_runner.h b/src/base/single_thread_task_runner.h
new file mode 100644
index 0000000..993d829
--- /dev/null
+++ b/src/base/single_thread_task_runner.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 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_SINGLE_THREAD_TASK_RUNNER_H_
+#define BASE_SINGLE_THREAD_TASK_RUNNER_H_
+
+#include "base/base_export.h"
+#include "base/sequenced_task_runner.h"
+
+namespace base {
+
+// A SingleThreadTaskRunner is a SequencedTaskRunner with one more
+// guarantee; namely, that all tasks are run on a single dedicated
+// thread. Most use cases require only a SequencedTaskRunner, unless
+// there is a specific need to run tasks on only a single dedicated.
+//
+// Some theoretical implementations of SingleThreadTaskRunner:
+//
+// - A SingleThreadTaskRunner that uses a single worker thread to
+// run posted tasks (i.e., a message loop).
+//
+// - A SingleThreadTaskRunner that stores the list of posted tasks
+// and has a method Run() that runs each runnable task in FIFO
+// order that must be run only from the thread the
+// SingleThreadTaskRunner was created on.
+class BASE_EXPORT SingleThreadTaskRunner : public SequencedTaskRunner {
+ public:
+ // A more explicit alias to RunsTasksOnCurrentThread().
+ bool BelongsToCurrentThread() const {
+ return RunsTasksOnCurrentThread();
+ }
+
+ protected:
+ virtual ~SingleThreadTaskRunner() {}
+};
+
+} // namespace base
+
+#endif // BASE_SERIAL_TASK_RUNNER_H_
diff --git a/src/base/state_machine_shell.cc b/src/base/state_machine_shell.cc
new file mode 100644
index 0000000..29a8786
--- /dev/null
+++ b/src/base/state_machine_shell.cc
@@ -0,0 +1,292 @@
+// Copyright (c) 2014 Google Inc. 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/state_machine_shell.h"
+
+#include "base/logging.h"
+
+// --- Macros for common logging idioms ---
+
+#define SM_STATE(s) GetStateString(s) << "(" << s << ")"
+#define SM_STATEN(s) GetStateString(s) << "(" << s.value_or(0) << ")"
+#define SM_EVENT(e) GetEventString(e) << "(" << e << ")"
+#define SM_PREFIX \
+ name_ << "-" << SM_STATEN(state_) << "v" << version_ << ": "
+#define SM_DLOG(level) DLOG(level) << SM_PREFIX
+
+namespace base {
+
+StateMachineBaseShell::StateMachineBaseShell(const std::string &name)
+ : name_(name),
+ version_(0),
+ is_handling_(false),
+ is_initialized_(false),
+ should_log_(false) {
+}
+
+void StateMachineBaseShell::Initialize() {
+ if (is_initialized_) {
+ return;
+ }
+
+ if (should_log_) {
+ SM_DLOG(INFO) << "INITIALIZING";
+ }
+ DCHECK(!state_) << SM_PREFIX;
+ DCHECK(!GetParentState(GetUserInitialState())) << SM_PREFIX;
+ is_initialized_ = true;
+ FollowInitialSubstates();
+}
+
+bool StateMachineBaseShell::IsIn(State state) const {
+ StateN currentState = state_;
+ while (currentState) {
+ if (state == currentState.value()) {
+ return true;
+ }
+
+ currentState = GetParentState(currentState);
+ }
+
+ return false;
+}
+
+const char *StateMachineBaseShell::GetStateString(StateN state) const {
+ if (!state) {
+ return "<none>";
+ }
+
+ const char *user = GetUserStateString(state.value());
+ if (user) {
+ return user;
+ }
+
+ return "UNKNOWN_STATE";
+}
+
+const char *StateMachineBaseShell::GetEventString(EventN event) const {
+ if (!event) {
+ return "<none>";
+ }
+
+ const char *user = GetUserEventString(event.value());
+ if (user) {
+ return user;
+ }
+
+ return "UNKNOWN_EVENT";
+}
+
+void StateMachineBaseShell::Handle(Event event, void *data) {
+ Initialize();
+
+ if (is_handling_) {
+ if (should_log_) {
+ SM_DLOG(INFO) << "QUEUEING " << SM_EVENT(event);
+ }
+
+ event_queue_.push(Bind(&StateMachineBaseShell::HandleOneEvent, Unretained(this),
+ event, Unretained(data)));
+ return;
+ }
+
+ HandleOneEvent(event, data);
+ HandleQueuedEvents();
+}
+
+StateMachineBaseShell::StateN
+StateMachineBaseShell::GetParentState(StateN state) const {
+ if (!state) {
+ return StateN();
+ }
+
+ return GetUserParentState(state.value());
+}
+
+StateMachineBaseShell::StateN
+StateMachineBaseShell::GetInitialSubstate(StateN state) const {
+ if (!state) {
+ return StateN();
+ }
+
+ return GetUserInitialSubstate(state.value());
+}
+
+void StateMachineBaseShell::HandleQueuedEvents() {
+ while (!event_queue_.empty()) {
+ event_queue_.front().Run();
+ event_queue_.pop();
+ }
+}
+
+void StateMachineBaseShell::HandleOneEvent(Event event, void *data) {
+ if (should_log_) {
+ SM_DLOG(INFO) << "HANDLING " << SM_EVENT(event);
+ }
+
+ DCHECK(!is_handling_) << SM_PREFIX;
+ is_handling_ = true;
+
+ // Walk up the hierarchy from the simplest current state, looking for event
+ // handlers, stopping at the first state that decides to handle the event.
+ StateN source = state_;
+ Result result;
+ while (source) {
+ result = HandleUserStateEvent(source.value(), event, data);
+ if (!result.is_handled) {
+ // Result was not handled, so we continue up the state chain.
+ source = GetParentState(source);
+ continue;
+ }
+
+ break;
+ }
+
+ // Log that the event was completely unhandled, because that's kinda weird.
+ // It's better if the state machine handler explicitly consumes and ignores
+ // events that should be ignored.
+ if (!result.is_handled && should_log_) {
+ SM_DLOG(WARNING) << "Event " << SM_EVENT(event) << " was unhandled.";
+ }
+
+ // If a transition was triggered, execute it.
+ if (result.is_transition) {
+ Transition(event, source.value(), result.target.value(),
+ result.is_external);
+ }
+
+ is_handling_ = false;
+}
+
+// Returns the index of |state| in |path|, or -1 if not found in |path_length|
+// elements. If |state| is null, then it will always return -1.
+static int IndexOf(StateMachineBaseShell::StateN state,
+ const StateMachineBaseShell::State *path,
+ size_t path_length) {
+ if (!state) {
+ return -1;
+ }
+
+ int i;
+ for (i = 0; !(path[i] == state) && i < path_length; ++i) {
+ }
+ return (i == path_length ? -1 : i);
+}
+
+// Finds the least common ancestor between the two state paths. If there is no
+// common ancestor, returns null.
+static StateMachineBaseShell::StateN FindLeastCommonAncestor(
+ const StateMachineBaseShell::State *path_a,
+ size_t length_a,
+ const StateMachineBaseShell::State *path_b,
+ size_t length_b) {
+ size_t max = std::min(length_a, length_b);
+ size_t i;
+ for (i = 0; path_a[i] == path_b[i] && i < max; ++i) { }
+ return (i == 0 ? StateMachineBaseShell::StateN() : path_a[i - 1]);
+}
+
+void StateMachineBaseShell::GetPath(State state, size_t max_depth, State *out_path,
+ size_t *out_depth) const {
+ if (max_depth == 0) {
+ SM_DLOG(FATAL) << "max_depth must be > 0";
+ if (out_depth) {
+ *out_depth = 0;
+ }
+ return;
+ }
+
+ size_t depth = 0;
+ StateN currentState = state;
+ while (currentState && depth < max_depth) {
+ out_path[depth] = currentState.value();
+ ++depth;
+ currentState = GetParentState(currentState);
+ }
+
+ // Reverse so the path is in ascending depth order.
+ std::reverse(out_path, out_path + depth);
+ if (out_depth) {
+ *out_depth = depth;
+ }
+}
+
+void StateMachineBaseShell::Transition(Event event, State source, State target,
+ bool is_external) {
+ if (should_log_) {
+ SM_DLOG(INFO) << SM_EVENT(event) << " caused " << SM_STATE(source) << " -> "
+ << SM_STATE(target) << (is_external ? " (external)" : "");
+ }
+
+ // Define a reasonable max depth so we don't have to heap allocate. I've never
+ // seen an HSM as deep as whatever arbitrary value this is defined to be at
+ // the moment.
+ static const size_t kMaxDepth = 16;
+ State source_path[kMaxDepth];
+ size_t source_depth;
+ GetPath(source, kMaxDepth, source_path, &source_depth);
+ State target_path[kMaxDepth];
+ size_t target_depth;
+ GetPath(target, kMaxDepth, target_path, &target_depth);
+ StateN least_common_ancestor =
+ FindLeastCommonAncestor(source_path, source_depth, target_path,
+ target_depth);
+
+ // External transitions must exit the source state and enter the target
+ // state, so if the LCA is one of the two, we need to go one level up.
+ if (is_external && (least_common_ancestor == source ||
+ least_common_ancestor == target)) {
+ least_common_ancestor = GetParentState(least_common_ancestor);
+ }
+
+ // Unwind (exit) states up to the closest common ancestor.
+ while (state_ && !(state_ == least_common_ancestor)) {
+ ExitCurrentState();
+ }
+
+ // Update version before we wind down to the target.
+ ++version_;
+
+ // Wind (enter) states down to the target state.
+ size_t next_depth = IndexOf(state_, target_path, target_depth) + 1;
+ DCHECK_LE(next_depth, target_depth);
+ while (next_depth < target_depth) {
+ EnterState(target_path[next_depth]);
+ ++next_depth;
+ }
+
+ FollowInitialSubstates();
+}
+
+void StateMachineBaseShell::FollowInitialSubstates() {
+ while (true) {
+ StateN substate =
+ (state_ ? GetInitialSubstate(state_) : GetUserInitialState());
+ if (!substate) {
+ break;
+ }
+ EnterState(substate.value());
+ }
+}
+
+void StateMachineBaseShell::EnterState(State state) {
+ state_ = state;
+
+ if (should_log_) {
+ SM_DLOG(INFO) << "ENTER";
+ }
+
+ HandleUserStateEnter(state);
+}
+
+void StateMachineBaseShell::ExitCurrentState() {
+ if (should_log_) {
+ SM_DLOG(INFO) << "EXIT";
+ }
+
+ HandleUserStateExit(state_.value());
+ state_ = GetParentState(state_);
+}
+
+} // namespace base
diff --git a/src/base/state_machine_shell.h b/src/base/state_machine_shell.h
new file mode 100644
index 0000000..a3e6d5b
--- /dev/null
+++ b/src/base/state_machine_shell.h
@@ -0,0 +1,552 @@
+// Copyright (c) 2014 Google Inc. 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_STATE_MACHINE_SHELL_H_
+#define BASE_STATE_MACHINE_SHELL_H_
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <climits>
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/optional.h"
+
+namespace base {
+
+// An approximate implementation of a run-to-completion (RTC) Hierarchical State
+// Machine (HSM), supporting most UML Statechart semantics.
+//
+// Some good background information on UML Statecharts, mostly written by Miro
+// Samek:
+// http://en.wikipedia.org/wiki/UML_state_machine
+//
+// And Miro Samek's 3-part article on practical UML Statecharts:
+// http://www.embedded.com/design/prototyping-and-development/4008247/A-crash-course-in-UML-state-machines-Part-1
+//
+// This class does not provide any intrinsic support for "orthogonal regions,"
+// "extended state," or "deferred events." "Guard conditions" are easily
+// implemented by the client directly in the event handler. It also minorly
+// deviates from the UML Statechart specification by calling transition handlers
+// before exiting the current states. This is because this implementation uses a
+// simplification which combines the transition handlers and the
+// state-event-transition map into a single function (HandleUserStateEvent).
+//
+// This class is not thread-safe. It is the user's responsibility to synchronize
+// calls into the state machine, either with locks or by simply using from a
+// single thread.
+//
+// Terse suggestions for using this class:
+// * Use the templated StateMachineShell wrapper class instead of this class
+// directly.
+// * Define two enums, one for your states, and one for your events.
+// * Subclass to define your state machine and event handlers.
+// * Avoid directly exposing or passing around state machines (wrap instead).
+// Handle() is not a great public interface.
+// * Include your state machine in another class as a private by-value member.
+// * Synchronize access to the state machine.
+// * Prefer writing state machine event handlers over checking if the machine
+// IsIn() a particular state.
+// * Convert public methods into events, get into the state machine as quickly
+// as possible.
+// * You only need to define a state when you are going to leave the state
+// machine in a particular condition where events can occur.
+// * Create a superstate when you have an event you want to handle the same
+// way for a collection of states.
+// * When managing resources, create a state or superstate that represents the
+// acquisition of that resource, and release the resource in the Exit
+// handler.
+//
+// Some Definitions:
+// Simple State - A State with no substates. The state machine is always
+// left in exactly one Simple State.
+// Composite State - A State with at least one substate.
+// Guard Condition - An external condition on which an event handler
+// branches.
+// Run-To-Completion - A property specifying that the state machine handles
+// one event at a time, and no events are handled until
+// the previous event is done being handled.
+//
+// See the unittests for this class for a contrived example state machine
+// implementation.
+class BASE_EXPORT StateMachineBaseShell {
+ public:
+ // --- Nested Types and Constants ---
+
+ typedef uint32_t State;
+ typedef uint32_t Event;
+
+ typedef optional<State> StateN;
+ typedef optional<Event> EventN;
+
+ // --- Public Methods ---
+
+ // Constructor with name. The name is helpful for debugging.
+ explicit StateMachineBaseShell(const std::string &name);
+ virtual ~StateMachineBaseShell() { }
+
+ // Enters the initial state, as specified by |GetUserInitialState()| and
+ // follows the initial substates down to the first simple (childless) state
+ // found. Idempotent. Will happen automatically on the first |Handle()| call,
+ // if not done explicitly.
+ void Initialize();
+
+ // Gets the name of this state machine, for logging purposes.
+ const char *name() const {
+ return name_.c_str();
+ }
+
+ // Gets a version number that increases monotonically with each state
+ // transition (unless it overflows |uint64_t|). This can be useful for timers
+ // and asynchronous events that only want to do their action if the state has
+ // not changed since they were queued.
+ //
+ // The state version changes exactly once per transition. In other words, it
+ // doesn't change for every Enter and Exit for a transition.
+ uint64_t version() const {
+ return version_;
+ }
+
+ // Gets the simplest current state that this state machine is in. To check if
+ // the state machine is in a particular composite state, use |IsIn()|. Returns
+ // null if uninitialized.
+ StateN state() const {
+ return state_;
+ }
+
+ // Reports whether the state machine is currently in the given state. A state
+ // machine is considered to be "in" the current simple state, but also "in"
+ // all superstates of the current simple state.
+ bool IsIn(State state) const;
+
+ // Gets a printable string for the given state.
+ const char *GetStateString(StateN state) const;
+
+ // Gets a printable string for the given event.
+ const char *GetEventString(EventN event) const;
+
+ // Handles the given event in the context of the state machine's current
+ // state, executing the appropriate event handlers and performing any
+ // precipitate state transitions. If called reentrantly, the event will be
+ // queued until the current event has run to completion.
+ //
+ // |data| is a way to pass auxiliary data to the event handler. No retention
+ // or ref-counting is done on that data, it is simply passed through to the
+ // event handler. Since |Handle()| will queue the event if (and only if)
+ // called from an event handler, it is up to the caller to ensure the lifetime
+ // of whatever is passed in here.
+ void Handle(Event event, void *data = NULL);
+
+
+ protected:
+ // --- Protected Nested Types ---
+
+ // A type that can be returned from a state-event handler, which will be
+ // coerced into the appropriate Result structure.
+ enum HandledState {
+ // The event handler returns this to say that the handler consume the event,
+ // but caused no state transition.
+ kHandled,
+
+ // The event handler returns this to say that the handler did not consume
+ // the event, and it should bubble up to the parent state, if any.
+ kNotHandled,
+ };
+
+ // Structure that handlers return, allowing them to cause state transitions or
+ // prevent the event from bubbling. State-event handlers may just return a
+ // HandledState or a state to transition to, and it will be coerced into this
+ // structure.
+ struct Result {
+ // Default constructor is unhandled.
+ Result()
+ : is_transition(false),
+ is_external(false),
+ is_handled(false) { }
+
+ // The no-transition constructor. Non-explicit so that the implementor of
+ // |HandleUserStateEvent()| just needs to return a |HandledState| and it
+ // does the right thing.
+ Result(HandledState handled)
+ : is_transition(false),
+ is_external(false),
+ is_handled(handled == kHandled) { }
+
+ // The state transition constructor. This implies that the event was
+ // handled. Non-explicit so that the implementor of |HandleUserStateEvent()|
+ // just needs to return a State and it does a non-external transition.
+ Result(State transition_target, bool external = false)
+ : target(transition_target),
+ is_transition(true),
+ is_external(external),
+ is_handled(true) { }
+
+ Result &operator=(HandledState rhs) {
+ target = base::nullopt;
+ is_transition = false;
+ is_external = false;
+ is_handled = (rhs == kHandled);
+ return *this;
+ }
+
+ Result &operator=(State transition_target) {
+ target = transition_target;
+ is_transition = true;
+ is_external = false;
+ is_handled = true;
+ return *this;
+ }
+
+ // State to transition to. Only valid if is_transition is true.
+ StateN target;
+
+ // Whether this result indicates a state transition.
+ bool is_transition;
+
+ // Whether the specified transition is external. Only meaningful if
+ // is_transition is true.
+ //
+ // For more on state transitions, see:
+ // http://en.wikipedia.org/wiki/UML_state_machine#Transition_execution_sequence
+ bool is_external;
+
+ // Whether the event was handled by the handler. If true, consumes the
+ // event, and prevents bubbling up to superstates. False propagates the
+ // event to superstates.
+ bool is_handled;
+ };
+
+
+ // --- Implementation Interface for Subclasses ---
+
+ // Abstract method for subclasses to define the state hierarchy. It is
+ // recommended that all user-defined states be descendents of a single "top"
+ // state.
+ virtual StateN GetUserParentState(State state) const = 0;
+
+ // Abstract method for subclasses to define the initial substate of their
+ // states, which is required for all complex states. If a state is a simple
+ // state (no substates), returns null.
+ virtual StateN GetUserInitialSubstate(State state) const = 0;
+
+ // Abstract method for subclasses to define the initial (top) state of their
+ // state machine. This must be a state that has no parent. This state will be
+ // entered upon calling |Initialize()|.
+ virtual State GetUserInitialState() const = 0;
+
+ // Optional method for subclasses to define strings for their states, solely
+ // used for debugging purposes.
+ virtual const char *GetUserStateString(State state) const { return NULL; }
+
+ // Optional method for subclasses to define strings for their events, solely
+ // used for debugging purposes.
+ virtual const char *GetUserEventString(Event event) const { return NULL; }
+
+ // Abstract method for subclasses to define handlers for events in given
+ // states. This method must return either:
+ // a) a state to transition to, meaning the event was handled and caused
+ // a transition
+ // b) |kHandled| meaning the event was handled but no transition occurred
+ // c) |kNotHandled|, meaning the event should bubble up to the parent state
+ // d) an explicit Result structure, mainly for external transitions.
+ //
+ // Implementations wishing to catch all unhandled events may do so in their
+ // top state.
+ //
+ // This method is generally implemented as a nested switch statement, with the
+ // outer switch on |state| and the inner switch on |event|. You may want to
+ // break this apart into per-state or per-state-event functions for
+ // readability and maintainability.
+ virtual Result HandleUserStateEvent(State state, Event event, void *data) = 0;
+
+ // Abstract method for subclasses to define state entry behaviors.
+ virtual void HandleUserStateEnter(State state) = 0;
+
+ // Abstract method for subclasses to define state exit behaviors.
+ virtual void HandleUserStateExit(State state) = 0;
+
+
+ // --- Helper Methods for Subclasses ---
+
+ // Subclasses can call this method to turn on logging. Logging is opt-in,
+ // because it can be very verbose, and is likely only useful during
+ // development of a particular state machine.
+ void EnableLogging() {
+ should_log_ = true;
+ }
+
+
+ private:
+ // --- Private Helper Methods ---
+
+ // Gets the parent state of the given state.
+ StateN GetParentState(StateN state) const;
+
+ // Gets the initial substate of given state.
+ StateN GetInitialSubstate(StateN state) const;
+
+ // Handles all queued events until there are no more to run. Event handlers
+ // may queue more events by calling |Handle()|, and this method will also
+ // process all precipitate events.
+ void HandleQueuedEvents();
+
+ // Workhorse method for handling a single event. Event handling is queued by
+ // binding the parameters to this method and queueing the resulting Closure.
+ void HandleOneEvent(Event event, void *data);
+
+ // Gets the path from the Top state to the given |state|, storing it in the
+ // given |out_path| array, up to |max_depth| entries. If specified,
+ // |out_depth| will be set to the number of valid states that fit in the given
+ // array.
+ void GetPath(State state, size_t max_depth, State *out_path,
+ size_t *out_depth) const;
+
+ // Transitions between the given source and target states, assuming that the
+ // source state is in the current state path to the Top state. The source
+ // state is the state whose handler generated the transition.
+ //
+ // See: http://en.wikipedia.org/wiki/UML_state_machine#Transition_execution_sequence
+ void Transition(Event event, State source, State target, bool is_external);
+
+ // Follows the initial substates from the current state until it reaches a
+ // simple state.
+ void FollowInitialSubstates();
+
+ // Enters the given state.
+ void EnterState(State state);
+
+ // Exits the current state to its parent.
+ void ExitCurrentState();
+
+
+ // --- Members ---
+
+ // The name of this state machine, for debugging purposes.
+ const std::string name_;
+
+ // The current state of this state machine. Null until initialized.
+ StateN state_;
+
+ // The unique version of this state machine's state, updated on every
+ // transition.
+ uint64_t version_;
+
+ // Queue of events to handle once the current event is done being
+ // handled. Should always be empty unless |is_handling_| is true.
+ std::queue<Closure> event_queue_;
+
+ // Whether this state machine is actively handling an event. Used to detect
+ // reentrant calls to |Handle()|.
+ bool is_handling_;
+
+ // Whether the state machine has been initialized into its initial state
+ // yet. Used to make |Initialize()| idempotent.
+ bool is_initialized_;
+
+ // Whether the state machine should DLOG information about state transitions.
+ bool should_log_;
+};
+
+
+// A convenience template wrapper for StateMachineBaseShell. See the above class
+// for complete documentation. Basically, you define your states and events as
+// two enums, and then pass them as template args to this template class. Your
+// state machine should then subclass this template class. It then does the work
+// of casting and converting back and forth from your enums to
+// StateMachineBaseShell's numeric State and Event definitions.
+//
+// All the methods in this class, protected and public, match the description
+// and behavioral contracts of the equivalently named method in
+// StateMachineBaseShell.
+template <typename StateEnum, typename EventEnum>
+class BASE_EXPORT StateMachineShell {
+ public:
+ // --- Nested Types and Constants ---
+
+ typedef optional<StateEnum> StateEnumN;
+ typedef optional<EventEnum> EventEnumN;
+
+ explicit StateMachineShell(const std::string &name)
+ : machine_(this, name) { }
+ virtual ~StateMachineShell() { }
+
+ void Initialize() {
+ machine_.Initialize();
+ }
+
+ const char *name() const {
+ return machine_.name();
+ }
+
+ uint64_t version() const {
+ return machine_.version();
+ }
+
+ StateEnumN state() const {
+ BaseStateN wrappedState = machine_.state();
+ return (wrappedState ? static_cast<StateEnum>(*wrappedState)
+ : StateEnumN());
+ }
+
+ bool IsIn(StateEnum state) const {
+ return machine_.IsIn(static_cast<BaseState>(state));
+ }
+
+ const char *GetStateString(StateEnumN state) const {
+ return machine_.GetStateString(state ? static_cast<BaseState>(*state)
+ : BaseStateN());
+ }
+
+ const char *GetEventString(EventEnumN event) const {
+ return machine_.GetEventString(event ? static_cast<BaseEvent>(*event)
+ : BaseEventN());
+ }
+
+ void Handle(EventEnum event, void *data = NULL) {
+ machine_.Handle(static_cast<BaseEvent>(event), data);
+ }
+
+ protected:
+ // See the other HandledState in StateMachineBaseShell.
+ enum HandledState {
+ kHandled,
+ kNotHandled,
+ };
+
+ // See the other Result in StateMachineBaseShell.
+ struct Result {
+ // Not explicit on purpose, please see the other Result for justification.
+ Result(HandledState handled)
+ : target(),
+ is_transition(false),
+ is_external(false),
+ is_handled(handled == kHandled) { }
+
+ // Not explicit on purpose, please see the other Result for justification.
+ Result(StateEnum transition_target, bool external = false)
+ : target(transition_target),
+ is_transition(true),
+ is_external(external),
+ is_handled(true) { }
+
+ Result &operator=(HandledState rhs) {
+ target = base::nullopt;
+ is_transition = false;
+ is_external = false;
+ is_handled = (rhs == kHandled);
+ return *this;
+ }
+
+ Result &operator=(StateEnum transition_target) {
+ target = transition_target;
+ is_transition = true;
+ is_external = false;
+ is_handled = true;
+ return *this;
+ }
+
+ StateEnumN target;
+ bool is_transition;
+ bool is_external;
+ bool is_handled;
+ };
+
+ virtual StateEnumN GetUserParentState(StateEnum state) const = 0;
+ virtual StateEnumN GetUserInitialSubstate(StateEnum state) const = 0;
+ virtual StateEnum GetUserInitialState() const = 0;
+ virtual const char *GetUserStateString(StateEnum state) const { return NULL; }
+ virtual const char *GetUserEventString(EventEnum event) const { return NULL; }
+ virtual Result HandleUserStateEvent(StateEnum state,
+ EventEnum event,
+ void *data) = 0;
+ virtual void HandleUserStateEnter(StateEnum state) = 0;
+ virtual void HandleUserStateExit(StateEnum state) = 0;
+
+ void EnableLogging() {
+ machine_.EnableLoggingPublic();
+ }
+
+ private:
+ typedef StateMachineBaseShell::State BaseState;
+ typedef StateMachineBaseShell::Event BaseEvent;
+ typedef StateMachineBaseShell::StateN BaseStateN;
+ typedef StateMachineBaseShell::EventN BaseEventN;
+
+ // Private contained subclass that forwards and adapts all virtual methods
+ // into this class's equivalent virtual methods.
+ class WrappedMachine : public StateMachineBaseShell {
+ public:
+ WrappedMachine(StateMachineShell<StateEnum, EventEnum> *wrapper,
+ const std::string &name)
+ : StateMachineBaseShell(name),
+ wrapper_(wrapper) {
+ }
+
+ StateN GetUserParentState(State state) const OVERRIDE {
+ StateEnumN result =
+ wrapper_->GetUserParentState(static_cast<StateEnum>(state));
+ return (result ? static_cast<State>(*result) : StateN());
+ }
+
+ StateN GetUserInitialSubstate(State state) const OVERRIDE {
+ StateEnumN result =
+ wrapper_->GetUserInitialSubstate(static_cast<StateEnum>(state));
+ return (result ? static_cast<State>(*result) : StateN());
+ }
+
+ State GetUserInitialState() const OVERRIDE {
+ return static_cast<State>(wrapper_->GetUserInitialState());
+ }
+
+ const char *GetUserStateString(State state) const OVERRIDE {
+ return wrapper_->GetUserStateString(static_cast<StateEnum>(state));
+ }
+
+ const char *GetUserEventString(Event event) const OVERRIDE {
+ return wrapper_->GetUserEventString(static_cast<EventEnum>(event));
+ }
+
+ Result HandleUserStateEvent(State state, Event event, void *data) OVERRIDE {
+ StateMachineShell<StateEnum, EventEnum>::Result result =
+ wrapper_->HandleUserStateEvent(static_cast<StateEnum>(state),
+ static_cast<EventEnum>(event),
+ data);
+ if (result.is_transition) {
+ return Result(static_cast<State>(*result.target),
+ result.is_external);
+ }
+
+ return result.is_handled ? kHandled : kNotHandled;
+ }
+
+ void HandleUserStateEnter(State state) OVERRIDE {
+ wrapper_->HandleUserStateEnter(static_cast<StateEnum>(state));
+ }
+
+ void HandleUserStateExit(State state) OVERRIDE {
+ wrapper_->HandleUserStateExit(static_cast<StateEnum>(state));
+ }
+
+ // A public exposure of EnableLogging so the wrapper can access it. Since
+ // this class is private to the wrapper, it is the only one who can see it.
+ void EnableLoggingPublic() {
+ EnableLogging();
+ }
+
+ private:
+ StateMachineShell<StateEnum, EventEnum> *wrapper_;
+ };
+
+ WrappedMachine machine_;
+};
+
+} // namespace base
+
+#endif // BASE_CIRCULAR_BUFFER_SHELL_H_
diff --git a/src/base/state_machine_shell_unittest.cc b/src/base/state_machine_shell_unittest.cc
new file mode 100644
index 0000000..0d2bda5
--- /dev/null
+++ b/src/base/state_machine_shell_unittest.cc
@@ -0,0 +1,727 @@
+// Copyright (c) 2014 Google Inc. 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/state_machine_shell.h"
+
+#include <list>
+#include <iostream>
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace hsm {
+
+// Constant to represent no version for TestHsm::Expect.
+static const uint64_t kNoVersion = static_cast<uint64_t>(-1);
+
+// States for the test HSM
+enum TestState {
+ kStateS0,
+ kStateS1,
+ kStateS11,
+ kStateS2,
+ kStateS21,
+ kStateS211,
+ kStateT0,
+};
+
+// Events for the test HSM
+enum TestEvent {
+ kEventA,
+ kEventB,
+ kEventC,
+ kEventD,
+ kEventE,
+ kEventF,
+ kEventG,
+ kEventH,
+ kEventI,
+ kEventJ,
+};
+
+// An enumeration of things that the HSM does that we can sense and then
+// assert about.
+enum HsmEvent {
+ kHsmEnter,
+ kHsmExit,
+ kHsmHandled
+};
+
+} // namespace hsm
+
+// --- Test Subclass ---
+
+// StateMachineShell is an abstract class, so we must subclass it to test it.
+// This class uses the sample test state machine specified by Miro Samek in his
+// Practical Statecharts book. It covers the interesting transitions and
+// topologies, so if it's fully exercised, it should represent a
+// close-to-canonical test of the state machine's facilities.
+//
+// The diagram of this statechart is reproduced here:
+// http://www.state-machine.com/resources/Heinzmann04.pdf
+//
+// This version has:
+// - A new event, I, in state S11, to test reentrant event handling.
+// - A new event, J, and new state T0, to test the no top state case.
+class TestHsm : public base::StateMachineShell<hsm::TestState, hsm::TestEvent> {
+ public:
+
+ // --- Some types to aid sensing ---
+
+ struct ResultEvent {
+ // The state the HSM was in when it handled the event.
+ const StateEnumN state;
+
+ // The data passed into the event, if any.
+ const void *data;
+
+ // The state that actually handled the event (could be an ancestor of the
+ // current state.
+ const StateEnumN event_state;
+
+ // The event that was handled.
+ const EventEnumN event;
+
+ // The "HSM Event" that occurred causing this to be recorded.
+ const hsm::HsmEvent hsm_event;
+
+ // The state version at the time the HsmEvent occured.
+ const uint64_t version;
+ };
+
+ // --- Extra state to aid sensing ---
+
+ // Foo is used as extended state and in guard conditions in the test HSM (see
+ // link above).
+ bool foo_;
+
+ // A counter for how many times the S11 I handler is invoked.
+ int event_i_count_;
+
+ // A collection of interesting things that has happened to this state
+ // machine. Used to validate that the HSM behaved as expected.
+ std::list<ResultEvent> results;
+
+ TestHsm()
+ : base::StateMachineShell<hsm::TestState, hsm::TestEvent>("TestHsm"),
+ foo_(true),
+ event_i_count_(0) {
+ if (VLOG_IS_ON(1)) {
+ EnableLogging();
+ }
+ }
+ virtual ~TestHsm() { }
+
+ // Clears the results list, so nothing bleeds between test cases.
+ void ClearResults() {
+ results.clear();
+ }
+
+ // Consumes and validates a ResultEvent from the results list.
+ void Expect(hsm::HsmEvent hsm_event,
+ StateEnumN event_state = StateEnumN(),
+ EventEnumN event = EventEnumN(),
+ StateEnumN current_state = StateEnumN(),
+ void *data = NULL,
+ uint64_t version = hsm::kNoVersion) {
+ VLOG(1) << __FUNCTION__ << ": hsm_event=" << hsm_event
+ << ", event_state=" << GetStateString(event_state)
+ << ", event=" << GetEventString(event)
+ << ", current_state=" << GetStateString(current_state)
+ << ", data=0x" << std::hex << data << ", version=" << version;
+
+ EXPECT_FALSE(results.empty());
+ TestHsm::ResultEvent result = results.front();
+ results.pop_front();
+ EXPECT_EQ(hsm_event, result.hsm_event);
+ if (event_state) {
+ EXPECT_EQ(event_state, result.event_state);
+ if (!current_state) {
+ EXPECT_EQ(event_state, result.state);
+ }
+ }
+
+ if (current_state) {
+ EXPECT_EQ(current_state, result.state);
+ }
+
+ if (event) {
+ EXPECT_EQ(event, result.event);
+ }
+ EXPECT_EQ(data, result.data);
+
+ if (version != hsm::kNoVersion) {
+ EXPECT_EQ(version, result.version);
+ }
+ }
+
+
+ // --- StateMachineShell Implementation ---
+ protected:
+ virtual StateEnumN GetUserParentState(hsm::TestState state) const OVERRIDE {
+ switch (state) {
+ case hsm::kStateS1:
+ case hsm::kStateS2:
+ return hsm::kStateS0;
+ case hsm::kStateS11:
+ return hsm::kStateS1;
+ case hsm::kStateS21:
+ return hsm::kStateS2;
+ case hsm::kStateS211:
+ return hsm::kStateS21;
+ default:
+ return StateEnumN();
+ }
+ }
+
+ virtual StateEnumN GetUserInitialSubstate(hsm::TestState state) const OVERRIDE {
+ switch (state) {
+ case hsm::kStateS0:
+ return hsm::kStateS1;
+ case hsm::kStateS1:
+ return hsm::kStateS11;
+ case hsm::kStateS2:
+ return hsm::kStateS21;
+ case hsm::kStateS21:
+ return hsm::kStateS211;
+ default:
+ return StateEnumN();
+ }
+ }
+
+ virtual hsm::TestState GetUserInitialState() const OVERRIDE {
+ return hsm::kStateS0;
+ }
+
+ virtual const char *GetUserStateString(hsm::TestState state) const OVERRIDE {
+ switch (state) {
+ case hsm::kStateS0:
+ return "S0";
+ case hsm::kStateS1:
+ return "S1";
+ case hsm::kStateS11:
+ return "S11";
+ case hsm::kStateS2:
+ return "S2";
+ case hsm::kStateS21:
+ return "S21";
+ case hsm::kStateS211:
+ return "S211";
+ case hsm::kStateT0:
+ return "T0";
+ default:
+ return NULL;
+ }
+ }
+
+ virtual const char *GetUserEventString(hsm::TestEvent event) const OVERRIDE {
+ switch (event) {
+ case hsm::kEventA:
+ return "A";
+ case hsm::kEventB:
+ return "B";
+ case hsm::kEventC:
+ return "C";
+ case hsm::kEventD:
+ return "D";
+ case hsm::kEventE:
+ return "E";
+ case hsm::kEventF:
+ return "F";
+ case hsm::kEventG:
+ return "G";
+ case hsm::kEventH:
+ return "H";
+ case hsm::kEventI:
+ return "I";
+ case hsm::kEventJ:
+ return "J";
+ default:
+ return NULL;
+ }
+ }
+
+ virtual Result HandleUserStateEvent(hsm::TestState state,
+ hsm::TestEvent event,
+ void *data) OVERRIDE {
+ VLOG(1) << __FUNCTION__ << "(" << GetStateString(state) << ", "
+ << GetEventString(event) << ", 0x" << std::hex << data << ");";
+
+ Result result(kNotHandled);
+ switch (state) {
+ case hsm::kStateS0:
+ switch (event) {
+ case hsm::kEventE:
+ result = hsm::kStateS211;
+ break;
+ case hsm::kEventJ:
+ result = hsm::kStateT0;
+ break;
+ default:
+ // Not Handled
+ break;
+ }
+ break;
+
+ case hsm::kStateS1:
+ switch (event) {
+ case hsm::kEventA:
+ result = Result(hsm::kStateS1, true);
+ break;
+ case hsm::kEventB:
+ result = hsm::kStateS11;
+ break;
+ case hsm::kEventC:
+ result = hsm::kStateS2;
+ break;
+ case hsm::kEventD:
+ result = hsm::kStateS0;
+ break;
+ case hsm::kEventF:
+ result = hsm::kStateS211;
+ break;
+ default:
+ // Not Handled
+ break;
+ }
+ break;
+
+ case hsm::kStateS11:
+ switch (event) {
+ case hsm::kEventG:
+ result = hsm::kStateS211;
+ break;
+ case hsm::kEventH:
+ if (foo_) {
+ foo_ = false;
+ result = kHandled;
+ break;
+ }
+ break;
+ case hsm::kEventI:
+ // Inject another I every other time I is handled so every I should
+ // ultimately increase event_i_count_ by 2.
+ ++event_i_count_;
+ if (event_i_count_ % 2 == 1) {
+ // This should queue and be run before Handle() returns.
+ Handle(hsm::kEventI);
+ result = hsm::kStateS1;
+ break;
+ } else {
+ result = kHandled;
+ break;
+ }
+ break;
+ default:
+ // Not Handled
+ break;
+ }
+ break;
+
+ case hsm::kStateS2:
+ switch (event) {
+ case hsm::kEventC:
+ result = hsm::kStateS1;
+ break;
+ case hsm::kEventF:
+ result = hsm::kStateS11;
+ break;
+ default:
+ // Not Handled
+ break;
+ }
+ break;
+
+ case hsm::kStateS21:
+ switch (event) {
+ case hsm::kEventB:
+ result = hsm::kStateS211;
+ break;
+ case hsm::kEventH:
+ if (!foo_) {
+ foo_ = true;
+ result = Result(hsm::kStateS21, true);
+ break;
+ }
+ break;
+ default:
+ // Not Handled
+ break;
+ }
+ break;
+
+ case hsm::kStateS211:
+ switch (event) {
+ case hsm::kEventD:
+ result = hsm::kStateS21;
+ break;
+ case hsm::kEventG:
+ result = hsm::kStateS0;
+ break;
+ default:
+ // Not Handled
+ break;
+ }
+ break;
+
+ case hsm::kStateT0:
+ switch (event) {
+ case hsm::kEventJ:
+ result = hsm::kStateS0;
+ break;
+ default:
+ // Not Handled
+ break;
+ }
+ break;
+
+ default:
+ // Not Handled
+ break;
+ }
+
+ if (result.is_handled) {
+ AddEvent(state, event, data, hsm::kHsmHandled);
+ }
+
+ return result;
+ }
+
+ virtual void HandleUserStateEnter(hsm::TestState state) OVERRIDE {
+ VLOG(1) << __FUNCTION__ << "(" << GetStateString(state) << ", ENTER);";
+
+ AddEvent(state, EventEnumN(), NULL, hsm::kHsmEnter);
+ }
+
+ virtual void HandleUserStateExit(hsm::TestState state) OVERRIDE {
+ VLOG(1) << __FUNCTION__ << "(" << GetStateString(state) << ", EXIT);";
+
+ AddEvent(state, EventEnumN(), NULL, hsm::kHsmExit);
+ }
+
+ private:
+ // Adds a new record to the result list.
+ void AddEvent(hsm::TestState state,
+ EventEnumN event,
+ void *data,
+ hsm::HsmEvent hsm_event) {
+ ResultEvent result_event = { this->state(), data, state, event, hsm_event,
+ this->version() };
+ results.push_back(result_event);
+ }
+};
+
+
+// --- Test Definitions ---
+
+// This test validates that a state machine will initialize itself when it
+// handles its first event, even if the user has not explicitly called
+// initialize.
+TEST(StateMachineShellTest, AutoInit) {
+ TestHsm hsm;
+
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventA);
+
+ // The HSM should Auto-Initialize
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS0);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS1);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+
+ // Then it should handle the event
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS1, hsm::kEventA, hsm::kStateS11, 0);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS1);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS1);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(1, hsm.version());
+ EXPECT_EQ(hsm::kStateS11, hsm.state());
+}
+
+
+// This test validates that if Handle() is called from within an event handler,
+// it queues the event rather than trying to Handle the next event
+// reentrantly. This behavior, or something like it, is required to make the
+// state machine truly run-to-completion.
+TEST(StateMachineShellTest, ReentrantHandle) {
+ TestHsm hsm;
+ hsm.Initialize();
+ EXPECT_EQ(0, hsm.version());
+ hsm.ClearResults();
+
+ // Test a Handle() inside Handle()
+ EXPECT_EQ(0, hsm.event_i_count_);
+ hsm.Handle(hsm::kEventI);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS11, hsm::kEventI, hsm::kStateS11,
+ NULL, 0);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS11, hsm::kEventI, hsm::kStateS11,
+ NULL, 1);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(1, hsm.version());
+ EXPECT_EQ(2, hsm.event_i_count_);
+}
+
+// This test validates that every meaningful event in every state in the test
+// state machine behaves as expected. This should cover all normal operation of
+// the state machine framework, except what is extracted into their own test
+// cases above.
+TEST(StateMachineShellTest, KitNKaboodle) {
+ TestHsm hsm;
+
+ // Test the initial state
+ EXPECT_EQ(0, hsm.version());
+ hsm.Initialize();
+ EXPECT_EQ(0, hsm.version());
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS0);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS1);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(hsm::kStateS11, hsm.state());
+
+ // Test IsIn
+ EXPECT_TRUE(hsm.IsIn(hsm::kStateS11));
+ EXPECT_TRUE(hsm.IsIn(hsm::kStateS1));
+ EXPECT_TRUE(hsm.IsIn(hsm::kStateS0));
+ EXPECT_FALSE(hsm.IsIn(hsm::kStateS2));
+ EXPECT_FALSE(hsm.IsIn(hsm::kStateS21));
+ EXPECT_FALSE(hsm.IsIn(hsm::kStateS211));
+
+ // State: S11, Event: A
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventA);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS1, hsm::kEventA, hsm::kStateS11, 0);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS1);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS1);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(1, hsm.version());
+ EXPECT_EQ(hsm::kStateS11, hsm.state());
+
+ // State: S11, Event: B
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventB);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS1, hsm::kEventB, hsm::kStateS11,
+ NULL, 1);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(2, hsm.version());
+ EXPECT_EQ(hsm::kStateS11, hsm.state());
+
+ // State: S11, Event: D
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventD);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS1, hsm::kEventD, hsm::kStateS11,
+ NULL, 2);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS1);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS1);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(3, hsm.version());
+ EXPECT_EQ(hsm::kStateS11, hsm.state());
+
+ // State: S11, Event: H (foo == true)
+ hsm.ClearResults();
+ EXPECT_TRUE(hsm.foo_);
+ hsm.Handle(hsm::kEventH);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS11, hsm::kEventH, hsm::kStateS11,
+ NULL, 3);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(3, hsm.version());
+ EXPECT_EQ(hsm::kStateS11, hsm.state());
+ EXPECT_FALSE(hsm.foo_);
+
+ // State: S11, Event: H (foo == false)
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventH);
+ EXPECT_FALSE(hsm.foo_);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(3, hsm.version());
+ EXPECT_EQ(hsm::kStateS11, hsm.state());
+
+ // State: S11, Event: C
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventC);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS1, hsm::kEventC, hsm::kStateS11,
+ NULL, 3);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS1);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS2);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS21);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS211);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(4, hsm.version());
+ EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+ // State: S211, Event: A
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventA);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+ // State: S211, Event: B
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventB);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS21, hsm::kEventB, hsm::kStateS211);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS211);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS211);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+ // State: S211, Event: D
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventD);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS211, hsm::kEventD, hsm::kStateS211);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS211);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS211);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+ // State: S211, Event: E
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventE);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS0, hsm::kEventE, hsm::kStateS211);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS211);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS21);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS2);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS2);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS21);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS211);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+ // State: S211, Event: F
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventF);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS2, hsm::kEventF, hsm::kStateS211);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS211);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS21);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS2);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS1);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(hsm::kStateS11, hsm.state());
+
+ // State: S11, Event: E
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventE);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS0, hsm::kEventE, hsm::kStateS11);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS1);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS2);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS21);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS211);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+ // State: S211, Event: G
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventG);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS211, hsm::kEventG, hsm::kStateS211);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS211);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS21);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS2);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS1);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(hsm::kStateS11, hsm.state());
+
+ // State: S11, Event: F
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventF);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS1, hsm::kEventF, hsm::kStateS11);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS1);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS2);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS21);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS211);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+ // State: S211, Event: H (foo == false)
+ EXPECT_FALSE(hsm.foo_);
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventH);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS21, hsm::kEventH, hsm::kStateS211);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS211);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS21);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS21);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS211);
+ EXPECT_TRUE(hsm.foo_);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+ // State: S211, Event: H (foo == true)
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventH);
+ EXPECT_TRUE(hsm.foo_);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+ // State: S211, Event: C
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventC);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS2, hsm::kEventC, hsm::kStateS211);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS211);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS21);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS2);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS1);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(hsm::kStateS11, hsm.state());
+
+ // State: S11, Event: G
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventG);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS11, hsm::kEventG, hsm::kStateS11);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS1);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS2);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS21);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS211);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(hsm::kStateS211, hsm.state());
+
+ // State: S211, Event: J
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventJ);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS0, hsm::kEventJ, hsm::kStateS211);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS211);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS21);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS2);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS0);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateT0);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(hsm::kStateT0, hsm.state());
+
+ // State: T0, Event: J
+ hsm.ClearResults();
+ hsm.Handle(hsm::kEventJ);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateT0, hsm::kEventJ);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateT0);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS0);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS1);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS11);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(hsm::kStateS11, hsm.state());
+
+ // Test that event data gets passed through to the handler
+ hsm.ClearResults();
+ void *data = reinterpret_cast<void *>(7);
+ hsm.Handle(hsm::kEventC, data);
+ hsm.Expect(hsm::kHsmHandled, hsm::kStateS1, hsm::kEventC, hsm::kStateS11,
+ data);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS11);
+ hsm.Expect(hsm::kHsmExit, hsm::kStateS1);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS2);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS21);
+ hsm.Expect(hsm::kHsmEnter, hsm::kStateS211);
+ EXPECT_EQ(0, hsm.results.size());
+ EXPECT_EQ(hsm::kStateS211, hsm.state());
+}
diff --git a/src/base/stl_util.h b/src/base/stl_util.h
new file mode 100644
index 0000000..c612769
--- /dev/null
+++ b/src/base/stl_util.h
@@ -0,0 +1,223 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_STL_UTIL_H_
+#define BASE_STL_UTIL_H_
+
+#include <algorithm>
+#include <functional>
+#if defined(__LB_SHELL__) // TODO: Starboard?
+#include <iterator>
+#endif
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+
+// Clears internal memory of an STL object.
+// STL clear()/reserve(0) does not always free internal memory allocated
+// This function uses swap/destructor to ensure the internal memory is freed.
+template<class T>
+void STLClearObject(T* obj) {
+ T tmp;
+ tmp.swap(*obj);
+ // Sometimes "T tmp" allocates objects with memory (arena implementation?).
+ // Hence using additional reserve(0) even if it doesn't always work.
+ obj->reserve(0);
+}
+
+// For a range within a container of pointers, calls delete (non-array version)
+// on these pointers.
+// NOTE: for these three functions, we could just implement a DeleteObject
+// functor and then call for_each() on the range and functor, but this
+// requires us to pull in all of algorithm.h, which seems expensive.
+// For hash_[multi]set, it is important that this deletes behind the iterator
+// because the hash_set may call the hash function on the iterator when it is
+// advanced, which could result in the hash function trying to deference a
+// stale pointer.
+template <class ForwardIterator>
+void STLDeleteContainerPointers(ForwardIterator begin, ForwardIterator end) {
+ while (begin != end) {
+ ForwardIterator temp = begin;
+ ++begin;
+ delete *temp;
+ }
+}
+
+// For a range within a container of pairs, calls delete (non-array version) on
+// BOTH items in the pairs.
+// NOTE: Like STLDeleteContainerPointers, it is important that this deletes
+// behind the iterator because if both the key and value are deleted, the
+// container may call the hash function on the iterator when it is advanced,
+// which could result in the hash function trying to dereference a stale
+// pointer.
+template <class ForwardIterator>
+void STLDeleteContainerPairPointers(ForwardIterator begin,
+ ForwardIterator end) {
+ while (begin != end) {
+ ForwardIterator temp = begin;
+ ++begin;
+ delete temp->first;
+ delete temp->second;
+ }
+}
+
+// For a range within a container of pairs, calls delete (non-array version) on
+// the FIRST item in the pairs.
+// NOTE: Like STLDeleteContainerPointers, deleting behind the iterator.
+template <class ForwardIterator>
+void STLDeleteContainerPairFirstPointers(ForwardIterator begin,
+ ForwardIterator end) {
+ while (begin != end) {
+ ForwardIterator temp = begin;
+ ++begin;
+ delete temp->first;
+ }
+}
+
+// For a range within a container of pairs, calls delete.
+// NOTE: Like STLDeleteContainerPointers, deleting behind the iterator.
+// Deleting the value does not always invalidate the iterator, but it may
+// do so if the key is a pointer into the value object.
+template <class ForwardIterator>
+void STLDeleteContainerPairSecondPointers(ForwardIterator begin,
+ ForwardIterator end) {
+ while (begin != end) {
+ ForwardIterator temp = begin;
+ ++begin;
+ delete temp->second;
+ }
+}
+
+// To treat a possibly-empty vector as an array, use these functions.
+// If you know the array will never be empty, you can use &*v.begin()
+// directly, but that is undefined behaviour if |v| is empty.
+template<typename T>
+inline T* vector_as_array(std::vector<T>* v) {
+ return v->empty() ? NULL : &*v->begin();
+}
+
+template<typename T>
+inline const T* vector_as_array(const std::vector<T>* v) {
+ return v->empty() ? NULL : &*v->begin();
+}
+
+// Return a mutable char* pointing to a string's internal buffer,
+// which may not be null-terminated. Writing through this pointer will
+// modify the string.
+//
+// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the
+// next call to a string method that invalidates iterators.
+//
+// As of 2006-04, there is no standard-blessed way of getting a
+// mutable reference to a string's internal buffer. However, issue 530
+// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-active.html#530)
+// proposes this as the method. According to Matt Austern, this should
+// already work on all current implementations.
+inline char* string_as_array(std::string* str) {
+ // DO NOT USE const_cast<char*>(str->data())
+ return str->empty() ? NULL : &*str->begin();
+}
+
+// The following functions are useful for cleaning up STL containers whose
+// elements point to allocated memory.
+
+// STLDeleteElements() deletes all the elements in an STL container and clears
+// the container. This function is suitable for use with a vector, set,
+// hash_set, or any other STL container which defines sensible begin(), end(),
+// and clear() methods.
+//
+// If container is NULL, this function is a no-op.
+//
+// As an alternative to calling STLDeleteElements() directly, consider
+// STLElementDeleter (defined below), which ensures that your container's
+// elements are deleted when the STLElementDeleter goes out of scope.
+template <class T>
+void STLDeleteElements(T* container) {
+ if (!container)
+ return;
+ STLDeleteContainerPointers(container->begin(), container->end());
+ container->clear();
+}
+
+// Given an STL container consisting of (key, value) pairs, STLDeleteValues
+// deletes all the "value" components and clears the container. Does nothing
+// in the case it's given a NULL pointer.
+template <class T>
+void STLDeleteValues(T* container) {
+ if (!container)
+ return;
+ for (typename T::iterator i(container->begin()); i != container->end(); ++i)
+ delete i->second;
+ container->clear();
+}
+
+
+// The following classes provide a convenient way to delete all elements or
+// values from STL containers when they goes out of scope. This greatly
+// simplifies code that creates temporary objects and has multiple return
+// statements. Example:
+//
+// vector<MyProto *> tmp_proto;
+// STLElementDeleter<vector<MyProto *> > d(&tmp_proto);
+// if (...) return false;
+// ...
+// return success;
+
+// Given a pointer to an STL container this class will delete all the element
+// pointers when it goes out of scope.
+template<class T>
+class STLElementDeleter {
+ public:
+ STLElementDeleter<T>(T* container) : container_(container) {}
+ ~STLElementDeleter<T>() { STLDeleteElements(container_); }
+
+ private:
+ T* container_;
+};
+
+// Given a pointer to an STL container this class will delete all the value
+// pointers when it goes out of scope.
+template<class T>
+class STLValueDeleter {
+ public:
+ STLValueDeleter<T>(T* container) : container_(container) {}
+ ~STLValueDeleter<T>() { STLDeleteValues(container_); }
+
+ private:
+ T* container_;
+};
+
+// Test to see if a set, map, hash_set or hash_map contains a particular key.
+// Returns true if the key is in the collection.
+template <typename Collection, typename Key>
+bool ContainsKey(const Collection& collection, const Key& key) {
+ return collection.find(key) != collection.end();
+}
+
+namespace base {
+
+// Returns true if the container is sorted.
+template <typename Container>
+bool STLIsSorted(const Container& cont) {
+ return std::adjacent_find(cont.begin(), cont.end(),
+ std::greater<typename Container::value_type>())
+ == cont.end();
+}
+
+// Returns a new ResultType containing the difference of two sorted containers.
+template <typename ResultType, typename Arg1, typename Arg2>
+ResultType STLSetDifference(const Arg1& a1, const Arg2& a2) {
+ DCHECK(STLIsSorted(a1));
+ DCHECK(STLIsSorted(a2));
+ ResultType difference;
+ std::set_difference(a1.begin(), a1.end(),
+ a2.begin(), a2.end(),
+ std::inserter(difference, difference.end()));
+ return difference;
+}
+
+} // namespace base
+
+#endif // BASE_STL_UTIL_H_
diff --git a/src/base/stl_util_unittest.cc b/src/base/stl_util_unittest.cc
new file mode 100644
index 0000000..63d5c5c
--- /dev/null
+++ b/src/base/stl_util_unittest.cc
@@ -0,0 +1,82 @@
+// Copyright 2012 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/stl_util.h"
+
+#include <set>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+TEST(STLUtilTest, STLIsSorted) {
+ {
+ std::set<int> set;
+ set.insert(24);
+ set.insert(1);
+ set.insert(12);
+ EXPECT_TRUE(STLIsSorted(set));
+ }
+
+ {
+ std::vector<int> vector;
+ vector.push_back(1);
+ vector.push_back(1);
+ vector.push_back(4);
+ vector.push_back(64);
+ vector.push_back(12432);
+ EXPECT_TRUE(STLIsSorted(vector));
+ vector.back() = 1;
+ EXPECT_FALSE(STLIsSorted(vector));
+ }
+}
+
+TEST(STLUtilTest, STLSetDifference) {
+ std::set<int> a1;
+ a1.insert(1);
+ a1.insert(2);
+ a1.insert(3);
+ a1.insert(4);
+
+ std::set<int> a2;
+ a2.insert(3);
+ a2.insert(4);
+ a2.insert(5);
+ a2.insert(6);
+ a2.insert(7);
+
+ {
+ std::set<int> difference;
+ difference.insert(1);
+ difference.insert(2);
+ EXPECT_EQ(difference, STLSetDifference<std::set<int> >(a1, a2));
+ }
+
+ {
+ std::set<int> difference;
+ difference.insert(5);
+ difference.insert(6);
+ difference.insert(7);
+ EXPECT_EQ(difference, STLSetDifference<std::set<int> >(a2, a1));
+ }
+
+ {
+ std::vector<int> difference;
+ difference.push_back(1);
+ difference.push_back(2);
+ EXPECT_EQ(difference, STLSetDifference<std::vector<int> >(a1, a2));
+ }
+
+ {
+ std::vector<int> difference;
+ difference.push_back(5);
+ difference.push_back(6);
+ difference.push_back(7);
+ EXPECT_EQ(difference, STLSetDifference<std::vector<int> >(a2, a1));
+ }
+}
+
+} // namespace
+} // namespace base
diff --git a/src/base/string16.cc b/src/base/string16.cc
new file mode 100644
index 0000000..930e09f
--- /dev/null
+++ b/src/base/string16.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2012 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/string16.h"
+
+#if defined(WCHAR_T_IS_UTF16)
+
+#error This file should not be used on 2-byte wchar_t systems
+// If this winds up being needed on 2-byte wchar_t systems, either the
+// definitions below can be used, or the host system's wide character
+// functions like wmemcmp can be wrapped.
+
+#elif defined(WCHAR_T_IS_UTF32)
+
+#include <ostream>
+
+#include "base/utf_string_conversions.h"
+
+namespace base {
+
+int c16memcmp(const char16* s1, const char16* s2, size_t n) {
+ // We cannot call memcmp because that changes the semantics.
+ while (n-- > 0) {
+ if (*s1 != *s2) {
+ // We cannot use (*s1 - *s2) because char16 is unsigned.
+ return ((*s1 < *s2) ? -1 : 1);
+ }
+ ++s1;
+ ++s2;
+ }
+ return 0;
+}
+
+size_t c16len(const char16* s) {
+ const char16 *s_orig = s;
+ while (*s) {
+ ++s;
+ }
+ return s - s_orig;
+}
+
+const char16* c16memchr(const char16* s, char16 c, size_t n) {
+ while (n-- > 0) {
+ if (*s == c) {
+ return s;
+ }
+ ++s;
+ }
+ return 0;
+}
+
+char16* c16memmove(char16* s1, const char16* s2, size_t n) {
+ return static_cast<char16*>(memmove(s1, s2, n * sizeof(char16)));
+}
+
+char16* c16memcpy(char16* s1, const char16* s2, size_t n) {
+ return static_cast<char16*>(memcpy(s1, s2, n * sizeof(char16)));
+}
+
+char16* c16memset(char16* s, char16 c, size_t n) {
+ char16 *s_orig = s;
+ while (n-- > 0) {
+ *s = c;
+ ++s;
+ }
+ return s_orig;
+}
+
+} // namespace base
+
+template class std::basic_string<char16, base::string16_char_traits>;
+
+namespace base {
+std::ostream& operator<<(std::ostream& out, const string16& str) {
+ return out << UTF16ToUTF8(str);
+}
+
+void PrintTo(const string16& str, std::ostream* out) {
+ *out << str;
+}
+}
+
+#endif // WCHAR_T_IS_UTF32
diff --git a/src/base/string16.h b/src/base/string16.h
new file mode 100644
index 0000000..e5d8e35
--- /dev/null
+++ b/src/base/string16.h
@@ -0,0 +1,185 @@
+// Copyright (c) 2012 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_STRING16_H_
+#define BASE_STRING16_H_
+
+// WHAT:
+// A version of std::basic_string that provides 2-byte characters even when
+// wchar_t is not implemented as a 2-byte type. You can access this class as
+// string16. We also define char16, which string16 is based upon.
+//
+// WHY:
+// On Windows, wchar_t is 2 bytes, and it can conveniently handle UTF-16/UCS-2
+// data. Plenty of existing code operates on strings encoded as UTF-16.
+//
+// On many other platforms, sizeof(wchar_t) is 4 bytes by default. We can make
+// it 2 bytes by using the GCC flag -fshort-wchar. But then std::wstring fails
+// at run time, because it calls some functions (like wcslen) that come from
+// the system's native C library -- which was built with a 4-byte wchar_t!
+// It's wasteful to use 4-byte wchar_t strings to carry UTF-16 data, and it's
+// entirely improper on those systems where the encoding of wchar_t is defined
+// as UTF-32.
+//
+// Here, we define string16, which is similar to std::wstring but replaces all
+// libc functions with custom, 2-byte-char compatible routines. It is capable
+// of carrying UTF-16-encoded data.
+
+#include <stdio.h>
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+#if defined(WCHAR_T_IS_UTF16)
+
+typedef wchar_t char16;
+typedef std::wstring string16;
+
+namespace base {
+typedef std::char_traits<wchar_t> string16_char_traits;
+}
+
+#elif defined(WCHAR_T_IS_UTF32)
+
+typedef uint16 char16;
+
+namespace base {
+
+// char16 versions of the functions required by string16_char_traits; these
+// are based on the wide character functions of similar names ("w" or "wcs"
+// instead of "c16").
+BASE_EXPORT int c16memcmp(const char16* s1, const char16* s2, size_t n);
+BASE_EXPORT size_t c16len(const char16* s);
+BASE_EXPORT const char16* c16memchr(const char16* s, char16 c, size_t n);
+BASE_EXPORT char16* c16memmove(char16* s1, const char16* s2, size_t n);
+BASE_EXPORT char16* c16memcpy(char16* s1, const char16* s2, size_t n);
+BASE_EXPORT char16* c16memset(char16* s, char16 c, size_t n);
+
+struct string16_char_traits {
+ typedef char16 char_type;
+ typedef int int_type;
+
+ // int_type needs to be able to hold each possible value of char_type, and in
+ // addition, the distinct value of eof().
+ COMPILE_ASSERT(sizeof(int_type) > sizeof(char_type), unexpected_type_width);
+
+ typedef std::streamoff off_type;
+ typedef mbstate_t state_type;
+ typedef std::fpos<state_type> pos_type;
+
+ static void assign(char_type& c1, const char_type& c2) {
+ c1 = c2;
+ }
+
+ static bool eq(const char_type& c1, const char_type& c2) {
+ return c1 == c2;
+ }
+ static bool lt(const char_type& c1, const char_type& c2) {
+ return c1 < c2;
+ }
+
+ static int compare(const char_type* s1, const char_type* s2, size_t n) {
+ return c16memcmp(s1, s2, n);
+ }
+
+ static size_t length(const char_type* s) {
+ return c16len(s);
+ }
+
+ static const char_type* find(const char_type* s, size_t n,
+ const char_type& a) {
+ return c16memchr(s, a, n);
+ }
+
+ static char_type* move(char_type* s1, const char_type* s2, int_type n) {
+ return c16memmove(s1, s2, n);
+ }
+
+ static char_type* copy(char_type* s1, const char_type* s2, size_t n) {
+ return c16memcpy(s1, s2, n);
+ }
+
+ static char_type* assign(char_type* s, size_t n, char_type a) {
+ return c16memset(s, a, n);
+ }
+
+ static int_type not_eof(const int_type& c) {
+ return eq_int_type(c, eof()) ? 0 : c;
+ }
+
+ static char_type to_char_type(const int_type& c) {
+ return char_type(c);
+ }
+
+ static int_type to_int_type(const char_type& c) {
+ return int_type(c);
+ }
+
+ static bool eq_int_type(const int_type& c1, const int_type& c2) {
+ return c1 == c2;
+ }
+
+ static int_type eof() {
+ return static_cast<int_type>(EOF);
+ }
+};
+
+} // namespace base
+
+// The string class will be explicitly instantiated only once, in string16.cc.
+//
+// std::basic_string<> in GNU libstdc++ contains a static data member,
+// _S_empty_rep_storage, to represent empty strings. When an operation such
+// as assignment or destruction is performed on a string, causing its existing
+// data member to be invalidated, it must not be freed if this static data
+// member is being used. Otherwise, it counts as an attempt to free static
+// (and not allocated) data, which is a memory error.
+//
+// Generally, due to C++ template magic, _S_empty_rep_storage will be marked
+// as a coalesced symbol, meaning that the linker will combine multiple
+// instances into a single one when generating output.
+//
+// If a string class is used by multiple shared libraries, a problem occurs.
+// Each library will get its own copy of _S_empty_rep_storage. When strings
+// are passed across a library boundary for alteration or destruction, memory
+// errors will result. GNU libstdc++ contains a configuration option,
+// --enable-fully-dynamic-string (_GLIBCXX_FULLY_DYNAMIC_STRING), which
+// disables the static data member optimization, but it's a good optimization
+// and non-STL code is generally at the mercy of the system's STL
+// configuration. Fully-dynamic strings are not the default for GNU libstdc++
+// libstdc++ itself or for the libstdc++ installations on the systems we care
+// about, such as Mac OS X and relevant flavors of Linux.
+//
+// See also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=24196 .
+//
+// To avoid problems, string classes need to be explicitly instantiated only
+// once, in exactly one library. All other string users see it via an "extern"
+// declaration. This is precisely how GNU libstdc++ handles
+// std::basic_string<char> (string) and std::basic_string<wchar_t> (wstring).
+//
+// This also works around a Mac OS X linker bug in ld64-85.2.1 (Xcode 3.1.2),
+// in which the linker does not fully coalesce symbols when dead code
+// stripping is enabled. This bug causes the memory errors described above
+// to occur even when a std::basic_string<> does not cross shared library
+// boundaries, such as in statically-linked executables.
+//
+// TODO(mark): File this bug with Apple and update this note with a bug number.
+
+extern template
+class BASE_EXPORT std::basic_string<char16, base::string16_char_traits>;
+
+typedef std::basic_string<char16, base::string16_char_traits> string16;
+
+namespace base {
+BASE_EXPORT extern std::ostream& operator<<(std::ostream& out,
+ const string16& str);
+
+// This is required by googletest to print a readable output on test failures.
+BASE_EXPORT extern void PrintTo(const string16& str, std::ostream* out);
+}
+
+#endif // WCHAR_T_IS_UTF32
+
+#endif // BASE_STRING16_H_
diff --git a/src/base/string16_unittest.cc b/src/base/string16_unittest.cc
new file mode 100644
index 0000000..e833ed2
--- /dev/null
+++ b/src/base/string16_unittest.cc
@@ -0,0 +1,53 @@
+// 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 <sstream>
+
+#include "base/string16.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(WCHAR_T_IS_UTF32)
+
+// We define a custom operator<< for string16 so we can use it with logging.
+// This tests that conversion.
+TEST(String16Test, OutputStream) {
+ // Basic stream test.
+ {
+ std::ostringstream stream;
+ stream << "Empty '" << string16() << "' standard '"
+ << string16(ASCIIToUTF16("Hello, world")) << "'";
+ EXPECT_STREQ("Empty '' standard 'Hello, world'",
+ stream.str().c_str());
+ }
+
+ // Interesting edge cases.
+ {
+ // These should each get converted to the invalid character: EF BF BD.
+ string16 initial_surrogate;
+ initial_surrogate.push_back(0xd800);
+ string16 final_surrogate;
+ final_surrogate.push_back(0xdc00);
+
+ // Old italic A = U+10300, will get converted to: F0 90 8C 80 'z'.
+ string16 surrogate_pair;
+ surrogate_pair.push_back(0xd800);
+ surrogate_pair.push_back(0xdf00);
+ surrogate_pair.push_back('z');
+
+ // Will get converted to the invalid char + 's': EF BF BD 's'.
+ string16 unterminated_surrogate;
+ unterminated_surrogate.push_back(0xd800);
+ unterminated_surrogate.push_back('s');
+
+ std::ostringstream stream;
+ stream << initial_surrogate << "," << final_surrogate << ","
+ << surrogate_pair << "," << unterminated_surrogate;
+
+ EXPECT_STREQ("\xef\xbf\xbd,\xef\xbf\xbd,\xf0\x90\x8c\x80z,\xef\xbf\xbds",
+ stream.str().c_str());
+ }
+}
+
+#endif
diff --git a/src/base/string_number_conversions.cc b/src/base/string_number_conversions.cc
new file mode 100644
index 0000000..1aafc4e
--- /dev/null
+++ b/src/base/string_number_conversions.cc
@@ -0,0 +1,426 @@
+// Copyright (c) 2012 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/string_number_conversions.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <wctype.h>
+
+#include <limits>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/third_party/dmg_fp/dmg_fp.h"
+#include "base/utf_string_conversions.h"
+
+namespace base {
+
+namespace {
+
+// The following template is used to convert a size in bytes value into an
+// unsigned integral type. For example, if we want to use the unsigned type
+// corresponding to int, we can use type MakeUnsigned<sizeof(int)>::Unsigned.
+template <size_t size_in_bytes>
+struct MakeUnsigned {};
+
+template <>
+struct MakeUnsigned<1> {
+ typedef uint8 Unsigned;
+};
+
+template <>
+struct MakeUnsigned<2> {
+ typedef uint16 Unsigned;
+};
+
+template <>
+struct MakeUnsigned<4> {
+ typedef uint32 Unsigned;
+};
+
+template <>
+struct MakeUnsigned<8> {
+ typedef uint64 Unsigned;
+};
+
+template <typename STR, typename INT>
+struct IntToStringT {
+ static STR IntToString(INT value) {
+ // log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4.
+ // So round up to allocate 3 output characters per byte, plus 1 for '-'.
+ const int kOutputBufSize = 3 * sizeof(INT) + 1;
+
+ // Allocate the whole string right away, we will right back to front, and
+ // then return the substr of what we ended up using.
+ STR outbuf(kOutputBufSize, 0);
+
+ // We cannot directly use 'value < 0' as it will generate a warning on
+ // unsigned types in certain compilers because such check is meaningless.
+ bool is_neg = value < 1 && value != 0;
+ // We cannot simply use '-value' as it will generate a warning on unsigned
+ // types in certain compilers which treats such operation as invalid.
+ typename MakeUnsigned<sizeof(value)>::Unsigned res =
+ is_neg ? 0 - value : value;
+
+ for (typename STR::iterator it = outbuf.end();;) {
+ DCHECK(it != outbuf.begin());
+ --it;
+ *it = static_cast<typename STR::value_type>((res % 10) + '0');
+ res /= 10;
+
+ // We're done..
+ if (res == 0) {
+ if (is_neg) {
+ DCHECK(it != outbuf.begin());
+ --it;
+ *it = static_cast<typename STR::value_type>('-');
+ }
+ return STR(it, outbuf.end());
+ }
+ }
+ NOTREACHED();
+ return STR();
+ }
+};
+
+// Utility to convert a character to a digit in a given base
+template<typename CHAR, int BASE, bool BASE_LTE_10> class BaseCharToDigit {
+};
+
+// Faster specialization for bases <= 10
+template<typename CHAR, int BASE> class BaseCharToDigit<CHAR, BASE, true> {
+ public:
+ static bool Convert(CHAR c, uint8* digit) {
+ if (c >= '0' && c < '0' + BASE) {
+ *digit = c - '0';
+ return true;
+ }
+ return false;
+ }
+};
+
+// Specialization for bases where 10 < base <= 36
+template<typename CHAR, int BASE> class BaseCharToDigit<CHAR, BASE, false> {
+ public:
+ static bool Convert(CHAR c, uint8* digit) {
+ if (c >= '0' && c <= '9') {
+ *digit = c - '0';
+ } else if (c >= 'a' && c < 'a' + BASE - 10) {
+ *digit = c - 'a' + 10;
+ } else if (c >= 'A' && c < 'A' + BASE - 10) {
+ *digit = c - 'A' + 10;
+ } else {
+ return false;
+ }
+ return true;
+ }
+};
+
+template<int BASE, typename CHAR> bool CharToDigit(CHAR c, uint8* digit) {
+ return BaseCharToDigit<CHAR, BASE, BASE <= 10>::Convert(c, digit);
+}
+
+// There is an IsWhitespace for wchars defined in string_util.h, but it is
+// locale independent, whereas the functions we are replacing were
+// locale-dependent. TBD what is desired, but for the moment let's not introduce
+// a change in behaviour.
+template<typename CHAR> class WhitespaceHelper {
+};
+
+template<> class WhitespaceHelper<char> {
+ public:
+ static bool Invoke(char c) {
+ return 0 != isspace(static_cast<unsigned char>(c));
+ }
+};
+
+template<> class WhitespaceHelper<char16> {
+ public:
+ static bool Invoke(char16 c) {
+ return 0 != iswspace(c);
+ }
+};
+
+template<typename CHAR> bool LocalIsWhitespace(CHAR c) {
+ return WhitespaceHelper<CHAR>::Invoke(c);
+}
+
+// IteratorRangeToNumberTraits should provide:
+// - a typedef for iterator_type, the iterator type used as input.
+// - a typedef for value_type, the target numeric type.
+// - static functions min, max (returning the minimum and maximum permitted
+// values)
+// - constant kBase, the base in which to interpret the input
+template<typename IteratorRangeToNumberTraits>
+class IteratorRangeToNumber {
+ public:
+ typedef IteratorRangeToNumberTraits traits;
+ typedef typename traits::iterator_type const_iterator;
+ typedef typename traits::value_type value_type;
+
+ // Generalized iterator-range-to-number conversion.
+ //
+ static bool Invoke(const_iterator begin,
+ const_iterator end,
+ value_type* output) {
+ bool valid = true;
+
+ while (begin != end && LocalIsWhitespace(*begin)) {
+ valid = false;
+ ++begin;
+ }
+
+ if (begin != end && *begin == '-') {
+ if (!Negative::Invoke(begin + 1, end, output)) {
+ valid = false;
+ }
+ // For unsigned types, any negative value is invalid and will be set to 0.
+ if (traits::min() == 0) {
+ if (*output != 0) {
+ valid = false;
+ }
+ *output = 0;
+ }
+ } else {
+ if (begin != end && *begin == '+') {
+ ++begin;
+ }
+ if (!Positive::Invoke(begin, end, output)) {
+ valid = false;
+ }
+ }
+
+ return valid;
+ }
+
+ private:
+ // Sign provides:
+ // - a static function, CheckBounds, that determines whether the next digit
+ // causes an overflow/underflow
+ // - a static function, Increment, that appends the next digit appropriately
+ // according to the sign of the number being parsed.
+ template<typename Sign>
+ class Base {
+ public:
+ static bool Invoke(const_iterator begin, const_iterator end,
+ typename traits::value_type* output) {
+ *output = 0;
+
+ if (begin == end) {
+ return false;
+ }
+
+ // Note: no performance difference was found when using template
+ // specialization to remove this check in bases other than 16
+ if (traits::kBase == 16 && end - begin > 2 && *begin == '0' &&
+ (*(begin + 1) == 'x' || *(begin + 1) == 'X')) {
+ begin += 2;
+ }
+
+ for (const_iterator current = begin; current != end; ++current) {
+ uint8 new_digit = 0;
+
+ if (!CharToDigit<traits::kBase>(*current, &new_digit)) {
+ return false;
+ }
+
+ if (current != begin) {
+ if (!Sign::CheckBounds(output, new_digit)) {
+ return false;
+ }
+ *output *= traits::kBase;
+ }
+
+ Sign::Increment(new_digit, output);
+ }
+ return true;
+ }
+ };
+
+ class Positive : public Base<Positive> {
+ public:
+ static bool CheckBounds(value_type* output, uint8 new_digit) {
+ if (*output > static_cast<value_type>(traits::max() / traits::kBase) ||
+ (*output == static_cast<value_type>(traits::max() / traits::kBase) &&
+ new_digit > traits::max() % traits::kBase)) {
+ *output = traits::max();
+ return false;
+ }
+ return true;
+ }
+ static void Increment(uint8 increment, value_type* output) {
+ *output += increment;
+ }
+ };
+
+ class Negative : public Base<Negative> {
+ public:
+ static bool CheckBounds(value_type* output, uint8 new_digit) {
+ if (*output < traits::min() / traits::kBase ||
+ (*output == traits::min() / traits::kBase &&
+ new_digit > 0 - traits::min() % traits::kBase)) {
+ *output = traits::min();
+ return false;
+ }
+ return true;
+ }
+ static void Increment(uint8 increment, value_type* output) {
+ *output -= increment;
+ }
+ };
+};
+
+template<typename ITERATOR, typename VALUE, int BASE>
+class BaseIteratorRangeToNumberTraits {
+ public:
+ typedef ITERATOR iterator_type;
+ typedef VALUE value_type;
+ static value_type min() {
+ return std::numeric_limits<value_type>::min();
+ }
+ static value_type max() {
+ return std::numeric_limits<value_type>::max();
+ }
+ static const int kBase = BASE;
+};
+
+template<typename ITERATOR>
+class BaseHexIteratorRangeToIntTraits
+ : public BaseIteratorRangeToNumberTraits<ITERATOR, int, 16> {
+ public:
+ // Allow parsing of 0xFFFFFFFF, which is technically an overflow
+ static unsigned int max() {
+ return std::numeric_limits<unsigned int>::max();
+ }
+};
+
+typedef BaseHexIteratorRangeToIntTraits<StringPiece::const_iterator>
+ HexIteratorRangeToIntTraits;
+
+template<typename STR>
+bool HexStringToBytesT(const STR& input, std::vector<uint8>* output) {
+ DCHECK_EQ(output->size(), 0u);
+ size_t count = input.size();
+ if (count == 0 || (count % 2) != 0)
+ return false;
+ for (uintptr_t i = 0; i < count / 2; ++i) {
+ uint8 msb = 0; // most significant 4 bits
+ uint8 lsb = 0; // least significant 4 bits
+ if (!CharToDigit<16>(input[i * 2], &msb) ||
+ !CharToDigit<16>(input[i * 2 + 1], &lsb))
+ return false;
+ output->push_back((msb << 4) | lsb);
+ }
+ return true;
+}
+
+template <typename VALUE, int BASE>
+class StringPieceToNumberTraits
+ : public BaseIteratorRangeToNumberTraits<StringPiece::const_iterator,
+ VALUE,
+ BASE> {};
+
+template <typename VALUE>
+bool StringToIntImpl(const StringPiece& input, VALUE* output) {
+ return IteratorRangeToNumber<StringPieceToNumberTraits<VALUE, 10> >::Invoke(
+ input.begin(), input.end(), output);
+}
+
+template <typename VALUE, int BASE>
+class StringPiece16ToNumberTraits
+ : public BaseIteratorRangeToNumberTraits<StringPiece16::const_iterator,
+ VALUE,
+ BASE> {};
+
+template <typename VALUE>
+bool String16ToIntImpl(const StringPiece16& input, VALUE* output) {
+ return IteratorRangeToNumber<StringPiece16ToNumberTraits<VALUE, 10> >::Invoke(
+ input.begin(), input.end(), output);
+}
+
+} // namespace
+
+#define DEFINE_INTEGRAL_TO_STRING_CONVERSIONS(Name, CppType) \
+ std::string Name##ToString(CppType value) { \
+ return IntToStringT<std::string, CppType>::IntToString(value); \
+ } \
+ string16 Name##ToString16(CppType value) { \
+ return IntToStringT<string16, CppType>::IntToString(value); \
+ }
+
+INTEGRAL_STRING_CONVERSIONS_FOR_EACH(DEFINE_INTEGRAL_TO_STRING_CONVERSIONS)
+#undef DEFINE_INTEGRAL_TO_STRING_CONVERSIONS
+
+std::string DoubleToString(double value) {
+ // According to g_fmt.cc, it is sufficient to declare a buffer of size 32.
+ char buffer[32];
+ dmg_fp::g_fmt(buffer, value);
+ return std::string(buffer);
+}
+
+#define DEFINE_STRING_TO_INTEGRAL_CONVERSIONS(Name, CppType) \
+ bool StringTo##Name(const StringPiece& input, CppType* output) { \
+ return StringToIntImpl(input, output); \
+ } \
+ bool StringTo##Name(const StringPiece16& input, CppType* output) { \
+ return String16ToIntImpl(input, output); \
+ }
+
+INTEGRAL_STRING_CONVERSIONS_FOR_EACH(DEFINE_STRING_TO_INTEGRAL_CONVERSIONS)
+#undef DEFINE_STRING_TO_INTEGRAL_CONVERSIONS
+
+bool StringToDouble(const std::string& input, double* output) {
+ errno = 0; // Thread-safe? It is on at least Mac, Linux, and Windows.
+ char* endptr = NULL;
+ *output = dmg_fp::strtod(input.c_str(), &endptr);
+
+ // Cases to return false:
+ // - If errno is ERANGE, there was an overflow or underflow.
+ // - If the input string is empty, there was nothing to parse.
+ // - If endptr does not point to the end of the string, there are either
+ // characters remaining in the string after a parsed number, or the string
+ // does not begin with a parseable number. endptr is compared to the
+ // expected end given the string's stated length to correctly catch cases
+ // where the string contains embedded NUL characters.
+ // - If the first character is a space, there was leading whitespace
+ return errno == 0 &&
+ !input.empty() &&
+ input.c_str() + input.length() == endptr &&
+ !isspace(input[0]);
+}
+
+// Note: if you need to add String16ToDouble, first ask yourself if it's
+// really necessary. If it is, probably the best implementation here is to
+// convert to 8-bit and then use the 8-bit version.
+
+// Note: if you need to add an iterator range version of StringToDouble, first
+// ask yourself if it's really necessary. If it is, probably the best
+// implementation here is to instantiate a string and use the string version.
+
+std::string HexEncode(const void* bytes, size_t size) {
+ static const char kHexChars[] = "0123456789ABCDEF";
+
+ // Each input byte creates two output hex characters.
+ std::string ret(size * 2, '\0');
+
+ for (size_t i = 0; i < size; ++i) {
+ char b = reinterpret_cast<const char*>(bytes)[i];
+ ret[(i * 2)] = kHexChars[(b >> 4) & 0xf];
+ ret[(i * 2) + 1] = kHexChars[b & 0xf];
+ }
+ return ret;
+}
+
+bool HexStringToInt(const StringPiece& input, int* output) {
+ return IteratorRangeToNumber<HexIteratorRangeToIntTraits>::Invoke(
+ input.begin(), input.end(), output);
+}
+
+bool HexStringToBytes(const std::string& input, std::vector<uint8>* output) {
+ return HexStringToBytesT(input, output);
+}
+
+} // namespace base
diff --git a/src/base/string_number_conversions.h b/src/base/string_number_conversions.h
new file mode 100644
index 0000000..7b7cfe1
--- /dev/null
+++ b/src/base/string_number_conversions.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2012 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_STRING_NUMBER_CONVERSIONS_H_
+#define BASE_STRING_NUMBER_CONVERSIONS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/string_piece.h"
+#include "base/string16.h"
+
+// ----------------------------------------------------------------------------
+// IMPORTANT MESSAGE FROM YOUR SPONSOR
+//
+// This file contains no "wstring" variants. New code should use string16. If
+// you need to make old code work, use the UTF8 version and convert. Please do
+// not add wstring variants.
+//
+// Please do not add "convenience" functions for converting strings to integers
+// that return the value and ignore success/failure. That encourages people to
+// write code that doesn't properly handle the error conditions.
+// ----------------------------------------------------------------------------
+
+namespace base {
+
+// The following macro expects a macro functor accepting two parameters: a name
+// and a C++ type. It is used to generate the declarations, definitions and
+// unit tests for types listed below.
+#define INTEGRAL_STRING_CONVERSIONS_FOR_EACH(MacroOp) \
+ MacroOp(Int, int) \
+ MacroOp(Uint, unsigned int) \
+ MacroOp(Int8, int8) \
+ MacroOp(Uint8, uint8) \
+ MacroOp(Int16, int16) \
+ MacroOp(Uint16, uint16) \
+ MacroOp(Int32, int32) \
+ MacroOp(Uint32, uint32) \
+ MacroOp(Int64, int64) \
+ MacroOp(Uint64, uint64) \
+ MacroOp(SizeT, size_t)
+
+// Number -> string conversions ------------------------------------------------
+#define DECLARE_INTEGRAL_TO_STRING_CONVERSIONS(Name, CppType) \
+ BASE_EXPORT std::string Name##ToString(CppType value); \
+ BASE_EXPORT string16 Name##ToString16(CppType value);
+
+INTEGRAL_STRING_CONVERSIONS_FOR_EACH(DECLARE_INTEGRAL_TO_STRING_CONVERSIONS)
+#undef DECLARE_INTEGRAL_TO_STRING_CONVERSIONS
+
+// DoubleToString converts the double to a string format that ignores the
+// locale. If you want to use locale specific formatting, use ICU.
+BASE_EXPORT std::string DoubleToString(double value);
+
+// String -> number conversions ------------------------------------------------
+
+// Perform a best-effort conversion of the input string to a numeric type,
+// setting |*output| to the result of the conversion. Returns true for
+// "perfect" conversions; returns false in the following cases:
+// - Overflow/underflow. |*output| will be set to the maximum value supported
+// by the data type.
+// - Trailing characters in the string after parsing the number. |*output|
+// will be set to the value of the number that was parsed.
+// - Leading whitespace in the string before parsing the number. |*output| will
+// be set to the value of the number that was parsed.
+// - No characters parseable as a number at the beginning of the string.
+// |*output| will be set to 0.
+// - Empty string. |*output| will be set to 0.
+#define DECLARE_STRING_TO_INTEGRAL_CONVERSIONS(Name, CppType) \
+ BASE_EXPORT bool StringTo##Name(const StringPiece& input, CppType* output); \
+ BASE_EXPORT bool StringTo##Name(const StringPiece16& input, CppType* output);
+
+INTEGRAL_STRING_CONVERSIONS_FOR_EACH(DECLARE_STRING_TO_INTEGRAL_CONVERSIONS)
+#undef DECLARE_STRING_TO_INTEGRAL_CONVERSIONS
+
+// For floating-point conversions, only conversions of input strings in decimal
+// form are defined to work. Behavior with strings representing floating-point
+// numbers in hexadecimal, and strings representing non-fininte values (such as
+// NaN and inf) is undefined. Otherwise, these behave the same as the integral
+// variants. This expects the input string to NOT be specific to the locale.
+// If your input is locale specific, use ICU to read the number.
+BASE_EXPORT bool StringToDouble(const std::string& input, double* output);
+
+// Hex encoding ----------------------------------------------------------------
+
+// Returns a hex string representation of a binary buffer. The returned hex
+// string will be in upper case. This function does not check if |size| is
+// within reasonable limits since it's written with trusted data in mind. If
+// you suspect that the data you want to format might be large, the absolute
+// max size for |size| should be is
+// std::numeric_limits<size_t>::max() / 2
+BASE_EXPORT std::string HexEncode(const void* bytes, size_t size);
+
+// Best effort conversion, see StringToInt above for restrictions.
+BASE_EXPORT bool HexStringToInt(const StringPiece& input, int* output);
+
+// Similar to the previous functions, except that output is a vector of bytes.
+// |*output| will contain as many bytes as were successfully parsed prior to the
+// error. There is no overflow, but input.size() must be evenly divisible by 2.
+// Leading 0x or +/- are not allowed.
+BASE_EXPORT bool HexStringToBytes(const std::string& input,
+ std::vector<uint8>* output);
+
+} // namespace base
+
+#endif // BASE_STRING_NUMBER_CONVERSIONS_H_
+
diff --git a/src/base/string_number_conversions_unittest.cc b/src/base/string_number_conversions_unittest.cc
new file mode 100644
index 0000000..0bb33b5
--- /dev/null
+++ b/src/base/string_number_conversions_unittest.cc
@@ -0,0 +1,388 @@
+// Copyright (c) 2012 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 <math.h>
+
+#include <limits>
+#include <sstream>
+
+#include "base/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+// When insert values of one byte type like char or uint8 into output streams,
+// the values will be output as a character. In our tests we'd like it to be
+// output as its numeric value. We introduce the following template class so
+// we can use 'oss << static_cast<StreamSafeType<int8>::Type>(value);' to output
+// the value in int8 as a number.
+template <typename T>
+struct StreamSafeType {
+ typedef T Type;
+};
+template <>
+struct StreamSafeType<int8> {
+ typedef int16 Type;
+};
+template <>
+struct StreamSafeType<uint8> {
+ typedef uint16 Type;
+};
+
+TEST(StringNumberConversionsTest, IntegralToString) {
+#define DEFINE_INTEGRAL_TO_STRING_TEST(Name, CppType) \
+ { \
+ static const CppType kValuesToTest[] = { \
+ 0, static_cast<CppType>(-1), 42, std::numeric_limits<CppType>::min(), \
+ std::numeric_limits<CppType>::max()}; \
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kValuesToTest); ++i) { \
+ CppType value = static_cast<CppType>(kValuesToTest[i]); \
+ std::ostringstream oss; \
+ oss << static_cast<StreamSafeType<CppType>::Type>(value); \
+ EXPECT_EQ(oss.str(), Name##ToString(value)); \
+ EXPECT_EQ(UTF8ToUTF16(oss.str()), Name##ToString16(value)); \
+ } \
+ }
+
+ INTEGRAL_STRING_CONVERSIONS_FOR_EACH(DEFINE_INTEGRAL_TO_STRING_TEST)
+#undef DEFINE_INTEGRAL_TO_STRING_TEST
+}
+
+TEST(StringNumberConversionsTest, StringToIntegral) {
+#define DEFINE_STRING_TO_INTEGRAL_TEST(Name, CppType) \
+ { \
+ static const CppType kValuesToTest[] = { \
+ 0, 42, std::numeric_limits<CppType>::min(), \
+ std::numeric_limits<CppType>::max()}; \
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kValuesToTest); ++i) { \
+ std::ostringstream oss; \
+ oss << static_cast<StreamSafeType<CppType>::Type>(kValuesToTest[i]); \
+ \
+ CppType output = 0; \
+ EXPECT_TRUE(StringTo##Name(oss.str(), &output)); \
+ EXPECT_EQ(kValuesToTest[i], output); \
+ \
+ string16 utf16_input = UTF8ToUTF16(oss.str()); \
+ output = 0; \
+ EXPECT_TRUE(StringTo##Name(utf16_input, &output)); \
+ EXPECT_EQ(kValuesToTest[i], output); \
+ } \
+ \
+ /* One additional test to verify that conversion of numbers in strings \
+ * with */ \
+ /* embedded NUL characters. The NUL and extra data after it should be */ \
+ /* interpreted as junk after the number. */ \
+ const char input[] = "6\06"; \
+ std::string input_string(input, arraysize(input) - 1); \
+ CppType output; \
+ EXPECT_FALSE(StringTo##Name(input_string, &output)); \
+ EXPECT_EQ(6, output); \
+ \
+ string16 utf16_input = UTF8ToUTF16(input_string); \
+ output = 0; \
+ EXPECT_FALSE(StringTo##Name(utf16_input, &output)); \
+ EXPECT_EQ(6, output); \
+ \
+ output = 0; \
+ const char16 negative_wide_input[] = {0xFF4D, '4', '2', 0}; \
+ EXPECT_FALSE(StringTo##Name(string16(negative_wide_input), &output)); \
+ EXPECT_EQ(0, output); \
+ }
+
+ INTEGRAL_STRING_CONVERSIONS_FOR_EACH(DEFINE_STRING_TO_INTEGRAL_TEST)
+#undef DEFINE_STRING_TO_INTEGRAL_TEST
+}
+
+TEST(StringNumberConversionsTest, StringToIntegralFailure) {
+#define DEFINE_STRING_TO_INTEGRAL_TEST(Name, CppType) \
+ { \
+ static const struct { \
+ std::string input; \
+ CppType output; \
+ } kCasesToTest[] = { \
+ {"42\x99", 42}, \
+ {"\x99" "42\x99", 0}, \
+ {"", 0}, \
+ {" 42", 42}, \
+ {"42 ", 42}, \
+ {"\t\n\v\f\r 42", 42}, \
+ {"blah42", 0}, \
+ {"42blah", 42}, \
+ {"blah42blah", 0}, \
+ {"-73.15", -73}, \
+ {"+98.6", 98}, \
+ {"--123", 0}, \
+ {"++123", 0}, \
+ {"-+123", 0}, \
+ {"+-123", 0}, \
+ {"-", 0}, \
+ {"-99999999999999999999999", std::numeric_limits<CppType>::min()}, \
+ {"99999999999999999999999", std::numeric_limits<CppType>::max()}, \
+ }; \
+ \
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kCasesToTest); ++i) { \
+ CppType expected_value = kCasesToTest[i].output; \
+ if (std::numeric_limits<CppType>::min() == 0 && \
+ kCasesToTest[i].input[0] == '-') { \
+ /* Any negative values should be mapped to 0 on unsigned types. */ \
+ expected_value = 0; \
+ } \
+ CppType output = 0; \
+ EXPECT_FALSE(StringTo##Name(kCasesToTest[i].input, &output)); \
+ EXPECT_EQ(expected_value, output); \
+ \
+ string16 utf16_input = UTF8ToUTF16(kCasesToTest[i].input); \
+ output = 0; \
+ EXPECT_FALSE(StringTo##Name(utf16_input, &output)); \
+ EXPECT_EQ(expected_value, output); \
+ } \
+ }
+
+ INTEGRAL_STRING_CONVERSIONS_FOR_EACH(DEFINE_STRING_TO_INTEGRAL_TEST)
+#undef DEFINE_STRING_TO_INTEGRAL_TEST
+}
+
+TEST(StringNumberConversionsTest, StringToIntegralOffByOne) {
+#define DEFINE_STRING_TO_INTEGRAL_TEST(Name, CppType) \
+ { \
+ CppType expected_value = std::numeric_limits<CppType>::min(); \
+ /* This check doesn't apply to unsigned types */ \
+ if (expected_value != 0) { \
+ std::ostringstream oss; \
+ oss << static_cast<StreamSafeType<CppType>::Type>(expected_value); \
+ std::string input = oss.str(); \
+ /* The lowest digit of the value cannot be '9'. */ \
+ ASSERT_NE(*input.rbegin(), '9'); \
+ /* Decrease the value by one, so the value is too small to represent. */ \
+ /* Note that to decrease a negative value by one, we have to actually */ \
+ /* increase its last digit by one. */ \
+ ++*input.rbegin(); \
+ \
+ CppType output = 0; \
+ EXPECT_FALSE(StringTo##Name(input, &output)); \
+ EXPECT_EQ(expected_value, output); \
+ \
+ string16 utf16_input = UTF8ToUTF16(input); \
+ output = 0; \
+ EXPECT_FALSE(StringTo##Name(utf16_input, &output)); \
+ EXPECT_EQ(expected_value, output); \
+ } \
+ expected_value = std::numeric_limits<CppType>::max(); \
+ std::ostringstream oss; \
+ oss << static_cast<StreamSafeType<CppType>::Type>(expected_value); \
+ std::string input = oss.str(); \
+ /* The lowest digit of the value cannot be '9'. */ \
+ ASSERT_NE(*input.rbegin(), '0'); \
+ /* Increase the value by one, so the value is too large to represent. */ \
+ ++*input.rbegin(); \
+ \
+ CppType output = 0; \
+ EXPECT_FALSE(StringTo##Name(input, &output)); \
+ EXPECT_EQ(expected_value, output); \
+ \
+ string16 utf16_input = UTF8ToUTF16(input); \
+ output = 0; \
+ EXPECT_FALSE(StringTo##Name(utf16_input, &output)); \
+ EXPECT_EQ(expected_value, output); \
+ }
+
+ INTEGRAL_STRING_CONVERSIONS_FOR_EACH(DEFINE_STRING_TO_INTEGRAL_TEST)
+#undef DEFINE_STRING_TO_INTEGRAL_TEST
+}
+
+TEST(StringNumberConversionsTest, HexStringToInt) {
+ static const struct {
+ std::string input;
+ int output;
+ bool success;
+ } cases[] = {
+ {"0", 0, true},
+ {"42", 66, true},
+ {"-42", -66, true},
+ {"+42", 66, true},
+ {"7fffffff", INT_MAX, true},
+ {"80000000", INT_MIN, true},
+ {"ffffffff", -1, true},
+ {"DeadBeef", 0xdeadbeef, true},
+ {"0x42", 66, true},
+ {"-0x42", -66, true},
+ {"+0x42", 66, true},
+ {"0x7fffffff", INT_MAX, true},
+ {"0x80000000", INT_MIN, true},
+ {"0xffffffff", -1, true},
+ {"0XDeadBeef", 0xdeadbeef, true},
+ {"0x0f", 15, true},
+ {"0f", 15, true},
+ {" 45", 0x45, false},
+ {"\t\n\v\f\r 0x45", 0x45, false},
+ {" 45", 0x45, false},
+ {"45 ", 0x45, false},
+ {"45:", 0x45, false},
+ {"efgh", 0xef, false},
+ {"0xefgh", 0xef, false},
+ {"hgfe", 0, false},
+ {"100000000", -1, false}, // don't care about |output|, just |success|
+ {"-", 0, false},
+ {"", 0, false},
+ {"0x", 0, false},
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+ int output = 0;
+ EXPECT_EQ(cases[i].success, HexStringToInt(cases[i].input, &output));
+ EXPECT_EQ(cases[i].output, output);
+ }
+ // One additional test to verify that conversion of numbers in strings with
+ // embedded NUL characters. The NUL and extra data after it should be
+ // interpreted as junk after the number.
+ const char input[] = "0xc0ffee\09";
+ std::string input_string(input, arraysize(input) - 1);
+ int output;
+ EXPECT_FALSE(HexStringToInt(input_string, &output));
+ EXPECT_EQ(0xc0ffee, output);
+}
+
+TEST(StringNumberConversionsTest, HexStringToBytes) {
+ static const struct {
+ const std::string input;
+ const char* output;
+ size_t output_len;
+ bool success;
+ } cases[] = {
+ {"0", "", 0, false}, // odd number of characters fails
+ {"00", "\0", 1, true},
+ {"42", "\x42", 1, true},
+ {"-42", "", 0, false}, // any non-hex value fails
+ {"+42", "", 0, false},
+ {"7fffffff", "\x7f\xff\xff\xff", 4, true},
+ {"80000000", "\x80\0\0\0", 4, true},
+ {"deadbeef", "\xde\xad\xbe\xef", 4, true},
+ {"DeadBeef", "\xde\xad\xbe\xef", 4, true},
+ {"0x42", "", 0, false}, // leading 0x fails (x is not hex)
+ {"0f", "\xf", 1, true},
+ {"45 ", "\x45", 1, false},
+ {"efgh", "\xef", 1, false},
+ {"", "", 0, false},
+ {"0123456789ABCDEF", "\x01\x23\x45\x67\x89\xAB\xCD\xEF", 8, true},
+ {"0123456789ABCDEF012345",
+ "\x01\x23\x45\x67\x89\xAB\xCD\xEF\x01\x23\x45", 11, true},
+ };
+
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+ std::vector<uint8> output;
+ std::vector<uint8> compare;
+ EXPECT_EQ(cases[i].success, HexStringToBytes(cases[i].input, &output)) <<
+ i << ": " << cases[i].input;
+ for (size_t j = 0; j < cases[i].output_len; ++j)
+ compare.push_back(static_cast<uint8>(cases[i].output[j]));
+ ASSERT_EQ(output.size(), compare.size()) << i << ": " << cases[i].input;
+ EXPECT_TRUE(std::equal(output.begin(), output.end(), compare.begin())) <<
+ i << ": " << cases[i].input;
+ }
+}
+
+TEST(StringNumberConversionsTest, StringToDouble) {
+ static const struct {
+ std::string input;
+ double output;
+ bool success;
+ } cases[] = {
+ {"0", 0.0, true},
+ {"42", 42.0, true},
+ {"-42", -42.0, true},
+ {"123.45", 123.45, true},
+ {"-123.45", -123.45, true},
+ {"+123.45", 123.45, true},
+ {"2.99792458e8", 299792458.0, true},
+ {"149597870.691E+3", 149597870691.0, true},
+ {"6.", 6.0, true},
+ {"9e99999999999999999999", HUGE_VAL, false},
+ {"-9e99999999999999999999", -HUGE_VAL, false},
+ {"1e-2", 0.01, true},
+ {"42 ", 42.0, false},
+ {" 1e-2", 0.01, false},
+ {"1e-2 ", 0.01, false},
+ {"-1E-7", -0.0000001, true},
+ {"01e02", 100, true},
+ {"2.3e15", 2.3e15, true},
+ {"\t\n\v\f\r -123.45e2", -12345.0, false},
+ {"+123 e4", 123.0, false},
+ {"123e ", 123.0, false},
+ {"123e", 123.0, false},
+ {" 2.99", 2.99, false},
+ {"1e3.4", 1000.0, false},
+ {"nothing", 0.0, false},
+ {"-", 0.0, false},
+ {"+", 0.0, false},
+ {"", 0.0, false},
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+ double output;
+ EXPECT_EQ(cases[i].success, StringToDouble(cases[i].input, &output));
+ EXPECT_DOUBLE_EQ(cases[i].output, output);
+ }
+
+ // One additional test to verify that conversion of numbers in strings with
+ // embedded NUL characters. The NUL and extra data after it should be
+ // interpreted as junk after the number.
+ const char input[] = "3.14\0159";
+ std::string input_string(input, arraysize(input) - 1);
+ double output;
+ EXPECT_FALSE(StringToDouble(input_string, &output));
+ EXPECT_DOUBLE_EQ(3.14, output);
+}
+
+TEST(StringNumberConversionsTest, DoubleToString) {
+ static const struct {
+ double input;
+ const char* expected;
+ } cases[] = {
+ {0.0, "0"},
+ {1.25, "1.25"},
+ {1.33518e+012, "1.33518e+12"},
+ {1.33489e+012, "1.33489e+12"},
+ {1.33505e+012, "1.33505e+12"},
+ {1.33545e+009, "1335450000"},
+ {1.33503e+009, "1335030000"},
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+ EXPECT_EQ(cases[i].expected, DoubleToString(cases[i].input));
+ }
+
+ // The following two values were seen in crashes in the wild.
+#if defined(ARCH_CPU_BIG_ENDIAN)
+ const char input_bytes[8] = {'\x42', '\x73', '\x6d', '\xee', 0, 0, 0, 0};
+#else
+ const char input_bytes[8] = {0, 0, 0, 0, '\xee', '\x6d', '\x73', '\x42'};
+#endif
+ double input = 0;
+ memcpy(&input, input_bytes, arraysize(input_bytes));
+ EXPECT_EQ("1335179083776", DoubleToString(input));
+#if defined(ARCH_CPU_BIG_ENDIAN)
+ const char input_bytes2[8] =
+ {'\x42', '\x73', '\x6c', '\xda', '\xa0', 0, 0, 0};
+#else
+ const char input_bytes2[8] =
+ {0, 0, 0, '\xa0', '\xda', '\x6c', '\x73', '\x42'};
+#endif
+ input = 0;
+ memcpy(&input, input_bytes2, arraysize(input_bytes2));
+ EXPECT_EQ("1334890332160", DoubleToString(input));
+}
+
+TEST(StringNumberConversionsTest, HexEncode) {
+ std::string hex(HexEncode(NULL, 0));
+ EXPECT_EQ(hex.length(), 0U);
+ unsigned char bytes[] = {0x01, 0xff, 0x02, 0xfe, 0x03, 0x80, 0x81};
+ hex = HexEncode(bytes, sizeof(bytes));
+ EXPECT_EQ(hex.compare("01FF02FE038081"), 0);
+}
+
+} // namespace
+} // namespace base
diff --git a/src/base/string_piece.cc b/src/base/string_piece.cc
new file mode 100644
index 0000000..a018cb7
--- /dev/null
+++ b/src/base/string_piece.cc
@@ -0,0 +1,255 @@
+// Copyright (c) 2012 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.
+// Copied from strings/stringpiece.cc with modifications
+
+#include <algorithm>
+#include <ostream>
+
+#include "base/string_piece.h"
+
+namespace base {
+
+// MSVC doesn't like complex extern templates and DLLs.
+#if !defined(COMPILER_MSVC) && !defined(__SNC__)
+namespace internal {
+template class StringPieceDetail<std::string>;
+template class StringPieceDetail<string16>;
+} // namespace internal
+
+template class BasicStringPiece<string16>;
+#endif
+
+bool operator==(const StringPiece& x, const StringPiece& y) {
+ if (x.size() != y.size())
+ return false;
+
+ return StringPiece::wordmemcmp(x.data(), y.data(), x.size()) == 0;
+}
+
+std::ostream& operator<<(std::ostream& o, const StringPiece& piece) {
+ o.write(piece.data(), static_cast<std::streamsize>(piece.size()));
+ return o;
+}
+
+namespace internal {
+void CopyToString(const StringPiece& self, std::string* target) {
+ target->assign(!self.empty() ? self.data() : "", self.size());
+}
+
+void AppendToString(const StringPiece& self, std::string* target) {
+ if (!self.empty())
+ target->append(self.data(), self.size());
+}
+
+StringPiece::size_type copy(const StringPiece& self,
+ char* buf,
+ StringPiece::size_type n,
+ StringPiece::size_type pos) {
+ StringPiece::size_type ret = std::min(self.size() - pos, n);
+ memcpy(buf, self.data() + pos, ret);
+ return ret;
+}
+
+StringPiece::size_type find(const StringPiece& self,
+ const StringPiece& s,
+ StringPiece::size_type pos) {
+ if (pos > self.size())
+ return StringPiece::npos;
+
+ StringPiece::const_iterator result =
+ std::search(self.begin() + pos, self.end(), s.begin(), s.end());
+ const StringPiece::size_type xpos =
+ static_cast<size_t>(result - self.begin());
+ return xpos + s.size() <= self.size() ? xpos : StringPiece::npos;
+}
+
+StringPiece::size_type find(const StringPiece& self,
+ char c,
+ StringPiece::size_type pos) {
+ if (pos >= self.size())
+ return StringPiece::npos;
+
+ StringPiece::const_iterator result =
+ std::find(self.begin() + pos, self.end(), c);
+ return result != self.end() ?
+ static_cast<size_t>(result - self.begin()) : StringPiece::npos;
+}
+
+StringPiece::size_type rfind(const StringPiece& self,
+ const StringPiece& s,
+ StringPiece::size_type pos) {
+ if (self.size() < s.size())
+ return StringPiece::npos;
+
+ if (s.empty())
+ return std::min(self.size(), pos);
+
+ StringPiece::const_iterator last =
+ self.begin() + std::min(self.size() - s.size(), pos) + s.size();
+ StringPiece::const_iterator result =
+ std::find_end(self.begin(), last, s.begin(), s.end());
+ return result != last ?
+ static_cast<size_t>(result - self.begin()) : StringPiece::npos;
+}
+
+StringPiece::size_type rfind(const StringPiece& self,
+ char c,
+ StringPiece::size_type pos) {
+ if (self.size() == 0)
+ return StringPiece::npos;
+
+ for (StringPiece::size_type i = std::min(pos, self.size() - 1); ; --i) {
+ if (self.data()[i] == c)
+ return i;
+ if (i == 0)
+ break;
+ }
+ return StringPiece::npos;
+}
+
+// For each character in characters_wanted, sets the index corresponding
+// to the ASCII code of that character to 1 in table. This is used by
+// the find_.*_of methods below to tell whether or not a character is in
+// the lookup table in constant time.
+// The argument `table' must be an array that is large enough to hold all
+// the possible values of an unsigned char. Thus it should be be declared
+// as follows:
+// bool table[UCHAR_MAX + 1]
+static inline void BuildLookupTable(const StringPiece& characters_wanted,
+ bool* table) {
+ const StringPiece::size_type length = characters_wanted.length();
+ const char* const data = characters_wanted.data();
+ for (StringPiece::size_type i = 0; i < length; ++i) {
+ table[static_cast<unsigned char>(data[i])] = true;
+ }
+}
+
+StringPiece::size_type find_first_of(const StringPiece& self,
+ const StringPiece& s,
+ StringPiece::size_type pos) {
+ if (self.size() == 0 || s.size() == 0)
+ return StringPiece::npos;
+
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.size() == 1)
+ return find(self, s.data()[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (StringPiece::size_type i = pos; i < self.size(); ++i) {
+ if (lookup[static_cast<unsigned char>(self.data()[i])]) {
+ return i;
+ }
+ }
+ return StringPiece::npos;
+}
+
+StringPiece::size_type find_first_not_of(const StringPiece& self,
+ const StringPiece& s,
+ StringPiece::size_type pos) {
+ if (self.size() == 0)
+ return StringPiece::npos;
+
+ if (s.size() == 0)
+ return 0;
+
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.size() == 1)
+ return find_first_not_of(self, s.data()[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (StringPiece::size_type i = pos; i < self.size(); ++i) {
+ if (!lookup[static_cast<unsigned char>(self.data()[i])]) {
+ return i;
+ }
+ }
+ return StringPiece::npos;
+}
+
+StringPiece::size_type find_first_not_of(const StringPiece& self,
+ char c,
+ StringPiece::size_type pos) {
+ if (self.size() == 0)
+ return StringPiece::npos;
+
+ for (; pos < self.size(); ++pos) {
+ if (self.data()[pos] != c) {
+ return pos;
+ }
+ }
+ return StringPiece::npos;
+}
+
+StringPiece::size_type find_last_of(const StringPiece& self,
+ const StringPiece& s,
+ StringPiece::size_type pos) {
+ if (self.size() == 0 || s.size() == 0)
+ return StringPiece::npos;
+
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.size() == 1)
+ return rfind(self, s.data()[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (StringPiece::size_type i = std::min(pos, self.size() - 1); ; --i) {
+ if (lookup[static_cast<unsigned char>(self.data()[i])])
+ return i;
+ if (i == 0)
+ break;
+ }
+ return StringPiece::npos;
+}
+
+StringPiece::size_type find_last_not_of(const StringPiece& self,
+ const StringPiece& s,
+ StringPiece::size_type pos) {
+ if (self.size() == 0)
+ return StringPiece::npos;
+
+ StringPiece::size_type i = std::min(pos, self.size() - 1);
+ if (s.size() == 0)
+ return i;
+
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.size() == 1)
+ return find_last_not_of(self, s.data()[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (; ; --i) {
+ if (!lookup[static_cast<unsigned char>(self.data()[i])])
+ return i;
+ if (i == 0)
+ break;
+ }
+ return StringPiece::npos;
+}
+
+StringPiece::size_type find_last_not_of(const StringPiece& self,
+ char c,
+ StringPiece::size_type pos) {
+ if (self.size() == 0)
+ return StringPiece::npos;
+
+ for (StringPiece::size_type i = std::min(pos, self.size() - 1); ; --i) {
+ if (self.data()[i] != c)
+ return i;
+ if (i == 0)
+ break;
+ }
+ return StringPiece::npos;
+}
+
+StringPiece substr(const StringPiece& self,
+ StringPiece::size_type pos,
+ StringPiece::size_type n) {
+ if (pos > self.size()) pos = self.size();
+ if (n > self.size() - pos) n = self.size() - pos;
+ return StringPiece(self.data() + pos, n);
+}
+
+} // namespace internal
+} // namespace base
diff --git a/src/base/string_piece.h b/src/base/string_piece.h
new file mode 100644
index 0000000..7881e21
--- /dev/null
+++ b/src/base/string_piece.h
@@ -0,0 +1,457 @@
+// Copyright (c) 2012 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.
+// Copied from strings/stringpiece.h with modifications
+//
+// A string-like object that points to a sized piece of memory.
+//
+// Functions or methods may use const StringPiece& parameters to accept either
+// a "const char*" or a "string" value that will be implicitly converted to
+// a StringPiece. The implicit conversion means that it is often appropriate
+// to include this .h file in other files rather than forward-declaring
+// StringPiece as would be appropriate for most other Google classes.
+//
+// Systematic usage of StringPiece is encouraged as it will reduce unnecessary
+// conversions from "const char*" to "string" and back again.
+//
+// StringPiece16 is similar to StringPiece but for base::string16 instead of
+// std::string. We do not define as large of a subset of the STL functions
+// from basic_string as in StringPiece, but this can be changed if these
+// functions (find, find_first_of, etc.) are found to be useful in this context.
+//
+
+#ifndef BASE_STRING_PIECE_H_
+#define BASE_STRING_PIECE_H_
+
+#include <algorithm>
+#include <iosfwd>
+#include <string>
+#include <stddef.h>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/hash_tables.h"
+#include "base/string16.h"
+
+namespace base {
+
+template <typename STRING_TYPE> class BasicStringPiece;
+typedef BasicStringPiece<std::string> StringPiece;
+typedef BasicStringPiece<string16> StringPiece16;
+
+namespace internal {
+
+// Defines the types, methods, operators, and data members common to both
+// StringPiece and StringPiece16. Do not refer to this class directly, but
+// rather to BasicStringPiece, StringPiece, or StringPiece16.
+template <typename STRING_TYPE> class StringPieceDetail {
+ public:
+ // standard STL container boilerplate
+ typedef size_t size_type;
+ typedef typename STRING_TYPE::value_type value_type;
+ typedef const value_type* pointer;
+ typedef const value_type& reference;
+ typedef const value_type& const_reference;
+ typedef ptrdiff_t difference_type;
+ typedef const value_type* const_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ static const size_type npos;
+
+ public:
+ // We provide non-explicit singleton constructors so users can pass
+ // in a "const char*" or a "string" wherever a "StringPiece" is
+ // expected (likewise for char16, string16, StringPiece16).
+ StringPieceDetail() : ptr_(NULL), length_(0) {}
+ StringPieceDetail(const value_type* str)
+ : ptr_(str),
+ length_((str == NULL) ? 0 : STRING_TYPE::traits_type::length(str)) {}
+ StringPieceDetail(const STRING_TYPE& str)
+ : ptr_(str.data()), length_(str.size()) {}
+ StringPieceDetail(const value_type* offset, size_type len)
+ : ptr_(offset), length_(len) {}
+ StringPieceDetail(const typename STRING_TYPE::const_iterator& begin,
+ const typename STRING_TYPE::const_iterator& end)
+ : ptr_((end > begin) ? &(*begin) : NULL),
+ length_((end > begin) ? (size_type)(end - begin) : 0) {}
+
+ // data() may return a pointer to a buffer with embedded NULs, and the
+ // returned buffer may or may not be null terminated. Therefore it is
+ // typically a mistake to pass data() to a routine that expects a NUL
+ // terminated string.
+ const value_type* data() const { return ptr_; }
+ size_type size() const { return length_; }
+ size_type length() const { return length_; }
+ bool empty() const { return length_ == 0; }
+
+ void clear() {
+ ptr_ = NULL;
+ length_ = 0;
+ }
+ void set(const value_type* data, size_type len) {
+ ptr_ = data;
+ length_ = len;
+ }
+ void set(const value_type* str) {
+ ptr_ = str;
+ length_ = str ? STRING_TYPE::traits_type::length(str) : 0;
+ }
+
+ value_type operator[](size_type i) const { return ptr_[i]; }
+
+ void remove_prefix(size_type n) {
+ ptr_ += n;
+ length_ -= n;
+ }
+
+ void remove_suffix(size_type n) {
+ length_ -= n;
+ }
+
+ int compare(const BasicStringPiece<STRING_TYPE>& x) const {
+ int r = wordmemcmp(
+ ptr_, x.ptr_, (length_ < x.length_ ? length_ : x.length_));
+ if (r == 0) {
+ if (length_ < x.length_) r = -1;
+ else if (length_ > x.length_) r = +1;
+ }
+ return r;
+ }
+
+ STRING_TYPE as_string() const {
+ // std::string doesn't like to take a NULL pointer even with a 0 size.
+ return empty() ? STRING_TYPE() : STRING_TYPE(data(), size());
+ }
+
+ const_iterator begin() const { return ptr_; }
+ const_iterator end() const { return ptr_ + length_; }
+ const_reverse_iterator rbegin() const {
+ return const_reverse_iterator(ptr_ + length_);
+ }
+ const_reverse_iterator rend() const {
+ return const_reverse_iterator(ptr_);
+ }
+
+ size_type max_size() const { return length_; }
+ size_type capacity() const { return length_; }
+
+ static int wordmemcmp(const value_type* p,
+ const value_type* p2,
+ size_type N) {
+ return STRING_TYPE::traits_type::compare(p, p2, N);
+ }
+
+ protected:
+ const value_type* ptr_;
+ size_type length_;
+};
+
+template <typename STRING_TYPE>
+const typename StringPieceDetail<STRING_TYPE>::size_type
+StringPieceDetail<STRING_TYPE>::npos =
+ typename StringPieceDetail<STRING_TYPE>::size_type(-1);
+
+// MSVC doesn't like complex extern templates and DLLs.
+#if !defined(COMPILER_MSVC) && !defined(COMPILER_SNC) && !defined(COMPILER_GHS)
+extern template class BASE_EXPORT StringPieceDetail<std::string>;
+extern template class BASE_EXPORT StringPieceDetail<string16>;
+#endif
+
+BASE_EXPORT void CopyToString(const StringPiece& self, std::string* target);
+BASE_EXPORT void AppendToString(const StringPiece& self, std::string* target);
+BASE_EXPORT StringPieceDetail<std::string>::size_type copy(
+ const StringPiece& self,
+ char* buf,
+ StringPieceDetail<std::string>::size_type n,
+ StringPieceDetail<std::string>::size_type pos);
+BASE_EXPORT StringPieceDetail<std::string>::size_type find(
+ const StringPiece& self,
+ const StringPiece& s,
+ StringPieceDetail<std::string>::size_type pos);
+BASE_EXPORT StringPieceDetail<std::string>::size_type find(
+ const StringPiece& self,
+ char c,
+ StringPieceDetail<std::string>::size_type pos);
+BASE_EXPORT StringPieceDetail<std::string>::size_type rfind(
+ const StringPiece& self,
+ const StringPiece& s,
+ StringPieceDetail<std::string>::size_type pos);
+BASE_EXPORT StringPieceDetail<std::string>::size_type rfind(
+ const StringPiece& self,
+ char c,
+ StringPieceDetail<std::string>::size_type pos);
+BASE_EXPORT StringPieceDetail<std::string>::size_type find_first_of(
+ const StringPiece& self,
+ const StringPiece& s,
+ StringPieceDetail<std::string>::size_type pos);
+BASE_EXPORT StringPieceDetail<std::string>::size_type find_first_not_of(
+ const StringPiece& self,
+ const StringPiece& s,
+ StringPieceDetail<std::string>::size_type pos);
+BASE_EXPORT StringPieceDetail<std::string>::size_type find_first_not_of(
+ const StringPiece& self,
+ char c,
+ StringPieceDetail<std::string>::size_type pos);
+BASE_EXPORT StringPieceDetail<std::string>::size_type find_last_of(
+ const StringPiece& self,
+ const StringPiece& s,
+ StringPieceDetail<std::string>::size_type pos);
+BASE_EXPORT StringPieceDetail<std::string>::size_type find_last_of(
+ const StringPiece& self,
+ char c,
+ StringPieceDetail<std::string>::size_type pos);
+BASE_EXPORT StringPieceDetail<std::string>::size_type find_last_not_of(
+ const StringPiece& self,
+ const StringPiece& s,
+ StringPieceDetail<std::string>::size_type pos);
+BASE_EXPORT StringPieceDetail<std::string>::size_type find_last_not_of(
+ const StringPiece& self,
+ char c,
+ StringPieceDetail<std::string>::size_type pos);
+BASE_EXPORT StringPiece substr(const StringPiece& self,
+ StringPieceDetail<std::string>::size_type pos,
+ StringPieceDetail<std::string>::size_type n);
+} // namespace internal
+
+// Defines the template type that is instantiated as either StringPiece or
+// StringPiece16.
+template <typename STRING_TYPE> class BasicStringPiece :
+ public internal::StringPieceDetail<STRING_TYPE> {
+ public:
+ typedef typename internal::StringPieceDetail<STRING_TYPE>::value_type
+ value_type;
+ typedef typename internal::StringPieceDetail<STRING_TYPE>::size_type
+ size_type;
+
+ BasicStringPiece() {}
+ BasicStringPiece(const value_type*str)
+ : internal::StringPieceDetail<STRING_TYPE>(str) {}
+ BasicStringPiece(const STRING_TYPE& str)
+ : internal::StringPieceDetail<STRING_TYPE>(str) {}
+ BasicStringPiece(const value_type* offset, size_type len)
+ : internal::StringPieceDetail<STRING_TYPE>(offset, len) {}
+ BasicStringPiece(const typename STRING_TYPE::const_iterator& begin,
+ const typename STRING_TYPE::const_iterator& end)
+ : internal::StringPieceDetail<STRING_TYPE>(begin, end) {}
+};
+
+// Specializes BasicStringPiece for std::string to add a few operations that
+// are not needed for string16.
+template <> class BasicStringPiece<std::string> :
+ public internal::StringPieceDetail<std::string> {
+ public:
+ BasicStringPiece() {}
+ BasicStringPiece(const char* str)
+ : internal::StringPieceDetail<std::string>(str) {}
+ BasicStringPiece(const std::string& str)
+ : internal::StringPieceDetail<std::string>(str) {}
+ BasicStringPiece(const char* offset, size_type len)
+ : internal::StringPieceDetail<std::string>(offset, len) {}
+ BasicStringPiece(const std::string::const_iterator& begin,
+ const std::string::const_iterator& end)
+ : internal::StringPieceDetail<std::string>(begin, end) {}
+
+ // Prevent the following overload of set() from hiding the definitions in the
+ // base class.
+ using internal::StringPieceDetail<std::string>::set;
+
+ void set(const void* data, size_type len) {
+ ptr_ = reinterpret_cast<const value_type*>(data);
+ length_ = len;
+ }
+
+ void CopyToString(std::string* target) const {
+ internal::CopyToString(*this, target);
+ }
+
+ void AppendToString(std::string* target) const {
+ internal::AppendToString(*this, target);
+ }
+
+ // Does "this" start with "x"
+ bool starts_with(const BasicStringPiece& x) const {
+ return ((length_ >= x.length_) &&
+ (wordmemcmp(ptr_, x.ptr_, x.length_) == 0));
+ }
+
+ // Does "this" end with "x"
+ bool ends_with(const BasicStringPiece& x) const {
+ return ((length_ >= x.length_) &&
+ (wordmemcmp(ptr_ + (length_-x.length_), x.ptr_, x.length_) == 0));
+ }
+
+ size_type copy(char* buf, size_type n, size_type pos = 0) const {
+ return internal::copy(*this, buf, n, pos);
+ }
+
+ size_type find(const BasicStringPiece& s, size_type pos = 0) const {
+ return internal::find(*this, s, pos);
+ }
+
+ size_type find(char c, size_type pos = 0) const {
+ return internal::find(*this, c, pos);
+ }
+
+ size_type rfind(const BasicStringPiece& s, size_type pos = npos) const {
+ return internal::rfind(*this, s, pos);
+ }
+
+ size_type rfind(char c, size_type pos = npos) const {
+ return internal::rfind(*this, c, pos);
+ }
+
+ size_type find_first_of(const BasicStringPiece& s, size_type pos = 0) const {
+ return internal::find_first_of(*this, s, pos);
+ }
+
+ size_type find_first_of(char c, size_type pos = 0) const {
+ return find(c, pos);
+ }
+
+ size_type find_first_not_of(const BasicStringPiece& s,
+ size_type pos = 0) const {
+ return internal::find_first_not_of(*this, s, pos);
+ }
+
+ size_type find_first_not_of(char c, size_type pos = 0) const {
+ return internal::find_first_not_of(*this, c, pos);
+ }
+
+ size_type find_last_of(const BasicStringPiece& s,
+ size_type pos = npos) const {
+ return internal::find_last_of(*this, s, pos);
+ }
+
+ size_type find_last_of(char c, size_type pos = npos) const {
+ return rfind(c, pos);
+ }
+
+ size_type find_last_not_of(const BasicStringPiece& s,
+ size_type pos = npos) const {
+ return internal::find_last_not_of(*this, s, pos);
+ }
+
+ size_type find_last_not_of(char c, size_type pos = npos) const {
+ return internal::find_last_not_of(*this, c, pos);
+ }
+
+ BasicStringPiece substr(size_type pos, size_type n = npos) const {
+ return internal::substr(*this, pos, n);
+ }
+};
+
+// MSVC doesn't like complex extern templates and DLLs.
+#if !defined(COMPILER_MSVC) && !defined(COMPILER_SNC) && !defined(COMPILER_GHS)
+// We can't explicitly declare the std::string instantiation here because it was
+// already instantiated when specialized, above. Not only is it a no-op, but
+// currently it also crashes Clang (see http://crbug.com/107412).
+extern template class BASE_EXPORT BasicStringPiece<string16>;
+#endif
+
+BASE_EXPORT bool operator==(const StringPiece& x, const StringPiece& y);
+
+inline bool operator!=(const StringPiece& x, const StringPiece& y) {
+ return !(x == y);
+}
+
+inline bool operator<(const StringPiece& x, const StringPiece& y) {
+ const int r = StringPiece::wordmemcmp(
+ x.data(), y.data(), (x.size() < y.size() ? x.size() : y.size()));
+ return ((r < 0) || ((r == 0) && (x.size() < y.size())));
+}
+
+inline bool operator>(const StringPiece& x, const StringPiece& y) {
+ return y < x;
+}
+
+inline bool operator<=(const StringPiece& x, const StringPiece& y) {
+ return !(x > y);
+}
+
+inline bool operator>=(const StringPiece& x, const StringPiece& y) {
+ return !(x < y);
+}
+
+inline bool operator==(const StringPiece16& x, const StringPiece16& y) {
+ if (x.size() != y.size())
+ return false;
+
+ return StringPiece16::wordmemcmp(x.data(), y.data(), x.size()) == 0;
+}
+
+inline bool operator!=(const StringPiece16& x, const StringPiece16& y) {
+ return !(x == y);
+}
+
+inline bool operator<(const StringPiece16& x, const StringPiece16& y) {
+ const int r = StringPiece16::wordmemcmp(
+ x.data(), y.data(), (x.size() < y.size() ? x.size() : y.size()));
+ return ((r < 0) || ((r == 0) && (x.size() < y.size())));
+}
+
+inline bool operator>(const StringPiece16& x, const StringPiece16& y) {
+ return y < x;
+}
+
+inline bool operator<=(const StringPiece16& x, const StringPiece16& y) {
+ return !(x > y);
+}
+
+inline bool operator>=(const StringPiece16& x, const StringPiece16& y) {
+ return !(x < y);
+}
+
+BASE_EXPORT std::ostream& operator<<(std::ostream& o,
+ const StringPiece& piece);
+
+} // namespace base
+
+// We provide appropriate hash functions so StringPiece and StringPiece16 can
+// be used as keys in hash sets and maps.
+
+// This hash function is copied from base/hash_tables.h. We don't use the
+// ones already defined for string and string16 directly because it would
+// require the string constructors to be called, which we don't want.
+#define HASH_STRING_PIECE(StringPieceType, string_piece) \
+ std::size_t result = 0; \
+ for (StringPieceType::const_iterator i = string_piece.begin(); \
+ i != string_piece.end(); ++i) \
+ result = (result * 131) + *i; \
+ return result; \
+
+namespace BASE_HASH_NAMESPACE {
+#if (!defined(OS_STARBOARD) && defined(COMPILER_GCC) && \
+ !(defined(__LB_SHELL__) && !defined(__LB_LINUX__))) || \
+ (defined(OS_STARBOARD) && \
+ !(defined(SB_HAS_HASH_VALUE) && SB_HAS_HASH_VALUE))
+
+template<>
+struct hash<base::StringPiece> {
+ std::size_t operator()(const base::StringPiece& sp) const {
+ HASH_STRING_PIECE(base::StringPiece, sp);
+ }
+};
+template<>
+struct hash<base::StringPiece16> {
+ std::size_t operator()(const base::StringPiece16& sp16) const {
+ HASH_STRING_PIECE(base::StringPiece16, sp16);
+ }
+};
+
+#elif (!defined(OS_STARBOARD) && \
+ (defined(COMPILER_MSVC) || defined(__LB_SHELL__))) || \
+ (defined(OS_STARBOARD) && \
+ defined(SB_HAS_HASH_VALUE) && SB_HAS_HASH_VALUE)
+
+inline size_t hash_value(const base::StringPiece& sp) {
+ HASH_STRING_PIECE(base::StringPiece, sp);
+}
+inline size_t hash_value(const base::StringPiece16& sp16) {
+ HASH_STRING_PIECE(base::StringPiece16, sp16);
+}
+
+#endif // COMPILER
+
+} // namespace BASE_HASH_NAMESPACE
+
+#endif // BASE_STRING_PIECE_H_
diff --git a/src/base/string_piece_unittest.cc b/src/base/string_piece_unittest.cc
new file mode 100644
index 0000000..e93dba8
--- /dev/null
+++ b/src/base/string_piece_unittest.cc
@@ -0,0 +1,677 @@
+// Copyright (c) 2012 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/string16.h"
+#include "base/string_piece.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+template <typename T>
+class CommonStringPieceTest : public ::testing::Test {
+ public:
+ static const T as_string(const char* input) {
+ return T(input);
+ }
+ static const T& as_string(const T& input) {
+ return input;
+ }
+};
+
+template <>
+class CommonStringPieceTest<string16> : public ::testing::Test {
+ public:
+ static const string16 as_string(const char* input) {
+ return ASCIIToUTF16(input);
+ }
+ static const string16 as_string(const std::string& input) {
+ return ASCIIToUTF16(input);
+ }
+};
+
+typedef ::testing::Types<std::string, string16> SupportedStringTypes;
+
+TYPED_TEST_CASE(CommonStringPieceTest, SupportedStringTypes);
+
+TYPED_TEST(CommonStringPieceTest, CheckComparisonOperators) {
+#define CMP_Y(op, x, y) \
+ { \
+ TypeParam lhs(TestFixture::as_string(x)); \
+ TypeParam rhs(TestFixture::as_string(y)); \
+ ASSERT_TRUE( (BasicStringPiece<TypeParam>((lhs.c_str())) op \
+ BasicStringPiece<TypeParam>((rhs.c_str())))); \
+ ASSERT_TRUE( (BasicStringPiece<TypeParam>((lhs.c_str())).compare( \
+ BasicStringPiece<TypeParam>((rhs.c_str()))) op 0)); \
+ }
+
+#define CMP_N(op, x, y) \
+ { \
+ TypeParam lhs(TestFixture::as_string(x)); \
+ TypeParam rhs(TestFixture::as_string(y)); \
+ ASSERT_FALSE( (BasicStringPiece<TypeParam>((lhs.c_str())) op \
+ BasicStringPiece<TypeParam>((rhs.c_str())))); \
+ ASSERT_FALSE( (BasicStringPiece<TypeParam>((lhs.c_str())).compare( \
+ BasicStringPiece<TypeParam>((rhs.c_str()))) op 0)); \
+ }
+
+ CMP_Y(==, "", "");
+ CMP_Y(==, "a", "a");
+ CMP_Y(==, "aa", "aa");
+ CMP_N(==, "a", "");
+ CMP_N(==, "", "a");
+ CMP_N(==, "a", "b");
+ CMP_N(==, "a", "aa");
+ CMP_N(==, "aa", "a");
+
+ CMP_N(!=, "", "");
+ CMP_N(!=, "a", "a");
+ CMP_N(!=, "aa", "aa");
+ CMP_Y(!=, "a", "");
+ CMP_Y(!=, "", "a");
+ CMP_Y(!=, "a", "b");
+ CMP_Y(!=, "a", "aa");
+ CMP_Y(!=, "aa", "a");
+
+ CMP_Y(<, "a", "b");
+ CMP_Y(<, "a", "aa");
+ CMP_Y(<, "aa", "b");
+ CMP_Y(<, "aa", "bb");
+ CMP_N(<, "a", "a");
+ CMP_N(<, "b", "a");
+ CMP_N(<, "aa", "a");
+ CMP_N(<, "b", "aa");
+ CMP_N(<, "bb", "aa");
+
+ CMP_Y(<=, "a", "a");
+ CMP_Y(<=, "a", "b");
+ CMP_Y(<=, "a", "aa");
+ CMP_Y(<=, "aa", "b");
+ CMP_Y(<=, "aa", "bb");
+ CMP_N(<=, "b", "a");
+ CMP_N(<=, "aa", "a");
+ CMP_N(<=, "b", "aa");
+ CMP_N(<=, "bb", "aa");
+
+ CMP_N(>=, "a", "b");
+ CMP_N(>=, "a", "aa");
+ CMP_N(>=, "aa", "b");
+ CMP_N(>=, "aa", "bb");
+ CMP_Y(>=, "a", "a");
+ CMP_Y(>=, "b", "a");
+ CMP_Y(>=, "aa", "a");
+ CMP_Y(>=, "b", "aa");
+ CMP_Y(>=, "bb", "aa");
+
+ CMP_N(>, "a", "a");
+ CMP_N(>, "a", "b");
+ CMP_N(>, "a", "aa");
+ CMP_N(>, "aa", "b");
+ CMP_N(>, "aa", "bb");
+ CMP_Y(>, "b", "a");
+ CMP_Y(>, "aa", "a");
+ CMP_Y(>, "b", "aa");
+ CMP_Y(>, "bb", "aa");
+
+ std::string x;
+ for (int i = 0; i < 256; i++) {
+ x += 'a';
+ std::string y = x;
+ CMP_Y(==, x, y);
+ for (int j = 0; j < i; j++) {
+ std::string z = x;
+ z[j] = 'b'; // Differs in position 'j'
+ CMP_N(==, x, z);
+ }
+ }
+
+#undef CMP_Y
+#undef CMP_N
+}
+
+TYPED_TEST(CommonStringPieceTest, CheckSTL) {
+ TypeParam alphabet(TestFixture::as_string("abcdefghijklmnopqrstuvwxyz"));
+ TypeParam abc(TestFixture::as_string("abc"));
+ TypeParam xyz(TestFixture::as_string("xyz"));
+ TypeParam foobar(TestFixture::as_string("foobar"));
+
+ BasicStringPiece<TypeParam> a(alphabet);
+ BasicStringPiece<TypeParam> b(abc);
+ BasicStringPiece<TypeParam> c(xyz);
+ BasicStringPiece<TypeParam> d(foobar);
+ BasicStringPiece<TypeParam> e;
+ TypeParam temp(TestFixture::as_string("123"));
+ temp += static_cast<typename TypeParam::value_type>(0);
+ temp += TestFixture::as_string("456");
+ BasicStringPiece<TypeParam> f(temp);
+
+ ASSERT_EQ(a[6], static_cast<typename TypeParam::value_type>('g'));
+ ASSERT_EQ(b[0], static_cast<typename TypeParam::value_type>('a'));
+ ASSERT_EQ(c[2], static_cast<typename TypeParam::value_type>('z'));
+ ASSERT_EQ(f[3], static_cast<typename TypeParam::value_type>('\0'));
+ ASSERT_EQ(f[5], static_cast<typename TypeParam::value_type>('5'));
+
+ ASSERT_EQ(*d.data(), static_cast<typename TypeParam::value_type>('f'));
+ ASSERT_EQ(d.data()[5], static_cast<typename TypeParam::value_type>('r'));
+ ASSERT_TRUE(e.data() == NULL);
+
+ ASSERT_EQ(*a.begin(), static_cast<typename TypeParam::value_type>('a'));
+ ASSERT_EQ(*(b.begin() + 2), static_cast<typename TypeParam::value_type>('c'));
+ ASSERT_EQ(*(c.end() - 1), static_cast<typename TypeParam::value_type>('z'));
+
+ ASSERT_EQ(*a.rbegin(), static_cast<typename TypeParam::value_type>('z'));
+ ASSERT_EQ(*(b.rbegin() + 2),
+ static_cast<typename TypeParam::value_type>('a'));
+ ASSERT_EQ(*(c.rend() - 1), static_cast<typename TypeParam::value_type>('x'));
+ ASSERT_TRUE(a.rbegin() + 26 == a.rend());
+
+ ASSERT_EQ(a.size(), 26U);
+ ASSERT_EQ(b.size(), 3U);
+ ASSERT_EQ(c.size(), 3U);
+ ASSERT_EQ(d.size(), 6U);
+ ASSERT_EQ(e.size(), 0U);
+ ASSERT_EQ(f.size(), 7U);
+
+ ASSERT_TRUE(!d.empty());
+ ASSERT_TRUE(d.begin() != d.end());
+ ASSERT_TRUE(d.begin() + 6 == d.end());
+
+ ASSERT_TRUE(e.empty());
+ ASSERT_TRUE(e.begin() == e.end());
+
+ d.clear();
+ ASSERT_EQ(d.size(), 0U);
+ ASSERT_TRUE(d.empty());
+ ASSERT_TRUE(d.data() == NULL);
+ ASSERT_TRUE(d.begin() == d.end());
+
+ ASSERT_GE(a.max_size(), a.capacity());
+ ASSERT_GE(a.capacity(), a.size());
+}
+
+// STL stuff only supported by the std::string version
+TEST(StringPieceTest, CheckSTL) {
+ StringPiece a("abcdefghijklmnopqrstuvwxyz");
+ StringPiece b("abc");
+ StringPiece c("xyz");
+ StringPiece d("foobar");
+ d.clear();
+ StringPiece e;
+ std::string temp("123");
+ temp += '\0';
+ temp += "456";
+ StringPiece f(temp);
+
+ char buf[4] = { '%', '%', '%', '%' };
+ ASSERT_EQ(a.copy(buf, 4), 4U);
+ ASSERT_EQ(buf[0], a[0]);
+ ASSERT_EQ(buf[1], a[1]);
+ ASSERT_EQ(buf[2], a[2]);
+ ASSERT_EQ(buf[3], a[3]);
+ ASSERT_EQ(a.copy(buf, 3, 7), 3U);
+ ASSERT_EQ(buf[0], a[7]);
+ ASSERT_EQ(buf[1], a[8]);
+ ASSERT_EQ(buf[2], a[9]);
+ ASSERT_EQ(buf[3], a[3]);
+ ASSERT_EQ(c.copy(buf, 99), 3U);
+ ASSERT_EQ(buf[0], c[0]);
+ ASSERT_EQ(buf[1], c[1]);
+ ASSERT_EQ(buf[2], c[2]);
+ ASSERT_EQ(buf[3], a[3]);
+
+ ASSERT_EQ(StringPiece::npos, std::string::npos);
+
+ ASSERT_EQ(a.find(b), 0U);
+ ASSERT_EQ(a.find(b, 1), StringPiece::npos);
+ ASSERT_EQ(a.find(c), 23U);
+ ASSERT_EQ(a.find(c, 9), 23U);
+ ASSERT_EQ(a.find(c, StringPiece::npos), StringPiece::npos);
+ ASSERT_EQ(b.find(c), StringPiece::npos);
+ ASSERT_EQ(b.find(c, StringPiece::npos), StringPiece::npos);
+ ASSERT_EQ(a.find(d), 0U);
+ ASSERT_EQ(a.find(e), 0U);
+ ASSERT_EQ(a.find(d, 12), 12U);
+ ASSERT_EQ(a.find(e, 17), 17U);
+ StringPiece g("xx not found bb");
+ ASSERT_EQ(a.find(g), StringPiece::npos);
+ // empty string nonsense
+ ASSERT_EQ(d.find(b), StringPiece::npos);
+ ASSERT_EQ(e.find(b), StringPiece::npos);
+ ASSERT_EQ(d.find(b, 4), StringPiece::npos);
+ ASSERT_EQ(e.find(b, 7), StringPiece::npos);
+
+ size_t empty_search_pos = std::string().find(std::string());
+ ASSERT_EQ(d.find(d), empty_search_pos);
+ ASSERT_EQ(d.find(e), empty_search_pos);
+ ASSERT_EQ(e.find(d), empty_search_pos);
+ ASSERT_EQ(e.find(e), empty_search_pos);
+ ASSERT_EQ(d.find(d, 4), std::string().find(std::string(), 4));
+ ASSERT_EQ(d.find(e, 4), std::string().find(std::string(), 4));
+ ASSERT_EQ(e.find(d, 4), std::string().find(std::string(), 4));
+ ASSERT_EQ(e.find(e, 4), std::string().find(std::string(), 4));
+
+ ASSERT_EQ(a.find('a'), 0U);
+ ASSERT_EQ(a.find('c'), 2U);
+ ASSERT_EQ(a.find('z'), 25U);
+ ASSERT_EQ(a.find('$'), StringPiece::npos);
+ ASSERT_EQ(a.find('\0'), StringPiece::npos);
+ ASSERT_EQ(f.find('\0'), 3U);
+ ASSERT_EQ(f.find('3'), 2U);
+ ASSERT_EQ(f.find('5'), 5U);
+ ASSERT_EQ(g.find('o'), 4U);
+ ASSERT_EQ(g.find('o', 4), 4U);
+ ASSERT_EQ(g.find('o', 5), 8U);
+ ASSERT_EQ(a.find('b', 5), StringPiece::npos);
+ // empty string nonsense
+ ASSERT_EQ(d.find('\0'), StringPiece::npos);
+ ASSERT_EQ(e.find('\0'), StringPiece::npos);
+ ASSERT_EQ(d.find('\0', 4), StringPiece::npos);
+ ASSERT_EQ(e.find('\0', 7), StringPiece::npos);
+ ASSERT_EQ(d.find('x'), StringPiece::npos);
+ ASSERT_EQ(e.find('x'), StringPiece::npos);
+ ASSERT_EQ(d.find('x', 4), StringPiece::npos);
+ ASSERT_EQ(e.find('x', 7), StringPiece::npos);
+
+ ASSERT_EQ(a.rfind(b), 0U);
+ ASSERT_EQ(a.rfind(b, 1), 0U);
+ ASSERT_EQ(a.rfind(c), 23U);
+ ASSERT_EQ(a.rfind(c, 22U), StringPiece::npos);
+ ASSERT_EQ(a.rfind(c, 1U), StringPiece::npos);
+ ASSERT_EQ(a.rfind(c, 0U), StringPiece::npos);
+ ASSERT_EQ(b.rfind(c), StringPiece::npos);
+ ASSERT_EQ(b.rfind(c, 0U), StringPiece::npos);
+ ASSERT_EQ(a.rfind(d), (size_t) a.as_string().rfind(std::string()));
+ ASSERT_EQ(a.rfind(e), a.as_string().rfind(std::string()));
+ ASSERT_EQ(a.rfind(d, 12), 12U);
+ ASSERT_EQ(a.rfind(e, 17), 17U);
+ ASSERT_EQ(a.rfind(g), StringPiece::npos);
+ ASSERT_EQ(d.rfind(b), StringPiece::npos);
+ ASSERT_EQ(e.rfind(b), StringPiece::npos);
+ ASSERT_EQ(d.rfind(b, 4), StringPiece::npos);
+ ASSERT_EQ(e.rfind(b, 7), StringPiece::npos);
+ // empty string nonsense
+ ASSERT_EQ(d.rfind(d, 4), std::string().rfind(std::string()));
+ ASSERT_EQ(e.rfind(d, 7), std::string().rfind(std::string()));
+ ASSERT_EQ(d.rfind(e, 4), std::string().rfind(std::string()));
+ ASSERT_EQ(e.rfind(e, 7), std::string().rfind(std::string()));
+ ASSERT_EQ(d.rfind(d), std::string().rfind(std::string()));
+ ASSERT_EQ(e.rfind(d), std::string().rfind(std::string()));
+ ASSERT_EQ(d.rfind(e), std::string().rfind(std::string()));
+ ASSERT_EQ(e.rfind(e), std::string().rfind(std::string()));
+
+ ASSERT_EQ(g.rfind('o'), 8U);
+ ASSERT_EQ(g.rfind('q'), StringPiece::npos);
+ ASSERT_EQ(g.rfind('o', 8), 8U);
+ ASSERT_EQ(g.rfind('o', 7), 4U);
+ ASSERT_EQ(g.rfind('o', 3), StringPiece::npos);
+ ASSERT_EQ(f.rfind('\0'), 3U);
+ ASSERT_EQ(f.rfind('\0', 12), 3U);
+ ASSERT_EQ(f.rfind('3'), 2U);
+ ASSERT_EQ(f.rfind('5'), 5U);
+ // empty string nonsense
+ ASSERT_EQ(d.rfind('o'), StringPiece::npos);
+ ASSERT_EQ(e.rfind('o'), StringPiece::npos);
+ ASSERT_EQ(d.rfind('o', 4), StringPiece::npos);
+ ASSERT_EQ(e.rfind('o', 7), StringPiece::npos);
+
+ ASSERT_EQ(
+ StringPiece("one,two:three;four").find_first_of(StringPiece(",:"), 1),
+ 3U);
+ ASSERT_EQ(a.find_first_of(b), 0U);
+ ASSERT_EQ(a.find_first_of(b, 0), 0U);
+ ASSERT_EQ(a.find_first_of(b, 1), 1U);
+ ASSERT_EQ(a.find_first_of(b, 2), 2U);
+ ASSERT_EQ(a.find_first_of(b, 3), StringPiece::npos);
+ ASSERT_EQ(a.find_first_of(c), 23U);
+ ASSERT_EQ(a.find_first_of(c, 23), 23U);
+ ASSERT_EQ(a.find_first_of(c, 24), 24U);
+ ASSERT_EQ(a.find_first_of(c, 25), 25U);
+ ASSERT_EQ(a.find_first_of(c, 26), StringPiece::npos);
+ ASSERT_EQ(g.find_first_of(b), 13U);
+ ASSERT_EQ(g.find_first_of(c), 0U);
+ ASSERT_EQ(a.find_first_of(f), StringPiece::npos);
+ ASSERT_EQ(f.find_first_of(a), StringPiece::npos);
+ // empty string nonsense
+ ASSERT_EQ(a.find_first_of(d), StringPiece::npos);
+ ASSERT_EQ(a.find_first_of(e), StringPiece::npos);
+ ASSERT_EQ(d.find_first_of(b), StringPiece::npos);
+ ASSERT_EQ(e.find_first_of(b), StringPiece::npos);
+ ASSERT_EQ(d.find_first_of(d), StringPiece::npos);
+ ASSERT_EQ(e.find_first_of(d), StringPiece::npos);
+ ASSERT_EQ(d.find_first_of(e), StringPiece::npos);
+ ASSERT_EQ(e.find_first_of(e), StringPiece::npos);
+
+ ASSERT_EQ(a.find_first_not_of(b), 3U);
+ ASSERT_EQ(a.find_first_not_of(c), 0U);
+ ASSERT_EQ(b.find_first_not_of(a), StringPiece::npos);
+ ASSERT_EQ(c.find_first_not_of(a), StringPiece::npos);
+ ASSERT_EQ(f.find_first_not_of(a), 0U);
+ ASSERT_EQ(a.find_first_not_of(f), 0U);
+ ASSERT_EQ(a.find_first_not_of(d), 0U);
+ ASSERT_EQ(a.find_first_not_of(e), 0U);
+ // empty string nonsense
+ ASSERT_EQ(d.find_first_not_of(a), StringPiece::npos);
+ ASSERT_EQ(e.find_first_not_of(a), StringPiece::npos);
+ ASSERT_EQ(d.find_first_not_of(d), StringPiece::npos);
+ ASSERT_EQ(e.find_first_not_of(d), StringPiece::npos);
+ ASSERT_EQ(d.find_first_not_of(e), StringPiece::npos);
+ ASSERT_EQ(e.find_first_not_of(e), StringPiece::npos);
+
+ StringPiece h("====");
+ ASSERT_EQ(h.find_first_not_of('='), StringPiece::npos);
+ ASSERT_EQ(h.find_first_not_of('=', 3), StringPiece::npos);
+ ASSERT_EQ(h.find_first_not_of('\0'), 0U);
+ ASSERT_EQ(g.find_first_not_of('x'), 2U);
+ ASSERT_EQ(f.find_first_not_of('\0'), 0U);
+ ASSERT_EQ(f.find_first_not_of('\0', 3), 4U);
+ ASSERT_EQ(f.find_first_not_of('\0', 2), 2U);
+ // empty string nonsense
+ ASSERT_EQ(d.find_first_not_of('x'), StringPiece::npos);
+ ASSERT_EQ(e.find_first_not_of('x'), StringPiece::npos);
+ ASSERT_EQ(d.find_first_not_of('\0'), StringPiece::npos);
+ ASSERT_EQ(e.find_first_not_of('\0'), StringPiece::npos);
+
+ // StringPiece g("xx not found bb");
+ StringPiece i("56");
+ ASSERT_EQ(h.find_last_of(a), StringPiece::npos);
+ ASSERT_EQ(g.find_last_of(a), g.size()-1);
+ ASSERT_EQ(a.find_last_of(b), 2U);
+ ASSERT_EQ(a.find_last_of(c), a.size()-1);
+ ASSERT_EQ(f.find_last_of(i), 6U);
+ ASSERT_EQ(a.find_last_of('a'), 0U);
+ ASSERT_EQ(a.find_last_of('b'), 1U);
+ ASSERT_EQ(a.find_last_of('z'), 25U);
+ ASSERT_EQ(a.find_last_of('a', 5), 0U);
+ ASSERT_EQ(a.find_last_of('b', 5), 1U);
+ ASSERT_EQ(a.find_last_of('b', 0), StringPiece::npos);
+ ASSERT_EQ(a.find_last_of('z', 25), 25U);
+ ASSERT_EQ(a.find_last_of('z', 24), StringPiece::npos);
+ ASSERT_EQ(f.find_last_of(i, 5), 5U);
+ ASSERT_EQ(f.find_last_of(i, 6), 6U);
+ ASSERT_EQ(f.find_last_of(a, 4), StringPiece::npos);
+ // empty string nonsense
+ ASSERT_EQ(f.find_last_of(d), StringPiece::npos);
+ ASSERT_EQ(f.find_last_of(e), StringPiece::npos);
+ ASSERT_EQ(f.find_last_of(d, 4), StringPiece::npos);
+ ASSERT_EQ(f.find_last_of(e, 4), StringPiece::npos);
+ ASSERT_EQ(d.find_last_of(d), StringPiece::npos);
+ ASSERT_EQ(d.find_last_of(e), StringPiece::npos);
+ ASSERT_EQ(e.find_last_of(d), StringPiece::npos);
+ ASSERT_EQ(e.find_last_of(e), StringPiece::npos);
+ ASSERT_EQ(d.find_last_of(f), StringPiece::npos);
+ ASSERT_EQ(e.find_last_of(f), StringPiece::npos);
+ ASSERT_EQ(d.find_last_of(d, 4), StringPiece::npos);
+ ASSERT_EQ(d.find_last_of(e, 4), StringPiece::npos);
+ ASSERT_EQ(e.find_last_of(d, 4), StringPiece::npos);
+ ASSERT_EQ(e.find_last_of(e, 4), StringPiece::npos);
+ ASSERT_EQ(d.find_last_of(f, 4), StringPiece::npos);
+ ASSERT_EQ(e.find_last_of(f, 4), StringPiece::npos);
+
+ ASSERT_EQ(a.find_last_not_of(b), a.size()-1);
+ ASSERT_EQ(a.find_last_not_of(c), 22U);
+ ASSERT_EQ(b.find_last_not_of(a), StringPiece::npos);
+ ASSERT_EQ(b.find_last_not_of(b), StringPiece::npos);
+ ASSERT_EQ(f.find_last_not_of(i), 4U);
+ ASSERT_EQ(a.find_last_not_of(c, 24), 22U);
+ ASSERT_EQ(a.find_last_not_of(b, 3), 3U);
+ ASSERT_EQ(a.find_last_not_of(b, 2), StringPiece::npos);
+ // empty string nonsense
+ ASSERT_EQ(f.find_last_not_of(d), f.size()-1);
+ ASSERT_EQ(f.find_last_not_of(e), f.size()-1);
+ ASSERT_EQ(f.find_last_not_of(d, 4), 4U);
+ ASSERT_EQ(f.find_last_not_of(e, 4), 4U);
+ ASSERT_EQ(d.find_last_not_of(d), StringPiece::npos);
+ ASSERT_EQ(d.find_last_not_of(e), StringPiece::npos);
+ ASSERT_EQ(e.find_last_not_of(d), StringPiece::npos);
+ ASSERT_EQ(e.find_last_not_of(e), StringPiece::npos);
+ ASSERT_EQ(d.find_last_not_of(f), StringPiece::npos);
+ ASSERT_EQ(e.find_last_not_of(f), StringPiece::npos);
+ ASSERT_EQ(d.find_last_not_of(d, 4), StringPiece::npos);
+ ASSERT_EQ(d.find_last_not_of(e, 4), StringPiece::npos);
+ ASSERT_EQ(e.find_last_not_of(d, 4), StringPiece::npos);
+ ASSERT_EQ(e.find_last_not_of(e, 4), StringPiece::npos);
+ ASSERT_EQ(d.find_last_not_of(f, 4), StringPiece::npos);
+ ASSERT_EQ(e.find_last_not_of(f, 4), StringPiece::npos);
+
+ ASSERT_EQ(h.find_last_not_of('x'), h.size() - 1);
+ ASSERT_EQ(h.find_last_not_of('='), StringPiece::npos);
+ ASSERT_EQ(b.find_last_not_of('c'), 1U);
+ ASSERT_EQ(h.find_last_not_of('x', 2), 2U);
+ ASSERT_EQ(h.find_last_not_of('=', 2), StringPiece::npos);
+ ASSERT_EQ(b.find_last_not_of('b', 1), 0U);
+ // empty string nonsense
+ ASSERT_EQ(d.find_last_not_of('x'), StringPiece::npos);
+ ASSERT_EQ(e.find_last_not_of('x'), StringPiece::npos);
+ ASSERT_EQ(d.find_last_not_of('\0'), StringPiece::npos);
+ ASSERT_EQ(e.find_last_not_of('\0'), StringPiece::npos);
+
+ ASSERT_EQ(a.substr(0, 3), b);
+ ASSERT_EQ(a.substr(23), c);
+ ASSERT_EQ(a.substr(23, 3), c);
+ ASSERT_EQ(a.substr(23, 99), c);
+ ASSERT_EQ(a.substr(0), a);
+ ASSERT_EQ(a.substr(3, 2), "de");
+ // empty string nonsense
+ ASSERT_EQ(a.substr(99, 2), e);
+ ASSERT_EQ(d.substr(99), e);
+ ASSERT_EQ(d.substr(0, 99), e);
+ ASSERT_EQ(d.substr(99, 99), e);
+}
+
+TYPED_TEST(CommonStringPieceTest, CheckCustom) {
+ TypeParam foobar(TestFixture::as_string("foobar"));
+ BasicStringPiece<TypeParam> a(foobar);
+ TypeParam s1(TestFixture::as_string("123"));
+ s1 += static_cast<typename TypeParam::value_type>('\0');
+ s1 += TestFixture::as_string("456");
+ BasicStringPiece<TypeParam> b(s1);
+ BasicStringPiece<TypeParam> e;
+ TypeParam s2;
+
+ // remove_prefix
+ BasicStringPiece<TypeParam> c(a);
+ c.remove_prefix(3);
+ ASSERT_EQ(c, TestFixture::as_string("bar"));
+ c = a;
+ c.remove_prefix(0);
+ ASSERT_EQ(c, a);
+ c.remove_prefix(c.size());
+ ASSERT_EQ(c, e);
+
+ // remove_suffix
+ c = a;
+ c.remove_suffix(3);
+ ASSERT_EQ(c, TestFixture::as_string("foo"));
+ c = a;
+ c.remove_suffix(0);
+ ASSERT_EQ(c, a);
+ c.remove_suffix(c.size());
+ ASSERT_EQ(c, e);
+
+ // set
+ c.set(foobar.c_str());
+ ASSERT_EQ(c, a);
+ c.set(foobar.c_str(), 6);
+ ASSERT_EQ(c, a);
+ c.set(foobar.c_str(), 0);
+ ASSERT_EQ(c, e);
+ c.set(foobar.c_str(), 7); // Note, has an embedded NULL
+ ASSERT_NE(c, a);
+
+ // as_string
+ TypeParam s3(a.as_string().c_str(), 7); // Note, has an embedded NULL
+ ASSERT_TRUE(c == s3);
+ TypeParam s4(e.as_string());
+ ASSERT_TRUE(s4.empty());
+}
+
+TEST(StringPieceTest, CheckCustom) {
+ StringPiece a("foobar");
+ std::string s1("123");
+ s1 += '\0';
+ s1 += "456";
+ StringPiece b(s1);
+ StringPiece e;
+ std::string s2;
+
+ // CopyToString
+ a.CopyToString(&s2);
+ ASSERT_EQ(s2.size(), 6U);
+ ASSERT_EQ(s2, "foobar");
+ b.CopyToString(&s2);
+ ASSERT_EQ(s2.size(), 7U);
+ ASSERT_EQ(s1, s2);
+ e.CopyToString(&s2);
+ ASSERT_TRUE(s2.empty());
+
+ // AppendToString
+ s2.erase();
+ a.AppendToString(&s2);
+ ASSERT_EQ(s2.size(), 6U);
+ ASSERT_EQ(s2, "foobar");
+ a.AppendToString(&s2);
+ ASSERT_EQ(s2.size(), 12U);
+ ASSERT_EQ(s2, "foobarfoobar");
+
+ // starts_with
+ ASSERT_TRUE(a.starts_with(a));
+ ASSERT_TRUE(a.starts_with("foo"));
+ ASSERT_TRUE(a.starts_with(e));
+ ASSERT_TRUE(b.starts_with(s1));
+ ASSERT_TRUE(b.starts_with(b));
+ ASSERT_TRUE(b.starts_with(e));
+ ASSERT_TRUE(e.starts_with(""));
+ ASSERT_TRUE(!a.starts_with(b));
+ ASSERT_TRUE(!b.starts_with(a));
+ ASSERT_TRUE(!e.starts_with(a));
+
+ // ends with
+ ASSERT_TRUE(a.ends_with(a));
+ ASSERT_TRUE(a.ends_with("bar"));
+ ASSERT_TRUE(a.ends_with(e));
+ ASSERT_TRUE(b.ends_with(s1));
+ ASSERT_TRUE(b.ends_with(b));
+ ASSERT_TRUE(b.ends_with(e));
+ ASSERT_TRUE(e.ends_with(""));
+ ASSERT_TRUE(!a.ends_with(b));
+ ASSERT_TRUE(!b.ends_with(a));
+ ASSERT_TRUE(!e.ends_with(a));
+
+ StringPiece c;
+ c.set(static_cast<const void*>("foobar"), 6);
+ ASSERT_EQ(c, a);
+ c.set(static_cast<const void*>("foobar"), 0);
+ ASSERT_EQ(c, e);
+ c.set(static_cast<const void*>("foobar"), 7);
+ ASSERT_NE(c, a);
+}
+
+TYPED_TEST(CommonStringPieceTest, CheckNULL) {
+ // we used to crash here, but now we don't.
+ BasicStringPiece<TypeParam> s(NULL);
+ ASSERT_EQ(s.data(), (const typename TypeParam::value_type*)NULL);
+ ASSERT_EQ(s.size(), 0U);
+
+ s.set(NULL);
+ ASSERT_EQ(s.data(), (const typename TypeParam::value_type*)NULL);
+ ASSERT_EQ(s.size(), 0U);
+
+ TypeParam str = s.as_string();
+ ASSERT_EQ(str.length(), 0U);
+ ASSERT_EQ(str, TypeParam());
+}
+
+TYPED_TEST(CommonStringPieceTest, CheckComparisons2) {
+ TypeParam alphabet(TestFixture::as_string("abcdefghijklmnopqrstuvwxyz"));
+ TypeParam alphabet_z(TestFixture::as_string("abcdefghijklmnopqrstuvwxyzz"));
+ TypeParam alphabet_y(TestFixture::as_string("abcdefghijklmnopqrstuvwxyy"));
+ BasicStringPiece<TypeParam> abc(alphabet);
+
+ // check comparison operations on strings longer than 4 bytes.
+ ASSERT_TRUE(abc == BasicStringPiece<TypeParam>(alphabet));
+ ASSERT_TRUE(abc.compare(BasicStringPiece<TypeParam>(alphabet)) == 0);
+
+ ASSERT_TRUE(abc < BasicStringPiece<TypeParam>(alphabet_z));
+ ASSERT_TRUE(abc.compare(BasicStringPiece<TypeParam>(alphabet_z)) < 0);
+
+ ASSERT_TRUE(abc > BasicStringPiece<TypeParam>(alphabet_y));
+ ASSERT_TRUE(abc.compare(BasicStringPiece<TypeParam>(alphabet_y)) > 0);
+}
+
+// Test operations only supported by std::string version.
+TEST(StringPieceTest, CheckComparisons2) {
+ StringPiece abc("abcdefghijklmnopqrstuvwxyz");
+
+ // starts_with
+ ASSERT_TRUE(abc.starts_with(abc));
+ ASSERT_TRUE(abc.starts_with("abcdefghijklm"));
+ ASSERT_TRUE(!abc.starts_with("abcdefguvwxyz"));
+
+ // ends_with
+ ASSERT_TRUE(abc.ends_with(abc));
+ ASSERT_TRUE(!abc.ends_with("abcdefguvwxyz"));
+ ASSERT_TRUE(abc.ends_with("nopqrstuvwxyz"));
+}
+
+TYPED_TEST(CommonStringPieceTest, StringCompareNotAmbiguous) {
+ ASSERT_TRUE(TestFixture::as_string("hello").c_str() ==
+ TestFixture::as_string("hello"));
+ ASSERT_TRUE(TestFixture::as_string("hello").c_str() <
+ TestFixture::as_string("world"));
+}
+
+TYPED_TEST(CommonStringPieceTest, HeterogenousStringPieceEquals) {
+ TypeParam hello(TestFixture::as_string("hello"));
+
+ ASSERT_TRUE(BasicStringPiece<TypeParam>(hello) == hello);
+ ASSERT_TRUE(hello.c_str() == BasicStringPiece<TypeParam>(hello));
+}
+
+// string16-specific stuff
+TEST(StringPiece16Test, CheckSTL) {
+ // Check some non-ascii characters.
+ string16 fifth(ASCIIToUTF16("123"));
+ fifth.push_back(0x0000);
+ fifth.push_back(0xd8c5);
+ fifth.push_back(0xdffe);
+ StringPiece16 f(fifth);
+
+ ASSERT_EQ(f[3], '\0');
+ ASSERT_EQ(f[5], static_cast<char16>(0xdffe));
+
+ ASSERT_EQ(f.size(), 6U);
+}
+
+
+
+TEST(StringPiece16Test, CheckConversion) {
+ // Make sure that we can convert from UTF8 to UTF16 and back. We use a two
+ // byte character (G clef) to test this.
+ ASSERT_EQ(
+ UTF16ToUTF8(
+ StringPiece16(UTF8ToUTF16("\xf0\x9d\x84\x9e")).as_string()),
+ "\xf0\x9d\x84\x9e");
+}
+
+TYPED_TEST(CommonStringPieceTest, CheckConstructors) {
+ TypeParam str(TestFixture::as_string("hello world"));
+ TypeParam empty;
+
+ ASSERT_TRUE(str == BasicStringPiece<TypeParam>(str));
+ ASSERT_TRUE(str == BasicStringPiece<TypeParam>(str.c_str()));
+ ASSERT_TRUE(TestFixture::as_string("hello") ==
+ BasicStringPiece<TypeParam>(str.c_str(), 5));
+ ASSERT_TRUE(empty == BasicStringPiece<TypeParam>(str.c_str(), 0U));
+ ASSERT_TRUE(empty == BasicStringPiece<TypeParam>(NULL));
+ ASSERT_TRUE(empty == BasicStringPiece<TypeParam>(NULL, 0U));
+ ASSERT_TRUE(empty == BasicStringPiece<TypeParam>());
+ ASSERT_TRUE(str == BasicStringPiece<TypeParam>(str.begin(), str.end()));
+ ASSERT_TRUE(empty == BasicStringPiece<TypeParam>(str.begin(), str.begin()));
+ ASSERT_TRUE(empty == BasicStringPiece<TypeParam>(empty));
+ ASSERT_TRUE(empty == BasicStringPiece<TypeParam>(empty.begin(), empty.end()));
+}
+
+} // namespace base
diff --git a/src/base/string_split.cc b/src/base/string_split.cc
new file mode 100644
index 0000000..e5befcf
--- /dev/null
+++ b/src/base/string_split.cc
@@ -0,0 +1,219 @@
+// Copyright (c) 2012 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/string_split.h"
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/third_party/icu/icu_utf.h"
+#include "base/utf_string_conversions.h"
+
+namespace base {
+
+template<typename STR>
+static void SplitStringT(const STR& str,
+ const typename STR::value_type s,
+ bool trim_whitespace,
+ std::vector<STR>* r) {
+ r->clear();
+ size_t last = 0;
+ size_t c = str.size();
+ for (size_t i = 0; i <= c; ++i) {
+ if (i == c || str[i] == s) {
+ STR tmp(str, last, i - last);
+ if (trim_whitespace)
+ TrimWhitespace(tmp, TRIM_ALL, &tmp);
+ // Avoid converting an empty or all-whitespace source string into a vector
+ // of one empty string.
+ if (i != c || !r->empty() || !tmp.empty())
+ r->push_back(tmp);
+ last = i + 1;
+ }
+ }
+}
+
+void SplitString(const string16& str,
+ char16 c,
+ std::vector<string16>* r) {
+ DCHECK(CBU16_IS_SINGLE(c));
+ SplitStringT(str, c, true, r);
+}
+
+void SplitString(const std::string& str,
+ char c,
+ std::vector<std::string>* r) {
+#if CHAR_MIN < 0
+ DCHECK(c >= 0);
+#endif
+ DCHECK(c < 0x7F);
+ SplitStringT(str, c, true, r);
+}
+
+bool SplitStringIntoKeyValues(
+ const std::string& line,
+ char key_value_delimiter,
+ std::string* key, std::vector<std::string>* values) {
+ key->clear();
+ values->clear();
+
+ // Find the key string.
+ size_t end_key_pos = line.find_first_of(key_value_delimiter);
+ if (end_key_pos == std::string::npos) {
+ DVLOG(1) << "cannot parse key from line: " << line;
+ return false; // no key
+ }
+ key->assign(line, 0, end_key_pos);
+
+ // Find the values string.
+ std::string remains(line, end_key_pos, line.size() - end_key_pos);
+ size_t begin_values_pos = remains.find_first_not_of(key_value_delimiter);
+ if (begin_values_pos == std::string::npos) {
+ DVLOG(1) << "cannot parse value from line: " << line;
+ return false; // no value
+ }
+ std::string values_string(remains, begin_values_pos,
+ remains.size() - begin_values_pos);
+
+ // Construct the values vector.
+ values->push_back(values_string);
+ return true;
+}
+
+bool SplitStringIntoKeyValuePairs(
+ const std::string& line,
+ char key_value_delimiter,
+ char key_value_pair_delimiter,
+ std::vector<std::pair<std::string, std::string> >* kv_pairs) {
+ kv_pairs->clear();
+
+ std::vector<std::string> pairs;
+ SplitString(line, key_value_pair_delimiter, &pairs);
+
+ bool success = true;
+ for (size_t i = 0; i < pairs.size(); ++i) {
+ // Empty pair. SplitStringIntoKeyValues is more strict about an empty pair
+ // line, so continue with the next pair.
+ if (pairs[i].empty())
+ continue;
+
+ std::string key;
+ std::vector<std::string> value;
+ if (!SplitStringIntoKeyValues(pairs[i],
+ key_value_delimiter,
+ &key, &value)) {
+ // Don't return here, to allow for keys without associated
+ // values; just record that our split failed.
+ success = false;
+ }
+ DCHECK_LE(value.size(), 1U);
+ kv_pairs->push_back(make_pair(key, value.empty()? "" : value[0]));
+ }
+ return success;
+}
+
+template <typename STR>
+static void SplitStringUsingSubstrT(const STR& str,
+ const STR& s,
+ std::vector<STR>* r) {
+ r->clear();
+ typename STR::size_type begin_index = 0;
+ while (true) {
+ const typename STR::size_type end_index = str.find(s, begin_index);
+ if (end_index == STR::npos) {
+ const STR term = str.substr(begin_index);
+ STR tmp;
+ TrimWhitespace(term, TRIM_ALL, &tmp);
+ r->push_back(tmp);
+ return;
+ }
+ const STR term = str.substr(begin_index, end_index - begin_index);
+ STR tmp;
+ TrimWhitespace(term, TRIM_ALL, &tmp);
+ r->push_back(tmp);
+ begin_index = end_index + s.size();
+ }
+}
+
+void SplitStringUsingSubstr(const string16& str,
+ const string16& s,
+ std::vector<string16>* r) {
+ SplitStringUsingSubstrT(str, s, r);
+}
+
+void SplitStringUsingSubstr(const std::string& str,
+ const std::string& s,
+ std::vector<std::string>* r) {
+ SplitStringUsingSubstrT(str, s, r);
+}
+
+void SplitStringDontTrim(const string16& str,
+ char16 c,
+ std::vector<string16>* r) {
+ DCHECK(CBU16_IS_SINGLE(c));
+ SplitStringT(str, c, false, r);
+}
+
+void SplitStringDontTrim(const std::string& str,
+ char c,
+ std::vector<std::string>* r) {
+ DCHECK(IsStringUTF8(str));
+#if CHAR_MIN < 0
+ DCHECK(c >= 0);
+#endif
+ DCHECK(c < 0x7F);
+ SplitStringT(str, c, false, r);
+}
+
+template<typename STR>
+void SplitStringAlongWhitespaceT(const STR& str, std::vector<STR>* result) {
+ result->clear();
+ const size_t length = str.length();
+ if (!length)
+ return;
+
+ bool last_was_ws = false;
+ size_t last_non_ws_start = 0;
+ for (size_t i = 0; i < length; ++i) {
+ switch (str[i]) {
+ // HTML 5 defines whitespace as: space, tab, LF, line tab, FF, or CR.
+ case L' ':
+ case L'\t':
+ case L'\xA':
+ case L'\xB':
+ case L'\xC':
+ case L'\xD':
+ if (!last_was_ws) {
+ if (i > 0) {
+ result->push_back(
+ str.substr(last_non_ws_start, i - last_non_ws_start));
+ }
+ last_was_ws = true;
+ }
+ break;
+
+ default: // Not a space character.
+ if (last_was_ws) {
+ last_was_ws = false;
+ last_non_ws_start = i;
+ }
+ break;
+ }
+ }
+ if (!last_was_ws) {
+ result->push_back(
+ str.substr(last_non_ws_start, length - last_non_ws_start));
+ }
+}
+
+void SplitStringAlongWhitespace(const string16& str,
+ std::vector<string16>* result) {
+ SplitStringAlongWhitespaceT(str, result);
+}
+
+void SplitStringAlongWhitespace(const std::string& str,
+ std::vector<std::string>* result) {
+ SplitStringAlongWhitespaceT(str, result);
+}
+
+} // namespace base
diff --git a/src/base/string_split.h b/src/base/string_split.h
new file mode 100644
index 0000000..f8022e3
--- /dev/null
+++ b/src/base/string_split.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2012 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_STRING_SPLIT_H_
+#define BASE_STRING_SPLIT_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/string16.h"
+
+namespace base {
+
+// Splits |str| into a vector of strings delimited by |s|, placing the results
+// in |r|. If several instances of |s| are contiguous, or if |str| begins with
+// or ends with |s|, then an empty string is inserted.
+//
+// Every substring is trimmed of any leading or trailing white space.
+// NOTE: |c| must be in BMP (Basic Multilingual Plane)
+BASE_EXPORT void SplitString(const string16& str,
+ char16 c,
+ std::vector<string16>* r);
+// |str| should not be in a multi-byte encoding like Shift-JIS or GBK in which
+// the trailing byte of a multi-byte character can be in the ASCII range.
+// UTF-8, and other single/multi-byte ASCII-compatible encodings are OK.
+// Note: |c| must be in the ASCII range.
+BASE_EXPORT void SplitString(const std::string& str,
+ char c,
+ std::vector<std::string>* r);
+
+BASE_EXPORT bool SplitStringIntoKeyValues(
+ const std::string& line,
+ char key_value_delimiter,
+ std::string* key, std::vector<std::string>* values);
+
+BASE_EXPORT bool SplitStringIntoKeyValuePairs(
+ const std::string& line,
+ char key_value_delimiter,
+ char key_value_pair_delimiter,
+ std::vector<std::pair<std::string, std::string> >* kv_pairs);
+
+// The same as SplitString, but use a substring delimiter instead of a char.
+BASE_EXPORT void SplitStringUsingSubstr(const string16& str,
+ const string16& s,
+ std::vector<string16>* r);
+BASE_EXPORT void SplitStringUsingSubstr(const std::string& str,
+ const std::string& s,
+ std::vector<std::string>* r);
+
+// The same as SplitString, but don't trim white space.
+// NOTE: |c| must be in BMP (Basic Multilingual Plane)
+BASE_EXPORT void SplitStringDontTrim(const string16& str,
+ char16 c,
+ std::vector<string16>* r);
+// |str| should not be in a multi-byte encoding like Shift-JIS or GBK in which
+// the trailing byte of a multi-byte character can be in the ASCII range.
+// UTF-8, and other single/multi-byte ASCII-compatible encodings are OK.
+// Note: |c| must be in the ASCII range.
+BASE_EXPORT void SplitStringDontTrim(const std::string& str,
+ char c,
+ std::vector<std::string>* r);
+
+// WARNING: this uses whitespace as defined by the HTML5 spec. If you need
+// a function similar to this but want to trim all types of whitespace, then
+// factor this out into a function that takes a string containing the characters
+// that are treated as whitespace.
+//
+// Splits the string along whitespace (where whitespace is the five space
+// characters defined by HTML 5). Each contiguous block of non-whitespace
+// characters is added to result.
+BASE_EXPORT void SplitStringAlongWhitespace(const string16& str,
+ std::vector<string16>* result);
+BASE_EXPORT void SplitStringAlongWhitespace(const std::string& str,
+ std::vector<std::string>* result);
+
+} // namespace base
+
+#endif // BASE_STRING_SPLIT_H
diff --git a/src/base/string_split_unittest.cc b/src/base/string_split_unittest.cc
new file mode 100644
index 0000000..a4412e4
--- /dev/null
+++ b/src/base/string_split_unittest.cc
@@ -0,0 +1,316 @@
+// Copyright (c) 2012 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/string_split.h"
+
+#include "base/utf_string_conversions.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::ElementsAre;
+
+namespace base {
+
+namespace {
+
+#if !defined(WCHAR_T_IS_UTF16)
+// Overload SplitString with a wide-char version to make it easier to
+// test the string16 version with wide character literals.
+void SplitString(const std::wstring& str,
+ wchar_t c,
+ std::vector<std::wstring>* result) {
+ std::vector<string16> result16;
+ SplitString(WideToUTF16(str), c, &result16);
+ for (size_t i = 0; i < result16.size(); ++i)
+ result->push_back(UTF16ToWide(result16[i]));
+}
+#endif
+
+} // anonymous namespace
+
+class SplitStringIntoKeyValuesTest : public testing::Test {
+ protected:
+ std::string key;
+ std::vector<std::string> values;
+};
+
+TEST_F(SplitStringIntoKeyValuesTest, EmptyInputMultipleValues) {
+ EXPECT_FALSE(SplitStringIntoKeyValues("", // Empty input
+ '\t', // Key separators
+ &key, &values));
+ EXPECT_TRUE(key.empty());
+ EXPECT_TRUE(values.empty());
+}
+
+TEST_F(SplitStringIntoKeyValuesTest, EmptyValueInputMultipleValues) {
+ EXPECT_FALSE(SplitStringIntoKeyValues("key_with_no_value\t",
+ '\t', // Key separators
+ &key, &values));
+ EXPECT_EQ("key_with_no_value", key);
+ EXPECT_TRUE(values.empty());
+}
+
+TEST_F(SplitStringIntoKeyValuesTest, EmptyKeyInputMultipleValues) {
+ EXPECT_TRUE(SplitStringIntoKeyValues("\tvalue for empty key",
+ '\t', // Key separators
+ &key, &values));
+ EXPECT_TRUE(key.empty());
+ ASSERT_EQ(1U, values.size());
+}
+
+TEST_F(SplitStringIntoKeyValuesTest, KeyWithMultipleValues) {
+ EXPECT_TRUE(SplitStringIntoKeyValues("key1\tvalue1, value2 value3",
+ '\t', // Key separators
+ &key, &values));
+ EXPECT_EQ("key1", key);
+ ASSERT_EQ(1U, values.size());
+ EXPECT_EQ("value1, value2 value3", values[0]);
+}
+
+TEST_F(SplitStringIntoKeyValuesTest, EmptyInputSingleValue) {
+ EXPECT_FALSE(SplitStringIntoKeyValues("", // Empty input
+ '\t', // Key separators
+ &key, &values));
+ EXPECT_TRUE(key.empty());
+ EXPECT_TRUE(values.empty());
+}
+
+TEST_F(SplitStringIntoKeyValuesTest, EmptyValueInputSingleValue) {
+ EXPECT_FALSE(SplitStringIntoKeyValues("key_with_no_value\t",
+ '\t', // Key separators
+ &key, &values));
+ EXPECT_EQ("key_with_no_value", key);
+ EXPECT_TRUE(values.empty());
+}
+
+TEST_F(SplitStringIntoKeyValuesTest, EmptyKeyInputSingleValue) {
+ EXPECT_TRUE(SplitStringIntoKeyValues("\tvalue for empty key",
+ '\t', // Key separators
+ &key, &values));
+ EXPECT_TRUE(key.empty());
+ ASSERT_EQ(1U, values.size());
+ EXPECT_EQ("value for empty key", values[0]);
+}
+
+TEST_F(SplitStringIntoKeyValuesTest, KeyWithSingleValue) {
+ EXPECT_TRUE(SplitStringIntoKeyValues("key1\tvalue1, value2 value3",
+ '\t', // Key separators
+ &key, &values));
+ EXPECT_EQ("key1", key);
+ ASSERT_EQ(1U, values.size());
+ EXPECT_EQ("value1, value2 value3", values[0]);
+}
+
+class SplitStringIntoKeyValuePairsTest : public testing::Test {
+ protected:
+ std::vector<std::pair<std::string, std::string> > kv_pairs;
+};
+
+TEST_F(SplitStringIntoKeyValuePairsTest, EmptyString) {
+ EXPECT_TRUE(SplitStringIntoKeyValuePairs("",
+ ':', // Key-value delimiters
+ ',', // Key-value pair delims
+ &kv_pairs));
+ EXPECT_TRUE(kv_pairs.empty());
+}
+
+TEST_F(SplitStringIntoKeyValuePairsTest, EmptySecondPair) {
+ EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1:value1,,key3:value3",
+ ':', // Key-value delimiters
+ ',', // Key-value pair delims
+ &kv_pairs));
+ ASSERT_EQ(2U, kv_pairs.size());
+ EXPECT_EQ("key1", kv_pairs[0].first);
+ EXPECT_EQ("value1", kv_pairs[0].second);
+ EXPECT_EQ("key3", kv_pairs[1].first);
+ EXPECT_EQ("value3", kv_pairs[1].second);
+}
+
+TEST_F(SplitStringIntoKeyValuePairsTest, EmptySecondValue) {
+ EXPECT_FALSE(SplitStringIntoKeyValuePairs("key1:value1 , key2:",
+ ':', // Key-value delimiters
+ ',', // Key-value pair delims
+ &kv_pairs));
+ ASSERT_EQ(2U, kv_pairs.size());
+ EXPECT_EQ("key1", kv_pairs[0].first);
+ EXPECT_EQ("value1", kv_pairs[0].second);
+ EXPECT_EQ("key2", kv_pairs[1].first);
+ EXPECT_EQ("", kv_pairs[1].second);
+}
+
+TEST_F(SplitStringIntoKeyValuePairsTest, DelimiterInValue) {
+ EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1:va:ue1 , key2:value2",
+ ':', // Key-value delimiters
+ ',', // Key-value pair delims
+ &kv_pairs));
+ ASSERT_EQ(2U, kv_pairs.size());
+ EXPECT_EQ("key1", kv_pairs[0].first);
+ EXPECT_EQ("va:ue1", kv_pairs[0].second);
+ EXPECT_EQ("key2", kv_pairs[1].first);
+ EXPECT_EQ("value2", kv_pairs[1].second);
+}
+
+TEST(SplitStringUsingSubstrTest, EmptyString) {
+ std::vector<std::string> results;
+ SplitStringUsingSubstr("", "DELIMITER", &results);
+ ASSERT_EQ(1u, results.size());
+ EXPECT_THAT(results, ElementsAre(""));
+}
+
+// Test for SplitString
+TEST(StringUtilTest, SplitString) {
+ std::vector<std::wstring> r;
+
+ SplitString(L"", L',', &r);
+ EXPECT_EQ(0U, r.size());
+ r.clear();
+
+ SplitString(L"a,b,c", L',', &r);
+ ASSERT_EQ(3U, r.size());
+ EXPECT_EQ(r[0], L"a");
+ EXPECT_EQ(r[1], L"b");
+ EXPECT_EQ(r[2], L"c");
+ r.clear();
+
+ SplitString(L"a, b, c", L',', &r);
+ ASSERT_EQ(3U, r.size());
+ EXPECT_EQ(r[0], L"a");
+ EXPECT_EQ(r[1], L"b");
+ EXPECT_EQ(r[2], L"c");
+ r.clear();
+
+ SplitString(L"a,,c", L',', &r);
+ ASSERT_EQ(3U, r.size());
+ EXPECT_EQ(r[0], L"a");
+ EXPECT_EQ(r[1], L"");
+ EXPECT_EQ(r[2], L"c");
+ r.clear();
+
+ SplitString(L" ", L'*', &r);
+ EXPECT_EQ(0U, r.size());
+ r.clear();
+
+ SplitString(L"foo", L'*', &r);
+ ASSERT_EQ(1U, r.size());
+ EXPECT_EQ(r[0], L"foo");
+ r.clear();
+
+ SplitString(L"foo ,", L',', &r);
+ ASSERT_EQ(2U, r.size());
+ EXPECT_EQ(r[0], L"foo");
+ EXPECT_EQ(r[1], L"");
+ r.clear();
+
+ SplitString(L",", L',', &r);
+ ASSERT_EQ(2U, r.size());
+ EXPECT_EQ(r[0], L"");
+ EXPECT_EQ(r[1], L"");
+ r.clear();
+
+ SplitString(L"\t\ta\t", L'\t', &r);
+ ASSERT_EQ(4U, r.size());
+ EXPECT_EQ(r[0], L"");
+ EXPECT_EQ(r[1], L"");
+ EXPECT_EQ(r[2], L"a");
+ EXPECT_EQ(r[3], L"");
+ r.clear();
+
+ SplitString(L"\ta\t\nb\tcc", L'\n', &r);
+ ASSERT_EQ(2U, r.size());
+ EXPECT_EQ(r[0], L"a");
+ EXPECT_EQ(r[1], L"b\tcc");
+ r.clear();
+}
+
+TEST(SplitStringUsingSubstrTest, StringWithNoDelimiter) {
+ std::vector<std::string> results;
+ SplitStringUsingSubstr("alongwordwithnodelimiter", "DELIMITER", &results);
+ ASSERT_EQ(1u, results.size());
+ EXPECT_THAT(results, ElementsAre("alongwordwithnodelimiter"));
+}
+
+TEST(SplitStringUsingSubstrTest, LeadingDelimitersSkipped) {
+ std::vector<std::string> results;
+ SplitStringUsingSubstr(
+ "DELIMITERDELIMITERDELIMITERoneDELIMITERtwoDELIMITERthree",
+ "DELIMITER",
+ &results);
+ ASSERT_EQ(6u, results.size());
+ EXPECT_THAT(results, ElementsAre("", "", "", "one", "two", "three"));
+}
+
+TEST(SplitStringUsingSubstrTest, ConsecutiveDelimitersSkipped) {
+ std::vector<std::string> results;
+ SplitStringUsingSubstr(
+ "unoDELIMITERDELIMITERDELIMITERdosDELIMITERtresDELIMITERDELIMITERcuatro",
+ "DELIMITER",
+ &results);
+ ASSERT_EQ(7u, results.size());
+ EXPECT_THAT(results, ElementsAre("uno", "", "", "dos", "tres", "", "cuatro"));
+}
+
+TEST(SplitStringUsingSubstrTest, TrailingDelimitersSkipped) {
+ std::vector<std::string> results;
+ SplitStringUsingSubstr(
+ "unDELIMITERdeuxDELIMITERtroisDELIMITERquatreDELIMITERDELIMITERDELIMITER",
+ "DELIMITER",
+ &results);
+ ASSERT_EQ(7u, results.size());
+ EXPECT_THAT(
+ results, ElementsAre("un", "deux", "trois", "quatre", "", "", ""));
+}
+
+TEST(StringSplitTest, StringSplitDontTrim) {
+ std::vector<std::string> r;
+
+ SplitStringDontTrim(" ", '*', &r);
+ ASSERT_EQ(1U, r.size());
+ EXPECT_EQ(r[0], " ");
+
+ SplitStringDontTrim("\t \ta\t ", '\t', &r);
+ ASSERT_EQ(4U, r.size());
+ EXPECT_EQ(r[0], "");
+ EXPECT_EQ(r[1], " ");
+ EXPECT_EQ(r[2], "a");
+ EXPECT_EQ(r[3], " ");
+
+ SplitStringDontTrim("\ta\t\nb\tcc", '\n', &r);
+ ASSERT_EQ(2U, r.size());
+ EXPECT_EQ(r[0], "\ta\t");
+ EXPECT_EQ(r[1], "b\tcc");
+}
+
+TEST(StringSplitTest, SplitStringAlongWhitespace) {
+ struct TestData {
+ const char* input;
+ const size_t expected_result_count;
+ const char* output1;
+ const char* output2;
+ } data[] = {
+ { "a", 1, "a", "" },
+ { " ", 0, "", "" },
+ { " a", 1, "a", "" },
+ { " ab ", 1, "ab", "" },
+ { " ab c", 2, "ab", "c" },
+ { " ab c ", 2, "ab", "c" },
+ { " ab cd", 2, "ab", "cd" },
+ { " ab cd ", 2, "ab", "cd" },
+ { " \ta\t", 1, "a", "" },
+ { " b\ta\t", 2, "b", "a" },
+ { " b\tat", 2, "b", "at" },
+ { "b\tat", 2, "b", "at" },
+ { "b\t at", 2, "b", "at" },
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) {
+ std::vector<std::string> results;
+ SplitStringAlongWhitespace(data[i].input, &results);
+ ASSERT_EQ(data[i].expected_result_count, results.size());
+ if (data[i].expected_result_count > 0)
+ ASSERT_EQ(data[i].output1, results[0]);
+ if (data[i].expected_result_count > 1)
+ ASSERT_EQ(data[i].output2, results[1]);
+ }
+}
+
+} // namespace base
diff --git a/src/base/string_tokenizer.h b/src/base/string_tokenizer.h
new file mode 100644
index 0000000..c2307a5
--- /dev/null
+++ b/src/base/string_tokenizer.h
@@ -0,0 +1,256 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_STRING_TOKENIZER_H_
+#define BASE_STRING_TOKENIZER_H_
+
+#include <algorithm>
+#include <string>
+
+#include "base/string_piece.h"
+
+// StringTokenizerT is a simple string tokenizer class. It works like an
+// iterator that with each step (see the Advance method) updates members that
+// refer to the next token in the input string. The user may optionally
+// configure the tokenizer to return delimiters.
+//
+// Warning: be careful not to pass a C string into the 2-arg constructor:
+// StringTokenizer t("this is a test", " "); // WRONG
+// This will create a temporary std::string, save the begin() and end()
+// iterators, and then the string will be freed before we actually start
+// tokenizing it.
+// Instead, use a std::string or use the 3 arg constructor of CStringTokenizer.
+//
+//
+// EXAMPLE 1:
+//
+// char input[] = "this is a test";
+// CStringTokenizer t(input, input + strlen(input), " ");
+// while (t.GetNext()) {
+// printf("%s\n", t.token().c_str());
+// }
+//
+// Output:
+//
+// this
+// is
+// a
+// test
+//
+//
+// EXAMPLE 2:
+//
+// std::string input = "no-cache=\"foo, bar\", private";
+// StringTokenizer t(input, ", ");
+// t.set_quote_chars("\"");
+// while (t.GetNext()) {
+// printf("%s\n", t.token().c_str());
+// }
+//
+// Output:
+//
+// no-cache="foo, bar"
+// private
+//
+//
+// EXAMPLE 3:
+//
+// bool next_is_option = false, next_is_value = false;
+// std::string input = "text/html; charset=UTF-8; foo=bar";
+// StringTokenizer t(input, "; =");
+// t.set_options(StringTokenizer::RETURN_DELIMS);
+// while (t.GetNext()) {
+// if (t.token_is_delim()) {
+// switch (*t.token_begin()) {
+// case ';':
+// next_is_option = true;
+// break;
+// case '=':
+// next_is_value = true;
+// break;
+// }
+// } else {
+// const char* label;
+// if (next_is_option) {
+// label = "option-name";
+// next_is_option = false;
+// } else if (next_is_value) {
+// label = "option-value";
+// next_is_value = false;
+// } else {
+// label = "mime-type";
+// }
+// printf("%s: %s\n", label, t.token().c_str());
+// }
+// }
+//
+//
+template <class str, class const_iterator>
+class StringTokenizerT {
+ public:
+ typedef typename str::value_type char_type;
+
+ // Options that may be pass to set_options()
+ enum {
+ // Specifies the delimiters should be returned as tokens
+ RETURN_DELIMS = 1 << 0,
+ };
+
+ // The string object must live longer than the tokenizer. (In particular this
+ // should not be constructed with a temporary.)
+ StringTokenizerT(const str& string,
+ const str& delims) {
+ Init(string.begin(), string.end(), delims);
+ }
+
+ StringTokenizerT(const_iterator string_begin,
+ const_iterator string_end,
+ const str& delims) {
+ Init(string_begin, string_end, delims);
+ }
+
+ // Set the options for this tokenizer. By default, this is 0.
+ void set_options(int options) { options_ = options; }
+
+ // Set the characters to regard as quotes. By default, this is empty. When
+ // a quote char is encountered, the tokenizer will switch into a mode where
+ // it ignores delimiters that it finds. It switches out of this mode once it
+ // finds another instance of the quote char. If a backslash is encountered
+ // within a quoted string, then the next character is skipped.
+ void set_quote_chars(const str& quotes) { quotes_ = quotes; }
+
+ // Call this method to advance the tokenizer to the next delimiter. This
+ // returns false if the tokenizer is complete. This method must be called
+ // before calling any of the token* methods.
+ bool GetNext() {
+ if (quotes_.empty() && options_ == 0)
+ return QuickGetNext();
+ else
+ return FullGetNext();
+ }
+
+ // Start iterating through tokens from the beginning of the string.
+ void Reset() {
+ token_end_ = start_pos_;
+ }
+
+ // Returns true if token is a delimiter. When the tokenizer is constructed
+ // with the RETURN_DELIMS option, this method can be used to check if the
+ // returned token is actually a delimiter.
+ bool token_is_delim() const { return token_is_delim_; }
+
+ // If GetNext() returned true, then these methods may be used to read the
+ // value of the token.
+ const_iterator token_begin() const { return token_begin_; }
+ const_iterator token_end() const { return token_end_; }
+ str token() const { return str(token_begin_, token_end_); }
+ base::StringPiece token_piece() const {
+ return base::StringPiece(&*token_begin_,
+ std::distance(token_begin_, token_end_));
+ }
+
+ private:
+ void Init(const_iterator string_begin,
+ const_iterator string_end,
+ const str& delims) {
+ start_pos_ = string_begin;
+ token_begin_ = string_begin;
+ token_end_ = string_begin;
+ end_ = string_end;
+ delims_ = delims;
+ options_ = 0;
+ token_is_delim_ = false;
+ }
+
+ // Implementation of GetNext() for when we have no quote characters. We have
+ // two separate implementations because AdvanceOne() is a hot spot in large
+ // text files with large tokens.
+ bool QuickGetNext() {
+ token_is_delim_ = false;
+ for (;;) {
+ token_begin_ = token_end_;
+ if (token_end_ == end_)
+ return false;
+ ++token_end_;
+ if (delims_.find(*token_begin_) == str::npos)
+ break;
+ // else skip over delimiter.
+ }
+ while (token_end_ != end_ && delims_.find(*token_end_) == str::npos)
+ ++token_end_;
+ return true;
+ }
+
+ // Implementation of GetNext() for when we have to take quotes into account.
+ bool FullGetNext() {
+ AdvanceState state;
+ token_is_delim_ = false;
+ for (;;) {
+ token_begin_ = token_end_;
+ if (token_end_ == end_)
+ return false;
+ ++token_end_;
+ if (AdvanceOne(&state, *token_begin_))
+ break;
+ if (options_ & RETURN_DELIMS) {
+ token_is_delim_ = true;
+ return true;
+ }
+ // else skip over delimiter.
+ }
+ while (token_end_ != end_ && AdvanceOne(&state, *token_end_))
+ ++token_end_;
+ return true;
+ }
+
+ bool IsDelim(char_type c) const {
+ return delims_.find(c) != str::npos;
+ }
+
+ bool IsQuote(char_type c) const {
+ return quotes_.find(c) != str::npos;
+ }
+
+ struct AdvanceState {
+ bool in_quote;
+ bool in_escape;
+ char_type quote_char;
+ AdvanceState() : in_quote(false), in_escape(false), quote_char('\0') {}
+ };
+
+ // Returns true if a delimiter was not hit.
+ bool AdvanceOne(AdvanceState* state, char_type c) {
+ if (state->in_quote) {
+ if (state->in_escape) {
+ state->in_escape = false;
+ } else if (c == '\\') {
+ state->in_escape = true;
+ } else if (c == state->quote_char) {
+ state->in_quote = false;
+ }
+ } else {
+ if (IsDelim(c))
+ return false;
+ state->in_quote = IsQuote(state->quote_char = c);
+ }
+ return true;
+ }
+
+ const_iterator start_pos_;
+ const_iterator token_begin_;
+ const_iterator token_end_;
+ const_iterator end_;
+ str delims_;
+ str quotes_;
+ int options_;
+ bool token_is_delim_;
+};
+
+typedef StringTokenizerT<std::string, std::string::const_iterator>
+ StringTokenizer;
+typedef StringTokenizerT<std::wstring, std::wstring::const_iterator>
+ WStringTokenizer;
+typedef StringTokenizerT<std::string, const char*> CStringTokenizer;
+
+#endif // BASE_STRING_TOKENIZER_H_
diff --git a/src/base/string_tokenizer_unittest.cc b/src/base/string_tokenizer_unittest.cc
new file mode 100644
index 0000000..61841f2
--- /dev/null
+++ b/src/base/string_tokenizer_unittest.cc
@@ -0,0 +1,230 @@
+// 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/string_tokenizer.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace {
+
+TEST(StringTokenizerTest, Simple) {
+ string input = "this is a test";
+ StringTokenizer t(input, " ");
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("this"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("is"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("a"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("test"), t.token());
+
+ EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, Reset) {
+ string input = "this is a test";
+ StringTokenizer t(input, " ");
+
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("this"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("is"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("a"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("test"), t.token());
+
+ EXPECT_FALSE(t.GetNext());
+ t.Reset();
+ }
+}
+
+TEST(StringTokenizerTest, RetDelims) {
+ string input = "this is a test";
+ StringTokenizer t(input, " ");
+ t.set_options(StringTokenizer::RETURN_DELIMS);
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("this"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string(" "), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("is"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string(" "), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("a"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string(" "), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("test"), t.token());
+
+ EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ManyDelims) {
+ string input = "this: is, a-test";
+ StringTokenizer t(input, ": ,-");
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("this"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("is"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("a"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("test"), t.token());
+
+ EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseHeader) {
+ string input = "Content-Type: text/html ; charset=UTF-8";
+ StringTokenizer t(input, ": ;=");
+ t.set_options(StringTokenizer::RETURN_DELIMS);
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_FALSE(t.token_is_delim());
+ EXPECT_EQ(string("Content-Type"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_TRUE(t.token_is_delim());
+ EXPECT_EQ(string(":"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_TRUE(t.token_is_delim());
+ EXPECT_EQ(string(" "), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_FALSE(t.token_is_delim());
+ EXPECT_EQ(string("text/html"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_TRUE(t.token_is_delim());
+ EXPECT_EQ(string(" "), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_TRUE(t.token_is_delim());
+ EXPECT_EQ(string(";"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_TRUE(t.token_is_delim());
+ EXPECT_EQ(string(" "), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_FALSE(t.token_is_delim());
+ EXPECT_EQ(string("charset"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_TRUE(t.token_is_delim());
+ EXPECT_EQ(string("="), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_FALSE(t.token_is_delim());
+ EXPECT_EQ(string("UTF-8"), t.token());
+
+ EXPECT_FALSE(t.GetNext());
+ EXPECT_FALSE(t.token_is_delim());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString) {
+ string input = "foo bar 'hello world' baz";
+ StringTokenizer t(input, " ");
+ t.set_quote_chars("'");
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("foo"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("bar"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("'hello world'"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("baz"), t.token());
+
+ EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString_Malformed) {
+ string input = "bar 'hello wo";
+ StringTokenizer t(input, " ");
+ t.set_quote_chars("'");
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("bar"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("'hello wo"), t.token());
+
+ EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString_Multiple) {
+ string input = "bar 'hel\"lo\" wo' baz\"";
+ StringTokenizer t(input, " ");
+ t.set_quote_chars("'\"");
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("bar"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("'hel\"lo\" wo'"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("baz\""), t.token());
+
+ EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString_EscapedQuotes) {
+ string input = "foo 'don\\'t do that'";
+ StringTokenizer t(input, " ");
+ t.set_quote_chars("'");
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("foo"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("'don\\'t do that'"), t.token());
+
+ EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString_EscapedQuotes2) {
+ string input = "foo='a, b', bar";
+ StringTokenizer t(input, ", ");
+ t.set_quote_chars("'");
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("foo='a, b'"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("bar"), t.token());
+
+ EXPECT_FALSE(t.GetNext());
+}
+
+} // namespace
diff --git a/src/base/string_util.cc b/src/base/string_util.cc
new file mode 100644
index 0000000..1acf1be
--- /dev/null
+++ b/src/base/string_util.cc
@@ -0,0 +1,1060 @@
+// Copyright (c) 2012 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/string_util.h"
+
+#include "build/build_config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/utf_string_conversion_utils.h"
+#include "base/utf_string_conversions.h"
+#include "base/third_party/icu/icu_utf.h"
+
+namespace {
+
+// Force the singleton used by Empty[W]String[16] to be a unique type. This
+// prevents other code that might accidentally use Singleton<string> from
+// getting our internal one.
+struct EmptyStrings {
+ EmptyStrings() {}
+ const std::string s;
+ const std::wstring ws;
+ const string16 s16;
+
+ static EmptyStrings* GetInstance() {
+ return Singleton<EmptyStrings>::get();
+ }
+};
+
+// Used by ReplaceStringPlaceholders to track the position in the string of
+// replaced parameters.
+struct ReplacementOffset {
+ ReplacementOffset(uintptr_t parameter, size_t offset)
+ : parameter(parameter),
+ offset(offset) {}
+
+ // Index of the parameter.
+ uintptr_t parameter;
+
+ // Starting position in the string.
+ size_t offset;
+};
+
+static bool CompareParameter(const ReplacementOffset& elem1,
+ const ReplacementOffset& elem2) {
+ return elem1.parameter < elem2.parameter;
+}
+
+} // namespace
+
+namespace base {
+
+bool IsWprintfFormatPortable(const wchar_t* format) {
+ for (const wchar_t* position = format; *position != '\0'; ++position) {
+ if (*position == '%') {
+ bool in_specification = true;
+ bool modifier_l = false;
+ while (in_specification) {
+ // Eat up characters until reaching a known specifier.
+ if (*++position == '\0') {
+ // The format string ended in the middle of a specification. Call
+ // it portable because no unportable specifications were found. The
+ // string is equally broken on all platforms.
+ return true;
+ }
+
+ if (*position == 'l') {
+ // 'l' is the only thing that can save the 's' and 'c' specifiers.
+ modifier_l = true;
+ } else if (((*position == 's' || *position == 'c') && !modifier_l) ||
+ *position == 'S' || *position == 'C' || *position == 'F' ||
+ *position == 'D' || *position == 'O' || *position == 'U') {
+ // Not portable.
+ return false;
+ }
+
+ if (wcschr(L"diouxXeEfgGaAcspn%", *position)) {
+ // Portable, keep scanning the rest of the format string.
+ in_specification = false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+} // namespace base
+
+
+const std::string& EmptyString() {
+ return EmptyStrings::GetInstance()->s;
+}
+
+const std::wstring& EmptyWString() {
+ return EmptyStrings::GetInstance()->ws;
+}
+
+const string16& EmptyString16() {
+ return EmptyStrings::GetInstance()->s16;
+}
+
+#define WHITESPACE_UNICODE \
+ 0x0009, /* <control-0009> to <control-000D> */ \
+ 0x000A, \
+ 0x000B, \
+ 0x000C, \
+ 0x000D, \
+ 0x0020, /* Space */ \
+ 0x0085, /* <control-0085> */ \
+ 0x00A0, /* No-Break Space */ \
+ 0x1680, /* Ogham Space Mark */ \
+ 0x180E, /* Mongolian Vowel Separator */ \
+ 0x2000, /* En Quad to Hair Space */ \
+ 0x2001, \
+ 0x2002, \
+ 0x2003, \
+ 0x2004, \
+ 0x2005, \
+ 0x2006, \
+ 0x2007, \
+ 0x2008, \
+ 0x2009, \
+ 0x200A, \
+ 0x200C, /* Zero Width Non-Joiner */ \
+ 0x2028, /* Line Separator */ \
+ 0x2029, /* Paragraph Separator */ \
+ 0x202F, /* Narrow No-Break Space */ \
+ 0x205F, /* Medium Mathematical Space */ \
+ 0x3000, /* Ideographic Space */ \
+ 0
+
+const wchar_t kWhitespaceWide[] = {
+ WHITESPACE_UNICODE
+};
+const char16 kWhitespaceUTF16[] = {
+ WHITESPACE_UNICODE
+};
+const char kWhitespaceASCII[] = {
+ 0x09, // <control-0009> to <control-000D>
+ 0x0A,
+ 0x0B,
+ 0x0C,
+ 0x0D,
+ 0x20, // Space
+ 0
+};
+
+const char kUtf8ByteOrderMark[] = "\xEF\xBB\xBF";
+
+template<typename STR>
+bool ReplaceCharsT(const STR& input,
+ const typename STR::value_type replace_chars[],
+ const STR& replace_with,
+ STR* output) {
+ bool removed = false;
+ size_t replace_length = replace_with.length();
+
+ *output = input;
+
+ size_t found = output->find_first_of(replace_chars);
+ while (found != STR::npos) {
+ removed = true;
+ output->replace(found, 1, replace_with);
+ found = output->find_first_of(replace_chars, found + replace_length);
+ }
+
+ return removed;
+}
+
+bool ReplaceChars(const string16& input,
+ const char16 replace_chars[],
+ const string16& replace_with,
+ string16* output) {
+ return ReplaceCharsT(input, replace_chars, replace_with, output);
+}
+
+bool ReplaceChars(const std::string& input,
+ const char replace_chars[],
+ const std::string& replace_with,
+ std::string* output) {
+ return ReplaceCharsT(input, replace_chars, replace_with, output);
+}
+
+bool RemoveChars(const string16& input,
+ const char16 remove_chars[],
+ string16* output) {
+ return ReplaceChars(input, remove_chars, string16(), output);
+}
+
+bool RemoveChars(const std::string& input,
+ const char remove_chars[],
+ std::string* output) {
+ return ReplaceChars(input, remove_chars, std::string(), output);
+}
+
+template<typename STR>
+TrimPositions TrimStringT(const STR& input,
+ const typename STR::value_type trim_chars[],
+ TrimPositions positions,
+ STR* output) {
+ // Find the edges of leading/trailing whitespace as desired.
+ const typename STR::size_type last_char = input.length() - 1;
+ const typename STR::size_type first_good_char = (positions & TRIM_LEADING) ?
+ input.find_first_not_of(trim_chars) : 0;
+ const typename STR::size_type last_good_char = (positions & TRIM_TRAILING) ?
+ input.find_last_not_of(trim_chars) : last_char;
+
+ // When the string was all whitespace, report that we stripped off whitespace
+ // from whichever position the caller was interested in. For empty input, we
+ // stripped no whitespace, but we still need to clear |output|.
+ if (input.empty() ||
+ (first_good_char == STR::npos) || (last_good_char == STR::npos)) {
+ bool input_was_empty = input.empty(); // in case output == &input
+ output->clear();
+ return input_was_empty ? TRIM_NONE : positions;
+ }
+
+ // Trim the whitespace.
+ *output =
+ input.substr(first_good_char, last_good_char - first_good_char + 1);
+
+ // Return where we trimmed from.
+ return static_cast<TrimPositions>(
+ ((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) |
+ ((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING));
+}
+
+bool TrimString(const std::wstring& input,
+ const wchar_t trim_chars[],
+ std::wstring* output) {
+ return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
+}
+
+#if !defined(WCHAR_T_IS_UTF16)
+bool TrimString(const string16& input,
+ const char16 trim_chars[],
+ string16* output) {
+ return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
+}
+#endif
+
+bool TrimString(const std::string& input,
+ const char trim_chars[],
+ std::string* output) {
+ return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
+}
+
+void TruncateUTF8ToByteSize(const std::string& input,
+ const size_t byte_size,
+ std::string* output) {
+ DCHECK(output);
+ if (byte_size > input.length()) {
+ *output = input;
+ return;
+ }
+ DCHECK_LE(byte_size, static_cast<uint32>(kint32max));
+ // Note: This cast is necessary because CBU8_NEXT uses int32s.
+ int32 truncation_length = static_cast<int32>(byte_size);
+ int32 char_index = truncation_length - 1;
+ const char* data = input.data();
+
+ // Using CBU8, we will move backwards from the truncation point
+ // to the beginning of the string looking for a valid UTF8
+ // character. Once a full UTF8 character is found, we will
+ // truncate the string to the end of that character.
+ while (char_index >= 0) {
+ int32 prev = char_index;
+ uint32 code_point = 0;
+ CBU8_NEXT(data, char_index, truncation_length, code_point);
+ if (!base::IsValidCharacter(code_point) ||
+ !base::IsValidCodepoint(code_point)) {
+ char_index = prev - 1;
+ } else {
+ break;
+ }
+ }
+
+ if (char_index >= 0 )
+ *output = input.substr(0, char_index);
+ else
+ output->clear();
+}
+
+TrimPositions TrimWhitespace(const string16& input,
+ TrimPositions positions,
+ string16* output) {
+ return TrimStringT(input, kWhitespaceUTF16, positions, output);
+}
+
+TrimPositions TrimWhitespaceASCII(const std::string& input,
+ TrimPositions positions,
+ std::string* output) {
+ return TrimStringT(input, kWhitespaceASCII, positions, output);
+}
+
+// This function is only for backward-compatibility.
+// To be removed when all callers are updated.
+TrimPositions TrimWhitespace(const std::string& input,
+ TrimPositions positions,
+ std::string* output) {
+ return TrimWhitespaceASCII(input, positions, output);
+}
+
+template<typename STR>
+STR CollapseWhitespaceT(const STR& text,
+ bool trim_sequences_with_line_breaks) {
+ STR result;
+ result.resize(text.size());
+
+ // Set flags to pretend we're already in a trimmed whitespace sequence, so we
+ // will trim any leading whitespace.
+ bool in_whitespace = true;
+ bool already_trimmed = true;
+
+ int chars_written = 0;
+ for (typename STR::const_iterator i(text.begin()); i != text.end(); ++i) {
+ if (IsWhitespace(*i)) {
+ if (!in_whitespace) {
+ // Reduce all whitespace sequences to a single space.
+ in_whitespace = true;
+ result[chars_written++] = L' ';
+ }
+ if (trim_sequences_with_line_breaks && !already_trimmed &&
+ ((*i == '\n') || (*i == '\r'))) {
+ // Whitespace sequences containing CR or LF are eliminated entirely.
+ already_trimmed = true;
+ --chars_written;
+ }
+ } else {
+ // Non-whitespace chracters are copied straight across.
+ in_whitespace = false;
+ already_trimmed = false;
+ result[chars_written++] = *i;
+ }
+ }
+
+ if (in_whitespace && !already_trimmed) {
+ // Any trailing whitespace is eliminated.
+ --chars_written;
+ }
+
+ result.resize(chars_written);
+ return result;
+}
+
+std::wstring CollapseWhitespace(const std::wstring& text,
+ bool trim_sequences_with_line_breaks) {
+ return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
+}
+
+#if !defined(WCHAR_T_IS_UTF16)
+string16 CollapseWhitespace(const string16& text,
+ bool trim_sequences_with_line_breaks) {
+ return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
+}
+#endif
+
+std::string CollapseWhitespaceASCII(const std::string& text,
+ bool trim_sequences_with_line_breaks) {
+ return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
+}
+
+bool ContainsOnlyWhitespaceASCII(const std::string& str) {
+ for (std::string::const_iterator i(str.begin()); i != str.end(); ++i) {
+ if (!IsAsciiWhitespace(*i))
+ return false;
+ }
+ return true;
+}
+
+bool ContainsOnlyWhitespace(const string16& str) {
+ return str.find_first_not_of(kWhitespaceUTF16) == string16::npos;
+}
+
+template<typename STR>
+static bool ContainsOnlyCharsT(const STR& input, const STR& characters) {
+ for (typename STR::const_iterator iter = input.begin();
+ iter != input.end(); ++iter) {
+ if (characters.find(*iter) == STR::npos)
+ return false;
+ }
+ return true;
+}
+
+bool ContainsOnlyChars(const std::wstring& input,
+ const std::wstring& characters) {
+ return ContainsOnlyCharsT(input, characters);
+}
+
+#if !defined(WCHAR_T_IS_UTF16)
+bool ContainsOnlyChars(const string16& input, const string16& characters) {
+ return ContainsOnlyCharsT(input, characters);
+}
+#endif
+
+bool ContainsOnlyChars(const std::string& input,
+ const std::string& characters) {
+ return ContainsOnlyCharsT(input, characters);
+}
+
+std::string WideToASCII(const std::wstring& wide) {
+ DCHECK(IsStringASCII(wide)) << wide;
+ return std::string(wide.begin(), wide.end());
+}
+
+std::string UTF16ToASCII(const string16& utf16) {
+ DCHECK(IsStringASCII(utf16)) << utf16;
+ return std::string(utf16.begin(), utf16.end());
+}
+
+// Latin1 is just the low range of Unicode, so we can copy directly to convert.
+bool WideToLatin1(const std::wstring& wide, std::string* latin1) {
+ std::string output;
+ output.resize(wide.size());
+ latin1->clear();
+ for (size_t i = 0; i < wide.size(); i++) {
+ if (wide[i] > 255)
+ return false;
+ output[i] = static_cast<char>(wide[i]);
+ }
+ latin1->swap(output);
+ return true;
+}
+
+template<class STR>
+static bool DoIsStringASCII(const STR& str) {
+ for (size_t i = 0; i < str.length(); i++) {
+ typename ToUnsigned<typename STR::value_type>::Unsigned c = str[i];
+ if (c > 0x7F)
+ return false;
+ }
+ return true;
+}
+
+bool IsStringASCII(const std::wstring& str) {
+ return DoIsStringASCII(str);
+}
+
+#if !defined(WCHAR_T_IS_UTF16)
+bool IsStringASCII(const string16& str) {
+ return DoIsStringASCII(str);
+}
+#endif
+
+bool IsStringASCII(const base::StringPiece& str) {
+ return DoIsStringASCII(str);
+}
+
+bool IsStringUTF8(const std::string& str) {
+ const char *src = str.data();
+ int32 src_len = static_cast<int32>(str.length());
+ int32 char_index = 0;
+
+ while (char_index < src_len) {
+ int32 code_point;
+ CBU8_NEXT(src, char_index, src_len, code_point);
+ if (!base::IsValidCharacter(code_point))
+ return false;
+ }
+ return true;
+}
+
+template<typename Iter>
+static inline bool DoLowerCaseEqualsASCII(Iter a_begin,
+ Iter a_end,
+ const char* b) {
+ for (Iter it = a_begin; it != a_end; ++it, ++b) {
+ if (!*b || base::ToLowerASCII(*it) != *b)
+ return false;
+ }
+ return *b == 0;
+}
+
+// Front-ends for LowerCaseEqualsASCII.
+bool LowerCaseEqualsASCII(const std::string& a, const char* b) {
+ return DoLowerCaseEqualsASCII(a.begin(), a.end(), b);
+}
+
+bool LowerCaseEqualsASCII(const std::wstring& a, const char* b) {
+ return DoLowerCaseEqualsASCII(a.begin(), a.end(), b);
+}
+
+#if !defined(WCHAR_T_IS_UTF16)
+bool LowerCaseEqualsASCII(const string16& a, const char* b) {
+ return DoLowerCaseEqualsASCII(a.begin(), a.end(), b);
+}
+#endif
+
+bool LowerCaseEqualsASCII(std::string::const_iterator a_begin,
+ std::string::const_iterator a_end,
+ const char* b) {
+ return DoLowerCaseEqualsASCII(a_begin, a_end, b);
+}
+
+bool LowerCaseEqualsASCII(std::wstring::const_iterator a_begin,
+ std::wstring::const_iterator a_end,
+ const char* b) {
+ return DoLowerCaseEqualsASCII(a_begin, a_end, b);
+}
+
+#if !defined(WCHAR_T_IS_UTF16)
+bool LowerCaseEqualsASCII(string16::const_iterator a_begin,
+ string16::const_iterator a_end,
+ const char* b) {
+ return DoLowerCaseEqualsASCII(a_begin, a_end, b);
+}
+#endif
+
+// TODO(port): Resolve wchar_t/iterator issues that require OS_ANDROID here.
+#if !defined(OS_ANDROID) && !defined(__LB_ANDROID__)
+bool LowerCaseEqualsASCII(const char* a_begin,
+ const char* a_end,
+ const char* b) {
+ return DoLowerCaseEqualsASCII(a_begin, a_end, b);
+}
+
+bool LowerCaseEqualsASCII(const wchar_t* a_begin,
+ const wchar_t* a_end,
+ const char* b) {
+ return DoLowerCaseEqualsASCII(a_begin, a_end, b);
+}
+
+#if !defined(WCHAR_T_IS_UTF16)
+bool LowerCaseEqualsASCII(const char16* a_begin,
+ const char16* a_end,
+ const char* b) {
+ return DoLowerCaseEqualsASCII(a_begin, a_end, b);
+}
+#endif
+
+#endif // !defined(OS_ANDROID) && !defined(__LB_ANDROID__)
+
+bool EqualsASCII(const string16& a, const base::StringPiece& b) {
+ if (a.length() != b.length())
+ return false;
+ return std::equal(b.begin(), b.end(), a.begin());
+}
+
+bool StartsWithASCII(const std::string& str,
+ const std::string& search,
+ bool case_sensitive) {
+ if (case_sensitive)
+ return str.compare(0, search.length(), search) == 0;
+ else
+ return base::strncasecmp(str.c_str(), search.c_str(), search.length()) == 0;
+}
+
+template <typename STR>
+bool StartsWithT(const STR& str, const STR& search, bool case_sensitive) {
+ if (case_sensitive) {
+ return str.compare(0, search.length(), search) == 0;
+ } else {
+ if (search.size() > str.size())
+ return false;
+ return std::equal(search.begin(), search.end(), str.begin(),
+ base::CaseInsensitiveCompare<typename STR::value_type>());
+ }
+}
+
+bool StartsWith(const std::wstring& str, const std::wstring& search,
+ bool case_sensitive) {
+ return StartsWithT(str, search, case_sensitive);
+}
+
+#if !defined(WCHAR_T_IS_UTF16)
+bool StartsWith(const string16& str, const string16& search,
+ bool case_sensitive) {
+ return StartsWithT(str, search, case_sensitive);
+}
+#endif
+
+template <typename STR>
+bool EndsWithT(const STR& str, const STR& search, bool case_sensitive) {
+ typename STR::size_type str_length = str.length();
+ typename STR::size_type search_length = search.length();
+ if (search_length > str_length)
+ return false;
+ if (case_sensitive) {
+ return str.compare(str_length - search_length, search_length, search) == 0;
+ } else {
+ return std::equal(search.begin(), search.end(),
+ str.begin() + (str_length - search_length),
+ base::CaseInsensitiveCompare<typename STR::value_type>());
+ }
+}
+
+bool EndsWith(const std::string& str, const std::string& search,
+ bool case_sensitive) {
+ return EndsWithT(str, search, case_sensitive);
+}
+
+bool EndsWith(const std::wstring& str, const std::wstring& search,
+ bool case_sensitive) {
+ return EndsWithT(str, search, case_sensitive);
+}
+
+#if !defined(WCHAR_T_IS_UTF16)
+bool EndsWith(const string16& str, const string16& search,
+ bool case_sensitive) {
+ return EndsWithT(str, search, case_sensitive);
+}
+#endif
+
+static const char* const kByteStringsUnlocalized[] = {
+ " B",
+ " kB",
+ " MB",
+ " GB",
+ " TB",
+ " PB"
+};
+
+string16 FormatBytesUnlocalized(int64 bytes) {
+ double unit_amount = static_cast<double>(bytes);
+ size_t dimension = 0;
+ const int kKilo = 1024;
+ while (unit_amount >= kKilo &&
+ dimension < arraysize(kByteStringsUnlocalized) - 1) {
+ unit_amount /= kKilo;
+ dimension++;
+ }
+
+ char buf[64];
+ if (bytes != 0 && dimension > 0 && unit_amount < 100) {
+ base::snprintf(buf, arraysize(buf), "%.1lf%s", unit_amount,
+ kByteStringsUnlocalized[dimension]);
+ } else {
+ base::snprintf(buf, arraysize(buf), "%.0lf%s", unit_amount,
+ kByteStringsUnlocalized[dimension]);
+ }
+
+ return ASCIIToUTF16(buf);
+}
+
+template<class StringType>
+void DoReplaceSubstringsAfterOffset(StringType* str,
+ typename StringType::size_type start_offset,
+ const StringType& find_this,
+ const StringType& replace_with,
+ bool replace_all) {
+ if ((start_offset == StringType::npos) || (start_offset >= str->length()))
+ return;
+
+ DCHECK(!find_this.empty());
+ for (typename StringType::size_type offs(str->find(find_this, start_offset));
+ offs != StringType::npos; offs = str->find(find_this, offs)) {
+ str->replace(offs, find_this.length(), replace_with);
+ offs += replace_with.length();
+
+ if (!replace_all)
+ break;
+ }
+}
+
+void ReplaceFirstSubstringAfterOffset(string16* str,
+ string16::size_type start_offset,
+ const string16& find_this,
+ const string16& replace_with) {
+ DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with,
+ false); // replace first instance
+}
+
+void ReplaceFirstSubstringAfterOffset(std::string* str,
+ std::string::size_type start_offset,
+ const std::string& find_this,
+ const std::string& replace_with) {
+ DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with,
+ false); // replace first instance
+}
+
+void ReplaceSubstringsAfterOffset(string16* str,
+ string16::size_type start_offset,
+ const string16& find_this,
+ const string16& replace_with) {
+ DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with,
+ true); // replace all instances
+}
+
+void ReplaceSubstringsAfterOffset(std::string* str,
+ std::string::size_type start_offset,
+ const std::string& find_this,
+ const std::string& replace_with) {
+ DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with,
+ true); // replace all instances
+}
+
+
+template<typename STR>
+static size_t TokenizeT(const STR& str,
+ const STR& delimiters,
+ std::vector<STR>* tokens) {
+ tokens->clear();
+
+ typename STR::size_type start = str.find_first_not_of(delimiters);
+ while (start != STR::npos) {
+ typename STR::size_type end = str.find_first_of(delimiters, start + 1);
+ if (end == STR::npos) {
+ tokens->push_back(str.substr(start));
+ break;
+ } else {
+ tokens->push_back(str.substr(start, end - start));
+ start = str.find_first_not_of(delimiters, end + 1);
+ }
+ }
+
+ return tokens->size();
+}
+
+size_t Tokenize(const std::wstring& str,
+ const std::wstring& delimiters,
+ std::vector<std::wstring>* tokens) {
+ return TokenizeT(str, delimiters, tokens);
+}
+
+#if !defined(WCHAR_T_IS_UTF16)
+size_t Tokenize(const string16& str,
+ const string16& delimiters,
+ std::vector<string16>* tokens) {
+ return TokenizeT(str, delimiters, tokens);
+}
+#endif
+
+size_t Tokenize(const std::string& str,
+ const std::string& delimiters,
+ std::vector<std::string>* tokens) {
+ return TokenizeT(str, delimiters, tokens);
+}
+
+size_t Tokenize(const base::StringPiece& str,
+ const base::StringPiece& delimiters,
+ std::vector<base::StringPiece>* tokens) {
+ return TokenizeT(str, delimiters, tokens);
+}
+
+template<typename STR>
+static STR JoinStringT(const std::vector<STR>& parts, const STR& sep) {
+ if (parts.empty())
+ return STR();
+
+ STR result(parts[0]);
+ typename std::vector<STR>::const_iterator iter = parts.begin();
+ ++iter;
+
+ for (; iter != parts.end(); ++iter) {
+ result += sep;
+ result += *iter;
+ }
+
+ return result;
+}
+
+std::string JoinString(const std::vector<std::string>& parts, char sep) {
+ return JoinStringT(parts, std::string(1, sep));
+}
+
+string16 JoinString(const std::vector<string16>& parts, char16 sep) {
+ return JoinStringT(parts, string16(1, sep));
+}
+
+std::string JoinString(const std::vector<std::string>& parts,
+ const std::string& separator) {
+ return JoinStringT(parts, separator);
+}
+
+string16 JoinString(const std::vector<string16>& parts,
+ const string16& separator) {
+ return JoinStringT(parts, separator);
+}
+
+template<class FormatStringType, class OutStringType>
+OutStringType DoReplaceStringPlaceholders(const FormatStringType& format_string,
+ const std::vector<OutStringType>& subst, std::vector<size_t>* offsets) {
+ size_t substitutions = subst.size();
+
+ size_t sub_length = 0;
+ for (typename std::vector<OutStringType>::const_iterator iter = subst.begin();
+ iter != subst.end(); ++iter) {
+ sub_length += iter->length();
+ }
+
+ OutStringType formatted;
+ formatted.reserve(format_string.length() + sub_length);
+
+ std::vector<ReplacementOffset> r_offsets;
+ for (typename FormatStringType::const_iterator i = format_string.begin();
+ i != format_string.end(); ++i) {
+ if ('$' == *i) {
+ if (i + 1 != format_string.end()) {
+ ++i;
+ DCHECK('$' == *i || '1' <= *i) << "Invalid placeholder: " << *i;
+ if ('$' == *i) {
+ while (i != format_string.end() && '$' == *i) {
+ formatted.push_back('$');
+ ++i;
+ }
+ --i;
+ } else {
+ uintptr_t index = 0;
+ while (i != format_string.end() && '0' <= *i && *i <= '9') {
+ index *= 10;
+ index += *i - '0';
+ ++i;
+ }
+ --i;
+ index -= 1;
+ if (offsets) {
+ ReplacementOffset r_offset(index,
+ static_cast<int>(formatted.size()));
+ r_offsets.insert(std::lower_bound(r_offsets.begin(),
+ r_offsets.end(),
+ r_offset,
+ &CompareParameter),
+ r_offset);
+ }
+ if (index < substitutions)
+ formatted.append(subst.at(index));
+ }
+ }
+ } else {
+ formatted.push_back(*i);
+ }
+ }
+ if (offsets) {
+ for (std::vector<ReplacementOffset>::const_iterator i = r_offsets.begin();
+ i != r_offsets.end(); ++i) {
+ offsets->push_back(i->offset);
+ }
+ }
+ return formatted;
+}
+
+string16 ReplaceStringPlaceholders(const string16& format_string,
+ const std::vector<string16>& subst,
+ std::vector<size_t>* offsets) {
+ return DoReplaceStringPlaceholders(format_string, subst, offsets);
+}
+
+std::string ReplaceStringPlaceholders(const base::StringPiece& format_string,
+ const std::vector<std::string>& subst,
+ std::vector<size_t>* offsets) {
+ return DoReplaceStringPlaceholders(format_string, subst, offsets);
+}
+
+string16 ReplaceStringPlaceholders(const string16& format_string,
+ const string16& a,
+ size_t* offset) {
+ std::vector<size_t> offsets;
+ std::vector<string16> subst;
+ subst.push_back(a);
+ string16 result = ReplaceStringPlaceholders(format_string, subst, &offsets);
+
+ DCHECK(offsets.size() == 1);
+ if (offset) {
+ *offset = offsets[0];
+ }
+ return result;
+}
+
+static bool IsWildcard(base_icu::UChar32 character) {
+ return character == '*' || character == '?';
+}
+
+// Move the strings pointers to the point where they start to differ.
+template <typename CHAR, typename NEXT>
+static void EatSameChars(const CHAR** pattern, const CHAR* pattern_end,
+ const CHAR** string, const CHAR* string_end,
+ NEXT next) {
+ const CHAR* escape = NULL;
+ while (*pattern != pattern_end && *string != string_end) {
+ if (!escape && IsWildcard(**pattern)) {
+ // We don't want to match wildcard here, except if it's escaped.
+ return;
+ }
+
+ // Check if the escapement char is found. If so, skip it and move to the
+ // next character.
+ if (!escape && **pattern == '\\') {
+ escape = *pattern;
+ next(pattern, pattern_end);
+ continue;
+ }
+
+ // Check if the chars match, if so, increment the ptrs.
+ const CHAR* pattern_next = *pattern;
+ const CHAR* string_next = *string;
+ base_icu::UChar32 pattern_char = next(&pattern_next, pattern_end);
+ if (pattern_char == next(&string_next, string_end) &&
+ pattern_char != (base_icu::UChar32) CBU_SENTINEL) {
+ *pattern = pattern_next;
+ *string = string_next;
+ } else {
+ // Uh ho, it did not match, we are done. If the last char was an
+ // escapement, that means that it was an error to advance the ptr here,
+ // let's put it back where it was. This also mean that the MatchPattern
+ // function will return false because if we can't match an escape char
+ // here, then no one will.
+ if (escape) {
+ *pattern = escape;
+ }
+ return;
+ }
+
+ escape = NULL;
+ }
+}
+
+template <typename CHAR, typename NEXT>
+static void EatWildcard(const CHAR** pattern, const CHAR* end, NEXT next) {
+ while (*pattern != end) {
+ if (!IsWildcard(**pattern))
+ return;
+ next(pattern, end);
+ }
+}
+
+template <typename CHAR, typename NEXT>
+static bool MatchPatternT(const CHAR* eval, const CHAR* eval_end,
+ const CHAR* pattern, const CHAR* pattern_end,
+ int depth,
+ NEXT next) {
+ const int kMaxDepth = 16;
+ if (depth > kMaxDepth)
+ return false;
+
+ // Eat all the matching chars.
+ EatSameChars(&pattern, pattern_end, &eval, eval_end, next);
+
+ // If the string is empty, then the pattern must be empty too, or contains
+ // only wildcards.
+ if (eval == eval_end) {
+ EatWildcard(&pattern, pattern_end, next);
+ return pattern == pattern_end;
+ }
+
+ // Pattern is empty but not string, this is not a match.
+ if (pattern == pattern_end)
+ return false;
+
+ // If this is a question mark, then we need to compare the rest with
+ // the current string or the string with one character eaten.
+ const CHAR* next_pattern = pattern;
+ next(&next_pattern, pattern_end);
+ if (pattern[0] == '?') {
+ if (MatchPatternT(eval, eval_end, next_pattern, pattern_end,
+ depth + 1, next))
+ return true;
+ const CHAR* next_eval = eval;
+ next(&next_eval, eval_end);
+ if (MatchPatternT(next_eval, eval_end, next_pattern, pattern_end,
+ depth + 1, next))
+ return true;
+ }
+
+ // This is a *, try to match all the possible substrings with the remainder
+ // of the pattern.
+ if (pattern[0] == '*') {
+ // Collapse duplicate wild cards (********** into *) so that the
+ // method does not recurse unnecessarily. http://crbug.com/52839
+ EatWildcard(&next_pattern, pattern_end, next);
+
+ while (eval != eval_end) {
+ if (MatchPatternT(eval, eval_end, next_pattern, pattern_end,
+ depth + 1, next))
+ return true;
+ eval++;
+ }
+
+ // We reached the end of the string, let see if the pattern contains only
+ // wildcards.
+ if (eval == eval_end) {
+ EatWildcard(&pattern, pattern_end, next);
+ if (pattern != pattern_end)
+ return false;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+struct NextCharUTF8 {
+ base_icu::UChar32 operator()(const char** p, const char* end) {
+ base_icu::UChar32 c;
+ int offset = 0;
+ CBU8_NEXT(*p, offset, end - *p, c);
+ *p += offset;
+ return c;
+ }
+};
+
+struct NextCharUTF16 {
+ base_icu::UChar32 operator()(const char16** p, const char16* end) {
+ base_icu::UChar32 c;
+ int offset = 0;
+ CBU16_NEXT(*p, offset, end - *p, c);
+ *p += offset;
+ return c;
+ }
+};
+
+bool MatchPattern(const base::StringPiece& eval,
+ const base::StringPiece& pattern) {
+ return MatchPatternT(eval.data(), eval.data() + eval.size(),
+ pattern.data(), pattern.data() + pattern.size(),
+ 0, NextCharUTF8());
+}
+
+bool MatchPattern(const string16& eval, const string16& pattern) {
+ return MatchPatternT(eval.c_str(), eval.c_str() + eval.size(),
+ pattern.c_str(), pattern.c_str() + pattern.size(),
+ 0, NextCharUTF16());
+}
+
+// The following code is compatible with the OpenBSD lcpy interface. See:
+// http://www.gratisoft.us/todd/papers/strlcpy.html
+// ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c
+
+namespace {
+
+template <typename CHAR>
+size_t lcpyT(CHAR* dst, const CHAR* src, size_t dst_size) {
+ for (size_t i = 0; i < dst_size; ++i) {
+ if ((dst[i] = src[i]) == 0) // We hit and copied the terminating NULL.
+ return i;
+ }
+
+ // We were left off at dst_size. We over copied 1 byte. Null terminate.
+ if (dst_size != 0)
+ dst[dst_size - 1] = 0;
+
+ // Count the rest of the |src|, and return it's length in characters.
+ while (src[dst_size]) ++dst_size;
+ return dst_size;
+}
+
+} // namespace
+
+size_t base::strlcpy(char* dst, const char* src, size_t dst_size) {
+ return lcpyT<char>(dst, src, dst_size);
+}
+size_t base::wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) {
+ return lcpyT<wchar_t>(dst, src, dst_size);
+}
diff --git a/src/base/string_util.h b/src/base/string_util.h
new file mode 100644
index 0000000..f1160fe
--- /dev/null
+++ b/src/base/string_util.h
@@ -0,0 +1,578 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file defines utility functions for working with strings.
+
+#ifndef BASE_STRING_UTIL_H_
+#define BASE_STRING_UTIL_H_
+
+#include <ctype.h>
+#include <stdarg.h> // va_list
+
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/string16.h"
+#include "base/string_piece.h" // For implicit conversions.
+
+// Safe standard library wrappers for all platforms.
+
+namespace base {
+
+// C standard-library functions like "strncasecmp" and "snprintf" that aren't
+// cross-platform are provided as "base::strncasecmp", and their prototypes
+// are listed below. These functions are then implemented as inline calls
+// to the platform-specific equivalents in the platform-specific headers.
+
+// Compares the two strings s1 and s2 without regard to case using
+// the current locale; returns 0 if they are equal, 1 if s1 > s2, and -1 if
+// s2 > s1 according to a lexicographic comparison.
+int strcasecmp(const char* s1, const char* s2);
+
+// Compares up to count characters of s1 and s2 without regard to case using
+// the current locale; returns 0 if they are equal, 1 if s1 > s2, and -1 if
+// s2 > s1 according to a lexicographic comparison.
+int strncasecmp(const char* s1, const char* s2, size_t count);
+
+// Same as strncmp but for char16 strings.
+int strncmp16(const char16* s1, const char16* s2, size_t count);
+
+// Wrapper for vsnprintf that always null-terminates and always returns the
+// number of characters that would be in an untruncated formatted
+// string, even when truncation occurs.
+int vsnprintf(char* buffer, size_t size, const char* format, va_list arguments)
+ PRINTF_FORMAT(3, 0);
+
+// vswprintf always null-terminates, but when truncation occurs, it will either
+// return -1 or the number of characters that would be in an untruncated
+// formatted string. The actual return value depends on the underlying
+// C library's vswprintf implementation.
+int vswprintf(wchar_t* buffer, size_t size,
+ const wchar_t* format, va_list arguments)
+ WPRINTF_FORMAT(3, 0);
+
+// Some of these implementations need to be inlined.
+
+// We separate the declaration from the implementation of this inline
+// function just so the PRINTF_FORMAT works.
+inline int snprintf(char* buffer, size_t size, const char* format, ...)
+ PRINTF_FORMAT(3, 4);
+inline int snprintf(char* buffer, size_t size, const char* format, ...) {
+ va_list arguments;
+ va_start(arguments, format);
+ int result = base::vsnprintf(buffer, size, format, arguments);
+ va_end(arguments);
+ return result;
+}
+
+// We separate the declaration from the implementation of this inline
+// function just so the WPRINTF_FORMAT works.
+inline int swprintf(wchar_t* buffer, size_t size, const wchar_t* format, ...)
+ WPRINTF_FORMAT(3, 4);
+inline int swprintf(wchar_t* buffer, size_t size, const wchar_t* format, ...) {
+ va_list arguments;
+ va_start(arguments, format);
+ int result = base::vswprintf(buffer, size, format, arguments);
+ va_end(arguments);
+ return result;
+}
+
+// BSD-style safe and consistent string copy functions.
+// Copies |src| to |dst|, where |dst_size| is the total allocated size of |dst|.
+// Copies at most |dst_size|-1 characters, and always NULL terminates |dst|, as
+// long as |dst_size| is not 0. Returns the length of |src| in characters.
+// If the return value is >= dst_size, then the output was truncated.
+// NOTE: All sizes are in number of characters, NOT in bytes.
+BASE_EXPORT size_t strlcpy(char* dst, const char* src, size_t dst_size);
+BASE_EXPORT size_t wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size);
+
+// Scan a wprintf format string to determine whether it's portable across a
+// variety of systems. This function only checks that the conversion
+// specifiers used by the format string are supported and have the same meaning
+// on a variety of systems. It doesn't check for other errors that might occur
+// within a format string.
+//
+// Nonportable conversion specifiers for wprintf are:
+// - 's' and 'c' without an 'l' length modifier. %s and %c operate on char
+// data on all systems except Windows, which treat them as wchar_t data.
+// Use %ls and %lc for wchar_t data instead.
+// - 'S' and 'C', which operate on wchar_t data on all systems except Windows,
+// which treat them as char data. Use %ls and %lc for wchar_t data
+// instead.
+// - 'F', which is not identified by Windows wprintf documentation.
+// - 'D', 'O', and 'U', which are deprecated and not available on all systems.
+// Use %ld, %lo, and %lu instead.
+//
+// Note that there is no portable conversion specifier for char data when
+// working with wprintf.
+//
+// This function is intended to be called from base::vswprintf.
+BASE_EXPORT bool IsWprintfFormatPortable(const wchar_t* format);
+
+// ASCII-specific tolower. The standard library's tolower is locale sensitive,
+// so we don't want to use it here.
+template <class Char> inline Char ToLowerASCII(Char c) {
+ return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c;
+}
+
+// ASCII-specific toupper. The standard library's toupper is locale sensitive,
+// so we don't want to use it here.
+template <class Char> inline Char ToUpperASCII(Char c) {
+ return (c >= 'a' && c <= 'z') ? (c + ('A' - 'a')) : c;
+}
+
+// Function objects to aid in comparing/searching strings.
+
+template<typename Char> struct CaseInsensitiveCompare {
+ public:
+ bool operator()(Char x, Char y) const {
+ // TODO(darin): Do we really want to do locale sensitive comparisons here?
+ // See http://crbug.com/24917
+ return tolower(x) == tolower(y);
+ }
+};
+
+template<typename Char> struct CaseInsensitiveCompareASCII {
+ public:
+ bool operator()(Char x, Char y) const {
+ return ToLowerASCII(x) == ToLowerASCII(y);
+ }
+};
+
+} // namespace base
+
+#if defined(OS_WIN)
+#include "base/string_util_win.h"
+#elif defined(OS_POSIX)
+#include "base/string_util_posix.h"
+#elif defined(OS_STARBOARD)
+#include "base/string_util_starboard.h"
+#else
+#error Define string operations appropriately for your platform
+#endif
+
+// These threadsafe functions return references to globally unique empty
+// strings.
+//
+// DO NOT USE THESE AS A GENERAL-PURPOSE SUBSTITUTE FOR DEFAULT CONSTRUCTORS.
+// There is only one case where you should use these: functions which need to
+// return a string by reference (e.g. as a class member accessor), and don't
+// have an empty string to use (e.g. in an error case). These should not be
+// used as initializers, function arguments, or return values for functions
+// which return by value or outparam.
+BASE_EXPORT const std::string& EmptyString();
+BASE_EXPORT const std::wstring& EmptyWString();
+BASE_EXPORT const string16& EmptyString16();
+
+BASE_EXPORT extern const wchar_t kWhitespaceWide[];
+BASE_EXPORT extern const char16 kWhitespaceUTF16[];
+BASE_EXPORT extern const char kWhitespaceASCII[];
+
+BASE_EXPORT extern const char kUtf8ByteOrderMark[];
+
+// Removes characters in |remove_chars| from anywhere in |input|. Returns true
+// if any characters were removed. |remove_chars| must be null-terminated.
+// NOTE: Safe to use the same variable for both |input| and |output|.
+BASE_EXPORT bool RemoveChars(const string16& input,
+ const char16 remove_chars[],
+ string16* output);
+BASE_EXPORT bool RemoveChars(const std::string& input,
+ const char remove_chars[],
+ std::string* output);
+
+// Replaces characters in |replace_chars| from anywhere in |input| with
+// |replace_with|. Each character in |replace_chars| will be replaced with
+// the |replace_with| string. Returns true if any characters were replaced.
+// |replace_chars| must be null-terminated.
+// NOTE: Safe to use the same variable for both |input| and |output|.
+BASE_EXPORT bool ReplaceChars(const string16& input,
+ const char16 replace_chars[],
+ const string16& replace_with,
+ string16* output);
+BASE_EXPORT bool ReplaceChars(const std::string& input,
+ const char replace_chars[],
+ const std::string& replace_with,
+ std::string* output);
+
+// Removes characters in |trim_chars| from the beginning and end of |input|.
+// |trim_chars| must be null-terminated.
+// NOTE: Safe to use the same variable for both |input| and |output|.
+BASE_EXPORT bool TrimString(const std::wstring& input,
+ const wchar_t trim_chars[],
+ std::wstring* output);
+BASE_EXPORT bool TrimString(const string16& input,
+ const char16 trim_chars[],
+ string16* output);
+BASE_EXPORT bool TrimString(const std::string& input,
+ const char trim_chars[],
+ std::string* output);
+
+// Truncates a string to the nearest UTF-8 character that will leave
+// the string less than or equal to the specified byte size.
+BASE_EXPORT void TruncateUTF8ToByteSize(const std::string& input,
+ const size_t byte_size,
+ std::string* output);
+
+// Trims any whitespace from either end of the input string. Returns where
+// whitespace was found.
+// The non-wide version has two functions:
+// * TrimWhitespaceASCII()
+// This function is for ASCII strings and only looks for ASCII whitespace;
+// Please choose the best one according to your usage.
+// NOTE: Safe to use the same variable for both input and output.
+enum TrimPositions {
+ TRIM_NONE = 0,
+ TRIM_LEADING = 1 << 0,
+ TRIM_TRAILING = 1 << 1,
+ TRIM_ALL = TRIM_LEADING | TRIM_TRAILING,
+};
+BASE_EXPORT TrimPositions TrimWhitespace(const string16& input,
+ TrimPositions positions,
+ string16* output);
+BASE_EXPORT TrimPositions TrimWhitespaceASCII(const std::string& input,
+ TrimPositions positions,
+ std::string* output);
+
+// Deprecated. This function is only for backward compatibility and calls
+// TrimWhitespaceASCII().
+BASE_EXPORT TrimPositions TrimWhitespace(const std::string& input,
+ TrimPositions positions,
+ std::string* output);
+
+// Searches for CR or LF characters. Removes all contiguous whitespace
+// strings that contain them. This is useful when trying to deal with text
+// copied from terminals.
+// Returns |text|, with the following three transformations:
+// (1) Leading and trailing whitespace is trimmed.
+// (2) If |trim_sequences_with_line_breaks| is true, any other whitespace
+// sequences containing a CR or LF are trimmed.
+// (3) All other whitespace sequences are converted to single spaces.
+BASE_EXPORT std::wstring CollapseWhitespace(
+ const std::wstring& text,
+ bool trim_sequences_with_line_breaks);
+BASE_EXPORT string16 CollapseWhitespace(
+ const string16& text,
+ bool trim_sequences_with_line_breaks);
+BASE_EXPORT std::string CollapseWhitespaceASCII(
+ const std::string& text,
+ bool trim_sequences_with_line_breaks);
+
+// Returns true if the passed string is empty or contains only white-space
+// characters.
+BASE_EXPORT bool ContainsOnlyWhitespaceASCII(const std::string& str);
+BASE_EXPORT bool ContainsOnlyWhitespace(const string16& str);
+
+// Returns true if |input| is empty or contains only characters found in
+// |characters|.
+BASE_EXPORT bool ContainsOnlyChars(const std::wstring& input,
+ const std::wstring& characters);
+BASE_EXPORT bool ContainsOnlyChars(const string16& input,
+ const string16& characters);
+BASE_EXPORT bool ContainsOnlyChars(const std::string& input,
+ const std::string& characters);
+
+// Converts to 7-bit ASCII by truncating. The result must be known to be ASCII
+// beforehand.
+BASE_EXPORT std::string WideToASCII(const std::wstring& wide);
+BASE_EXPORT std::string UTF16ToASCII(const string16& utf16);
+
+// Converts the given wide string to the corresponding Latin1. This will fail
+// (return false) if any characters are more than 255.
+BASE_EXPORT bool WideToLatin1(const std::wstring& wide, std::string* latin1);
+
+// Returns true if the specified string matches the criteria. How can a wide
+// string be 8-bit or UTF8? It contains only characters that are < 256 (in the
+// first case) or characters that use only 8-bits and whose 8-bit
+// representation looks like a UTF-8 string (the second case).
+//
+// Note that IsStringUTF8 checks not only if the input is structurally
+// valid but also if it doesn't contain any non-character codepoint
+// (e.g. U+FFFE). It's done on purpose because all the existing callers want
+// to have the maximum 'discriminating' power from other encodings. If
+// there's a use case for just checking the structural validity, we have to
+// add a new function for that.
+BASE_EXPORT bool IsStringUTF8(const std::string& str);
+BASE_EXPORT bool IsStringASCII(const std::wstring& str);
+BASE_EXPORT bool IsStringASCII(const base::StringPiece& str);
+BASE_EXPORT bool IsStringASCII(const string16& str);
+
+// Converts the elements of the given string. This version uses a pointer to
+// clearly differentiate it from the non-pointer variant.
+template <class str> inline void StringToLowerASCII(str* s) {
+ for (typename str::iterator i = s->begin(); i != s->end(); ++i)
+ *i = base::ToLowerASCII(*i);
+}
+
+template <class str> inline str StringToLowerASCII(const str& s) {
+ // for std::string and std::wstring
+ str output(s);
+ StringToLowerASCII(&output);
+ return output;
+}
+
+// Converts the elements of the given string. This version uses a pointer to
+// clearly differentiate it from the non-pointer variant.
+template <class str> inline void StringToUpperASCII(str* s) {
+ for (typename str::iterator i = s->begin(); i != s->end(); ++i)
+ *i = base::ToUpperASCII(*i);
+}
+
+template <class str> inline str StringToUpperASCII(const str& s) {
+ // for std::string and std::wstring
+ str output(s);
+ StringToUpperASCII(&output);
+ return output;
+}
+
+// Compare the lower-case form of the given string against the given ASCII
+// string. This is useful for doing checking if an input string matches some
+// token, and it is optimized to avoid intermediate string copies. This API is
+// borrowed from the equivalent APIs in Mozilla.
+BASE_EXPORT bool LowerCaseEqualsASCII(const std::string& a, const char* b);
+BASE_EXPORT bool LowerCaseEqualsASCII(const std::wstring& a, const char* b);
+BASE_EXPORT bool LowerCaseEqualsASCII(const string16& a, const char* b);
+
+// Same thing, but with string iterators instead.
+BASE_EXPORT bool LowerCaseEqualsASCII(std::string::const_iterator a_begin,
+ std::string::const_iterator a_end,
+ const char* b);
+BASE_EXPORT bool LowerCaseEqualsASCII(std::wstring::const_iterator a_begin,
+ std::wstring::const_iterator a_end,
+ const char* b);
+BASE_EXPORT bool LowerCaseEqualsASCII(string16::const_iterator a_begin,
+ string16::const_iterator a_end,
+ const char* b);
+BASE_EXPORT bool LowerCaseEqualsASCII(const char* a_begin,
+ const char* a_end,
+ const char* b);
+BASE_EXPORT bool LowerCaseEqualsASCII(const wchar_t* a_begin,
+ const wchar_t* a_end,
+ const char* b);
+BASE_EXPORT bool LowerCaseEqualsASCII(const char16* a_begin,
+ const char16* a_end,
+ const char* b);
+
+// Performs a case-sensitive string compare. The behavior is undefined if both
+// strings are not ASCII.
+BASE_EXPORT bool EqualsASCII(const string16& a, const base::StringPiece& b);
+
+// Returns true if str starts with search, or false otherwise.
+BASE_EXPORT bool StartsWithASCII(const std::string& str,
+ const std::string& search,
+ bool case_sensitive);
+BASE_EXPORT bool StartsWith(const std::wstring& str,
+ const std::wstring& search,
+ bool case_sensitive);
+BASE_EXPORT bool StartsWith(const string16& str,
+ const string16& search,
+ bool case_sensitive);
+
+// Returns true if str ends with search, or false otherwise.
+BASE_EXPORT bool EndsWith(const std::string& str,
+ const std::string& search,
+ bool case_sensitive);
+BASE_EXPORT bool EndsWith(const std::wstring& str,
+ const std::wstring& search,
+ bool case_sensitive);
+BASE_EXPORT bool EndsWith(const string16& str,
+ const string16& search,
+ bool case_sensitive);
+
+
+// Determines the type of ASCII character, independent of locale (the C
+// library versions will change based on locale).
+template <typename Char>
+inline bool IsAsciiWhitespace(Char c) {
+ return c == ' ' || c == '\r' || c == '\n' || c == '\t';
+}
+template <typename Char>
+inline bool IsAsciiAlpha(Char c) {
+ return ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'));
+}
+template <typename Char>
+inline bool IsAsciiDigit(Char c) {
+ return c >= '0' && c <= '9';
+}
+
+template <typename Char>
+inline bool IsHexDigit(Char c) {
+ return (c >= '0' && c <= '9') ||
+ (c >= 'A' && c <= 'F') ||
+ (c >= 'a' && c <= 'f');
+}
+
+template <typename Char>
+inline Char HexDigitToInt(Char c) {
+ DCHECK(IsHexDigit(c));
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ return 0;
+}
+
+// Returns true if it's a whitespace character.
+inline bool IsWhitespace(wchar_t c) {
+ return wcschr(kWhitespaceWide, c) != NULL;
+}
+
+// Return a byte string in human-readable format with a unit suffix. Not
+// appropriate for use in any UI; use of FormatBytes and friends in ui/base is
+// highly recommended instead. TODO(avi): Figure out how to get callers to use
+// FormatBytes instead; remove this.
+BASE_EXPORT string16 FormatBytesUnlocalized(int64 bytes);
+
+// Starting at |start_offset| (usually 0), replace the first instance of
+// |find_this| with |replace_with|.
+BASE_EXPORT void ReplaceFirstSubstringAfterOffset(
+ string16* str,
+ string16::size_type start_offset,
+ const string16& find_this,
+ const string16& replace_with);
+BASE_EXPORT void ReplaceFirstSubstringAfterOffset(
+ std::string* str,
+ std::string::size_type start_offset,
+ const std::string& find_this,
+ const std::string& replace_with);
+
+// Starting at |start_offset| (usually 0), look through |str| and replace all
+// instances of |find_this| with |replace_with|.
+//
+// This does entire substrings; use std::replace in <algorithm> for single
+// characters, for example:
+// std::replace(str.begin(), str.end(), 'a', 'b');
+BASE_EXPORT void ReplaceSubstringsAfterOffset(
+ string16* str,
+ string16::size_type start_offset,
+ const string16& find_this,
+ const string16& replace_with);
+BASE_EXPORT void ReplaceSubstringsAfterOffset(
+ std::string* str,
+ std::string::size_type start_offset,
+ const std::string& find_this,
+ const std::string& replace_with);
+
+// Reserves enough memory in |str| to accommodate |length_with_null| characters,
+// sets the size of |str| to |length_with_null - 1| characters, and returns a
+// pointer to the underlying contiguous array of characters. This is typically
+// used when calling a function that writes results into a character array, but
+// the caller wants the data to be managed by a string-like object. It is
+// convenient in that is can be used inline in the call, and fast in that it
+// avoids copying the results of the call from a char* into a string.
+//
+// |length_with_null| must be at least 2, since otherwise the underlying string
+// would have size 0, and trying to access &((*str)[0]) in that case can result
+// in a number of problems.
+//
+// Internally, this takes linear time because the resize() call 0-fills the
+// underlying array for potentially all
+// (|length_with_null - 1| * sizeof(string_type::value_type)) bytes. Ideally we
+// could avoid this aspect of the resize() call, as we expect the caller to
+// immediately write over this memory, but there is no other way to set the size
+// of the string, and not doing that will mean people who access |str| rather
+// than str.c_str() will get back a string of whatever size |str| had on entry
+// to this function (probably 0).
+template <class string_type>
+inline typename string_type::value_type* WriteInto(string_type* str,
+ size_t length_with_null) {
+ DCHECK_GT(length_with_null, 1u);
+ str->reserve(length_with_null);
+ str->resize(length_with_null - 1);
+ return &((*str)[0]);
+}
+
+//-----------------------------------------------------------------------------
+
+// Splits a string into its fields delimited by any of the characters in
+// |delimiters|. Each field is added to the |tokens| vector. Returns the
+// number of tokens found.
+BASE_EXPORT size_t Tokenize(const std::wstring& str,
+ const std::wstring& delimiters,
+ std::vector<std::wstring>* tokens);
+BASE_EXPORT size_t Tokenize(const string16& str,
+ const string16& delimiters,
+ std::vector<string16>* tokens);
+BASE_EXPORT size_t Tokenize(const std::string& str,
+ const std::string& delimiters,
+ std::vector<std::string>* tokens);
+BASE_EXPORT size_t Tokenize(const base::StringPiece& str,
+ const base::StringPiece& delimiters,
+ std::vector<base::StringPiece>* tokens);
+
+// Does the opposite of SplitString().
+BASE_EXPORT string16 JoinString(const std::vector<string16>& parts, char16 s);
+BASE_EXPORT std::string JoinString(
+ const std::vector<std::string>& parts, char s);
+
+// Join |parts| using |separator|.
+BASE_EXPORT std::string JoinString(
+ const std::vector<std::string>& parts,
+ const std::string& separator);
+BASE_EXPORT string16 JoinString(
+ const std::vector<string16>& parts,
+ const string16& separator);
+
+// Replace $1-$2-$3..$9 in the format string with |a|-|b|-|c|..|i| respectively.
+// Additionally, any number of consecutive '$' characters is replaced by that
+// number less one. Eg $$->$, $$$->$$, etc. The offsets parameter here can be
+// NULL. This only allows you to use up to nine replacements.
+BASE_EXPORT string16 ReplaceStringPlaceholders(
+ const string16& format_string,
+ const std::vector<string16>& subst,
+ std::vector<size_t>* offsets);
+
+BASE_EXPORT std::string ReplaceStringPlaceholders(
+ const base::StringPiece& format_string,
+ const std::vector<std::string>& subst,
+ std::vector<size_t>* offsets);
+
+// Single-string shortcut for ReplaceStringHolders. |offset| may be NULL.
+BASE_EXPORT string16 ReplaceStringPlaceholders(const string16& format_string,
+ const string16& a,
+ size_t* offset);
+
+// Returns true if the string passed in matches the pattern. The pattern
+// string can contain wildcards like * and ?
+// The backslash character (\) is an escape character for * and ?
+// We limit the patterns to having a max of 16 * or ? characters.
+// ? matches 0 or 1 character, while * matches 0 or more characters.
+BASE_EXPORT bool MatchPattern(const base::StringPiece& string,
+ const base::StringPiece& pattern);
+BASE_EXPORT bool MatchPattern(const string16& string, const string16& pattern);
+
+// Hack to convert any char-like type to its unsigned counterpart.
+// For example, it will convert char, signed char and unsigned char to unsigned
+// char.
+template<typename T>
+struct ToUnsigned {
+ typedef T Unsigned;
+};
+
+template<>
+struct ToUnsigned<char> {
+ typedef unsigned char Unsigned;
+};
+template<>
+struct ToUnsigned<signed char> {
+ typedef unsigned char Unsigned;
+};
+template<>
+struct ToUnsigned<wchar_t> {
+#if defined(WCHAR_T_IS_UTF16)
+ typedef unsigned short Unsigned;
+#elif defined(WCHAR_T_IS_UTF32)
+ typedef uint32 Unsigned;
+#endif
+};
+template<>
+struct ToUnsigned<short> {
+ typedef unsigned short Unsigned;
+};
+
+#endif // BASE_STRING_UTIL_H_
diff --git a/src/base/string_util_posix.h b/src/base/string_util_posix.h
new file mode 100644
index 0000000..9b776e6
--- /dev/null
+++ b/src/base/string_util_posix.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_STRING_UTIL_POSIX_H_
+#define BASE_STRING_UTIL_POSIX_H_
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+
+#include "base/logging.h"
+#include "base/string_util.h"
+
+namespace base {
+
+// Chromium code style is to not use malloc'd strings; this is only for use
+// for interaction with APIs that require it.
+inline char* strdup(const char* str) {
+ return ::strdup(str);
+}
+
+inline int strcasecmp(const char* string1, const char* string2) {
+ return ::strcasecmp(string1, string2);
+}
+
+inline int strncasecmp(const char* string1, const char* string2, size_t count) {
+ return ::strncasecmp(string1, string2, count);
+}
+
+inline int vsnprintf(char* buffer, size_t size,
+ const char* format, va_list arguments) {
+ return ::vsnprintf(buffer, size, format, arguments);
+}
+
+inline int strncmp16(const char16* s1, const char16* s2, size_t count) {
+#if defined(WCHAR_T_IS_UTF16)
+ return ::wcsncmp(s1, s2, count);
+#elif defined(WCHAR_T_IS_UTF32)
+ return c16memcmp(s1, s2, count);
+#endif
+}
+
+inline int vswprintf(wchar_t* buffer, size_t size,
+ const wchar_t* format, va_list arguments) {
+ DCHECK(IsWprintfFormatPortable(format));
+ return ::vswprintf(buffer, size, format, arguments);
+}
+
+} // namespace base
+
+#endif // BASE_STRING_UTIL_POSIX_H_
diff --git a/src/base/string_util_starboard.h b/src/base/string_util_starboard.h
new file mode 100644
index 0000000..533f493
--- /dev/null
+++ b/src/base/string_util_starboard.h
@@ -0,0 +1,59 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef BASE_STRING_UTIL_STARBOARD_H_
+#define BASE_STRING_UTIL_STARBOARD_H_
+
+#include <stdarg.h>
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "starboard/string.h"
+
+namespace base {
+
+inline char* strdup(const char* str) {
+ return SbStringDuplicate(str);
+}
+
+inline int strcasecmp(const char* string1, const char* string2) {
+ return SbStringCompareNoCase(string1, string2);
+}
+
+inline int strncasecmp(const char* string1, const char* string2, size_t count) {
+ return SbStringCompareNoCaseN(string1, string2, count);
+}
+
+inline int vsnprintf(char* buffer, size_t size,
+ const char* format, va_list arguments) {
+ return SbStringFormat(buffer, size, format, arguments);
+}
+
+inline int strncmp16(const char16* s1, const char16* s2, size_t count) {
+#if defined(WCHAR_T_IS_UTF16)
+ return SbStringCompareWide(s1, s2, count);
+#elif defined(WCHAR_T_IS_UTF32)
+ return c16memcmp(s1, s2, count);
+#endif
+}
+
+inline int vswprintf(wchar_t* buffer, size_t size,
+ const wchar_t* format, va_list arguments) {
+ DCHECK(IsWprintfFormatPortable(format));
+ return SbStringFormatWide(buffer, size, format, arguments);
+}
+
+} // namespace base
+
+#endif // BASE_STRING_UTIL_STARBOARD_H_
diff --git a/src/base/string_util_unittest.cc b/src/base/string_util_unittest.cc
new file mode 100644
index 0000000..d36b955
--- /dev/null
+++ b/src/base/string_util_unittest.cc
@@ -0,0 +1,1190 @@
+// Copyright (c) 2012 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 <math.h>
+#include <stdarg.h>
+
+#include <limits>
+#include <sstream>
+
+#include "base/basictypes.h"
+#include "base/string16.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::ElementsAre;
+
+namespace base {
+
+static const struct trim_case {
+ const wchar_t* input;
+ const TrimPositions positions;
+ const wchar_t* output;
+ const TrimPositions return_value;
+} trim_cases[] = {
+ {L" Google Video ", TRIM_LEADING, L"Google Video ", TRIM_LEADING},
+ {L" Google Video ", TRIM_TRAILING, L" Google Video", TRIM_TRAILING},
+ {L" Google Video ", TRIM_ALL, L"Google Video", TRIM_ALL},
+ {L"Google Video", TRIM_ALL, L"Google Video", TRIM_NONE},
+ {L"", TRIM_ALL, L"", TRIM_NONE},
+ {L" ", TRIM_LEADING, L"", TRIM_LEADING},
+ {L" ", TRIM_TRAILING, L"", TRIM_TRAILING},
+ {L" ", TRIM_ALL, L"", TRIM_ALL},
+ {L"\t\rTest String\n", TRIM_ALL, L"Test String", TRIM_ALL},
+ {L"\x2002Test String\x00A0\x3000", TRIM_ALL, L"Test String", TRIM_ALL},
+};
+
+static const struct trim_case_ascii {
+ const char* input;
+ const TrimPositions positions;
+ const char* output;
+ const TrimPositions return_value;
+} trim_cases_ascii[] = {
+ {" Google Video ", TRIM_LEADING, "Google Video ", TRIM_LEADING},
+ {" Google Video ", TRIM_TRAILING, " Google Video", TRIM_TRAILING},
+ {" Google Video ", TRIM_ALL, "Google Video", TRIM_ALL},
+ {"Google Video", TRIM_ALL, "Google Video", TRIM_NONE},
+ {"", TRIM_ALL, "", TRIM_NONE},
+ {" ", TRIM_LEADING, "", TRIM_LEADING},
+ {" ", TRIM_TRAILING, "", TRIM_TRAILING},
+ {" ", TRIM_ALL, "", TRIM_ALL},
+ {"\t\rTest String\n", TRIM_ALL, "Test String", TRIM_ALL},
+};
+
+namespace {
+
+// Helper used to test TruncateUTF8ToByteSize.
+bool Truncated(const std::string& input, const size_t byte_size,
+ std::string* output) {
+ size_t prev = input.length();
+ TruncateUTF8ToByteSize(input, byte_size, output);
+ return prev != output->length();
+}
+
+} // namespace
+
+TEST(StringUtilTest, TruncateUTF8ToByteSize) {
+ std::string output;
+
+ // Empty strings and invalid byte_size arguments
+ EXPECT_FALSE(Truncated("", 0, &output));
+ EXPECT_EQ(output, "");
+ EXPECT_TRUE(Truncated("\xe1\x80\xbf", 0, &output));
+ EXPECT_EQ(output, "");
+ EXPECT_FALSE(Truncated("\xe1\x80\xbf", -1, &output));
+ EXPECT_FALSE(Truncated("\xe1\x80\xbf", 4, &output));
+
+ // Testing the truncation of valid UTF8 correctly
+ EXPECT_TRUE(Truncated("abc", 2, &output));
+ EXPECT_EQ(output, "ab");
+ EXPECT_TRUE(Truncated("\xc2\x81\xc2\x81", 2, &output));
+ EXPECT_EQ(output.compare("\xc2\x81"), 0);
+ EXPECT_TRUE(Truncated("\xc2\x81\xc2\x81", 3, &output));
+ EXPECT_EQ(output.compare("\xc2\x81"), 0);
+ EXPECT_FALSE(Truncated("\xc2\x81\xc2\x81", 4, &output));
+ EXPECT_EQ(output.compare("\xc2\x81\xc2\x81"), 0);
+
+ {
+ const char array[] = "\x00\x00\xc2\x81\xc2\x81";
+ const std::string array_string(array, arraysize(array));
+ EXPECT_TRUE(Truncated(array_string, 4, &output));
+ EXPECT_EQ(output.compare(std::string("\x00\x00\xc2\x81", 4)), 0);
+ }
+
+ {
+ const char array[] = "\x00\xc2\x81\xc2\x81";
+ const std::string array_string(array, arraysize(array));
+ EXPECT_TRUE(Truncated(array_string, 4, &output));
+ EXPECT_EQ(output.compare(std::string("\x00\xc2\x81", 3)), 0);
+ }
+
+ // Testing invalid UTF8
+ EXPECT_TRUE(Truncated("\xed\xa0\x80\xed\xbf\xbf", 6, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_TRUE(Truncated("\xed\xa0\x8f", 3, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_TRUE(Truncated("\xed\xbf\xbf", 3, &output));
+ EXPECT_EQ(output.compare(""), 0);
+
+ // Testing invalid UTF8 mixed with valid UTF8
+ EXPECT_FALSE(Truncated("\xe1\x80\xbf", 3, &output));
+ EXPECT_EQ(output.compare("\xe1\x80\xbf"), 0);
+ EXPECT_FALSE(Truncated("\xf1\x80\xa0\xbf", 4, &output));
+ EXPECT_EQ(output.compare("\xf1\x80\xa0\xbf"), 0);
+ EXPECT_FALSE(Truncated("a\xc2\x81\xe1\x80\xbf\xf1\x80\xa0\xbf",
+ 10, &output));
+ EXPECT_EQ(output.compare("a\xc2\x81\xe1\x80\xbf\xf1\x80\xa0\xbf"), 0);
+ EXPECT_TRUE(Truncated("a\xc2\x81\xe1\x80\xbf\xf1""a""\x80\xa0",
+ 10, &output));
+ EXPECT_EQ(output.compare("a\xc2\x81\xe1\x80\xbf\xf1""a"), 0);
+ EXPECT_FALSE(Truncated("\xef\xbb\xbf" "abc", 6, &output));
+ EXPECT_EQ(output.compare("\xef\xbb\xbf" "abc"), 0);
+
+ // Overlong sequences
+ EXPECT_TRUE(Truncated("\xc0\x80", 2, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_TRUE(Truncated("\xc1\x80\xc1\x81", 4, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_TRUE(Truncated("\xe0\x80\x80", 3, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_TRUE(Truncated("\xe0\x82\x80", 3, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_TRUE(Truncated("\xe0\x9f\xbf", 3, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_TRUE(Truncated("\xf0\x80\x80\x8D", 4, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_TRUE(Truncated("\xf0\x80\x82\x91", 4, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_TRUE(Truncated("\xf0\x80\xa0\x80", 4, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_TRUE(Truncated("\xf0\x8f\xbb\xbf", 4, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_TRUE(Truncated("\xf8\x80\x80\x80\xbf", 5, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_TRUE(Truncated("\xfc\x80\x80\x80\xa0\xa5", 6, &output));
+ EXPECT_EQ(output.compare(""), 0);
+
+ // Beyond U+10FFFF (the upper limit of Unicode codespace)
+ EXPECT_TRUE(Truncated("\xf4\x90\x80\x80", 4, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_TRUE(Truncated("\xf8\xa0\xbf\x80\xbf", 5, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_TRUE(Truncated("\xfc\x9c\xbf\x80\xbf\x80", 6, &output));
+ EXPECT_EQ(output.compare(""), 0);
+
+ // BOMs in UTF-16(BE|LE) and UTF-32(BE|LE)
+ EXPECT_TRUE(Truncated("\xfe\xff", 2, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_TRUE(Truncated("\xff\xfe", 2, &output));
+ EXPECT_EQ(output.compare(""), 0);
+
+ {
+ const char array[] = "\x00\x00\xfe\xff";
+ const std::string array_string(array, arraysize(array));
+ EXPECT_TRUE(Truncated(array_string, 4, &output));
+ EXPECT_EQ(output.compare(std::string("\x00\x00", 2)), 0);
+ }
+
+ // Variants on the previous test
+ {
+ const char array[] = "\xff\xfe\x00\x00";
+ const std::string array_string(array, 4);
+ EXPECT_FALSE(Truncated(array_string, 4, &output));
+ EXPECT_EQ(output.compare(std::string("\xff\xfe\x00\x00", 4)), 0);
+ }
+ {
+ const char array[] = "\xff\x00\x00\xfe";
+ const std::string array_string(array, arraysize(array));
+ EXPECT_TRUE(Truncated(array_string, 4, &output));
+ EXPECT_EQ(output.compare(std::string("\xff\x00\x00", 3)), 0);
+ }
+
+ // Non-characters : U+xxFFF[EF] where xx is 0x00 through 0x10 and <FDD0,FDEF>
+ EXPECT_TRUE(Truncated("\xef\xbf\xbe", 3, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_TRUE(Truncated("\xf0\x8f\xbf\xbe", 4, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_TRUE(Truncated("\xf3\xbf\xbf\xbf", 4, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_TRUE(Truncated("\xef\xb7\x90", 3, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_TRUE(Truncated("\xef\xb7\xaf", 3, &output));
+ EXPECT_EQ(output.compare(""), 0);
+
+ // Strings in legacy encodings that are valid in UTF-8, but
+ // are invalid as UTF-8 in real data.
+ EXPECT_TRUE(Truncated("caf\xe9", 4, &output));
+ EXPECT_EQ(output.compare("caf"), 0);
+ EXPECT_TRUE(Truncated("\xb0\xa1\xb0\xa2", 4, &output));
+ EXPECT_EQ(output.compare(""), 0);
+ EXPECT_FALSE(Truncated("\xa7\x41\xa6\x6e", 4, &output));
+ EXPECT_EQ(output.compare("\xa7\x41\xa6\x6e"), 0);
+ EXPECT_TRUE(Truncated("\xa7\x41\xa6\x6e\xd9\xee\xe4\xee", 7,
+ &output));
+ EXPECT_EQ(output.compare("\xa7\x41\xa6\x6e"), 0);
+
+ // Testing using the same string as input and output.
+ EXPECT_FALSE(Truncated(output, 4, &output));
+ EXPECT_EQ(output.compare("\xa7\x41\xa6\x6e"), 0);
+ EXPECT_TRUE(Truncated(output, 3, &output));
+ EXPECT_EQ(output.compare("\xa7\x41"), 0);
+
+ // "abc" with U+201[CD] in windows-125[0-8]
+ EXPECT_TRUE(Truncated("\x93" "abc\x94", 5, &output));
+ EXPECT_EQ(output.compare("\x93" "abc"), 0);
+
+ // U+0639 U+064E U+0644 U+064E in ISO-8859-6
+ EXPECT_TRUE(Truncated("\xd9\xee\xe4\xee", 4, &output));
+ EXPECT_EQ(output.compare(""), 0);
+
+ // U+03B3 U+03B5 U+03B9 U+03AC in ISO-8859-7
+ EXPECT_TRUE(Truncated("\xe3\xe5\xe9\xdC", 4, &output));
+ EXPECT_EQ(output.compare(""), 0);
+}
+
+TEST(StringUtilTest, TrimWhitespace) {
+ string16 output; // Allow contents to carry over to next testcase
+ for (size_t i = 0; i < arraysize(trim_cases); ++i) {
+ const trim_case& value = trim_cases[i];
+ EXPECT_EQ(value.return_value,
+ TrimWhitespace(WideToUTF16(value.input), value.positions,
+ &output));
+ EXPECT_EQ(WideToUTF16(value.output), output);
+ }
+
+ // Test that TrimWhitespace() can take the same string for input and output
+ output = ASCIIToUTF16(" This is a test \r\n");
+ EXPECT_EQ(TRIM_ALL, TrimWhitespace(output, TRIM_ALL, &output));
+ EXPECT_EQ(ASCIIToUTF16("This is a test"), output);
+
+ // Once more, but with a string of whitespace
+ output = ASCIIToUTF16(" \r\n");
+ EXPECT_EQ(TRIM_ALL, TrimWhitespace(output, TRIM_ALL, &output));
+ EXPECT_EQ(string16(), output);
+
+ std::string output_ascii;
+ for (size_t i = 0; i < arraysize(trim_cases_ascii); ++i) {
+ const trim_case_ascii& value = trim_cases_ascii[i];
+ EXPECT_EQ(value.return_value,
+ TrimWhitespace(value.input, value.positions, &output_ascii));
+ EXPECT_EQ(value.output, output_ascii);
+ }
+}
+
+static const struct collapse_case {
+ const wchar_t* input;
+ const bool trim;
+ const wchar_t* output;
+} collapse_cases[] = {
+ {L" Google Video ", false, L"Google Video"},
+ {L"Google Video", false, L"Google Video"},
+ {L"", false, L""},
+ {L" ", false, L""},
+ {L"\t\rTest String\n", false, L"Test String"},
+ {L"\x2002Test String\x00A0\x3000", false, L"Test String"},
+ {L" Test \n \t String ", false, L"Test String"},
+ {L"\x2002Test\x1680 \x2028 \tString\x00A0\x3000", false, L"Test String"},
+ {L" Test String", false, L"Test String"},
+ {L"Test String ", false, L"Test String"},
+ {L"Test String", false, L"Test String"},
+ {L"", true, L""},
+ {L"\n", true, L""},
+ {L" \r ", true, L""},
+ {L"\nFoo", true, L"Foo"},
+ {L"\r Foo ", true, L"Foo"},
+ {L" Foo bar ", true, L"Foo bar"},
+ {L" \tFoo bar \n", true, L"Foo bar"},
+ {L" a \r b\n c \r\n d \t\re \t f \n ", true, L"abcde f"},
+};
+
+TEST(StringUtilTest, CollapseWhitespace) {
+ for (size_t i = 0; i < arraysize(collapse_cases); ++i) {
+ const collapse_case& value = collapse_cases[i];
+ EXPECT_EQ(value.output, CollapseWhitespace(value.input, value.trim));
+ }
+}
+
+static const struct collapse_case_ascii {
+ const char* input;
+ const bool trim;
+ const char* output;
+} collapse_cases_ascii[] = {
+ {" Google Video ", false, "Google Video"},
+ {"Google Video", false, "Google Video"},
+ {"", false, ""},
+ {" ", false, ""},
+ {"\t\rTest String\n", false, "Test String"},
+ {" Test \n \t String ", false, "Test String"},
+ {" Test String", false, "Test String"},
+ {"Test String ", false, "Test String"},
+ {"Test String", false, "Test String"},
+ {"", true, ""},
+ {"\n", true, ""},
+ {" \r ", true, ""},
+ {"\nFoo", true, "Foo"},
+ {"\r Foo ", true, "Foo"},
+ {" Foo bar ", true, "Foo bar"},
+ {" \tFoo bar \n", true, "Foo bar"},
+ {" a \r b\n c \r\n d \t\re \t f \n ", true, "abcde f"},
+};
+
+TEST(StringUtilTest, CollapseWhitespaceASCII) {
+ for (size_t i = 0; i < arraysize(collapse_cases_ascii); ++i) {
+ const collapse_case_ascii& value = collapse_cases_ascii[i];
+ EXPECT_EQ(value.output, CollapseWhitespaceASCII(value.input, value.trim));
+ }
+}
+
+TEST(StringUtilTest, ContainsOnlyWhitespaceASCII) {
+ EXPECT_TRUE(ContainsOnlyWhitespaceASCII(""));
+ EXPECT_TRUE(ContainsOnlyWhitespaceASCII(" "));
+ EXPECT_TRUE(ContainsOnlyWhitespaceASCII("\t"));
+ EXPECT_TRUE(ContainsOnlyWhitespaceASCII("\t \r \n "));
+ EXPECT_FALSE(ContainsOnlyWhitespaceASCII("a"));
+ EXPECT_FALSE(ContainsOnlyWhitespaceASCII("\thello\r \n "));
+}
+
+TEST(StringUtilTest, ContainsOnlyWhitespace) {
+ EXPECT_TRUE(ContainsOnlyWhitespace(string16()));
+ EXPECT_TRUE(ContainsOnlyWhitespace(ASCIIToUTF16(" ")));
+ EXPECT_TRUE(ContainsOnlyWhitespace(ASCIIToUTF16("\t")));
+ EXPECT_TRUE(ContainsOnlyWhitespace(ASCIIToUTF16("\t \r \n ")));
+ EXPECT_FALSE(ContainsOnlyWhitespace(ASCIIToUTF16("a")));
+ EXPECT_FALSE(ContainsOnlyWhitespace(ASCIIToUTF16("\thello\r \n ")));
+}
+
+TEST(StringUtilTest, IsStringUTF8) {
+ EXPECT_TRUE(IsStringUTF8("abc"));
+ EXPECT_TRUE(IsStringUTF8("\xc2\x81"));
+ EXPECT_TRUE(IsStringUTF8("\xe1\x80\xbf"));
+ EXPECT_TRUE(IsStringUTF8("\xf1\x80\xa0\xbf"));
+ EXPECT_TRUE(IsStringUTF8("a\xc2\x81\xe1\x80\xbf\xf1\x80\xa0\xbf"));
+ EXPECT_TRUE(IsStringUTF8("\xef\xbb\xbf" "abc")); // UTF-8 BOM
+
+ // surrogate code points
+ EXPECT_FALSE(IsStringUTF8("\xed\xa0\x80\xed\xbf\xbf"));
+ EXPECT_FALSE(IsStringUTF8("\xed\xa0\x8f"));
+ EXPECT_FALSE(IsStringUTF8("\xed\xbf\xbf"));
+
+ // overlong sequences
+ EXPECT_FALSE(IsStringUTF8("\xc0\x80")); // U+0000
+ EXPECT_FALSE(IsStringUTF8("\xc1\x80\xc1\x81")); // "AB"
+ EXPECT_FALSE(IsStringUTF8("\xe0\x80\x80")); // U+0000
+ EXPECT_FALSE(IsStringUTF8("\xe0\x82\x80")); // U+0080
+ EXPECT_FALSE(IsStringUTF8("\xe0\x9f\xbf")); // U+07ff
+ EXPECT_FALSE(IsStringUTF8("\xf0\x80\x80\x8D")); // U+000D
+ EXPECT_FALSE(IsStringUTF8("\xf0\x80\x82\x91")); // U+0091
+ EXPECT_FALSE(IsStringUTF8("\xf0\x80\xa0\x80")); // U+0800
+ EXPECT_FALSE(IsStringUTF8("\xf0\x8f\xbb\xbf")); // U+FEFF (BOM)
+ EXPECT_FALSE(IsStringUTF8("\xf8\x80\x80\x80\xbf")); // U+003F
+ EXPECT_FALSE(IsStringUTF8("\xfc\x80\x80\x80\xa0\xa5")); // U+00A5
+
+ // Beyond U+10FFFF (the upper limit of Unicode codespace)
+ EXPECT_FALSE(IsStringUTF8("\xf4\x90\x80\x80")); // U+110000
+ EXPECT_FALSE(IsStringUTF8("\xf8\xa0\xbf\x80\xbf")); // 5 bytes
+ EXPECT_FALSE(IsStringUTF8("\xfc\x9c\xbf\x80\xbf\x80")); // 6 bytes
+
+ // BOMs in UTF-16(BE|LE) and UTF-32(BE|LE)
+ EXPECT_FALSE(IsStringUTF8("\xfe\xff"));
+ EXPECT_FALSE(IsStringUTF8("\xff\xfe"));
+ EXPECT_FALSE(IsStringUTF8(std::string("\x00\x00\xfe\xff", 4)));
+ EXPECT_FALSE(IsStringUTF8("\xff\xfe\x00\x00"));
+
+ // Non-characters : U+xxFFF[EF] where xx is 0x00 through 0x10 and <FDD0,FDEF>
+ EXPECT_FALSE(IsStringUTF8("\xef\xbf\xbe")); // U+FFFE)
+ EXPECT_FALSE(IsStringUTF8("\xf0\x8f\xbf\xbe")); // U+1FFFE
+ EXPECT_FALSE(IsStringUTF8("\xf3\xbf\xbf\xbf")); // U+10FFFF
+ EXPECT_FALSE(IsStringUTF8("\xef\xb7\x90")); // U+FDD0
+ EXPECT_FALSE(IsStringUTF8("\xef\xb7\xaf")); // U+FDEF
+ // Strings in legacy encodings. We can certainly make up strings
+ // in a legacy encoding that are valid in UTF-8, but in real data,
+ // most of them are invalid as UTF-8.
+ EXPECT_FALSE(IsStringUTF8("caf\xe9")); // cafe with U+00E9 in ISO-8859-1
+ EXPECT_FALSE(IsStringUTF8("\xb0\xa1\xb0\xa2")); // U+AC00, U+AC001 in EUC-KR
+ EXPECT_FALSE(IsStringUTF8("\xa7\x41\xa6\x6e")); // U+4F60 U+597D in Big5
+ // "abc" with U+201[CD] in windows-125[0-8]
+ EXPECT_FALSE(IsStringUTF8("\x93" "abc\x94"));
+ // U+0639 U+064E U+0644 U+064E in ISO-8859-6
+ EXPECT_FALSE(IsStringUTF8("\xd9\xee\xe4\xee"));
+ // U+03B3 U+03B5 U+03B9 U+03AC in ISO-8859-7
+ EXPECT_FALSE(IsStringUTF8("\xe3\xe5\xe9\xdC"));
+
+ // Check that we support Embedded Nulls. The first uses the canonical UTF-8
+ // representation, and the second uses a 2-byte sequence. The second version
+ // is invalid UTF-8 since UTF-8 states that the shortest encoding for a
+ // given codepoint must be used.
+ static const char kEmbeddedNull[] = "embedded\0null";
+ EXPECT_TRUE(IsStringUTF8(
+ std::string(kEmbeddedNull, sizeof(kEmbeddedNull))));
+ EXPECT_FALSE(IsStringUTF8("embedded\xc0\x80U+0000"));
+}
+
+TEST(StringUtilTest, ConvertASCII) {
+ static const char* char_cases[] = {
+ "Google Video",
+ "Hello, world\n",
+ "0123ABCDwxyz \a\b\t\r\n!+,.~"
+ };
+
+ static const wchar_t* const wchar_cases[] = {
+ L"Google Video",
+ L"Hello, world\n",
+ L"0123ABCDwxyz \a\b\t\r\n!+,.~"
+ };
+
+ for (size_t i = 0; i < arraysize(char_cases); ++i) {
+ EXPECT_TRUE(IsStringASCII(char_cases[i]));
+ std::wstring wide = ASCIIToWide(char_cases[i]);
+ EXPECT_EQ(wchar_cases[i], wide);
+
+ EXPECT_TRUE(IsStringASCII(wchar_cases[i]));
+ std::string ascii = WideToASCII(wchar_cases[i]);
+ EXPECT_EQ(char_cases[i], ascii);
+ }
+
+ EXPECT_FALSE(IsStringASCII("Google \x80Video"));
+ EXPECT_FALSE(IsStringASCII(L"Google \x80Video"));
+
+ // Convert empty strings.
+ std::wstring wempty;
+ std::string empty;
+ EXPECT_EQ(empty, WideToASCII(wempty));
+ EXPECT_EQ(wempty, ASCIIToWide(empty));
+
+ // Convert strings with an embedded NUL character.
+ const char chars_with_nul[] = "test\0string";
+ const int length_with_nul = arraysize(chars_with_nul) - 1;
+ std::string string_with_nul(chars_with_nul, length_with_nul);
+ std::wstring wide_with_nul = ASCIIToWide(string_with_nul);
+ EXPECT_EQ(static_cast<std::wstring::size_type>(length_with_nul),
+ wide_with_nul.length());
+ std::string narrow_with_nul = WideToASCII(wide_with_nul);
+ EXPECT_EQ(static_cast<std::string::size_type>(length_with_nul),
+ narrow_with_nul.length());
+ EXPECT_EQ(0, string_with_nul.compare(narrow_with_nul));
+}
+
+TEST(StringUtilTest, ToUpperASCII) {
+ EXPECT_EQ('C', ToUpperASCII('C'));
+ EXPECT_EQ('C', ToUpperASCII('c'));
+ EXPECT_EQ('2', ToUpperASCII('2'));
+
+ EXPECT_EQ(L'C', ToUpperASCII(L'C'));
+ EXPECT_EQ(L'C', ToUpperASCII(L'c'));
+ EXPECT_EQ(L'2', ToUpperASCII(L'2'));
+
+ std::string in_place_a("Cc2");
+ StringToUpperASCII(&in_place_a);
+ EXPECT_EQ("CC2", in_place_a);
+
+ std::wstring in_place_w(L"Cc2");
+ StringToUpperASCII(&in_place_w);
+ EXPECT_EQ(L"CC2", in_place_w);
+
+ std::string original_a("Cc2");
+ std::string upper_a = StringToUpperASCII(original_a);
+ EXPECT_EQ("CC2", upper_a);
+
+ std::wstring original_w(L"Cc2");
+ std::wstring upper_w = StringToUpperASCII(original_w);
+ EXPECT_EQ(L"CC2", upper_w);
+}
+
+TEST(StringUtilTest, LowerCaseEqualsASCII) {
+ static const struct {
+ const wchar_t* src_w;
+ const char* src_a;
+ const char* dst;
+ } lowercase_cases[] = {
+ { L"FoO", "FoO", "foo" },
+ { L"foo", "foo", "foo" },
+ { L"FOO", "FOO", "foo" },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(lowercase_cases); ++i) {
+ EXPECT_TRUE(LowerCaseEqualsASCII(lowercase_cases[i].src_w,
+ lowercase_cases[i].dst));
+ EXPECT_TRUE(LowerCaseEqualsASCII(lowercase_cases[i].src_a,
+ lowercase_cases[i].dst));
+ }
+}
+
+TEST(StringUtilTest, FormatBytesUnlocalized) {
+ static const struct {
+ int64 bytes;
+ const char* expected;
+ } cases[] = {
+ // Expected behavior: we show one post-decimal digit when we have
+ // under two pre-decimal digits, except in cases where it makes no
+ // sense (zero or bytes).
+ // Since we switch units once we cross the 1000 mark, this keeps
+ // the display of file sizes or bytes consistently around three
+ // digits.
+ {0, "0 B"},
+ {512, "512 B"},
+ {1024*1024, "1.0 MB"},
+ {1024*1024*1024, "1.0 GB"},
+ {10LL*1024*1024*1024, "10.0 GB"},
+ {99LL*1024*1024*1024, "99.0 GB"},
+ {105LL*1024*1024*1024, "105 GB"},
+ {105LL*1024*1024*1024 + 500LL*1024*1024, "105 GB"},
+ {~(1LL<<63), "8192 PB"},
+
+ {99*1024 + 103, "99.1 kB"},
+ {1024*1024 + 103, "1.0 MB"},
+ {1024*1024 + 205 * 1024, "1.2 MB"},
+ {1024*1024*1024 + (927 * 1024*1024), "1.9 GB"},
+ {10LL*1024*1024*1024, "10.0 GB"},
+ {100LL*1024*1024*1024, "100 GB"},
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+ EXPECT_EQ(ASCIIToUTF16(cases[i].expected),
+ FormatBytesUnlocalized(cases[i].bytes));
+ }
+}
+TEST(StringUtilTest, ReplaceSubstringsAfterOffset) {
+ static const struct {
+ const char* str;
+ string16::size_type start_offset;
+ const char* find_this;
+ const char* replace_with;
+ const char* expected;
+ } cases[] = {
+ {"aaa", 0, "a", "b", "bbb"},
+ {"abb", 0, "ab", "a", "ab"},
+ {"Removing some substrings inging", 0, "ing", "", "Remov some substrs "},
+ {"Not found", 0, "x", "0", "Not found"},
+ {"Not found again", 5, "x", "0", "Not found again"},
+ {" Making it much longer ", 0, " ", "Four score and seven years ago",
+ "Four score and seven years agoMakingFour score and seven years agoit"
+ "Four score and seven years agomuchFour score and seven years agolonger"
+ "Four score and seven years ago"},
+ {"Invalid offset", 9999, "t", "foobar", "Invalid offset"},
+ {"Replace me only me once", 9, "me ", "", "Replace me only once"},
+ {"abababab", 2, "ab", "c", "abccc"},
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
+ string16 str = ASCIIToUTF16(cases[i].str);
+ ReplaceSubstringsAfterOffset(&str, cases[i].start_offset,
+ ASCIIToUTF16(cases[i].find_this),
+ ASCIIToUTF16(cases[i].replace_with));
+ EXPECT_EQ(ASCIIToUTF16(cases[i].expected), str);
+ }
+}
+
+TEST(StringUtilTest, ReplaceFirstSubstringAfterOffset) {
+ static const struct {
+ const char* str;
+ string16::size_type start_offset;
+ const char* find_this;
+ const char* replace_with;
+ const char* expected;
+ } cases[] = {
+ {"aaa", 0, "a", "b", "baa"},
+ {"abb", 0, "ab", "a", "ab"},
+ {"Removing some substrings inging", 0, "ing", "",
+ "Remov some substrings inging"},
+ {"Not found", 0, "x", "0", "Not found"},
+ {"Not found again", 5, "x", "0", "Not found again"},
+ {" Making it much longer ", 0, " ", "Four score and seven years ago",
+ "Four score and seven years agoMaking it much longer "},
+ {"Invalid offset", 9999, "t", "foobar", "Invalid offset"},
+ {"Replace me only me once", 4, "me ", "", "Replace only me once"},
+ {"abababab", 2, "ab", "c", "abcabab"},
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
+ string16 str = ASCIIToUTF16(cases[i].str);
+ ReplaceFirstSubstringAfterOffset(&str, cases[i].start_offset,
+ ASCIIToUTF16(cases[i].find_this),
+ ASCIIToUTF16(cases[i].replace_with));
+ EXPECT_EQ(ASCIIToUTF16(cases[i].expected), str);
+ }
+}
+
+TEST(StringUtilTest, HexDigitToInt) {
+ EXPECT_EQ(0, HexDigitToInt('0'));
+ EXPECT_EQ(1, HexDigitToInt('1'));
+ EXPECT_EQ(2, HexDigitToInt('2'));
+ EXPECT_EQ(3, HexDigitToInt('3'));
+ EXPECT_EQ(4, HexDigitToInt('4'));
+ EXPECT_EQ(5, HexDigitToInt('5'));
+ EXPECT_EQ(6, HexDigitToInt('6'));
+ EXPECT_EQ(7, HexDigitToInt('7'));
+ EXPECT_EQ(8, HexDigitToInt('8'));
+ EXPECT_EQ(9, HexDigitToInt('9'));
+ EXPECT_EQ(10, HexDigitToInt('A'));
+ EXPECT_EQ(11, HexDigitToInt('B'));
+ EXPECT_EQ(12, HexDigitToInt('C'));
+ EXPECT_EQ(13, HexDigitToInt('D'));
+ EXPECT_EQ(14, HexDigitToInt('E'));
+ EXPECT_EQ(15, HexDigitToInt('F'));
+
+ // Verify the lower case as well.
+ EXPECT_EQ(10, HexDigitToInt('a'));
+ EXPECT_EQ(11, HexDigitToInt('b'));
+ EXPECT_EQ(12, HexDigitToInt('c'));
+ EXPECT_EQ(13, HexDigitToInt('d'));
+ EXPECT_EQ(14, HexDigitToInt('e'));
+ EXPECT_EQ(15, HexDigitToInt('f'));
+}
+
+// This checks where we can use the assignment operator for a va_list. We need
+// a way to do this since Visual C doesn't support va_copy, but assignment on
+// va_list is not guaranteed to be a copy. See StringAppendVT which uses this
+// capability.
+static void VariableArgsFunc(const char* format, ...) {
+ va_list org;
+ va_start(org, format);
+
+ va_list dup;
+ GG_VA_COPY(dup, org);
+ int i1 = va_arg(org, int);
+ int j1 = va_arg(org, int);
+ char* s1 = va_arg(org, char*);
+ double d1 = va_arg(org, double);
+ va_end(org);
+
+ int i2 = va_arg(dup, int);
+ int j2 = va_arg(dup, int);
+ char* s2 = va_arg(dup, char*);
+ double d2 = va_arg(dup, double);
+
+ EXPECT_EQ(i1, i2);
+ EXPECT_EQ(j1, j2);
+ EXPECT_STREQ(s1, s2);
+ EXPECT_EQ(d1, d2);
+
+ va_end(dup);
+}
+
+TEST(StringUtilTest, VAList) {
+ VariableArgsFunc("%d %d %s %lf", 45, 92, "This is interesting", 9.21);
+}
+
+// Test for Tokenize
+template <typename STR>
+void TokenizeTest() {
+ std::vector<STR> r;
+ size_t size;
+
+ size = Tokenize(STR("This is a string"), STR(" "), &r);
+ EXPECT_EQ(4U, size);
+ ASSERT_EQ(4U, r.size());
+ EXPECT_EQ(r[0], STR("This"));
+ EXPECT_EQ(r[1], STR("is"));
+ EXPECT_EQ(r[2], STR("a"));
+ EXPECT_EQ(r[3], STR("string"));
+ r.clear();
+
+ size = Tokenize(STR("one,two,three"), STR(","), &r);
+ EXPECT_EQ(3U, size);
+ ASSERT_EQ(3U, r.size());
+ EXPECT_EQ(r[0], STR("one"));
+ EXPECT_EQ(r[1], STR("two"));
+ EXPECT_EQ(r[2], STR("three"));
+ r.clear();
+
+ size = Tokenize(STR("one,two:three;four"), STR(",:"), &r);
+ EXPECT_EQ(3U, size);
+ ASSERT_EQ(3U, r.size());
+ EXPECT_EQ(r[0], STR("one"));
+ EXPECT_EQ(r[1], STR("two"));
+ EXPECT_EQ(r[2], STR("three;four"));
+ r.clear();
+
+ size = Tokenize(STR("one,two:three;four"), STR(";,:"), &r);
+ EXPECT_EQ(4U, size);
+ ASSERT_EQ(4U, r.size());
+ EXPECT_EQ(r[0], STR("one"));
+ EXPECT_EQ(r[1], STR("two"));
+ EXPECT_EQ(r[2], STR("three"));
+ EXPECT_EQ(r[3], STR("four"));
+ r.clear();
+
+ size = Tokenize(STR("one, two, three"), STR(","), &r);
+ EXPECT_EQ(3U, size);
+ ASSERT_EQ(3U, r.size());
+ EXPECT_EQ(r[0], STR("one"));
+ EXPECT_EQ(r[1], STR(" two"));
+ EXPECT_EQ(r[2], STR(" three"));
+ r.clear();
+
+ size = Tokenize(STR("one, two, three, "), STR(","), &r);
+ EXPECT_EQ(4U, size);
+ ASSERT_EQ(4U, r.size());
+ EXPECT_EQ(r[0], STR("one"));
+ EXPECT_EQ(r[1], STR(" two"));
+ EXPECT_EQ(r[2], STR(" three"));
+ EXPECT_EQ(r[3], STR(" "));
+ r.clear();
+
+ size = Tokenize(STR("one, two, three,"), STR(","), &r);
+ EXPECT_EQ(3U, size);
+ ASSERT_EQ(3U, r.size());
+ EXPECT_EQ(r[0], STR("one"));
+ EXPECT_EQ(r[1], STR(" two"));
+ EXPECT_EQ(r[2], STR(" three"));
+ r.clear();
+
+ size = Tokenize(STR(""), STR(","), &r);
+ EXPECT_EQ(0U, size);
+ ASSERT_EQ(0U, r.size());
+ r.clear();
+
+ size = Tokenize(STR(","), STR(","), &r);
+ EXPECT_EQ(0U, size);
+ ASSERT_EQ(0U, r.size());
+ r.clear();
+
+ size = Tokenize(STR(",;:."), STR(".:;,"), &r);
+ EXPECT_EQ(0U, size);
+ ASSERT_EQ(0U, r.size());
+ r.clear();
+
+ size = Tokenize(STR("\t\ta\t"), STR("\t"), &r);
+ EXPECT_EQ(1U, size);
+ ASSERT_EQ(1U, r.size());
+ EXPECT_EQ(r[0], STR("a"));
+ r.clear();
+
+ size = Tokenize(STR("\ta\t\nb\tcc"), STR("\n"), &r);
+ EXPECT_EQ(2U, size);
+ ASSERT_EQ(2U, r.size());
+ EXPECT_EQ(r[0], STR("\ta\t"));
+ EXPECT_EQ(r[1], STR("b\tcc"));
+ r.clear();
+}
+
+TEST(StringUtilTest, TokenizeStdString) {
+ TokenizeTest<std::string>();
+}
+
+TEST(StringUtilTest, TokenizeStringPiece) {
+ TokenizeTest<base::StringPiece>();
+}
+
+// Test for JoinString
+TEST(StringUtilTest, JoinString) {
+ std::vector<std::string> in;
+ EXPECT_EQ("", JoinString(in, ','));
+
+ in.push_back("a");
+ EXPECT_EQ("a", JoinString(in, ','));
+
+ in.push_back("b");
+ in.push_back("c");
+ EXPECT_EQ("a,b,c", JoinString(in, ','));
+
+ in.push_back("");
+ EXPECT_EQ("a,b,c,", JoinString(in, ','));
+ in.push_back(" ");
+ EXPECT_EQ("a|b|c|| ", JoinString(in, '|'));
+}
+
+// Test for JoinString overloaded with std::string separator
+TEST(StringUtilTest, JoinStringWithString) {
+ std::string separator(", ");
+ std::vector<std::string> parts;
+ EXPECT_EQ(std::string(), JoinString(parts, separator));
+
+ parts.push_back("a");
+ EXPECT_EQ("a", JoinString(parts, separator));
+
+ parts.push_back("b");
+ parts.push_back("c");
+ EXPECT_EQ("a, b, c", JoinString(parts, separator));
+
+ parts.push_back("");
+ EXPECT_EQ("a, b, c, ", JoinString(parts, separator));
+ parts.push_back(" ");
+ EXPECT_EQ("a|b|c|| ", JoinString(parts, "|"));
+}
+
+// Test for JoinString overloaded with string16 separator
+TEST(StringUtilTest, JoinStringWithString16) {
+ string16 separator = ASCIIToUTF16(", ");
+ std::vector<string16> parts;
+ EXPECT_EQ(string16(), JoinString(parts, separator));
+
+ parts.push_back(ASCIIToUTF16("a"));
+ EXPECT_EQ(ASCIIToUTF16("a"), JoinString(parts, separator));
+
+ parts.push_back(ASCIIToUTF16("b"));
+ parts.push_back(ASCIIToUTF16("c"));
+ EXPECT_EQ(ASCIIToUTF16("a, b, c"), JoinString(parts, separator));
+
+ parts.push_back(ASCIIToUTF16(""));
+ EXPECT_EQ(ASCIIToUTF16("a, b, c, "), JoinString(parts, separator));
+ parts.push_back(ASCIIToUTF16(" "));
+ EXPECT_EQ(ASCIIToUTF16("a|b|c|| "), JoinString(parts, ASCIIToUTF16("|")));
+}
+
+TEST(StringUtilTest, StartsWith) {
+ EXPECT_TRUE(StartsWithASCII("javascript:url", "javascript", true));
+ EXPECT_FALSE(StartsWithASCII("JavaScript:url", "javascript", true));
+ EXPECT_TRUE(StartsWithASCII("javascript:url", "javascript", false));
+ EXPECT_TRUE(StartsWithASCII("JavaScript:url", "javascript", false));
+ EXPECT_FALSE(StartsWithASCII("java", "javascript", true));
+ EXPECT_FALSE(StartsWithASCII("java", "javascript", false));
+ EXPECT_FALSE(StartsWithASCII("", "javascript", false));
+ EXPECT_FALSE(StartsWithASCII("", "javascript", true));
+ EXPECT_TRUE(StartsWithASCII("java", "", false));
+ EXPECT_TRUE(StartsWithASCII("java", "", true));
+
+ EXPECT_TRUE(StartsWith(L"javascript:url", L"javascript", true));
+ EXPECT_FALSE(StartsWith(L"JavaScript:url", L"javascript", true));
+ EXPECT_TRUE(StartsWith(L"javascript:url", L"javascript", false));
+ EXPECT_TRUE(StartsWith(L"JavaScript:url", L"javascript", false));
+ EXPECT_FALSE(StartsWith(L"java", L"javascript", true));
+ EXPECT_FALSE(StartsWith(L"java", L"javascript", false));
+ EXPECT_FALSE(StartsWith(L"", L"javascript", false));
+ EXPECT_FALSE(StartsWith(L"", L"javascript", true));
+ EXPECT_TRUE(StartsWith(L"java", L"", false));
+ EXPECT_TRUE(StartsWith(L"java", L"", true));
+}
+
+TEST(StringUtilTest, EndsWith) {
+ EXPECT_TRUE(EndsWith(L"Foo.plugin", L".plugin", true));
+ EXPECT_FALSE(EndsWith(L"Foo.Plugin", L".plugin", true));
+ EXPECT_TRUE(EndsWith(L"Foo.plugin", L".plugin", false));
+ EXPECT_TRUE(EndsWith(L"Foo.Plugin", L".plugin", false));
+ EXPECT_FALSE(EndsWith(L".plug", L".plugin", true));
+ EXPECT_FALSE(EndsWith(L".plug", L".plugin", false));
+ EXPECT_FALSE(EndsWith(L"Foo.plugin Bar", L".plugin", true));
+ EXPECT_FALSE(EndsWith(L"Foo.plugin Bar", L".plugin", false));
+ EXPECT_FALSE(EndsWith(L"", L".plugin", false));
+ EXPECT_FALSE(EndsWith(L"", L".plugin", true));
+ EXPECT_TRUE(EndsWith(L"Foo.plugin", L"", false));
+ EXPECT_TRUE(EndsWith(L"Foo.plugin", L"", true));
+ EXPECT_TRUE(EndsWith(L".plugin", L".plugin", false));
+ EXPECT_TRUE(EndsWith(L".plugin", L".plugin", true));
+ EXPECT_TRUE(EndsWith(L"", L"", false));
+ EXPECT_TRUE(EndsWith(L"", L"", true));
+}
+
+TEST(StringUtilTest, GetStringFWithOffsets) {
+ std::vector<string16> subst;
+ subst.push_back(ASCIIToUTF16("1"));
+ subst.push_back(ASCIIToUTF16("2"));
+ std::vector<size_t> offsets;
+
+ ReplaceStringPlaceholders(ASCIIToUTF16("Hello, $1. Your number is $2."),
+ subst,
+ &offsets);
+ EXPECT_EQ(2U, offsets.size());
+ EXPECT_EQ(7U, offsets[0]);
+ EXPECT_EQ(25U, offsets[1]);
+ offsets.clear();
+
+ ReplaceStringPlaceholders(ASCIIToUTF16("Hello, $2. Your number is $1."),
+ subst,
+ &offsets);
+ EXPECT_EQ(2U, offsets.size());
+ EXPECT_EQ(25U, offsets[0]);
+ EXPECT_EQ(7U, offsets[1]);
+ offsets.clear();
+}
+
+TEST(StringUtilTest, ReplaceStringPlaceholdersTooFew) {
+ // Test whether replacestringplaceholders works as expected when there
+ // are fewer inputs than outputs.
+ std::vector<string16> subst;
+ subst.push_back(ASCIIToUTF16("9a"));
+ subst.push_back(ASCIIToUTF16("8b"));
+ subst.push_back(ASCIIToUTF16("7c"));
+
+ string16 formatted =
+ ReplaceStringPlaceholders(
+ ASCIIToUTF16("$1a,$2b,$3c,$4d,$5e,$6f,$1g,$2h,$3i"), subst, NULL);
+
+ EXPECT_EQ(formatted, ASCIIToUTF16("9aa,8bb,7cc,d,e,f,9ag,8bh,7ci"));
+}
+
+TEST(StringUtilTest, ReplaceStringPlaceholders) {
+ std::vector<string16> subst;
+ subst.push_back(ASCIIToUTF16("9a"));
+ subst.push_back(ASCIIToUTF16("8b"));
+ subst.push_back(ASCIIToUTF16("7c"));
+ subst.push_back(ASCIIToUTF16("6d"));
+ subst.push_back(ASCIIToUTF16("5e"));
+ subst.push_back(ASCIIToUTF16("4f"));
+ subst.push_back(ASCIIToUTF16("3g"));
+ subst.push_back(ASCIIToUTF16("2h"));
+ subst.push_back(ASCIIToUTF16("1i"));
+
+ string16 formatted =
+ ReplaceStringPlaceholders(
+ ASCIIToUTF16("$1a,$2b,$3c,$4d,$5e,$6f,$7g,$8h,$9i"), subst, NULL);
+
+ EXPECT_EQ(formatted, ASCIIToUTF16("9aa,8bb,7cc,6dd,5ee,4ff,3gg,2hh,1ii"));
+}
+
+TEST(StringUtilTest, ReplaceStringPlaceholdersMoreThan9Replacements) {
+ std::vector<string16> subst;
+ subst.push_back(ASCIIToUTF16("9a"));
+ subst.push_back(ASCIIToUTF16("8b"));
+ subst.push_back(ASCIIToUTF16("7c"));
+ subst.push_back(ASCIIToUTF16("6d"));
+ subst.push_back(ASCIIToUTF16("5e"));
+ subst.push_back(ASCIIToUTF16("4f"));
+ subst.push_back(ASCIIToUTF16("3g"));
+ subst.push_back(ASCIIToUTF16("2h"));
+ subst.push_back(ASCIIToUTF16("1i"));
+ subst.push_back(ASCIIToUTF16("0j"));
+ subst.push_back(ASCIIToUTF16("-1k"));
+ subst.push_back(ASCIIToUTF16("-2l"));
+ subst.push_back(ASCIIToUTF16("-3m"));
+ subst.push_back(ASCIIToUTF16("-4n"));
+
+ string16 formatted =
+ ReplaceStringPlaceholders(
+ ASCIIToUTF16("$1a,$2b,$3c,$4d,$5e,$6f,$7g,$8h,$9i,"
+ "$10j,$11k,$12l,$13m,$14n,$1"), subst, NULL);
+
+ EXPECT_EQ(formatted, ASCIIToUTF16("9aa,8bb,7cc,6dd,5ee,4ff,3gg,2hh,"
+ "1ii,0jj,-1kk,-2ll,-3mm,-4nn,9a"));
+}
+
+TEST(StringUtilTest, StdStringReplaceStringPlaceholders) {
+ std::vector<std::string> subst;
+ subst.push_back("9a");
+ subst.push_back("8b");
+ subst.push_back("7c");
+ subst.push_back("6d");
+ subst.push_back("5e");
+ subst.push_back("4f");
+ subst.push_back("3g");
+ subst.push_back("2h");
+ subst.push_back("1i");
+
+ std::string formatted =
+ ReplaceStringPlaceholders(
+ "$1a,$2b,$3c,$4d,$5e,$6f,$7g,$8h,$9i", subst, NULL);
+
+ EXPECT_EQ(formatted, "9aa,8bb,7cc,6dd,5ee,4ff,3gg,2hh,1ii");
+}
+
+TEST(StringUtilTest, ReplaceStringPlaceholdersConsecutiveDollarSigns) {
+ std::vector<std::string> subst;
+ subst.push_back("a");
+ subst.push_back("b");
+ subst.push_back("c");
+ EXPECT_EQ(ReplaceStringPlaceholders("$$1 $$$2 $$$$3", subst, NULL),
+ "$1 $$2 $$$3");
+}
+
+TEST(StringUtilTest, MatchPatternTest) {
+ EXPECT_TRUE(MatchPattern("www.google.com", "*.com"));
+ EXPECT_TRUE(MatchPattern("www.google.com", "*"));
+ EXPECT_FALSE(MatchPattern("www.google.com", "www*.g*.org"));
+ EXPECT_TRUE(MatchPattern("Hello", "H?l?o"));
+ EXPECT_FALSE(MatchPattern("www.google.com", "http://*)"));
+ EXPECT_FALSE(MatchPattern("www.msn.com", "*.COM"));
+ EXPECT_TRUE(MatchPattern("Hello*1234", "He??o\\*1*"));
+ EXPECT_FALSE(MatchPattern("", "*.*"));
+ EXPECT_TRUE(MatchPattern("", "*"));
+ EXPECT_TRUE(MatchPattern("", "?"));
+ EXPECT_TRUE(MatchPattern("", ""));
+ EXPECT_FALSE(MatchPattern("Hello", ""));
+ EXPECT_TRUE(MatchPattern("Hello*", "Hello*"));
+ // Stop after a certain recursion depth.
+ EXPECT_FALSE(MatchPattern("123456789012345678", "?????????????????*"));
+
+ // Test UTF8 matching.
+ EXPECT_TRUE(MatchPattern("heart: \xe2\x99\xa0", "*\xe2\x99\xa0"));
+ EXPECT_TRUE(MatchPattern("heart: \xe2\x99\xa0.", "heart: ?."));
+ EXPECT_TRUE(MatchPattern("hearts: \xe2\x99\xa0\xe2\x99\xa0", "*"));
+ // Invalid sequences should be handled as a single invalid character.
+ EXPECT_TRUE(MatchPattern("invalid: \xef\xbf\xbe", "invalid: ?"));
+ // If the pattern has invalid characters, it shouldn't match anything.
+ EXPECT_FALSE(MatchPattern("\xf4\x90\x80\x80", "\xf4\x90\x80\x80"));
+
+ // Test UTF16 character matching.
+ EXPECT_TRUE(MatchPattern(UTF8ToUTF16("www.google.com"),
+ UTF8ToUTF16("*.com")));
+ EXPECT_TRUE(MatchPattern(UTF8ToUTF16("Hello*1234"),
+ UTF8ToUTF16("He??o\\*1*")));
+
+ // This test verifies that consecutive wild cards are collapsed into 1
+ // wildcard (when this doesn't occur, MatchPattern reaches it's maximum
+ // recursion depth).
+ EXPECT_TRUE(MatchPattern(UTF8ToUTF16("Hello"),
+ UTF8ToUTF16("He********************************o")));
+}
+
+TEST(StringUtilTest, LcpyTest) {
+ // Test the normal case where we fit in our buffer.
+ {
+ char dst[10];
+ wchar_t wdst[10];
+ EXPECT_EQ(7U, base::strlcpy(dst, "abcdefg", arraysize(dst)));
+ EXPECT_EQ(0, memcmp(dst, "abcdefg", 8));
+ EXPECT_EQ(7U, base::wcslcpy(wdst, L"abcdefg", arraysize(wdst)));
+ EXPECT_EQ(0, memcmp(wdst, L"abcdefg", sizeof(wchar_t) * 8));
+ }
+
+ // Test dst_size == 0, nothing should be written to |dst| and we should
+ // have the equivalent of strlen(src).
+ {
+ char dst[2] = {1, 2};
+ wchar_t wdst[2] = {1, 2};
+ EXPECT_EQ(7U, base::strlcpy(dst, "abcdefg", 0));
+ EXPECT_EQ(1, dst[0]);
+ EXPECT_EQ(2, dst[1]);
+ EXPECT_EQ(7U, base::wcslcpy(wdst, L"abcdefg", 0));
+#if defined(WCHAR_T_IS_UNSIGNED)
+ EXPECT_EQ(1U, wdst[0]);
+ EXPECT_EQ(2U, wdst[1]);
+#else
+ EXPECT_EQ(1, wdst[0]);
+ EXPECT_EQ(2, wdst[1]);
+#endif
+ }
+
+ // Test the case were we _just_ competely fit including the null.
+ {
+ char dst[8];
+ wchar_t wdst[8];
+ EXPECT_EQ(7U, base::strlcpy(dst, "abcdefg", arraysize(dst)));
+ EXPECT_EQ(0, memcmp(dst, "abcdefg", 8));
+ EXPECT_EQ(7U, base::wcslcpy(wdst, L"abcdefg", arraysize(wdst)));
+ EXPECT_EQ(0, memcmp(wdst, L"abcdefg", sizeof(wchar_t) * 8));
+ }
+
+ // Test the case were we we are one smaller, so we can't fit the null.
+ {
+ char dst[7];
+ wchar_t wdst[7];
+ EXPECT_EQ(7U, base::strlcpy(dst, "abcdefg", arraysize(dst)));
+ EXPECT_EQ(0, memcmp(dst, "abcdef", 7));
+ EXPECT_EQ(7U, base::wcslcpy(wdst, L"abcdefg", arraysize(wdst)));
+ EXPECT_EQ(0, memcmp(wdst, L"abcdef", sizeof(wchar_t) * 7));
+ }
+
+ // Test the case were we are just too small.
+ {
+ char dst[3];
+ wchar_t wdst[3];
+ EXPECT_EQ(7U, base::strlcpy(dst, "abcdefg", arraysize(dst)));
+ EXPECT_EQ(0, memcmp(dst, "ab", 3));
+ EXPECT_EQ(7U, base::wcslcpy(wdst, L"abcdefg", arraysize(wdst)));
+ EXPECT_EQ(0, memcmp(wdst, L"ab", sizeof(wchar_t) * 3));
+ }
+}
+
+TEST(StringUtilTest, WprintfFormatPortabilityTest) {
+ static const struct {
+ const wchar_t* input;
+ bool portable;
+ } cases[] = {
+ { L"%ls", true },
+ { L"%s", false },
+ { L"%S", false },
+ { L"%lS", false },
+ { L"Hello, %s", false },
+ { L"%lc", true },
+ { L"%c", false },
+ { L"%C", false },
+ { L"%lC", false },
+ { L"%ls %s", false },
+ { L"%s %ls", false },
+ { L"%s %ls %s", false },
+ { L"%f", true },
+ { L"%f %F", false },
+ { L"%d %D", false },
+ { L"%o %O", false },
+ { L"%u %U", false },
+ { L"%f %d %o %u", true },
+ { L"%-8d (%02.1f%)", true },
+ { L"% 10s", false },
+ { L"% 10ls", true }
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i)
+ EXPECT_EQ(cases[i].portable, base::IsWprintfFormatPortable(cases[i].input));
+}
+
+TEST(StringUtilTest, RemoveChars) {
+ const char* kRemoveChars = "-/+*";
+ std::string input = "A-+bc/d!*";
+ EXPECT_TRUE(RemoveChars(input, kRemoveChars, &input));
+ EXPECT_EQ("Abcd!", input);
+
+ // No characters match kRemoveChars.
+ EXPECT_FALSE(RemoveChars(input, kRemoveChars, &input));
+ EXPECT_EQ("Abcd!", input);
+
+ // Empty string.
+ input.clear();
+ EXPECT_FALSE(RemoveChars(input, kRemoveChars, &input));
+ EXPECT_EQ(std::string(), input);
+}
+
+TEST(StringUtilTest, ReplaceChars) {
+ struct TestData {
+ const char* input;
+ const char* replace_chars;
+ const char* replace_with;
+ const char* output;
+ bool result;
+ } cases[] = {
+ { "", "", "", "", false },
+ { "test", "", "", "test", false },
+ { "test", "", "!", "test", false },
+ { "test", "z", "!", "test", false },
+ { "test", "e", "!", "t!st", true },
+ { "test", "e", "!?", "t!?st", true },
+ { "test", "ez", "!", "t!st", true },
+ { "test", "zed", "!?", "t!?st", true },
+ { "test", "t", "!?", "!?es!?", true },
+ { "test", "et", "!>", "!>!>s!>", true },
+ { "test", "zest", "!", "!!!!", true },
+ { "test", "szt", "!", "!e!!", true },
+ { "test", "t", "test", "testestest", true },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+ std::string output;
+ bool result = ReplaceChars(cases[i].input,
+ cases[i].replace_chars,
+ cases[i].replace_with,
+ &output);
+ EXPECT_EQ(cases[i].result, result);
+ EXPECT_EQ(cases[i].output, output);
+ }
+}
+
+TEST(StringUtilTest, ContainsOnlyChars) {
+ // Providing an empty list of characters should return false but for the empty
+ // string.
+ EXPECT_TRUE(ContainsOnlyChars("", ""));
+ EXPECT_FALSE(ContainsOnlyChars("Hello", ""));
+
+ EXPECT_TRUE(ContainsOnlyChars("", "1234"));
+ EXPECT_TRUE(ContainsOnlyChars("1", "1234"));
+ EXPECT_TRUE(ContainsOnlyChars("1", "4321"));
+ EXPECT_TRUE(ContainsOnlyChars("123", "4321"));
+ EXPECT_FALSE(ContainsOnlyChars("123a", "4321"));
+}
+
+class WriteIntoTest : public testing::Test {
+ protected:
+ static void WritesCorrectly(size_t num_chars) {
+ std::string buffer;
+ char kOriginal[] = "supercali";
+ strncpy(WriteInto(&buffer, num_chars + 1), kOriginal, num_chars);
+ // Using std::string(buffer.c_str()) instead of |buffer| truncates the
+ // string at the first \0.
+ EXPECT_EQ(std::string(kOriginal,
+ std::min(num_chars, arraysize(kOriginal) - 1)),
+ std::string(buffer.c_str()));
+ EXPECT_EQ(num_chars, buffer.size());
+ }
+};
+
+TEST_F(WriteIntoTest, WriteInto) {
+ // Validate that WriteInto reserves enough space and
+ // sizes a string correctly.
+ WritesCorrectly(1);
+ WritesCorrectly(2);
+ WritesCorrectly(5000);
+
+ // Validate that WriteInto doesn't modify other strings
+ // when using a Copy-on-Write implementation.
+ const char kLive[] = "live";
+ const char kDead[] = "dead";
+ const std::string live = kLive;
+ std::string dead = live;
+ strncpy(WriteInto(&dead, 5), kDead, 4);
+ EXPECT_EQ(kDead, dead);
+ EXPECT_EQ(4u, dead.size());
+ EXPECT_EQ(kLive, live);
+ EXPECT_EQ(4u, live.size());
+}
+
+} // namespace base
diff --git a/src/base/string_util_win.h b/src/base/string_util_win.h
new file mode 100644
index 0000000..2a4d3fd
--- /dev/null
+++ b/src/base/string_util_win.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 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_STRING_UTIL_WIN_H_
+#define BASE_STRING_UTIL_WIN_H_
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+
+#include "base/logging.h"
+
+namespace base {
+
+// Chromium code style is to not use malloc'd strings; this is only for use
+// for interaction with APIs that require it.
+inline char* strdup(const char* str) {
+ return _strdup(str);
+}
+
+inline int strcasecmp(const char* s1, const char* s2) {
+ return _stricmp(s1, s2);
+}
+
+inline int strncasecmp(const char* s1, const char* s2, size_t count) {
+ return _strnicmp(s1, s2, count);
+}
+
+inline int strncmp16(const char16* s1, const char16* s2, size_t count) {
+ return ::wcsncmp(s1, s2, count);
+}
+
+inline int vsnprintf(char* buffer, size_t size,
+ const char* format, va_list arguments) {
+ int length = _vsprintf_p(buffer, size, format, arguments);
+ if (length < 0) {
+ if (size > 0)
+ buffer[0] = 0;
+ return _vscprintf_p(format, arguments);
+ }
+ return length;
+}
+
+inline int vswprintf(wchar_t* buffer, size_t size,
+ const wchar_t* format, va_list arguments) {
+ DCHECK(IsWprintfFormatPortable(format));
+
+ int length = _vswprintf_p(buffer, size, format, arguments);
+ if (length < 0) {
+ if (size > 0)
+ buffer[0] = 0;
+ return _vscwprintf_p(format, arguments);
+ }
+ return length;
+}
+
+} // namespace base
+
+#endif // BASE_STRING_UTIL_WIN_H_
diff --git a/src/base/stringize_macros.h b/src/base/stringize_macros.h
new file mode 100644
index 0000000..1d53e48
--- /dev/null
+++ b/src/base/stringize_macros.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file defines preprocessor macros for stringizing preprocessor
+// symbols (or their output) and manipulating preprocessor symbols
+// that define strings.
+
+#ifndef BASE_STRINGIZE_MACROS_H_
+#define BASE_STRINGIZE_MACROS_H_
+
+#include "build/build_config.h"
+
+// This is not very useful as it does not expand defined symbols if
+// called directly. Use its counterpart without the _NO_EXPANSION
+// suffix, below.
+#define STRINGIZE_NO_EXPANSION(x) #x
+
+// Use this to quote the provided parameter, first expanding it if it
+// is a preprocessor symbol.
+//
+// For example, if:
+// #define A FOO
+// #define B(x) myobj->FunctionCall(x)
+//
+// Then:
+// STRINGIZE(A) produces "FOO"
+// STRINGIZE(B(y)) produces "myobj->FunctionCall(y)"
+#define STRINGIZE(x) STRINGIZE_NO_EXPANSION(x)
+
+// The following are defined only on Windows (for use when interacting
+// with Windows APIs) as wide strings are otherwise deprecated.
+#if defined(OS_WIN)
+
+// Second-level utility macros to let us expand symbols.
+#define LSTRINGIZE_NO_EXPANSION(x) L ## #x
+#define TO_L_STRING_NO_EXPANSION(x) L ## x
+
+// L version of STRINGIZE(). For examples above,
+// LSTRINGIZE(A) produces L"FOO"
+// LSTRINGIZE(B(y)) produces L"myobj->FunctionCall(y)"
+#define LSTRINGIZE(x) LSTRINGIZE_NO_EXPANSION(x)
+
+// Adds an L in front of an existing ASCII string constant (after
+// expanding symbols). Does not do any quoting.
+//
+// For example, if:
+// #define C "foo"
+//
+// Then:
+// TO_L_STRING(C) produces L"foo"
+#define TO_L_STRING(x) TO_L_STRING_NO_EXPANSION(x)
+
+#endif // defined(OS_WIN)
+
+#endif // BASE_STRINGIZE_MACROS_H_
diff --git a/src/base/stringize_macros_unittest.cc b/src/base/stringize_macros_unittest.cc
new file mode 100644
index 0000000..8d92d53
--- /dev/null
+++ b/src/base/stringize_macros_unittest.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Unit tests for stringize_macros.h
+
+#include "base/stringize_macros.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Macros as per documentation in header file.
+#define PREPROCESSOR_UTIL_UNITTEST_A FOO
+#define PREPROCESSOR_UTIL_UNITTEST_B(x) myobj->FunctionCall(x)
+#define PREPROCESSOR_UTIL_UNITTEST_C "foo"
+
+TEST(StringizeTest, Ansi) {
+ EXPECT_STREQ(
+ "PREPROCESSOR_UTIL_UNITTEST_A",
+ STRINGIZE_NO_EXPANSION(PREPROCESSOR_UTIL_UNITTEST_A));
+ EXPECT_STREQ(
+ "PREPROCESSOR_UTIL_UNITTEST_B(y)",
+ STRINGIZE_NO_EXPANSION(PREPROCESSOR_UTIL_UNITTEST_B(y)));
+ EXPECT_STREQ(
+ "PREPROCESSOR_UTIL_UNITTEST_C",
+ STRINGIZE_NO_EXPANSION(PREPROCESSOR_UTIL_UNITTEST_C));
+
+ EXPECT_STREQ("FOO", STRINGIZE(PREPROCESSOR_UTIL_UNITTEST_A));
+ EXPECT_STREQ("myobj->FunctionCall(y)",
+ STRINGIZE(PREPROCESSOR_UTIL_UNITTEST_B(y)));
+ EXPECT_STREQ("\"foo\"", STRINGIZE(PREPROCESSOR_UTIL_UNITTEST_C));
+}
+
+#if defined(OS_WIN)
+
+TEST(StringizeTest, Wide) {
+ EXPECT_STREQ(
+ L"PREPROCESSOR_UTIL_UNITTEST_A",
+ LSTRINGIZE_NO_EXPANSION(PREPROCESSOR_UTIL_UNITTEST_A));
+ EXPECT_STREQ(
+ L"PREPROCESSOR_UTIL_UNITTEST_B(y)",
+ LSTRINGIZE_NO_EXPANSION(PREPROCESSOR_UTIL_UNITTEST_B(y)));
+ EXPECT_STREQ(
+ L"PREPROCESSOR_UTIL_UNITTEST_C",
+ LSTRINGIZE_NO_EXPANSION(PREPROCESSOR_UTIL_UNITTEST_C));
+
+ EXPECT_STREQ(L"FOO", LSTRINGIZE(PREPROCESSOR_UTIL_UNITTEST_A));
+ EXPECT_STREQ(L"myobj->FunctionCall(y)",
+ LSTRINGIZE(PREPROCESSOR_UTIL_UNITTEST_B(y)));
+ EXPECT_STREQ(L"\"foo\"", LSTRINGIZE(PREPROCESSOR_UTIL_UNITTEST_C));
+}
+
+TEST(ToLStringTest, Main) {
+ EXPECT_STREQ(L"blat", TO_L_STRING_NO_EXPANSION("blat"));
+
+ EXPECT_STREQ(L"foo", TO_L_STRING(PREPROCESSOR_UTIL_UNITTEST_C));
+ EXPECT_STREQ(L"blat", TO_L_STRING("blat"));
+}
+
+#endif // defined(OS_WIN)
diff --git a/src/base/stringprintf.cc b/src/base/stringprintf.cc
new file mode 100644
index 0000000..76a6dbd
--- /dev/null
+++ b/src/base/stringprintf.cc
@@ -0,0 +1,195 @@
+// Copyright (c) 2012 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/stringprintf.h"
+
+#include <errno.h>
+
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+
+namespace base {
+
+namespace {
+
+#if defined(__LB_SHELL__) || defined(COBALT)
+// Emperically derived lowest number required to pass unit tests.
+const int kMaxStringPrintfBufferSize = 8 * 1024; // 8kB
+#else
+const int kMaxStringPrintfBufferSize = 32 * 1024 * 1024; // 32MB
+#endif
+
+// Overloaded wrappers around vsnprintf and vswprintf. The buf_size parameter
+// is the size of the buffer. These return the number of characters in the
+// formatted string excluding the NUL terminator. If the buffer is not
+// large enough to accommodate the formatted string without truncation, they
+// return the number of characters that would be in the fully-formatted string
+// (vsnprintf, and vswprintf on Windows), or -1 (vswprintf on POSIX platforms).
+inline int vsnprintfT(char* buffer,
+ size_t buf_size,
+ const char* format,
+ va_list argptr) {
+ return base::vsnprintf(buffer, buf_size, format, argptr);
+}
+
+#if !defined(OS_ANDROID)
+inline int vsnprintfT(wchar_t* buffer,
+ size_t buf_size,
+ const wchar_t* format,
+ va_list argptr) {
+ return base::vswprintf(buffer, buf_size, format, argptr);
+}
+#endif
+
+// Templatized backend for StringPrintF/StringAppendF. This does not finalize
+// the va_list, the caller is expected to do that.
+template <class StringType>
+static void StringAppendVT(StringType* dst,
+ const typename StringType::value_type* format,
+ va_list ap) {
+ // First try with a small fixed size buffer.
+ // This buffer size should be kept in sync with StringUtilTest.GrowBoundary
+ // and StringUtilTest.StringPrintfBounds.
+ typename StringType::value_type stack_buf[1024];
+
+ va_list ap_copy;
+ GG_VA_COPY(ap_copy, ap);
+
+#if !defined(OS_WIN)
+ errno = 0;
+#endif
+ int result = vsnprintfT(stack_buf, arraysize(stack_buf), format, ap_copy);
+ va_end(ap_copy);
+
+ if (result >= 0 && result < static_cast<int>(arraysize(stack_buf))) {
+ // It fit.
+ dst->append(stack_buf, result);
+ return;
+ }
+
+ // Repeatedly increase buffer size until it fits.
+ int mem_length = arraysize(stack_buf);
+ while (true) {
+ if (result < 0) {
+#if defined(__LB_XB1__) || defined(__LB_XB360__)
+ // On XB1, we get a -1 with ERANGE when the buffer is not big enough.
+ if (result != -1 || (errno != 0 && errno != ERANGE))
+#elif !defined(OS_WIN)
+ // On Windows, vsnprintfT always returns the number of characters in a
+ // fully-formatted string, so if we reach this point, something else is
+ // wrong and no amount of buffer-doubling is going to fix it.
+ if (errno != 0 && errno != EOVERFLOW)
+#endif
+ {
+ // If an error other than overflow occurred, it's never going to work.
+ DLOG(WARNING) << "Unable to printf the requested string due to error.";
+ return;
+ }
+ // Try doubling the buffer size.
+ mem_length *= 2;
+ } else {
+ // We need exactly "result + 1" characters.
+ mem_length = result + 1;
+ }
+
+ if (mem_length > kMaxStringPrintfBufferSize) {
+ // That should be plenty, don't try anything larger. This protects
+ // against huge allocations when using vsnprintfT implementations that
+ // return -1 for reasons other than overflow without setting errno.
+ DLOG(WARNING) << "Unable to printf the requested string due to size.";
+ return;
+ }
+
+ std::vector<typename StringType::value_type> mem_buf(mem_length);
+
+ // NOTE: You can only use a va_list once. Since we're in a while loop, we
+ // need to make a new copy each time so we don't use up the original.
+ GG_VA_COPY(ap_copy, ap);
+ result = vsnprintfT(&mem_buf[0], mem_length, format, ap_copy);
+ va_end(ap_copy);
+
+ if ((result >= 0) && (result < mem_length)) {
+ // It fit.
+ dst->append(&mem_buf[0], result);
+ return;
+ }
+ }
+}
+
+} // namespace
+
+std::string StringPrintf(const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ std::string result;
+ StringAppendV(&result, format, ap);
+ va_end(ap);
+ return result;
+}
+
+#if !defined(OS_ANDROID)
+std::wstring StringPrintf(const wchar_t* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ std::wstring result;
+ StringAppendV(&result, format, ap);
+ va_end(ap);
+ return result;
+}
+#endif
+
+std::string StringPrintV(const char* format, va_list ap) {
+ std::string result;
+ StringAppendV(&result, format, ap);
+ return result;
+}
+
+const std::string& SStringPrintf(std::string* dst, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ dst->clear();
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+ return *dst;
+}
+
+#if !defined(OS_ANDROID)
+const std::wstring& SStringPrintf(std::wstring* dst,
+ const wchar_t* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ dst->clear();
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+ return *dst;
+}
+#endif
+
+void StringAppendF(std::string* dst, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+}
+
+#if !defined(OS_ANDROID)
+void StringAppendF(std::wstring* dst, const wchar_t* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+}
+#endif
+
+void StringAppendV(std::string* dst, const char* format, va_list ap) {
+ StringAppendVT(dst, format, ap);
+}
+
+#if !defined(OS_ANDROID)
+void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) {
+ StringAppendVT(dst, format, ap);
+}
+#endif
+
+} // namespace base
diff --git a/src/base/stringprintf.h b/src/base/stringprintf.h
new file mode 100644
index 0000000..9a99237
--- /dev/null
+++ b/src/base/stringprintf.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2012 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_STRINGPRINTF_H_
+#define BASE_STRINGPRINTF_H_
+
+#include <stdarg.h> // va_list
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+
+namespace base {
+
+// Return a C++ string given printf-like input.
+BASE_EXPORT std::string StringPrintf(const char* format, ...)
+ PRINTF_FORMAT(1, 2);
+// OS_ANDROID's libc does not support wchar_t, so several overloads are omitted.
+#if !defined(OS_ANDROID)
+BASE_EXPORT std::wstring StringPrintf(const wchar_t* format, ...)
+ WPRINTF_FORMAT(1, 2);
+#endif
+
+// Return a C++ string given vprintf-like input.
+BASE_EXPORT std::string StringPrintV(const char* format, va_list ap)
+ PRINTF_FORMAT(1, 0);
+
+// Store result into a supplied string and return it.
+BASE_EXPORT const std::string& SStringPrintf(std::string* dst,
+ const char* format, ...)
+ PRINTF_FORMAT(2, 3);
+#if !defined(OS_ANDROID)
+BASE_EXPORT const std::wstring& SStringPrintf(std::wstring* dst,
+ const wchar_t* format, ...)
+ WPRINTF_FORMAT(2, 3);
+#endif
+
+// Append result to a supplied string.
+BASE_EXPORT void StringAppendF(std::string* dst, const char* format, ...)
+ PRINTF_FORMAT(2, 3);
+#if !defined(OS_ANDROID)
+// TODO(evanm): this is only used in a few places in the code;
+// replace with string16 version.
+BASE_EXPORT void StringAppendF(std::wstring* dst, const wchar_t* format, ...)
+ WPRINTF_FORMAT(2, 3);
+#endif
+
+// Lower-level routine that takes a va_list and appends to a specified
+// string. All other routines are just convenience wrappers around it.
+BASE_EXPORT void StringAppendV(std::string* dst, const char* format, va_list ap)
+ PRINTF_FORMAT(2, 0);
+#if !defined(OS_ANDROID)
+BASE_EXPORT void StringAppendV(std::wstring* dst,
+ const wchar_t* format, va_list ap)
+ WPRINTF_FORMAT(2, 0);
+#endif
+
+} // namespace base
+
+// Don't require the namespace for legacy code. New code should use "base::" or
+// have its own using decl.
+//
+// TODO(brettw) remove these when calling code is converted.
+using base::StringPrintf;
+
+#endif // BASE_STRINGPRINTF_H_
diff --git a/src/base/stringprintf_unittest.cc b/src/base/stringprintf_unittest.cc
new file mode 100644
index 0000000..d8fdfee
--- /dev/null
+++ b/src/base/stringprintf_unittest.cc
@@ -0,0 +1,198 @@
+// Copyright (c) 2012 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/basictypes.h"
+#include "base/stringprintf.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#if defined(OS_STARBOARD)
+#include "starboard/string.h"
+#endif
+
+namespace base {
+
+namespace {
+
+// A helper for the StringAppendV test that follows.
+//
+// Just forwards its args to StringAppendV.
+static void StringAppendVTestHelper(std::string* out, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ StringAppendV(out, format, ap);
+ va_end(ap);
+}
+
+#if defined(OS_STARBOARD)
+// Same deal for SbStringFormat
+static void SbStringFormatHelper(char *out_buffer,
+ size_t buffer_size,
+ const char *format,
+ ...) {
+ va_list ap;
+ va_start(ap, format);
+ SbStringFormat(out_buffer, buffer_size, format, ap);
+ va_end(ap);
+}
+#endif
+
+} // namespace
+
+TEST(StringPrintfTest, StringPrintfEmpty) {
+ EXPECT_EQ("", StringPrintf("%s", ""));
+}
+
+TEST(StringPrintfTest, StringPrintfMisc) {
+ EXPECT_EQ("123hello w", StringPrintf("%3d%2s %1c", 123, "hello", 'w'));
+#if !defined(OS_ANDROID) && !defined(__LB_ANDROID__)
+ EXPECT_EQ(L"123hello w", StringPrintf(L"%3d%2ls %1lc", 123, L"hello", 'w'));
+#endif
+}
+
+TEST(StringPrintfTest, StringAppendfEmptyString) {
+ std::string value("Hello");
+ StringAppendF(&value, "%s", "");
+ EXPECT_EQ("Hello", value);
+
+#if !defined(OS_ANDROID) && !defined(__LB_ANDROID__)
+ std::wstring valuew(L"Hello");
+ StringAppendF(&valuew, L"%ls", L"");
+ EXPECT_EQ(L"Hello", valuew);
+#endif
+}
+
+TEST(StringPrintfTest, StringAppendfString) {
+ std::string value("Hello");
+ StringAppendF(&value, " %s", "World");
+ EXPECT_EQ("Hello World", value);
+
+#if !defined(OS_ANDROID) && !defined(__LB_ANDROID__)
+ std::wstring valuew(L"Hello");
+ StringAppendF(&valuew, L" %ls", L"World");
+ EXPECT_EQ(L"Hello World", valuew);
+#endif
+}
+
+TEST(StringPrintfTest, StringAppendfInt) {
+ std::string value("Hello");
+ StringAppendF(&value, " %d", 123);
+ EXPECT_EQ("Hello 123", value);
+
+#if !defined(OS_ANDROID) && !defined(__LB_ANDROID__)
+ std::wstring valuew(L"Hello");
+ StringAppendF(&valuew, L" %d", 123);
+ EXPECT_EQ(L"Hello 123", valuew);
+#endif
+}
+
+// Make sure that lengths exactly around the initial buffer size are handled
+// correctly.
+TEST(StringPrintfTest, StringPrintfBounds) {
+ const int kSrcLen = 1026;
+ char src[kSrcLen];
+ for (size_t i = 0; i < arraysize(src); i++)
+ src[i] = 'A';
+
+ wchar_t srcw[kSrcLen];
+ for (size_t i = 0; i < arraysize(srcw); i++)
+ srcw[i] = 'A';
+
+ for (int i = 1; i < 3; i++) {
+ src[kSrcLen - i] = 0;
+ std::string out;
+ SStringPrintf(&out, "%s", src);
+ EXPECT_STREQ(src, out.c_str());
+
+#if !defined(OS_ANDROID) && !defined(__LB_ANDROID__)
+ srcw[kSrcLen - i] = 0;
+ std::wstring outw;
+ SStringPrintf(&outw, L"%ls", srcw);
+ EXPECT_STREQ(srcw, outw.c_str());
+#endif
+ }
+}
+
+// Test very large sprintfs that will cause the buffer to grow.
+TEST(StringPrintfTest, Grow) {
+ char src[1026];
+ for (size_t i = 0; i < arraysize(src); i++)
+ src[i] = 'A';
+ src[1025] = 0;
+
+ const char* fmt = "%sB%sB%sB%sB%sB%sB%s";
+
+ std::string out;
+ SStringPrintf(&out, fmt, src, src, src, src, src, src, src);
+
+ const int kRefSize = 320000;
+ char* ref = new char[kRefSize];
+#if defined(OS_WIN)
+ sprintf_s(ref, kRefSize, fmt, src, src, src, src, src, src, src);
+#elif defined(OS_POSIX)
+ snprintf(ref, kRefSize, fmt, src, src, src, src, src, src, src);
+#elif defined(OS_STARBOARD)
+ SbStringFormatHelper(ref, kRefSize, fmt, src, src, src, src, src, src, src);
+#endif
+
+ EXPECT_STREQ(ref, out.c_str());
+ delete[] ref;
+}
+
+TEST(StringPrintfTest, StringAppendV) {
+ std::string out;
+ StringAppendVTestHelper(&out, "%d foo %s", 1, "bar");
+ EXPECT_EQ("1 foo bar", out);
+}
+
+// Test the boundary condition for the size of the string_util's
+// internal buffer.
+TEST(StringPrintfTest, GrowBoundary) {
+ const int string_util_buf_len = 1024;
+ // Our buffer should be one larger than the size of StringAppendVT's stack
+ // buffer.
+ const int buf_len = string_util_buf_len + 1;
+ char src[buf_len + 1]; // Need extra one for NULL-terminator.
+ for (int i = 0; i < buf_len; ++i)
+ src[i] = 'a';
+ src[buf_len] = 0;
+
+ std::string out;
+ SStringPrintf(&out, "%s", src);
+
+ EXPECT_STREQ(src, out.c_str());
+}
+
+// TODO(evanm): what's the proper cross-platform test here?
+#if defined(OS_WIN) || defined(__LB_XB1__) || defined(__LB_XB360__)
+// sprintf in Visual Studio fails when given U+FFFF. This tests that the
+// failure case is gracefuly handled.
+TEST(StringPrintfTest, Invalid) {
+ wchar_t invalid[2];
+ invalid[0] = 0xffff;
+ invalid[1] = 0;
+
+ std::wstring out;
+ SStringPrintf(&out, L"%ls", invalid);
+ EXPECT_STREQ(L"", out.c_str());
+}
+#endif
+
+// lbshell platforms do not support positional parameters,
+// and lbshell does not use the few parts of chromium that
+// leverage positional parameter support in the OS.
+#if !defined(__LB_SHELL__)
+// Test that the positional parameters work.
+TEST(StringPrintfTest, PositionalParameters) {
+ std::string out;
+ SStringPrintf(&out, "%1$s %1$s", "test");
+ EXPECT_STREQ("test test", out.c_str());
+
+#if defined(OS_WIN)
+ std::wstring wout;
+ SStringPrintf(&wout, L"%1$ls %1$ls", L"test");
+ EXPECT_STREQ(L"test test", wout.c_str());
+#endif
+}
+#endif
+
+} // namespace base
diff --git a/src/base/supports_user_data.cc b/src/base/supports_user_data.cc
new file mode 100644
index 0000000..2a0263e
--- /dev/null
+++ b/src/base/supports_user_data.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 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/supports_user_data.h"
+
+namespace base {
+
+SupportsUserData::SupportsUserData() {
+ // Harmless to construct on a different thread to subsequent usage.
+ thread_checker_.DetachFromThread();
+}
+
+SupportsUserData::Data* SupportsUserData::GetUserData(const void* key) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DataMap::const_iterator found = user_data_.find(key);
+ if (found != user_data_.end())
+ return found->second.get();
+ return NULL;
+}
+
+void SupportsUserData::SetUserData(const void* key, Data* data) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ user_data_[key] = linked_ptr<Data>(data);
+}
+
+void SupportsUserData::RemoveUserData(const void* key) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ user_data_.erase(key);
+}
+
+void SupportsUserData::DetachUserDataThread() {
+ thread_checker_.DetachFromThread();
+}
+
+SupportsUserData::~SupportsUserData() {
+ DCHECK(thread_checker_.CalledOnValidThread() || user_data_.empty());
+}
+
+} // namespace base
diff --git a/src/base/supports_user_data.h b/src/base/supports_user_data.h
new file mode 100644
index 0000000..7736755
--- /dev/null
+++ b/src/base/supports_user_data.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2012 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_SUPPORTS_USER_DATA_H_
+#define BASE_SUPPORTS_USER_DATA_H_
+
+#include <map>
+
+#include "base/base_export.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+
+namespace base {
+
+// This is a helper for classes that want to allow users to stash random data by
+// key. At destruction all the objects will be destructed.
+class BASE_EXPORT SupportsUserData {
+ public:
+ SupportsUserData();
+
+ // Derive from this class and add your own data members to associate extra
+ // information with this object. Alternatively, add this as a public base
+ // class to any class with a virtual destructor.
+ class BASE_EXPORT Data {
+ public:
+ virtual ~Data() {}
+ };
+
+ // The user data allows the clients to associate data with this object.
+ // Multiple user data values can be stored under different keys.
+ // This object will TAKE OWNERSHIP of the given data pointer, and will
+ // delete the object if it is changed or the object is destroyed.
+ Data* GetUserData(const void* key) const;
+ void SetUserData(const void* key, Data* data);
+ void RemoveUserData(const void* key);
+
+ // SupportsUserData is not thread-safe, and on debug build will assert it is
+ // only used on one thread. Calling this method allows the caller to hand
+ // the SupportsUserData instance across threads. Use only if you are taking
+ // full control of the synchronization of that hand over.
+ void DetachUserDataThread();
+
+ protected:
+ virtual ~SupportsUserData();
+
+ private:
+ typedef std::map<const void*, linked_ptr<Data> > DataMap;
+
+ // Externally-defined data accessible by key.
+ DataMap user_data_;
+ // Guards usage of |user_data_|
+ ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(SupportsUserData);
+};
+
+// Adapter class that releases a refcounted object when the
+// SupportsUserData::Data object is deleted.
+template <typename T>
+class UserDataAdapter : public base::SupportsUserData::Data {
+ public:
+ static T* Get(SupportsUserData* supports_user_data, const char* key) {
+ UserDataAdapter* data =
+ static_cast<UserDataAdapter*>(supports_user_data->GetUserData(key));
+ return data ? static_cast<T*>(data->object_.get()) : NULL;
+ }
+
+ UserDataAdapter(T* object) : object_(object) {}
+ T* release() { return object_.release(); }
+
+ private:
+ scoped_refptr<T> object_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserDataAdapter);
+};
+
+} // namespace base
+
+#endif // BASE_SUPPORTS_USER_DATA_H_
diff --git a/src/base/sync_socket.h b/src/base/sync_socket.h
new file mode 100644
index 0000000..eb3eeb5
--- /dev/null
+++ b/src/base/sync_socket.h
@@ -0,0 +1,124 @@
+// Copyright (c) 2012 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_SYNC_SOCKET_H_
+#define BASE_SYNC_SOCKET_H_
+
+// A socket abstraction used for sending and receiving plain
+// data. Because the receiving is blocking, they can be used to perform
+// rudimentary cross-process synchronization with low latency.
+
+#include "base/basictypes.h"
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+#include <sys/types.h>
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/synchronization/waitable_event.h"
+
+namespace base {
+
+class BASE_EXPORT SyncSocket {
+ public:
+#if defined(OS_WIN)
+ typedef HANDLE Handle;
+#else
+ typedef int Handle;
+#endif
+ static const Handle kInvalidHandle;
+
+ SyncSocket();
+
+ // Creates a SyncSocket from a Handle. Used in transport.
+ explicit SyncSocket(Handle handle) : handle_(handle) {}
+ virtual ~SyncSocket();
+
+ // Initializes and connects a pair of sockets.
+ // |socket_a| and |socket_b| must not hold a valid handle. Upon successful
+ // return, the sockets will both be valid and connected.
+ static bool CreatePair(SyncSocket* socket_a, SyncSocket* socket_b);
+
+ // Closes the SyncSocket. Returns true on success, false on failure.
+ virtual bool Close();
+
+ // Sends the message to the remote peer of the SyncSocket.
+ // Note it is not safe to send messages from the same socket handle by
+ // multiple threads simultaneously.
+ // buffer is a pointer to the data to send.
+ // length is the length of the data to send (must be non-zero).
+ // Returns the number of bytes sent, or 0 upon failure.
+ virtual size_t Send(const void* buffer, size_t length);
+
+ // Receives a message from an SyncSocket.
+ // buffer is a pointer to the buffer to receive data.
+ // length is the number of bytes of data to receive (must be non-zero).
+ // Returns the number of bytes received, or 0 upon failure.
+ virtual size_t Receive(void* buffer, size_t length);
+
+ // Returns the number of bytes available. If non-zero, Receive() will not
+ // not block when called. NOTE: Some implementations cannot reliably
+ // determine the number of bytes available so avoid using the returned
+ // size as a promise and simply test against zero.
+ size_t Peek();
+
+ // Extracts the contained handle. Used for transferring between
+ // processes.
+ Handle handle() const { return handle_; }
+
+ protected:
+ Handle handle_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SyncSocket);
+};
+
+// Derives from SyncSocket and adds support for shutting down the socket from
+// another thread while a blocking Receive or Send is being done from the
+// thread that owns the socket.
+class BASE_EXPORT CancelableSyncSocket : public SyncSocket {
+ public:
+ CancelableSyncSocket();
+ explicit CancelableSyncSocket(Handle handle);
+ virtual ~CancelableSyncSocket() {}
+
+ // Initializes a pair of cancelable sockets. See documentation for
+ // SyncSocket::CreatePair for more details.
+ static bool CreatePair(CancelableSyncSocket* socket_a,
+ CancelableSyncSocket* socket_b);
+
+ // A way to shut down a socket even if another thread is currently performing
+ // a blocking Receive or Send.
+ bool Shutdown();
+
+#if defined(OS_WIN)
+ // Since the Linux and Mac implementations actually use a socket, shutting
+ // them down from another thread is pretty simple - we can just call
+ // shutdown(). However, the Windows implementation relies on named pipes
+ // and there isn't a way to cancel a blocking synchronous Read that is
+ // supported on <Vista. So, for Windows only, we override these
+ // SyncSocket methods in order to support shutting down the 'socket'.
+ virtual bool Close() OVERRIDE;
+ virtual size_t Receive(void* buffer, size_t length) OVERRIDE;
+#endif
+
+ // Send() is overridden to catch cases where the remote end is not responding
+ // and we fill the local socket buffer. When the buffer is full, this
+ // implementation of Send() will not block indefinitely as
+ // SyncSocket::Send will, but instead return 0, as no bytes could be sent.
+ // Note that the socket will not be closed in this case.
+ virtual size_t Send(const void* buffer, size_t length) OVERRIDE;
+
+ private:
+#if defined(OS_WIN)
+ WaitableEvent shutdown_event_;
+ WaitableEvent file_operation_;
+#endif
+ DISALLOW_COPY_AND_ASSIGN(CancelableSyncSocket);
+};
+
+} // namespace base
+
+#endif // BASE_SYNC_SOCKET_H_
diff --git a/src/base/sync_socket_nacl.cc b/src/base/sync_socket_nacl.cc
new file mode 100644
index 0000000..6d6405d
--- /dev/null
+++ b/src/base/sync_socket_nacl.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2012 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/sync_socket.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+
+
+namespace base {
+
+const SyncSocket::Handle SyncSocket::kInvalidHandle = -1;
+
+SyncSocket::SyncSocket() : handle_(kInvalidHandle) {
+}
+
+SyncSocket::~SyncSocket() {
+}
+
+// static
+bool SyncSocket::CreatePair(SyncSocket* socket_a, SyncSocket* socket_b) {
+ return false;
+}
+
+bool SyncSocket::Close() {
+ if (handle_ != kInvalidHandle) {
+ if (close(handle_) < 0)
+ DPLOG(ERROR) << "close";
+ handle_ = -1;
+ }
+ return true;
+}
+
+size_t SyncSocket::Send(const void* buffer, size_t length) {
+ // Not implemented since it's not needed by any client code yet.
+ return -1;
+}
+
+size_t SyncSocket::Receive(void* buffer, size_t length) {
+ return read(handle_, buffer, length);
+}
+
+size_t SyncSocket::Peek() {
+ return -1;
+}
+
+CancelableSyncSocket::CancelableSyncSocket() {
+}
+
+CancelableSyncSocket::CancelableSyncSocket(Handle handle)
+ : SyncSocket(handle) {
+}
+
+size_t CancelableSyncSocket::Send(const void* buffer, size_t length) {
+ return -1;
+}
+
+bool CancelableSyncSocket::Shutdown() {
+ return false;
+}
+
+// static
+bool CancelableSyncSocket::CreatePair(CancelableSyncSocket* socket_a,
+ CancelableSyncSocket* socket_b) {
+ return SyncSocket::CreatePair(socket_a, socket_b);
+}
+
+} // namespace base
diff --git a/src/base/sync_socket_posix.cc b/src/base/sync_socket_posix.cc
new file mode 100644
index 0000000..257916d
--- /dev/null
+++ b/src/base/sync_socket_posix.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2012 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/sync_socket.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#if defined(OS_SOLARIS)
+#include <sys/filio.h>
+#endif
+
+#include "base/file_util.h"
+#include "base/logging.h"
+
+
+namespace base {
+
+namespace {
+// To avoid users sending negative message lengths to Send/Receive
+// we clamp message lengths, which are size_t, to no more than INT_MAX.
+const size_t kMaxMessageLength = static_cast<size_t>(INT_MAX);
+
+} // namespace
+
+const SyncSocket::Handle SyncSocket::kInvalidHandle = -1;
+
+SyncSocket::SyncSocket() : handle_(kInvalidHandle) {}
+
+SyncSocket::~SyncSocket() {
+ Close();
+}
+
+// static
+bool SyncSocket::CreatePair(SyncSocket* socket_a, SyncSocket* socket_b) {
+ DCHECK(socket_a != socket_b);
+ DCHECK(socket_a->handle_ == kInvalidHandle);
+ DCHECK(socket_b->handle_ == kInvalidHandle);
+
+#if defined(OS_MACOSX)
+ int nosigpipe = 1;
+#endif // defined(OS_MACOSX)
+
+ Handle handles[2] = { kInvalidHandle, kInvalidHandle };
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, handles) != 0)
+ goto cleanup;
+
+#if defined(OS_MACOSX)
+ // On OSX an attempt to read or write to a closed socket may generate a
+ // SIGPIPE rather than returning -1. setsockopt will shut this off.
+ if (0 != setsockopt(handles[0], SOL_SOCKET, SO_NOSIGPIPE,
+ &nosigpipe, sizeof nosigpipe) ||
+ 0 != setsockopt(handles[1], SOL_SOCKET, SO_NOSIGPIPE,
+ &nosigpipe, sizeof nosigpipe)) {
+ goto cleanup;
+ }
+#endif
+
+ // Copy the handles out for successful return.
+ socket_a->handle_ = handles[0];
+ socket_b->handle_ = handles[1];
+
+ return true;
+
+ cleanup:
+ if (handles[0] != kInvalidHandle) {
+ if (HANDLE_EINTR(close(handles[0])) < 0)
+ DPLOG(ERROR) << "close";
+ }
+ if (handles[1] != kInvalidHandle) {
+ if (HANDLE_EINTR(close(handles[1])) < 0)
+ DPLOG(ERROR) << "close";
+ }
+
+ return false;
+}
+
+bool SyncSocket::Close() {
+ if (handle_ == kInvalidHandle) {
+ return false;
+ }
+ int retval = HANDLE_EINTR(close(handle_));
+ if (retval < 0)
+ DPLOG(ERROR) << "close";
+ handle_ = kInvalidHandle;
+ return (retval == 0);
+}
+
+size_t SyncSocket::Send(const void* buffer, size_t length) {
+ DCHECK_LE(length, kMaxMessageLength);
+ const char* charbuffer = static_cast<const char*>(buffer);
+ int len = file_util::WriteFileDescriptor(handle_, charbuffer, length);
+
+ return (len == -1) ? 0 : static_cast<size_t>(len);
+}
+
+size_t SyncSocket::Receive(void* buffer, size_t length) {
+ DCHECK_LE(length, kMaxMessageLength);
+ char* charbuffer = static_cast<char*>(buffer);
+ if (file_util::ReadFromFD(handle_, charbuffer, length))
+ return length;
+ return 0;
+}
+
+size_t SyncSocket::Peek() {
+ int number_chars;
+ if (-1 == ioctl(handle_, FIONREAD, &number_chars)) {
+ // If there is an error in ioctl, signal that the channel would block.
+ return 0;
+ }
+ return (size_t) number_chars;
+}
+
+CancelableSyncSocket::CancelableSyncSocket() {}
+CancelableSyncSocket::CancelableSyncSocket(Handle handle)
+ : SyncSocket(handle) {
+}
+
+bool CancelableSyncSocket::Shutdown() {
+ return HANDLE_EINTR(shutdown(handle(), SHUT_RDWR)) >= 0;
+}
+
+size_t CancelableSyncSocket::Send(const void* buffer, size_t length) {
+ long flags = 0;
+ flags = fcntl(handle_, F_GETFL, NULL);
+ if (flags != -1 && (flags & O_NONBLOCK) == 0) {
+ // Set the socket to non-blocking mode for sending if its original mode
+ // is blocking.
+ fcntl(handle_, F_SETFL, flags | O_NONBLOCK);
+ }
+
+ size_t len = SyncSocket::Send(buffer, length);
+
+ if (flags != -1 && (flags & O_NONBLOCK) == 0) {
+ // Restore the original flags.
+ fcntl(handle_, F_SETFL, flags);
+ }
+
+ return len;
+}
+
+// static
+bool CancelableSyncSocket::CreatePair(CancelableSyncSocket* socket_a,
+ CancelableSyncSocket* socket_b) {
+ return SyncSocket::CreatePair(socket_a, socket_b);
+}
+
+} // namespace base
diff --git a/src/base/sync_socket_win.cc b/src/base/sync_socket_win.cc
new file mode 100644
index 0000000..4fcd572
--- /dev/null
+++ b/src/base/sync_socket_win.cc
@@ -0,0 +1,271 @@
+// Copyright (c) 2012 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/sync_socket.h"
+
+#include "base/logging.h"
+#include "base/win/scoped_handle.h"
+
+namespace base {
+
+using win::ScopedHandle;
+
+namespace {
+// IMPORTANT: do not change how this name is generated because it will break
+// in sandboxed scenarios as we might have by-name policies that allow pipe
+// creation. Also keep the secure random number generation.
+const wchar_t kPipeNameFormat[] = L"\\\\.\\pipe\\chrome.sync.%u.%u.%lu";
+const size_t kPipePathMax = arraysize(kPipeNameFormat) + (3 * 10) + 1;
+
+// To avoid users sending negative message lengths to Send/Receive
+// we clamp message lengths, which are size_t, to no more than INT_MAX.
+const size_t kMaxMessageLength = static_cast<size_t>(INT_MAX);
+
+const int kOutBufferSize = 4096;
+const int kInBufferSize = 4096;
+const int kDefaultTimeoutMilliSeconds = 1000;
+
+bool CreatePairImpl(HANDLE* socket_a, HANDLE* socket_b, bool overlapped) {
+ DCHECK(socket_a != socket_b);
+ DCHECK(*socket_a == SyncSocket::kInvalidHandle);
+ DCHECK(*socket_b == SyncSocket::kInvalidHandle);
+
+ wchar_t name[kPipePathMax];
+ ScopedHandle handle_a;
+ DWORD flags = PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE;
+ if (overlapped)
+ flags |= FILE_FLAG_OVERLAPPED;
+
+ do {
+ unsigned int rnd_name;
+ if (rand_s(&rnd_name) != 0)
+ return false;
+
+ swprintf(name, kPipePathMax,
+ kPipeNameFormat,
+ GetCurrentProcessId(),
+ GetCurrentThreadId(),
+ rnd_name);
+
+ handle_a.Set(CreateNamedPipeW(
+ name,
+ flags,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
+ 1,
+ kOutBufferSize,
+ kInBufferSize,
+ kDefaultTimeoutMilliSeconds,
+ NULL));
+ } while (!handle_a.IsValid() &&
+ (GetLastError() == ERROR_PIPE_BUSY));
+
+ if (!handle_a.IsValid()) {
+ NOTREACHED();
+ return false;
+ }
+
+ // The SECURITY_ANONYMOUS flag means that the server side (handle_a) cannot
+ // impersonate the client (handle_b). This allows us not to care which side
+ // ends up in which side of a privilege boundary.
+ flags = SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS;
+ if (overlapped)
+ flags |= FILE_FLAG_OVERLAPPED;
+
+ ScopedHandle handle_b(CreateFileW(name,
+ GENERIC_READ | GENERIC_WRITE,
+ 0, // no sharing.
+ NULL, // default security attributes.
+ OPEN_EXISTING, // opens existing pipe.
+ flags,
+ NULL)); // no template file.
+ if (!handle_b.IsValid()) {
+ DPLOG(ERROR) << "CreateFileW failed";
+ return false;
+ }
+
+ if (!ConnectNamedPipe(handle_a, NULL)) {
+ DWORD error = GetLastError();
+ if (error != ERROR_PIPE_CONNECTED) {
+ DPLOG(ERROR) << "ConnectNamedPipe failed";
+ return false;
+ }
+ }
+
+ *socket_a = handle_a.Take();
+ *socket_b = handle_b.Take();
+
+ return true;
+}
+
+// Inline helper to avoid having the cast everywhere.
+DWORD GetNextChunkSize(size_t current_pos, size_t max_size) {
+ // The following statement is for 64 bit portability.
+ return static_cast<DWORD>(((max_size - current_pos) <= UINT_MAX) ?
+ (max_size - current_pos) : UINT_MAX);
+}
+
+// Template function that supports calling ReadFile or WriteFile in an
+// overlapped fashion and waits for IO completion. The function also waits
+// on an event that can be used to cancel the operation. If the operation
+// is cancelled, the function returns and closes the relevant socket object.
+template <typename BufferType, typename Function>
+size_t CancelableFileOperation(Function operation, HANDLE file,
+ BufferType* buffer, size_t length,
+ base::WaitableEvent* io_event,
+ base::WaitableEvent* cancel_event,
+ CancelableSyncSocket* socket,
+ DWORD timeout_in_ms) {
+ // The buffer must be byte size or the length check won't make much sense.
+ COMPILE_ASSERT(sizeof(buffer[0]) == sizeof(char), incorrect_buffer_type);
+ DCHECK_LE(length, kMaxMessageLength);
+
+ OVERLAPPED ol = {0};
+ ol.hEvent = io_event->handle();
+ size_t count = 0;
+ while (count < length) {
+ DWORD chunk = GetNextChunkSize(count, length);
+ // This is either the ReadFile or WriteFile call depending on whether
+ // we're receiving or sending data.
+ DWORD len = 0;
+ BOOL ok = operation(file, static_cast<BufferType*>(buffer) + count, chunk,
+ &len, &ol);
+ if (!ok) {
+ if (::GetLastError() == ERROR_IO_PENDING) {
+ HANDLE events[] = { io_event->handle(), cancel_event->handle() };
+ int wait_result = WaitForMultipleObjects(
+ arraysize(events), events, FALSE, timeout_in_ms);
+ if (wait_result == (WAIT_OBJECT_0 + 0)) {
+ GetOverlappedResult(file, &ol, &len, TRUE);
+ } else if (wait_result == (WAIT_OBJECT_0 + 1)) {
+ VLOG(1) << "Shutdown was signaled. Closing socket.";
+ CancelIo(file);
+ socket->Close();
+ count = 0;
+ break;
+ } else {
+ // Timeout happened.
+ DCHECK_EQ(WAIT_TIMEOUT, wait_result);
+ if (!CancelIo(file)){
+ DLOG(WARNING) << "CancelIo() failed";
+ }
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ count += len;
+
+ // Quit the operation if we can't write/read anymore.
+ if (len != chunk)
+ break;
+ }
+
+ return (count > 0) ? count : 0;
+}
+
+} // namespace
+
+const SyncSocket::Handle SyncSocket::kInvalidHandle = INVALID_HANDLE_VALUE;
+
+SyncSocket::SyncSocket() : handle_(kInvalidHandle) {}
+
+SyncSocket::~SyncSocket() {
+ Close();
+}
+
+// static
+bool SyncSocket::CreatePair(SyncSocket* socket_a, SyncSocket* socket_b) {
+ return CreatePairImpl(&socket_a->handle_, &socket_b->handle_, false);
+}
+
+bool SyncSocket::Close() {
+ if (handle_ == kInvalidHandle)
+ return false;
+
+ BOOL retval = CloseHandle(handle_);
+ handle_ = kInvalidHandle;
+ return retval ? true : false;
+}
+
+size_t SyncSocket::Send(const void* buffer, size_t length) {
+ DCHECK_LE(length, kMaxMessageLength);
+ size_t count = 0;
+ while (count < length) {
+ DWORD len;
+ DWORD chunk = GetNextChunkSize(count, length);
+ if (WriteFile(handle_, static_cast<const char*>(buffer) + count,
+ chunk, &len, NULL) == FALSE) {
+ return (0 < count) ? count : 0;
+ }
+ count += len;
+ }
+ return count;
+}
+
+size_t SyncSocket::Receive(void* buffer, size_t length) {
+ DCHECK_LE(length, kMaxMessageLength);
+ size_t count = 0;
+ while (count < length) {
+ DWORD len;
+ DWORD chunk = GetNextChunkSize(count, length);
+ if (ReadFile(handle_, static_cast<char*>(buffer) + count,
+ chunk, &len, NULL) == FALSE) {
+ return (0 < count) ? count : 0;
+ }
+ count += len;
+ }
+ return count;
+}
+
+size_t SyncSocket::Peek() {
+ DWORD available = 0;
+ PeekNamedPipe(handle_, NULL, 0, NULL, &available, NULL);
+ return available;
+}
+
+CancelableSyncSocket::CancelableSyncSocket()
+ : shutdown_event_(true, false), file_operation_(true, false) {
+}
+
+CancelableSyncSocket::CancelableSyncSocket(Handle handle)
+ : SyncSocket(handle), shutdown_event_(true, false),
+ file_operation_(true, false) {
+}
+
+bool CancelableSyncSocket::Shutdown() {
+ // This doesn't shut down the pipe immediately, but subsequent Receive or Send
+ // methods will fail straight away.
+ shutdown_event_.Signal();
+ return true;
+}
+
+bool CancelableSyncSocket::Close() {
+ bool ret = SyncSocket::Close();
+ shutdown_event_.Reset();
+ return ret;
+}
+
+size_t CancelableSyncSocket::Send(const void* buffer, size_t length) {
+ static const DWORD kWaitTimeOutInMs = 500;
+ return CancelableFileOperation(
+ &WriteFile, handle_, reinterpret_cast<const char*>(buffer),
+ length, &file_operation_, &shutdown_event_, this, kWaitTimeOutInMs);
+}
+
+size_t CancelableSyncSocket::Receive(void* buffer, size_t length) {
+ return CancelableFileOperation(&ReadFile, handle_,
+ reinterpret_cast<char*>(buffer), length, &file_operation_,
+ &shutdown_event_, this, INFINITE);
+}
+
+// static
+bool CancelableSyncSocket::CreatePair(CancelableSyncSocket* socket_a,
+ CancelableSyncSocket* socket_b) {
+ return CreatePairImpl(&socket_a->handle_, &socket_b->handle_, true);
+}
+
+
+} // namespace base
diff --git a/src/base/synchronization/cancellation_flag.cc b/src/base/synchronization/cancellation_flag.cc
new file mode 100644
index 0000000..ad3b551
--- /dev/null
+++ b/src/base/synchronization/cancellation_flag.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/synchronization/cancellation_flag.h"
+
+#include "base/logging.h"
+
+namespace base {
+
+void CancellationFlag::Set() {
+#if !defined(NDEBUG)
+ DCHECK_EQ(set_on_, PlatformThread::CurrentId());
+#endif
+ base::subtle::Release_Store(&flag_, 1);
+}
+
+bool CancellationFlag::IsSet() const {
+ return base::subtle::Acquire_Load(&flag_) != 0;
+}
+
+} // namespace base
diff --git a/src/base/synchronization/cancellation_flag.h b/src/base/synchronization/cancellation_flag.h
new file mode 100644
index 0000000..51a4def
--- /dev/null
+++ b/src/base/synchronization/cancellation_flag.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SYNCHRONIZATION_CANCELLATION_FLAG_H_
+#define BASE_SYNCHRONIZATION_CANCELLATION_FLAG_H_
+
+#include "base/base_export.h"
+#include "base/atomicops.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+// CancellationFlag allows one thread to cancel jobs executed on some worker
+// thread. Calling Set() from one thread and IsSet() from a number of threads
+// is thread-safe.
+//
+// This class IS NOT intended for synchronization between threads.
+class BASE_EXPORT CancellationFlag {
+ public:
+ CancellationFlag() : flag_(false) {
+#if !defined(NDEBUG)
+ set_on_ = PlatformThread::CurrentId();
+#endif
+ }
+ ~CancellationFlag() {}
+
+ // Set the flag. May only be called on the thread which owns the object.
+ void Set();
+ bool IsSet() const; // Returns true iff the flag was set.
+
+ private:
+ base::subtle::Atomic32 flag_;
+#if !defined(NDEBUG)
+ PlatformThreadId set_on_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(CancellationFlag);
+};
+
+} // namespace base
+
+#endif // BASE_SYNCHRONIZATION_CANCELLATION_FLAG_H_
diff --git a/src/base/synchronization/cancellation_flag_unittest.cc b/src/base/synchronization/cancellation_flag_unittest.cc
new file mode 100644
index 0000000..02a31b5
--- /dev/null
+++ b/src/base/synchronization/cancellation_flag_unittest.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Tests of CancellationFlag class.
+
+#include "base/synchronization/cancellation_flag.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/synchronization/spin_wait.h"
+#include "base/time.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace base {
+
+namespace {
+
+//------------------------------------------------------------------------------
+// Define our test class.
+//------------------------------------------------------------------------------
+
+void CancelHelper(CancellationFlag* flag) {
+#if GTEST_HAS_DEATH_TEST
+ ASSERT_DEBUG_DEATH(flag->Set(), "");
+#endif
+}
+
+TEST(CancellationFlagTest, SimpleSingleThreadedTest) {
+ CancellationFlag flag;
+ ASSERT_FALSE(flag.IsSet());
+ flag.Set();
+ ASSERT_TRUE(flag.IsSet());
+}
+
+TEST(CancellationFlagTest, DoubleSetTest) {
+ CancellationFlag flag;
+ ASSERT_FALSE(flag.IsSet());
+ flag.Set();
+ ASSERT_TRUE(flag.IsSet());
+ flag.Set();
+ ASSERT_TRUE(flag.IsSet());
+}
+
+TEST(CancellationFlagTest, SetOnDifferentThreadDeathTest) {
+ // Checks that Set() can't be called from any other thread.
+ // CancellationFlag should die on a DCHECK if Set() is called from
+ // other thread.
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ Thread t("CancellationFlagTest.SetOnDifferentThreadDeathTest");
+ ASSERT_TRUE(t.Start());
+ ASSERT_TRUE(t.message_loop());
+ ASSERT_TRUE(t.IsRunning());
+
+ CancellationFlag flag;
+ t.message_loop()->PostTask(FROM_HERE, base::Bind(&CancelHelper, &flag));
+}
+
+} // namespace
+
+} // namespace base
diff --git a/src/base/synchronization/condition_variable.h b/src/base/synchronization/condition_variable.h
new file mode 100644
index 0000000..fb42d2e
--- /dev/null
+++ b/src/base/synchronization/condition_variable.h
@@ -0,0 +1,129 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// 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_SYNCHRONIZATION_CONDITION_VARIABLE_H_
+#define BASE_SYNCHRONIZATION_CONDITION_VARIABLE_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_STARBOARD)
+#include "starboard/condition_variable.h"
+#include "starboard/mutex.h"
+#elif defined(__LB_SHELL__)
+#include "lb_mutex.h"
+#elif defined(OS_POSIX)
+#include <pthread.h>
+#endif
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/synchronization/lock.h"
+
+namespace base {
+
+class ConditionVarImpl;
+class TimeDelta;
+
+class BASE_EXPORT 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 TimeDelta& max_time);
+
+ // Broadcast() revives all waiting threads.
+ void Broadcast();
+ // Signal() revives one waiting thread.
+ void Signal();
+
+ private:
+
+#if defined(OS_WIN)
+ ConditionVarImpl* impl_;
+#elif defined(OS_STARBOARD)
+ SbConditionVariable condition_;
+ SbMutex* user_mutex_;
+#elif defined(__LB_SHELL__)
+ lb_shell_cond_t condition_;
+ lb_shell_mutex_t* user_mutex_;
+#elif defined(OS_POSIX)
+ pthread_cond_t condition_;
+ pthread_mutex_t* user_mutex_;
+#endif
+
+#if !defined(NDEBUG) && !defined(OS_WIN)
+ base::Lock* user_lock_; // Needed to adjust shadow lock state on wait.
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ConditionVariable);
+};
+
+} // namespace base
+
+#endif // BASE_SYNCHRONIZATION_CONDITION_VARIABLE_H_
diff --git a/src/base/synchronization/condition_variable_posix.cc b/src/base/synchronization/condition_variable_posix.cc
new file mode 100644
index 0000000..da2bb31
--- /dev/null
+++ b/src/base/synchronization/condition_variable_posix.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/synchronization/condition_variable.h"
+
+#include <errno.h>
+#include <sys/time.h>
+
+#include "base/logging.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time.h"
+
+namespace base {
+
+ConditionVariable::ConditionVariable(Lock* user_lock)
+ : user_mutex_(user_lock->lock_.os_lock())
+#if !defined(NDEBUG)
+ , user_lock_(user_lock)
+#endif
+{
+ int rv = pthread_cond_init(&condition_, NULL);
+ DCHECK_EQ(0, rv);
+}
+
+ConditionVariable::~ConditionVariable() {
+ int rv = pthread_cond_destroy(&condition_);
+ DCHECK_EQ(0, rv);
+}
+
+void ConditionVariable::Wait() {
+ base::ThreadRestrictions::AssertWaitAllowed();
+#if !defined(NDEBUG)
+ user_lock_->CheckHeldAndUnmark();
+#endif
+ int rv = pthread_cond_wait(&condition_, user_mutex_);
+ DCHECK_EQ(0, rv);
+#if !defined(NDEBUG)
+ user_lock_->CheckUnheldAndMark();
+#endif
+}
+
+void ConditionVariable::TimedWait(const TimeDelta& max_time) {
+ base::ThreadRestrictions::AssertWaitAllowed();
+ 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_GE(abstime.tv_sec, now.tv_sec); // Overflow paranoia
+
+#if !defined(NDEBUG)
+ user_lock_->CheckHeldAndUnmark();
+#endif
+ int rv = pthread_cond_timedwait(&condition_, user_mutex_, &abstime);
+ DCHECK(rv == 0 || rv == ETIMEDOUT);
+#if !defined(NDEBUG)
+ user_lock_->CheckUnheldAndMark();
+#endif
+}
+
+void ConditionVariable::Broadcast() {
+ int rv = pthread_cond_broadcast(&condition_);
+ DCHECK_EQ(0, rv);
+}
+
+void ConditionVariable::Signal() {
+ int rv = pthread_cond_signal(&condition_);
+ DCHECK_EQ(0, rv);
+}
+
+} // namespace base
diff --git a/src/base/synchronization/condition_variable_shell.cc b/src/base/synchronization/condition_variable_shell.cc
new file mode 100644
index 0000000..57c63af
--- /dev/null
+++ b/src/base/synchronization/condition_variable_shell.cc
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2012 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/synchronization/condition_variable.h"
+
+#include <errno.h>
+
+#include "base/logging.h"
+#include "base/synchronization/lock.h"
+#include "base/time.h"
+
+namespace base {
+
+ConditionVariable::ConditionVariable(Lock* user_lock)
+ : user_mutex_(user_lock->lock_.os_lock())
+#if !defined(NDEBUG)
+ , user_lock_(user_lock)
+#endif
+{
+ int rv = lb_shell_cond_init(&condition_, user_mutex_);
+ DCHECK_EQ(0, rv);
+}
+
+ConditionVariable::~ConditionVariable() {
+ int rv = lb_shell_cond_destroy(&condition_);
+ DCHECK_EQ(0, rv);
+}
+
+void ConditionVariable::Wait() {
+#if !defined(NDEBUG)
+ user_lock_->CheckHeldAndUnmark();
+#endif
+ int rv = lb_shell_cond_wait(&condition_, user_mutex_);
+ DCHECK_EQ(0, rv);
+#if !defined(NDEBUG)
+ user_lock_->CheckUnheldAndMark();
+#endif
+}
+
+void ConditionVariable::TimedWait(const TimeDelta& max_time) {
+ int64 usecs = max_time.InMicroseconds();
+
+#if !defined(NDEBUG)
+ user_lock_->CheckHeldAndUnmark();
+#endif
+ int rv = lb_shell_cond_timedwait(&condition_, user_mutex_, usecs);
+ DCHECK(rv == 0 || rv == ETIMEDOUT);
+#if !defined(NDEBUG)
+ user_lock_->CheckUnheldAndMark();
+#endif
+}
+
+void ConditionVariable::Broadcast() {
+ int rv = lb_shell_cond_broadcast(&condition_);
+ DCHECK_EQ(0, rv);
+}
+
+void ConditionVariable::Signal() {
+ int rv = lb_shell_cond_signal(&condition_);
+ DCHECK_EQ(0, rv);
+}
+
+} // namespace base
diff --git a/src/base/synchronization/condition_variable_starboard.cc b/src/base/synchronization/condition_variable_starboard.cc
new file mode 100644
index 0000000..816a2f4
--- /dev/null
+++ b/src/base/synchronization/condition_variable_starboard.cc
@@ -0,0 +1,76 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/synchronization/condition_variable.h"
+
+#include "base/logging.h"
+#include "base/synchronization/lock.h"
+#include "base/time.h"
+#include "starboard/condition_variable.h"
+#include "starboard/mutex.h"
+
+namespace base {
+
+ConditionVariable::ConditionVariable(Lock* user_lock)
+ : user_mutex_(user_lock->lock_.os_lock())
+#if !defined(NDEBUG)
+ , user_lock_(user_lock)
+#endif
+{
+ bool result = SbConditionVariableCreate(&condition_, user_mutex_);
+ DCHECK(result);
+}
+
+ConditionVariable::~ConditionVariable() {
+ bool result = SbConditionVariableDestroy(&condition_);
+ DCHECK(result);
+}
+
+void ConditionVariable::Wait() {
+#if !defined(NDEBUG)
+ user_lock_->CheckHeldAndUnmark();
+#endif
+ SbConditionVariableResult result =
+ SbConditionVariableWait(&condition_, user_mutex_);
+ DCHECK(SbConditionVariableIsSignaled(result));
+#if !defined(NDEBUG)
+ user_lock_->CheckUnheldAndMark();
+#endif
+}
+
+void ConditionVariable::TimedWait(const TimeDelta& max_time) {
+ SbTime duration = max_time.ToSbTime();
+
+#if !defined(NDEBUG)
+ user_lock_->CheckHeldAndUnmark();
+#endif
+ SbConditionVariableResult result =
+ SbConditionVariableWaitTimed(&condition_, user_mutex_, duration);
+ DCHECK_NE(kSbConditionVariableFailed, result);
+#if !defined(NDEBUG)
+ user_lock_->CheckUnheldAndMark();
+#endif
+}
+
+void ConditionVariable::Broadcast() {
+ bool result = SbConditionVariableBroadcast(&condition_);
+ DCHECK(result);
+}
+
+void ConditionVariable::Signal() {
+ bool result = SbConditionVariableSignal(&condition_);
+ DCHECK(result);
+}
+
+} // namespace base
diff --git a/src/base/synchronization/condition_variable_unittest.cc b/src/base/synchronization/condition_variable_unittest.cc
new file mode 100644
index 0000000..bc8201f
--- /dev/null
+++ b/src/base/synchronization/condition_variable_unittest.cc
@@ -0,0 +1,727 @@
+// Copyright (c) 2012 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/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/spin_wait.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread_collision_warner.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+#if defined(OS_STARBOARD)
+#include "starboard/configuration.h"
+#endif
+
+namespace base {
+
+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);
+ virtual ~WorkQueue();
+
+ // PlatformThread::Delegate interface.
+ virtual void ThreadMain() OVERRIDE;
+
+ //----------------------------------------------------------------------------
+ // 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);
+
+ // The following must be called without locking, and will spin wait until the
+ // threads are all in a wait state.
+ void SpinUntilAllThreadsAreWaiting();
+ void SpinUntilTaskCountLessThan(int task_count);
+
+ // 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_;
+ int waiting_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();
+}
+
+
+// Suddenly got flaky on Win, see http://crbug.com/10607 (starting at
+// comment #15)
+#if defined(OS_WIN)
+#define MAYBE_MultiThreadConsumerTest DISABLED_MultiThreadConsumerTest
+#else
+#define MAYBE_MultiThreadConsumerTest MultiThreadConsumerTest
+#endif
+// Test serial task servicing, as well as two parallel task servicing methods.
+TEST_F(ConditionVariableTest, MAYBE_MultiThreadConsumerTest) {
+ const int kThreadCount = 10;
+ WorkQueue queue(kThreadCount); // Start the threads.
+
+ const int kTaskCount = 10; // Number of tasks in each mini-test here.
+
+ Time start_time; // Used to time task processing.
+
+ {
+ base::AutoLock auto_lock(*queue.lock());
+ while (!queue.EveryIdWasAllocated())
+ queue.all_threads_have_ids()->Wait();
+ }
+
+ // If threads aren't in a wait state, they may start to gobble up tasks in
+ // parallel, short-circuiting (breaking) this test.
+ queue.SpinUntilAllThreadsAreWaiting();
+
+ {
+ // Since we have no tasks yet, all threads should be waiting by now.
+ base::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 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 = Time::Now();
+ }
+
+ queue.work_is_available()->Signal(); // But each worker can signal another.
+ // Wait till we at least start to handle tasks (and we're not all waiting).
+ queue.SpinUntilTaskCountLessThan(kTaskCount);
+ // Wait to allow the all workers to get done.
+ queue.SpinUntilAllThreadsAreWaiting();
+
+ {
+ // Wait until all work tasks have at least been assigned.
+ base::AutoLock auto_lock(*queue.lock());
+ while (queue.task_count())
+ queue.no_more_tasks()->Wait();
+
+ // To avoid racy assumptions, we'll just assert that at least 2 threads
+ // did work. We know that the first worker should have gone to sleep, and
+ // hence a second worker should have gotten an assignment.
+ 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 till we at least start to handle tasks (and we're not all waiting).
+ queue.SpinUntilTaskCountLessThan(3);
+ // Wait to allow the 3 workers to get done.
+ queue.SpinUntilAllThreadsAreWaiting();
+
+ {
+ base::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(); // Signal all threads.
+ // Wait till we at least start to handle tasks (and we're not all waiting).
+ queue.SpinUntilTaskCountLessThan(3);
+ // Wait to allow the 3 workers to get done.
+ queue.SpinUntilAllThreadsAreWaiting();
+
+ {
+ base::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); // 2 tasks per thread.
+ queue.SetWorkTime(kThirtyMs);
+ queue.SetAllowHelp(true);
+ }
+ queue.work_is_available()->Signal(); // But each worker can signal another.
+ // Wait till we at least start to handle tasks (and we're not all waiting).
+ queue.SpinUntilTaskCountLessThan(20);
+ // Wait to allow the 10 workers to get done.
+ queue.SpinUntilAllThreadsAreWaiting(); // Should take about 60 ms.
+
+ {
+ base::AutoLock auto_lock(*queue.lock());
+ EXPECT_EQ(10, queue.GetNumThreadsTakingAssignments());
+ EXPECT_EQ(10, queue.GetNumThreadsCompletingTasks());
+ EXPECT_EQ(0, queue.task_count());
+ EXPECT_EQ(20, queue.GetNumberOfCompletedTasks());
+
+ // Same as last test, but with Broadcast().
+ queue.ResetHistory();
+ queue.SetTaskCount(20); // 2 tasks per thread.
+ queue.SetWorkTime(kThirtyMs);
+ queue.SetAllowHelp(true);
+ }
+ queue.work_is_available()->Broadcast();
+ // Wait till we at least start to handle tasks (and we're not all waiting).
+ queue.SpinUntilTaskCountLessThan(20);
+ // Wait to allow the 10 workers to get done.
+ queue.SpinUntilAllThreadsAreWaiting(); // Should take about 60 ms.
+
+ {
+ base::AutoLock auto_lock(*queue.lock());
+ EXPECT_EQ(10, queue.GetNumThreadsTakingAssignments());
+ EXPECT_EQ(10, queue.GetNumThreadsCompletingTasks());
+ EXPECT_EQ(0, queue.task_count());
+ 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));
+}
+
+TEST_F(ConditionVariableTest, LargeFastTaskTest) {
+#if defined(__LB_SHELL__) && !defined(__LB_LINUX__)
+ // Game consoles don't support that many threads. We have a max threads
+ // macro in pthread.h. Leave a few to system and test with the rest.
+ const int kThreadCount = SHELL_MAX_THREADS - 8;
+#elif defined(OS_STARBOARD)
+ const int kThreadCount = SB_MAX_THREADS - 8;
+#else
+ const int kThreadCount = 200;
+#endif
+ WorkQueue queue(kThreadCount); // Start the threads.
+
+ Lock private_lock; // Used locally for master to wait.
+ base::AutoLock private_held_lock(private_lock);
+ ConditionVariable private_cv(&private_lock);
+
+ {
+ base::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.
+ queue.SpinUntilAllThreadsAreWaiting();
+
+ {
+ // Since we have no tasks, all threads should be waiting by now.
+ base::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.
+ {
+ base::AutoLock auto_lock(*queue.lock());
+ while (queue.task_count() != 0)
+ queue.no_more_tasks()->Wait();
+ }
+
+ // Wait till the last of the tasks complete.
+ queue.SpinUntilAllThreadsAreWaiting();
+
+ {
+ // With Broadcast(), every thread should have participated.
+ // but with racing.. they may not all have done equal numbers of tasks.
+ base::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
+ {
+ base::AutoLock auto_lock(*queue.lock());
+ while (queue.task_count() != 0)
+ queue.no_more_tasks()->Wait();
+ }
+
+ // Wait till the last of the tasks complete.
+ queue.SpinUntilAllThreadsAreWaiting();
+
+ {
+ // With Signal(), every thread should have participated.
+ // but with racing.. they may not all have done four tasks.
+ base::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));
+}
+
+//------------------------------------------------------------------------------
+// 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),
+ waiting_thread_count_(0),
+ 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() {
+ {
+ base::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]);
+ }
+ EXPECT_EQ(0, waiting_thread_count_);
+}
+
+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;
+ base::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;
+}
+
+void WorkQueue::SpinUntilAllThreadsAreWaiting() {
+ while (true) {
+ {
+ base::AutoLock auto_lock(lock_);
+ if (waiting_thread_count_ == thread_count_)
+ break;
+ }
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(30));
+ }
+}
+
+void WorkQueue::SpinUntilTaskCountLessThan(int task_count) {
+ while (true) {
+ {
+ base::AutoLock auto_lock(lock_);
+ if (task_count_ < task_count)
+ break;
+ }
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(30));
+ }
+}
+
+
+//------------------------------------------------------------------------------
+// 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;
+ {
+ base::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;
+ {
+ base::AutoLock auto_lock(lock_);
+ while (0 == task_count() && !shutdown()) {
+ ++waiting_thread_count_;
+ work_is_available()->Wait();
+ --waiting_thread_count_;
+ }
+ 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.
+ base::AutoLock auto_lock(private_lock);
+ ConditionVariable private_cv(&private_lock);
+ private_cv.TimedWait(work_time); // Unsynchronized waiting.
+ }
+
+ {
+ base::AutoLock auto_lock(lock_);
+ // Send notification that we completed our "work."
+ WorkIsCompleted(thread_id);
+ }
+ }
+}
+
+} // namespace
+
+} // namespace base
diff --git a/src/base/synchronization/condition_variable_win.cc b/src/base/synchronization/condition_variable_win.cc
new file mode 100644
index 0000000..f32778f
--- /dev/null
+++ b/src/base/synchronization/condition_variable_win.cc
@@ -0,0 +1,669 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/synchronization/condition_variable.h"
+
+#include <windows.h>
+#include <stack>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time.h"
+
+namespace {
+// We can't use the linker supported delay-load for kernel32 so all this
+// cruft here is to manually late-bind the needed functions.
+typedef void (WINAPI *InitializeConditionVariableFn)(PCONDITION_VARIABLE);
+typedef BOOL (WINAPI *SleepConditionVariableCSFn)(PCONDITION_VARIABLE,
+ PCRITICAL_SECTION, DWORD);
+typedef void (WINAPI *WakeConditionVariableFn)(PCONDITION_VARIABLE);
+typedef void (WINAPI *WakeAllConditionVariableFn)(PCONDITION_VARIABLE);
+
+InitializeConditionVariableFn initialize_condition_variable_fn;
+SleepConditionVariableCSFn sleep_condition_variable_fn;
+WakeConditionVariableFn wake_condition_variable_fn;
+WakeAllConditionVariableFn wake_all_condition_variable_fn;
+
+bool BindVistaCondVarFunctions() {
+ HMODULE kernel32 = GetModuleHandleA("kernel32.dll");
+ initialize_condition_variable_fn =
+ reinterpret_cast<InitializeConditionVariableFn>(
+ GetProcAddress(kernel32, "InitializeConditionVariable"));
+ if (!initialize_condition_variable_fn)
+ return false;
+ sleep_condition_variable_fn =
+ reinterpret_cast<SleepConditionVariableCSFn>(
+ GetProcAddress(kernel32, "SleepConditionVariableCS"));
+ if (!sleep_condition_variable_fn)
+ return false;
+ wake_condition_variable_fn =
+ reinterpret_cast<WakeConditionVariableFn>(
+ GetProcAddress(kernel32, "WakeConditionVariable"));
+ if (!wake_condition_variable_fn)
+ return false;
+ wake_all_condition_variable_fn =
+ reinterpret_cast<WakeAllConditionVariableFn>(
+ GetProcAddress(kernel32, "WakeAllConditionVariable"));
+ if (!wake_all_condition_variable_fn)
+ return false;
+ return true;
+}
+
+} // namespace.
+
+namespace base {
+// Abstract base class of the pimpl idiom.
+class ConditionVarImpl {
+ public:
+ virtual ~ConditionVarImpl() {};
+ virtual void Wait() = 0;
+ virtual void TimedWait(const TimeDelta& max_time) = 0;
+ virtual void Broadcast() = 0;
+ virtual void Signal() = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Windows Vista and Win7 implementation.
+///////////////////////////////////////////////////////////////////////////////
+
+class WinVistaCondVar: public ConditionVarImpl {
+ public:
+ WinVistaCondVar(Lock* user_lock);
+ ~WinVistaCondVar() {};
+ // Overridden from ConditionVarImpl.
+ virtual void Wait() OVERRIDE;
+ virtual void TimedWait(const TimeDelta& max_time) OVERRIDE;
+ virtual void Broadcast() OVERRIDE;
+ virtual void Signal() OVERRIDE;
+
+ private:
+ base::Lock& user_lock_;
+ CONDITION_VARIABLE cv_;
+};
+
+WinVistaCondVar::WinVistaCondVar(Lock* user_lock)
+ : user_lock_(*user_lock) {
+ initialize_condition_variable_fn(&cv_);
+ DCHECK(user_lock);
+}
+
+void WinVistaCondVar::Wait() {
+ TimedWait(TimeDelta::FromMilliseconds(INFINITE));
+}
+
+void WinVistaCondVar::TimedWait(const TimeDelta& max_time) {
+ base::ThreadRestrictions::AssertWaitAllowed();
+ DWORD timeout = static_cast<DWORD>(max_time.InMilliseconds());
+ CRITICAL_SECTION* cs = user_lock_.lock_.os_lock();
+
+#if !defined(NDEBUG)
+ user_lock_.CheckHeldAndUnmark();
+#endif
+
+ if (FALSE == sleep_condition_variable_fn(&cv_, cs, timeout)) {
+ DCHECK(GetLastError() != WAIT_TIMEOUT);
+ }
+
+#if !defined(NDEBUG)
+ user_lock_.CheckUnheldAndMark();
+#endif
+}
+
+void WinVistaCondVar::Broadcast() {
+ wake_all_condition_variable_fn(&cv_);
+}
+
+void WinVistaCondVar::Signal() {
+ wake_condition_variable_fn(&cv_);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Windows XP implementation.
+///////////////////////////////////////////////////////////////////////////////
+
+class WinXPCondVar : public ConditionVarImpl {
+ public:
+ WinXPCondVar(Lock* user_lock);
+ ~WinXPCondVar();
+ // Overridden from ConditionVarImpl.
+ virtual void Wait() OVERRIDE;
+ virtual void TimedWait(const TimeDelta& max_time) OVERRIDE;
+ virtual void Broadcast() OVERRIDE;
+ virtual void Signal() OVERRIDE;
+
+ // 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.
+ base::Lock internal_lock_;
+
+ // Lock that is acquired before calling Wait().
+ base::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_;
+};
+
+WinXPCondVar::WinXPCondVar(Lock* user_lock)
+ : user_lock_(*user_lock),
+ run_state_(RUNNING),
+ allocation_counter_(0),
+ recycling_list_size_(0) {
+ DCHECK(user_lock);
+}
+
+WinXPCondVar::~WinXPCondVar() {
+ 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 WinXPCondVar::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 WinXPCondVar::TimedWait(const TimeDelta& max_time) {
+ base::ThreadRestrictions::AssertWaitAllowed();
+ 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 WinXPCondVar::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 WinXPCondVar::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.
+WinXPCondVar::Event* WinXPCondVar::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_++;
+ DCHECK(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 WinXPCondVar::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.
+
+WinXPCondVar::Event::Event() : handle_(0) {
+ next_ = prev_ = this; // Self referencing circular.
+}
+
+WinXPCondVar::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 WinXPCondVar::Event::InitListElement() {
+ DCHECK(!handle_);
+ handle_ = CreateEvent(NULL, false, false, NULL);
+ DCHECK(handle_);
+}
+
+// Methods for use on lists.
+bool WinXPCondVar::Event::IsEmpty() const {
+ DCHECK(ValidateAsList());
+ return IsSingleton();
+}
+
+void WinXPCondVar::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));
+}
+
+WinXPCondVar::Event* WinXPCondVar::Event::PopFront() {
+ DCHECK(ValidateAsList());
+ DCHECK(!IsSingleton());
+ return next_->Extract();
+}
+
+WinXPCondVar::Event* WinXPCondVar::Event::PopBack() {
+ DCHECK(ValidateAsList());
+ DCHECK(!IsSingleton());
+ return prev_->Extract();
+}
+
+// Methods for use on list elements.
+// Accessor method.
+HANDLE WinXPCondVar::Event::handle() const {
+ DCHECK(ValidateAsItem());
+ return handle_;
+}
+
+// Pull an element from a list (if it's in one).
+WinXPCondVar::Event* WinXPCondVar::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 WinXPCondVar::Event::IsSingleton() const {
+ DCHECK(ValidateLinks());
+ return next_ == this;
+}
+
+// Provide pre/post conditions to validate correct manipulations.
+bool WinXPCondVar::Event::ValidateAsDistinct(Event* other) const {
+ return ValidateLinks() && other->ValidateLinks() && (this != other);
+}
+
+bool WinXPCondVar::Event::ValidateAsItem() const {
+ return (0 != handle_) && ValidateLinks();
+}
+
+bool WinXPCondVar::Event::ValidateAsList() const {
+ return (0 == handle_) && ValidateLinks();
+}
+
+bool WinXPCondVar::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 WinXPCondVar 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.
+
+*/
+
+ConditionVariable::ConditionVariable(Lock* user_lock)
+ : impl_(NULL) {
+ static bool use_vista_native_cv = BindVistaCondVarFunctions();
+ if (use_vista_native_cv)
+ impl_= new WinVistaCondVar(user_lock);
+ else
+ impl_ = new WinXPCondVar(user_lock);
+}
+
+ConditionVariable::~ConditionVariable() {
+ delete impl_;
+}
+
+void ConditionVariable::Wait() {
+ impl_->Wait();
+}
+
+void ConditionVariable::TimedWait(const TimeDelta& max_time) {
+ impl_->TimedWait(max_time);
+}
+
+void ConditionVariable::Broadcast() {
+ impl_->Broadcast();
+}
+
+void ConditionVariable::Signal() {
+ impl_->Signal();
+}
+
+} // namespace base
diff --git a/src/base/synchronization/lock.cc b/src/base/synchronization/lock.cc
new file mode 100644
index 0000000..6445ce8
--- /dev/null
+++ b/src/base/synchronization/lock.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is used for debugging assertion support. The Lock class
+// is functionally a wrapper around the LockImpl class, so the only
+// real intelligence in the class is in the debugging logic.
+
+#if !defined(NDEBUG)
+
+#include "base/synchronization/lock.h"
+#include "base/logging.h"
+
+namespace base {
+
+Lock::Lock() : lock_() {
+ owned_by_thread_ = false;
+ owning_thread_id_ = static_cast<PlatformThreadId>(0);
+}
+
+void Lock::AssertAcquired() const {
+ DCHECK(owned_by_thread_);
+ DCHECK_EQ(owning_thread_id_, PlatformThread::CurrentId());
+}
+
+void Lock::CheckHeldAndUnmark() {
+ DCHECK(owned_by_thread_);
+ DCHECK_EQ(owning_thread_id_, PlatformThread::CurrentId());
+ owned_by_thread_ = false;
+ owning_thread_id_ = static_cast<PlatformThreadId>(0);
+}
+
+void Lock::CheckUnheldAndMark() {
+ DCHECK(!owned_by_thread_);
+ owned_by_thread_ = true;
+ owning_thread_id_ = PlatformThread::CurrentId();
+}
+
+} // namespace base
+
+#endif // NDEBUG
diff --git a/src/base/synchronization/lock.h b/src/base/synchronization/lock.h
new file mode 100644
index 0000000..dce7fdb
--- /dev/null
+++ b/src/base/synchronization/lock.h
@@ -0,0 +1,134 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SYNCHRONIZATION_LOCK_H_
+#define BASE_SYNCHRONIZATION_LOCK_H_
+
+#include "base/base_export.h"
+#include "base/synchronization/lock_impl.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+// A convenient wrapper for an OS specific critical section. The only real
+// intelligence in this class is in debug mode for the support for the
+// AssertAcquired() method.
+class BASE_EXPORT Lock {
+ public:
+#if defined(NDEBUG) // Optimized wrapper implementation
+ Lock() : lock_() {}
+ ~Lock() {}
+ void Acquire() { lock_.Lock(); }
+ void Release() { lock_.Unlock(); }
+
+ // If the lock is not held, take it and return true. If the lock is already
+ // held by another thread, immediately return false. This must not be called
+ // by a thread already holding the lock (what happens is undefined and an
+ // assertion may fail).
+ bool Try() { return lock_.Try(); }
+
+ // Null implementation if not debug.
+ void AssertAcquired() const {}
+#else
+ Lock();
+ ~Lock() {}
+
+ // NOTE: Although windows critical sections support recursive locks, we do not
+ // allow this, and we will commonly fire a DCHECK() if a thread attempts to
+ // acquire the lock a second time (while already holding it).
+ void Acquire() {
+ lock_.Lock();
+ CheckUnheldAndMark();
+ }
+ void Release() {
+ CheckHeldAndUnmark();
+ lock_.Unlock();
+ }
+
+ bool Try() {
+ bool rv = lock_.Try();
+ if (rv) {
+ CheckUnheldAndMark();
+ }
+ return rv;
+ }
+
+ void AssertAcquired() const;
+#endif // NDEBUG
+
+#if defined(OS_POSIX) || defined(OS_STARBOARD)
+ // The posix implementation of ConditionVariable needs to be able
+ // to see our lock and tweak our debugging counters, as it releases
+ // and acquires locks inside of pthread_cond_{timed,}wait.
+ friend class ConditionVariable;
+#elif defined(OS_WIN)
+ // The Windows Vista implementation of ConditionVariable needs the
+ // native handle of the critical section.
+ friend class WinVistaCondVar;
+#endif
+
+ private:
+#if !defined(NDEBUG)
+ // Members and routines taking care of locks assertions.
+ // Note that this checks for recursive locks and allows them
+ // if the variable is set. This is allowed by the underlying implementation
+ // on windows but not on Posix, so we're doing unneeded checks on Posix.
+ // It's worth it to share the code.
+ void CheckHeldAndUnmark();
+ void CheckUnheldAndMark();
+
+ // All private data is implicitly protected by lock_.
+ // Be VERY careful to only access members under that lock.
+
+ // Determines validity of owning_thread_id_. Needed as we don't have
+ // a null owning_thread_id_ value.
+ bool owned_by_thread_;
+ base::PlatformThreadId owning_thread_id_;
+#endif // NDEBUG
+
+ // Platform specific underlying lock implementation.
+ internal::LockImpl lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(Lock);
+};
+
+// A helper class that acquires the given Lock while the AutoLock is in scope.
+class AutoLock {
+ public:
+ explicit AutoLock(Lock& lock) : lock_(lock) {
+ lock_.Acquire();
+ }
+
+ ~AutoLock() {
+ lock_.AssertAcquired();
+ lock_.Release();
+ }
+
+ private:
+ Lock& lock_;
+ DISALLOW_COPY_AND_ASSIGN(AutoLock);
+};
+
+// AutoUnlock is a helper that will Release() the |lock| argument in the
+// constructor, and re-Acquire() it in the destructor.
+class AutoUnlock {
+ public:
+ explicit AutoUnlock(Lock& lock) : lock_(lock) {
+ // We require our caller to have the lock.
+ lock_.AssertAcquired();
+ lock_.Release();
+ }
+
+ ~AutoUnlock() {
+ lock_.Acquire();
+ }
+
+ private:
+ Lock& lock_;
+ DISALLOW_COPY_AND_ASSIGN(AutoUnlock);
+};
+
+} // namespace base
+
+#endif // BASE_SYNCHRONIZATION_LOCK_H_
diff --git a/src/base/synchronization/lock_impl.h b/src/base/synchronization/lock_impl.h
new file mode 100644
index 0000000..308154e
--- /dev/null
+++ b/src/base/synchronization/lock_impl.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SYNCHRONIZATION_LOCK_IMPL_H_
+#define BASE_SYNCHRONIZATION_LOCK_IMPL_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_STARBOARD)
+#include "starboard/mutex.h"
+#elif defined(__LB_SHELL__)
+#include "lb_mutex.h"
+#elif defined(OS_POSIX)
+#include <pthread.h>
+#endif
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+namespace internal {
+
+// This class implements the underlying platform-specific spin-lock mechanism
+// used for the Lock class. Most users should not use LockImpl directly, but
+// should instead use Lock.
+class BASE_EXPORT LockImpl {
+ public:
+#if defined(OS_WIN)
+ typedef CRITICAL_SECTION OSLockType;
+#elif defined(OS_STARBOARD)
+ typedef SbMutex OSLockType;
+#elif defined(__LB_SHELL__)
+ typedef lb_shell_mutex_t OSLockType;
+#elif defined(OS_POSIX)
+ typedef pthread_mutex_t OSLockType;
+#endif
+
+ LockImpl();
+ ~LockImpl();
+
+ // If the lock is not held, take it and return true. If the lock is already
+ // held by something else, immediately return false.
+ bool Try();
+
+ // Take the lock, blocking until it is available if necessary.
+ void Lock();
+
+ // Release the lock. This must only be called by the lock's holder: after
+ // a successful call to Try, or a call to Lock.
+ void Unlock();
+
+ // Return the native underlying lock.
+ // TODO(awalker): refactor lock and condition variables so that this is
+ // unnecessary.
+ OSLockType* os_lock() { return &os_lock_; }
+
+ private:
+ OSLockType os_lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(LockImpl);
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_SYNCHRONIZATION_LOCK_IMPL_H_
diff --git a/src/base/synchronization/lock_impl_posix.cc b/src/base/synchronization/lock_impl_posix.cc
new file mode 100644
index 0000000..f638fcd
--- /dev/null
+++ b/src/base/synchronization/lock_impl_posix.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/synchronization/lock_impl.h"
+
+#include <errno.h>
+
+#include "base/logging.h"
+
+namespace base {
+namespace internal {
+
+LockImpl::LockImpl() {
+#ifndef NDEBUG
+ // In debug, setup attributes for lock error checking.
+ pthread_mutexattr_t mta;
+ int rv = pthread_mutexattr_init(&mta);
+ DCHECK_EQ(rv, 0);
+ rv = pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_ERRORCHECK);
+ DCHECK_EQ(rv, 0);
+ rv = pthread_mutex_init(&os_lock_, &mta);
+ DCHECK_EQ(rv, 0);
+ rv = pthread_mutexattr_destroy(&mta);
+ DCHECK_EQ(rv, 0);
+#else
+ // In release, go with the default lock attributes.
+ pthread_mutex_init(&os_lock_, NULL);
+#endif
+}
+
+LockImpl::~LockImpl() {
+ int rv = pthread_mutex_destroy(&os_lock_);
+ DCHECK_EQ(rv, 0);
+}
+
+bool LockImpl::Try() {
+ int rv = pthread_mutex_trylock(&os_lock_);
+ DCHECK(rv == 0 || rv == EBUSY);
+ return rv == 0;
+}
+
+void LockImpl::Lock() {
+ int rv = pthread_mutex_lock(&os_lock_);
+ DCHECK_EQ(rv, 0);
+}
+
+void LockImpl::Unlock() {
+ int rv = pthread_mutex_unlock(&os_lock_);
+ DCHECK_EQ(rv, 0);
+}
+
+} // namespace internal
+} // namespace base
diff --git a/src/base/synchronization/lock_impl_shell.cc b/src/base/synchronization/lock_impl_shell.cc
new file mode 100644
index 0000000..7cf6483
--- /dev/null
+++ b/src/base/synchronization/lock_impl_shell.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2012 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/synchronization/lock_impl.h"
+
+#include <errno.h>
+
+#include "base/logging.h"
+
+namespace base {
+namespace internal {
+
+LockImpl::LockImpl() {
+ int rv = lb_shell_mutex_init(&os_lock_);
+ DCHECK_EQ(rv, 0);
+}
+
+LockImpl::~LockImpl() {
+ int rv = lb_shell_mutex_destroy(&os_lock_);
+ DCHECK_EQ(rv, 0);
+}
+
+bool LockImpl::Try() {
+ int rv = lb_shell_mutex_trylock(&os_lock_);
+ DCHECK(rv == 0 || rv == EBUSY);
+ return rv == 0;
+}
+
+void LockImpl::Lock() {
+ int rv = lb_shell_mutex_lock(&os_lock_);
+ DCHECK_EQ(rv, 0);
+}
+
+void LockImpl::Unlock() {
+ int rv = lb_shell_mutex_unlock(&os_lock_);
+ DCHECK_EQ(rv, 0);
+}
+
+} // namespace internal
+} // namespace base
diff --git a/src/base/synchronization/lock_impl_starboard.cc b/src/base/synchronization/lock_impl_starboard.cc
new file mode 100644
index 0000000..296981a
--- /dev/null
+++ b/src/base/synchronization/lock_impl_starboard.cc
@@ -0,0 +1,50 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/synchronization/lock_impl.h"
+
+#include "base/logging.h"
+#include "starboard/mutex.h"
+
+namespace base {
+namespace internal {
+
+LockImpl::LockImpl() {
+ bool result = SbMutexCreate(&os_lock_);
+ DCHECK(result);
+}
+
+LockImpl::~LockImpl() {
+ bool result = SbMutexDestroy(&os_lock_);
+ DCHECK(result);
+}
+
+bool LockImpl::Try() {
+ SbMutexResult result = SbMutexAcquireTry(&os_lock_);
+ DCHECK_NE(kSbMutexDestroyed, result);
+ return SbMutexIsSuccess(result);
+}
+
+void LockImpl::Lock() {
+ SbMutexResult result = SbMutexAcquire(&os_lock_);
+ DCHECK_NE(kSbMutexDestroyed, result);
+}
+
+void LockImpl::Unlock() {
+ bool result = SbMutexRelease(&os_lock_);
+ DCHECK(result);
+}
+
+} // namespace internal
+} // namespace base
diff --git a/src/base/synchronization/lock_impl_win.cc b/src/base/synchronization/lock_impl_win.cc
new file mode 100644
index 0000000..bb8a23d
--- /dev/null
+++ b/src/base/synchronization/lock_impl_win.cc
@@ -0,0 +1,36 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/synchronization/lock_impl.h"
+
+namespace base {
+namespace internal {
+
+LockImpl::LockImpl() {
+ // The second parameter is the spin count, for short-held locks it avoid the
+ // contending thread from going to sleep which helps performance greatly.
+ ::InitializeCriticalSectionAndSpinCount(&os_lock_, 2000);
+}
+
+LockImpl::~LockImpl() {
+ ::DeleteCriticalSection(&os_lock_);
+}
+
+bool LockImpl::Try() {
+ if (::TryEnterCriticalSection(&os_lock_) != FALSE) {
+ return true;
+ }
+ return false;
+}
+
+void LockImpl::Lock() {
+ ::EnterCriticalSection(&os_lock_);
+}
+
+void LockImpl::Unlock() {
+ ::LeaveCriticalSection(&os_lock_);
+}
+
+} // namespace internal
+} // namespace base
diff --git a/src/base/synchronization/lock_unittest.cc b/src/base/synchronization/lock_unittest.cc
new file mode 100644
index 0000000..a048f85
--- /dev/null
+++ b/src/base/synchronization/lock_unittest.cc
@@ -0,0 +1,216 @@
+// Copyright (c) 2012 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/synchronization/lock.h"
+
+#include <stdlib.h>
+
+#include "base/compiler_specific.h"
+#include "base/threading/platform_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+// Basic test to make sure that Acquire()/Release()/Try() don't crash ----------
+
+class BasicLockTestThread : public PlatformThread::Delegate {
+ public:
+ BasicLockTestThread(Lock* lock) : lock_(lock), acquired_(0) {}
+
+ virtual void ThreadMain() OVERRIDE {
+ for (int i = 0; i < 10; i++) {
+ lock_->Acquire();
+ acquired_++;
+ lock_->Release();
+ }
+ for (int i = 0; i < 10; i++) {
+ lock_->Acquire();
+ acquired_++;
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 20));
+ lock_->Release();
+ }
+ for (int i = 0; i < 10; i++) {
+ if (lock_->Try()) {
+ acquired_++;
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 20));
+ lock_->Release();
+ }
+ }
+ }
+
+ int acquired() const { return acquired_; }
+
+ private:
+ Lock* lock_;
+ int acquired_;
+
+ DISALLOW_COPY_AND_ASSIGN(BasicLockTestThread);
+};
+
+TEST(LockTest, Basic) {
+ Lock lock;
+ BasicLockTestThread thread(&lock);
+ PlatformThreadHandle handle = kNullThreadHandle;
+
+ ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
+
+ int acquired = 0;
+ for (int i = 0; i < 5; i++) {
+ lock.Acquire();
+ acquired++;
+ lock.Release();
+ }
+ for (int i = 0; i < 10; i++) {
+ lock.Acquire();
+ acquired++;
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 20));
+ lock.Release();
+ }
+ for (int i = 0; i < 10; i++) {
+ if (lock.Try()) {
+ acquired++;
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 20));
+ lock.Release();
+ }
+ }
+ for (int i = 0; i < 5; i++) {
+ lock.Acquire();
+ acquired++;
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 20));
+ lock.Release();
+ }
+
+ PlatformThread::Join(handle);
+
+ EXPECT_GE(acquired, 20);
+ EXPECT_GE(thread.acquired(), 20);
+}
+
+// Test that Try() works as expected -------------------------------------------
+
+class TryLockTestThread : public PlatformThread::Delegate {
+ public:
+ TryLockTestThread(Lock* lock) : lock_(lock), got_lock_(false) {}
+
+ virtual void ThreadMain() OVERRIDE {
+ got_lock_ = lock_->Try();
+ if (got_lock_)
+ lock_->Release();
+ }
+
+ bool got_lock() const { return got_lock_; }
+
+ private:
+ Lock* lock_;
+ bool got_lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(TryLockTestThread);
+};
+
+TEST(LockTest, TryLock) {
+ Lock lock;
+
+ ASSERT_TRUE(lock.Try());
+ // We now have the lock....
+
+ // This thread will not be able to get the lock.
+ {
+ TryLockTestThread thread(&lock);
+ PlatformThreadHandle handle = kNullThreadHandle;
+
+ ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
+
+ PlatformThread::Join(handle);
+
+ ASSERT_FALSE(thread.got_lock());
+ }
+
+ lock.Release();
+
+ // This thread will....
+ {
+ TryLockTestThread thread(&lock);
+ PlatformThreadHandle handle = kNullThreadHandle;
+
+ ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
+
+ PlatformThread::Join(handle);
+
+ ASSERT_TRUE(thread.got_lock());
+ // But it released it....
+ ASSERT_TRUE(lock.Try());
+ }
+
+ lock.Release();
+}
+
+// Tests that locks actually exclude -------------------------------------------
+
+class MutexLockTestThread : public PlatformThread::Delegate {
+ public:
+ MutexLockTestThread(Lock* lock, int* value) : lock_(lock), value_(value) {}
+
+ // Static helper which can also be called from the main thread.
+ static void DoStuff(Lock* lock, int* value) {
+ for (int i = 0; i < 40; i++) {
+ lock->Acquire();
+ int v = *value;
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(rand() % 10));
+ *value = v + 1;
+ lock->Release();
+ }
+ }
+
+ virtual void ThreadMain() OVERRIDE {
+ DoStuff(lock_, value_);
+ }
+
+ private:
+ Lock* lock_;
+ int* value_;
+
+ DISALLOW_COPY_AND_ASSIGN(MutexLockTestThread);
+};
+
+TEST(LockTest, MutexTwoThreads) {
+ Lock lock;
+ int value = 0;
+
+ MutexLockTestThread thread(&lock, &value);
+ PlatformThreadHandle handle = kNullThreadHandle;
+
+ ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
+
+ MutexLockTestThread::DoStuff(&lock, &value);
+
+ PlatformThread::Join(handle);
+
+ EXPECT_EQ(2 * 40, value);
+}
+
+TEST(LockTest, MutexFourThreads) {
+ Lock lock;
+ int value = 0;
+
+ MutexLockTestThread thread1(&lock, &value);
+ MutexLockTestThread thread2(&lock, &value);
+ MutexLockTestThread thread3(&lock, &value);
+ PlatformThreadHandle handle1 = kNullThreadHandle;
+ PlatformThreadHandle handle2 = kNullThreadHandle;
+ PlatformThreadHandle handle3 = kNullThreadHandle;
+
+ ASSERT_TRUE(PlatformThread::Create(0, &thread1, &handle1));
+ ASSERT_TRUE(PlatformThread::Create(0, &thread2, &handle2));
+ ASSERT_TRUE(PlatformThread::Create(0, &thread3, &handle3));
+
+ MutexLockTestThread::DoStuff(&lock, &value);
+
+ PlatformThread::Join(handle1);
+ PlatformThread::Join(handle2);
+ PlatformThread::Join(handle3);
+
+ EXPECT_EQ(4 * 40, value);
+}
+
+} // namespace base
diff --git a/src/base/synchronization/spin_wait.h b/src/base/synchronization/spin_wait.h
new file mode 100644
index 0000000..ad54ca8
--- /dev/null
+++ b/src/base/synchronization/spin_wait.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 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 provides a macro ONLY for use in testing.
+// DO NOT USE IN PRODUCTION CODE. There are much better ways to wait.
+
+// This code is very helpful in testing multi-threaded code, without depending
+// on almost any primitives. This is especially helpful if you are testing
+// those primitive multi-threaded constructs.
+
+// We provide a simple one argument spin wait (for 1 second), and a generic
+// spin wait (for longer periods of time).
+
+#ifndef BASE_SYNCHRONIZATION_SPIN_WAIT_H_
+#define BASE_SYNCHRONIZATION_SPIN_WAIT_H_
+
+#include "base/threading/platform_thread.h"
+#include "base/time.h"
+
+// Provide a macro that will wait no longer than 1 second for an asynchronous
+// change is the value of an expression.
+// A typical use would be:
+//
+// SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 == f(x));
+//
+// The expression will be evaluated repeatedly until it is true, or until
+// the time (1 second) expires.
+// Since tests generally have a 5 second watch dog timer, this spin loop is
+// typically used to get the padding needed on a given test platform to assure
+// that the test passes, even if load varies, and external events vary.
+
+#define SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(expression) \
+ SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(base::TimeDelta::FromSeconds(1), \
+ (expression))
+
+#define SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(delta, expression) do { \
+ base::TimeTicks start = base::TimeTicks::Now(); \
+ const base::TimeDelta kTimeout = delta; \
+ while (!(expression)) { \
+ if (kTimeout < base::TimeTicks::Now() - start) { \
+ EXPECT_LE((base::TimeTicks::Now() - start).InMilliseconds(), \
+ kTimeout.InMilliseconds()) << "Timed out"; \
+ break; \
+ } \
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50)); \
+ } \
+ } while (0)
+
+#endif // BASE_SYNCHRONIZATION_SPIN_WAIT_H_
diff --git a/src/base/synchronization/waitable_event.h b/src/base/synchronization/waitable_event.h
new file mode 100644
index 0000000..dabf885
--- /dev/null
+++ b/src/base/synchronization/waitable_event.h
@@ -0,0 +1,190 @@
+// Copyright (c) 2012 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_SYNCHRONIZATION_WAITABLE_EVENT_H_
+#define BASE_SYNCHRONIZATION_WAITABLE_EVENT_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#if defined(OS_POSIX) || defined(OS_STARBOARD)
+#if defined(__LB_SHELL__) || defined(OS_STARBOARD)
+#include <vector>
+#else
+#include <list>
+#endif
+#include <utility>
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#endif
+
+namespace base {
+
+// This replaces INFINITE from Win32
+static const int kNoTimeout = -1;
+
+class TimeDelta;
+
+// A WaitableEvent can be a useful thread synchronization tool when you want to
+// allow one thread to wait for another thread to finish some work. For
+// non-Windows systems, this can only be used from within a single address
+// space.
+//
+// Use a WaitableEvent when you would otherwise use a Lock+ConditionVariable to
+// protect a simple boolean value. However, if you find yourself using a
+// WaitableEvent in conjunction with a Lock to wait for a more complex state
+// change (e.g., for an item to be added to a queue), then you should probably
+// be using a ConditionVariable instead of a WaitableEvent.
+//
+// NOTE: On Windows, this class provides a subset of the functionality afforded
+// by a Windows event object. This is intentional. If you are writing Windows
+// specific code and you need other features of a Windows event, then you might
+// be better off just using an Windows event directly.
+class BASE_EXPORT WaitableEvent {
+ public:
+ // If manual_reset is true, then to set the event state to non-signaled, a
+ // consumer must call the Reset method. If this parameter is false, then the
+ // system automatically resets the event state to non-signaled after a single
+ // waiting thread has been released.
+ WaitableEvent(bool manual_reset, bool initially_signaled);
+
+#if defined(OS_WIN)
+ // Create a WaitableEvent from an Event HANDLE which has already been
+ // created. This objects takes ownership of the HANDLE and will close it when
+ // deleted.
+ explicit WaitableEvent(HANDLE event_handle);
+
+ // Releases ownership of the handle from this object.
+ HANDLE Release();
+#endif
+
+ ~WaitableEvent();
+
+ // Put the event in the un-signaled state.
+ void Reset();
+
+ // Put the event in the signaled state. Causing any thread blocked on Wait
+ // to be woken up.
+ void Signal();
+
+ // Returns true if the event is in the signaled state, else false. If this
+ // is not a manual reset event, then this test will cause a reset.
+ bool IsSignaled();
+
+ // Wait indefinitely for the event to be signaled.
+ void Wait();
+
+ // Wait up until max_time has passed for the event to be signaled. Returns
+ // true if the event was signaled. If this method returns false, then it
+ // does not necessarily mean that max_time was exceeded.
+ bool TimedWait(const TimeDelta& max_time);
+
+#if defined(OS_WIN)
+ HANDLE handle() const { return handle_; }
+#endif
+
+ // Wait, synchronously, on multiple events.
+ // waitables: an array of WaitableEvent pointers
+ // count: the number of elements in @waitables
+ //
+ // returns: the index of a WaitableEvent which has been signaled.
+ //
+ // You MUST NOT delete any of the WaitableEvent objects while this wait is
+ // happening.
+ static size_t WaitMany(WaitableEvent** waitables, size_t count);
+
+ // For asynchronous waiting, see WaitableEventWatcher
+
+ // This is a private helper class. It's here because it's used by friends of
+ // this class (such as WaitableEventWatcher) to be able to enqueue elements
+ // of the wait-list
+ class Waiter {
+ public:
+ // Signal the waiter to wake up.
+ //
+ // Consider the case of a Waiter which is in multiple WaitableEvent's
+ // wait-lists. Each WaitableEvent is automatic-reset and two of them are
+ // signaled at the same time. Now, each will wake only the first waiter in
+ // the wake-list before resetting. However, if those two waiters happen to
+ // be the same object (as can happen if another thread didn't have a chance
+ // to dequeue the waiter from the other wait-list in time), two auto-resets
+ // will have happened, but only one waiter has been signaled!
+ //
+ // Because of this, a Waiter may "reject" a wake by returning false. In
+ // this case, the auto-reset WaitableEvent shouldn't act as if anything has
+ // been notified.
+ virtual bool Fire(WaitableEvent* signaling_event) = 0;
+
+ // Waiters may implement this in order to provide an extra condition for
+ // two Waiters to be considered equal. In WaitableEvent::Dequeue, if the
+ // pointers match then this function is called as a final check. See the
+ // comments in ~Handle for why.
+ virtual bool Compare(void* tag) = 0;
+
+ protected:
+ virtual ~Waiter() {}
+ };
+
+ private:
+ friend class WaitableEventWatcher;
+
+#if defined(OS_WIN)
+ HANDLE handle_;
+#else
+ // On Windows, one can close a HANDLE which is currently being waited on. The
+ // MSDN documentation says that the resulting behaviour is 'undefined', but
+ // it doesn't crash. However, if we were to include the following members
+ // directly then, on POSIX, one couldn't use WaitableEventWatcher to watch an
+ // event which gets deleted. This mismatch has bitten us several times now,
+ // so we have a kernel of the WaitableEvent, which is reference counted.
+ // WaitableEventWatchers may then take a reference and thus match the Windows
+ // behaviour.
+ struct WaitableEventKernel :
+ public RefCountedThreadSafe<WaitableEventKernel> {
+ public:
+ WaitableEventKernel(bool manual_reset, bool initially_signaled);
+
+ bool Dequeue(Waiter* waiter, void* tag);
+
+ base::Lock lock_;
+ const bool manual_reset_;
+ bool signaled_;
+#if defined(__LB_SHELL__) || defined(OS_STARBOARD)
+ std::vector<Waiter*> waiters_;
+#else
+ std::list<Waiter*> waiters_;
+#endif
+
+ private:
+ friend class RefCountedThreadSafe<WaitableEventKernel>;
+ ~WaitableEventKernel();
+ };
+
+ typedef std::pair<WaitableEvent*, size_t> WaiterAndIndex;
+
+ // When dealing with arrays of WaitableEvent*, we want to sort by the address
+ // of the WaitableEvent in order to have a globally consistent locking order.
+ // In that case we keep them, in sorted order, in an array of pairs where the
+ // second element is the index of the WaitableEvent in the original,
+ // unsorted, array.
+ static size_t EnqueueMany(WaiterAndIndex* waitables,
+ size_t count, Waiter* waiter);
+
+ bool SignalAll();
+ bool SignalOne();
+ void Enqueue(Waiter* waiter);
+
+ scoped_refptr<WaitableEventKernel> kernel_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(WaitableEvent);
+};
+
+} // namespace base
+
+#endif // BASE_SYNCHRONIZATION_WAITABLE_EVENT_H_
diff --git a/src/base/synchronization/waitable_event_posix.cc b/src/base/synchronization/waitable_event_posix.cc
new file mode 100644
index 0000000..7066aee
--- /dev/null
+++ b/src/base/synchronization/waitable_event_posix.cc
@@ -0,0 +1,423 @@
+// Copyright (c) 2012 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 <algorithm>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_restrictions.h"
+
+// -----------------------------------------------------------------------------
+// A WaitableEvent on POSIX is implemented as a wait-list. Currently we don't
+// support cross-process events (where one process can signal an event which
+// others are waiting on). Because of this, we can avoid having one thread per
+// listener in several cases.
+//
+// The WaitableEvent maintains a list of waiters, protected by a lock. Each
+// waiter is either an async wait, in which case we have a Task and the
+// MessageLoop to run it on, or a blocking wait, in which case we have the
+// condition variable to signal.
+//
+// Waiting involves grabbing the lock and adding oneself to the wait list. Async
+// waits can be canceled, which means grabbing the lock and removing oneself
+// from the list.
+//
+// Waiting on multiple events is handled by adding a single, synchronous wait to
+// the wait-list of many events. An event passes a pointer to itself when
+// firing a waiter and so we can store that pointer to find out which event
+// triggered.
+// -----------------------------------------------------------------------------
+
+namespace base {
+
+// -----------------------------------------------------------------------------
+// This is just an abstract base class for waking the two types of waiters
+// -----------------------------------------------------------------------------
+WaitableEvent::WaitableEvent(bool manual_reset, bool initially_signaled)
+ : kernel_(new WaitableEventKernel(manual_reset, initially_signaled)) {
+}
+
+WaitableEvent::~WaitableEvent() {
+}
+
+void WaitableEvent::Reset() {
+ base::AutoLock locked(kernel_->lock_);
+ kernel_->signaled_ = false;
+}
+
+void WaitableEvent::Signal() {
+ base::AutoLock locked(kernel_->lock_);
+
+ if (kernel_->signaled_)
+ return;
+
+ if (kernel_->manual_reset_) {
+ SignalAll();
+ kernel_->signaled_ = true;
+ } else {
+ // In the case of auto reset, if no waiters were woken, we remain
+ // signaled.
+ if (!SignalOne())
+ kernel_->signaled_ = true;
+ }
+}
+
+bool WaitableEvent::IsSignaled() {
+ base::AutoLock locked(kernel_->lock_);
+
+ const bool result = kernel_->signaled_;
+ if (result && !kernel_->manual_reset_)
+ kernel_->signaled_ = false;
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+// Synchronous waits
+
+// -----------------------------------------------------------------------------
+// This is a synchronous waiter. The thread is waiting on the given condition
+// variable and the fired flag in this object.
+// -----------------------------------------------------------------------------
+class SyncWaiter : public WaitableEvent::Waiter {
+ public:
+ SyncWaiter()
+ : fired_(false),
+ signaling_event_(NULL),
+ lock_(),
+ cv_(&lock_) {
+ }
+
+ virtual bool Fire(WaitableEvent* signaling_event) OVERRIDE {
+ base::AutoLock locked(lock_);
+
+ if (fired_)
+ return false;
+
+ fired_ = true;
+ signaling_event_ = signaling_event;
+
+ cv_.Broadcast();
+
+ // Unlike AsyncWaiter objects, SyncWaiter objects are stack-allocated on
+ // the blocking thread's stack. There is no |delete this;| in Fire. The
+ // SyncWaiter object is destroyed when it goes out of scope.
+
+ return true;
+ }
+
+ WaitableEvent* signaling_event() const {
+ return signaling_event_;
+ }
+
+ // ---------------------------------------------------------------------------
+ // These waiters are always stack allocated and don't delete themselves. Thus
+ // there's no problem and the ABA tag is the same as the object pointer.
+ // ---------------------------------------------------------------------------
+ virtual bool Compare(void* tag) OVERRIDE {
+ return this == tag;
+ }
+
+ // ---------------------------------------------------------------------------
+ // Called with lock held.
+ // ---------------------------------------------------------------------------
+ bool fired() const {
+ return fired_;
+ }
+
+ // ---------------------------------------------------------------------------
+ // During a TimedWait, we need a way to make sure that an auto-reset
+ // WaitableEvent doesn't think that this event has been signaled between
+ // unlocking it and removing it from the wait-list. Called with lock held.
+ // ---------------------------------------------------------------------------
+ void Disable() {
+ fired_ = true;
+ }
+
+ base::Lock* lock() {
+ return &lock_;
+ }
+
+ base::ConditionVariable* cv() {
+ return &cv_;
+ }
+
+ private:
+ bool fired_;
+ WaitableEvent* signaling_event_; // The WaitableEvent which woke us
+ base::Lock lock_;
+ base::ConditionVariable cv_;
+};
+
+void WaitableEvent::Wait() {
+ bool result = TimedWait(TimeDelta::FromSeconds(-1));
+ DCHECK(result) << "TimedWait() should never fail with infinite timeout";
+}
+
+bool WaitableEvent::TimedWait(const TimeDelta& max_time) {
+ base::ThreadRestrictions::AssertWaitAllowed();
+ const Time end_time(Time::Now() + max_time);
+ const bool finite_time = max_time.ToInternalValue() >= 0;
+
+ kernel_->lock_.Acquire();
+ if (kernel_->signaled_) {
+ if (!kernel_->manual_reset_) {
+ // In this case we were signaled when we had no waiters. Now that
+ // someone has waited upon us, we can automatically reset.
+ kernel_->signaled_ = false;
+ }
+
+ kernel_->lock_.Release();
+ return true;
+ }
+
+ SyncWaiter sw;
+ sw.lock()->Acquire();
+
+ Enqueue(&sw);
+ kernel_->lock_.Release();
+ // We are violating locking order here by holding the SyncWaiter lock but not
+ // the WaitableEvent lock. However, this is safe because we don't lock @lock_
+ // again before unlocking it.
+
+ for (;;) {
+ const Time current_time(Time::Now());
+
+ if (sw.fired() || (finite_time && current_time >= end_time)) {
+ const bool return_value = sw.fired();
+
+ // We can't acquire @lock_ before releasing the SyncWaiter lock (because
+ // of locking order), however, in between the two a signal could be fired
+ // and @sw would accept it, however we will still return false, so the
+ // signal would be lost on an auto-reset WaitableEvent. Thus we call
+ // Disable which makes sw::Fire return false.
+ sw.Disable();
+ sw.lock()->Release();
+
+ kernel_->lock_.Acquire();
+ kernel_->Dequeue(&sw, &sw);
+ kernel_->lock_.Release();
+
+ return return_value;
+ }
+
+ if (finite_time) {
+ const TimeDelta max_wait(end_time - current_time);
+ sw.cv()->TimedWait(max_wait);
+ } else {
+ sw.cv()->Wait();
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Synchronous waiting on multiple objects.
+
+static bool // StrictWeakOrdering
+cmp_fst_addr(const std::pair<WaitableEvent*, size_t> &a,
+ const std::pair<WaitableEvent*, size_t> &b) {
+ return a.first < b.first;
+}
+
+// static
+size_t WaitableEvent::WaitMany(WaitableEvent** raw_waitables,
+ size_t count) {
+ base::ThreadRestrictions::AssertWaitAllowed();
+ DCHECK(count) << "Cannot wait on no events";
+
+ // We need to acquire the locks in a globally consistent order. Thus we sort
+ // the array of waitables by address. We actually sort a pairs so that we can
+ // map back to the original index values later.
+ std::vector<std::pair<WaitableEvent*, size_t> > waitables;
+ waitables.reserve(count);
+ for (size_t i = 0; i < count; ++i)
+ waitables.push_back(std::make_pair(raw_waitables[i], i));
+
+ DCHECK_EQ(count, waitables.size());
+
+ sort(waitables.begin(), waitables.end(), cmp_fst_addr);
+
+ // The set of waitables must be distinct. Since we have just sorted by
+ // address, we can check this cheaply by comparing pairs of consecutive
+ // elements.
+ for (size_t i = 0; i < waitables.size() - 1; ++i) {
+ DCHECK(waitables[i].first != waitables[i+1].first);
+ }
+
+ SyncWaiter sw;
+
+ const size_t r = EnqueueMany(&waitables[0], count, &sw);
+ if (r) {
+ // One of the events is already signaled. The SyncWaiter has not been
+ // enqueued anywhere. EnqueueMany returns the count of remaining waitables
+ // when the signaled one was seen, so the index of the signaled event is
+ // @count - @r.
+ return waitables[count - r].second;
+ }
+
+ // At this point, we hold the locks on all the WaitableEvents and we have
+ // enqueued our waiter in them all.
+ sw.lock()->Acquire();
+ // Release the WaitableEvent locks in the reverse order
+ for (size_t i = 0; i < count; ++i) {
+ waitables[count - (1 + i)].first->kernel_->lock_.Release();
+ }
+
+ for (;;) {
+ if (sw.fired())
+ break;
+
+ sw.cv()->Wait();
+ }
+ sw.lock()->Release();
+
+ // The address of the WaitableEvent which fired is stored in the SyncWaiter.
+ WaitableEvent *const signaled_event = sw.signaling_event();
+ // This will store the index of the raw_waitables which fired.
+ size_t signaled_index = 0;
+
+ // Take the locks of each WaitableEvent in turn (except the signaled one) and
+ // remove our SyncWaiter from the wait-list
+ for (size_t i = 0; i < count; ++i) {
+ if (raw_waitables[i] != signaled_event) {
+ raw_waitables[i]->kernel_->lock_.Acquire();
+ // There's no possible ABA issue with the address of the SyncWaiter here
+ // because it lives on the stack. Thus the tag value is just the pointer
+ // value again.
+ raw_waitables[i]->kernel_->Dequeue(&sw, &sw);
+ raw_waitables[i]->kernel_->lock_.Release();
+ } else {
+ signaled_index = i;
+ }
+ }
+
+ return signaled_index;
+}
+
+// -----------------------------------------------------------------------------
+// If return value == 0:
+// The locks of the WaitableEvents have been taken in order and the Waiter has
+// been enqueued in the wait-list of each. None of the WaitableEvents are
+// currently signaled
+// else:
+// None of the WaitableEvent locks are held. The Waiter has not been enqueued
+// in any of them and the return value is the index of the first WaitableEvent
+// which was signaled, from the end of the array.
+// -----------------------------------------------------------------------------
+// static
+size_t WaitableEvent::EnqueueMany
+ (std::pair<WaitableEvent*, size_t>* waitables,
+ size_t count, Waiter* waiter) {
+ if (!count)
+ return 0;
+
+ waitables[0].first->kernel_->lock_.Acquire();
+ if (waitables[0].first->kernel_->signaled_) {
+ if (!waitables[0].first->kernel_->manual_reset_)
+ waitables[0].first->kernel_->signaled_ = false;
+ waitables[0].first->kernel_->lock_.Release();
+ return count;
+ }
+
+ const size_t r = EnqueueMany(waitables + 1, count - 1, waiter);
+ if (r) {
+ waitables[0].first->kernel_->lock_.Release();
+ } else {
+ waitables[0].first->Enqueue(waiter);
+ }
+
+ return r;
+}
+
+// -----------------------------------------------------------------------------
+
+
+// -----------------------------------------------------------------------------
+// Private functions...
+
+WaitableEvent::WaitableEventKernel::WaitableEventKernel(bool manual_reset,
+ bool initially_signaled)
+ : manual_reset_(manual_reset),
+ signaled_(initially_signaled) {
+#if defined(__LB_SHELL__) || defined(OS_STARBOARD)
+ waiters_.reserve(1);
+#endif
+}
+
+WaitableEvent::WaitableEventKernel::~WaitableEventKernel() {
+}
+
+// -----------------------------------------------------------------------------
+// Wake all waiting waiters. Called with lock held.
+// -----------------------------------------------------------------------------
+bool WaitableEvent::SignalAll() {
+ bool signaled_at_least_one = false;
+#if defined(__LB_SHELL__) || defined(OS_STARBOARD)
+ for (std::vector<Waiter*>::iterator
+#else
+ for (std::list<Waiter*>::iterator
+#endif
+ i = kernel_->waiters_.begin(); i != kernel_->waiters_.end(); ++i) {
+ if ((*i)->Fire(this))
+ signaled_at_least_one = true;
+ }
+
+ kernel_->waiters_.clear();
+ return signaled_at_least_one;
+}
+
+// ---------------------------------------------------------------------------
+// Try to wake a single waiter. Return true if one was woken. Called with lock
+// held.
+// ---------------------------------------------------------------------------
+bool WaitableEvent::SignalOne() {
+ for (;;) {
+ if (kernel_->waiters_.empty())
+ return false;
+
+ const bool r = (*kernel_->waiters_.begin())->Fire(this);
+#if defined(__LB_SHELL__) || defined(OS_STARBOARD)
+ // This appears to be slow, but the size of the vector is almost always 1.
+ // Therefore, this line is usually constant time.
+ kernel_->waiters_.erase(kernel_->waiters_.begin());
+#else
+ kernel_->waiters_.pop_front();
+#endif
+ if (r)
+ return true;
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Add a waiter to the list of those waiting. Called with lock held.
+// -----------------------------------------------------------------------------
+void WaitableEvent::Enqueue(Waiter* waiter) {
+ kernel_->waiters_.push_back(waiter);
+}
+
+// -----------------------------------------------------------------------------
+// Remove a waiter from the list of those waiting. Return true if the waiter was
+// actually removed. Called with lock held.
+// -----------------------------------------------------------------------------
+bool WaitableEvent::WaitableEventKernel::Dequeue(Waiter* waiter, void* tag) {
+#if defined(__LB_SHELL__) || defined(OS_STARBOARD)
+ for (std::vector<Waiter*>::iterator
+#else
+ for (std::list<Waiter*>::iterator
+#endif
+ i = waiters_.begin(); i != waiters_.end(); ++i) {
+ if (*i == waiter && (*i)->Compare(tag)) {
+ waiters_.erase(i);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+} // namespace base
diff --git a/src/base/synchronization/waitable_event_shell.cc b/src/base/synchronization/waitable_event_shell.cc
new file mode 100644
index 0000000..9df0680
--- /dev/null
+++ b/src/base/synchronization/waitable_event_shell.cc
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2012 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// We use the same code as posix version
+#include "waitable_event_posix.cc"
diff --git a/src/base/synchronization/waitable_event_starboard.cc b/src/base/synchronization/waitable_event_starboard.cc
new file mode 100644
index 0000000..a49cc7c
--- /dev/null
+++ b/src/base/synchronization/waitable_event_starboard.cc
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// We use the same code as the POSIX version.
+#include "base/synchronization/waitable_event_posix.cc"
diff --git a/src/base/synchronization/waitable_event_unittest.cc b/src/base/synchronization/waitable_event_unittest.cc
new file mode 100644
index 0000000..d00adc7
--- /dev/null
+++ b/src/base/synchronization/waitable_event_unittest.cc
@@ -0,0 +1,108 @@
+// Copyright (c) 2012 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/synchronization/waitable_event.h"
+
+#include "base/compiler_specific.h"
+#include "base/time.h"
+#include "base/threading/platform_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(WaitableEventTest, ManualBasics) {
+ WaitableEvent event(true, false);
+
+ EXPECT_FALSE(event.IsSignaled());
+
+ event.Signal();
+ EXPECT_TRUE(event.IsSignaled());
+ EXPECT_TRUE(event.IsSignaled());
+
+ event.Reset();
+ EXPECT_FALSE(event.IsSignaled());
+ EXPECT_FALSE(event.TimedWait(TimeDelta::FromMilliseconds(10)));
+
+ event.Signal();
+ event.Wait();
+ EXPECT_TRUE(event.TimedWait(TimeDelta::FromMilliseconds(10)));
+}
+
+TEST(WaitableEventTest, AutoBasics) {
+ WaitableEvent event(false, false);
+
+ EXPECT_FALSE(event.IsSignaled());
+
+ event.Signal();
+ EXPECT_TRUE(event.IsSignaled());
+ EXPECT_FALSE(event.IsSignaled());
+
+ event.Reset();
+ EXPECT_FALSE(event.IsSignaled());
+ EXPECT_FALSE(event.TimedWait(TimeDelta::FromMilliseconds(10)));
+
+ event.Signal();
+ event.Wait();
+ EXPECT_FALSE(event.TimedWait(TimeDelta::FromMilliseconds(10)));
+
+ event.Signal();
+ EXPECT_TRUE(event.TimedWait(TimeDelta::FromMilliseconds(10)));
+}
+
+TEST(WaitableEventTest, WaitManyShortcut) {
+ WaitableEvent* ev[5];
+ for (unsigned i = 0; i < 5; ++i)
+ ev[i] = new WaitableEvent(false, false);
+
+ ev[3]->Signal();
+ EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 3u);
+
+ ev[3]->Signal();
+ EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 3u);
+
+ ev[4]->Signal();
+ EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 4u);
+
+ ev[0]->Signal();
+ EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 0u);
+
+ for (unsigned i = 0; i < 5; ++i)
+ delete ev[i];
+}
+
+class WaitableEventSignaler : public PlatformThread::Delegate {
+ public:
+ WaitableEventSignaler(double seconds, WaitableEvent* ev)
+ : seconds_(seconds),
+ ev_(ev) {
+ }
+
+ virtual void ThreadMain() OVERRIDE {
+ PlatformThread::Sleep(TimeDelta::FromSeconds(static_cast<int>(seconds_)));
+ ev_->Signal();
+ }
+
+ private:
+ const double seconds_;
+ WaitableEvent *const ev_;
+};
+
+TEST(WaitableEventTest, WaitMany) {
+ WaitableEvent* ev[5];
+ for (unsigned i = 0; i < 5; ++i)
+ ev[i] = new WaitableEvent(false, false);
+
+ WaitableEventSignaler signaler(0.1, ev[2]);
+ PlatformThreadHandle thread;
+ PlatformThread::Create(0, &signaler, &thread);
+
+ EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 2u);
+
+ PlatformThread::Join(thread);
+
+ for (unsigned i = 0; i < 5; ++i)
+ delete ev[i];
+}
+
+} // namespace base
diff --git a/src/base/synchronization/waitable_event_watcher.h b/src/base/synchronization/waitable_event_watcher.h
new file mode 100644
index 0000000..f5c1510
--- /dev/null
+++ b/src/base/synchronization/waitable_event_watcher.h
@@ -0,0 +1,164 @@
+// Copyright (c) 2012 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_SYNCHRONIZATION_WAITABLE_EVENT_WATCHER_H_
+#define BASE_SYNCHRONIZATION_WAITABLE_EVENT_WATCHER_H_
+
+#include "base/base_export.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include "base/win/object_watcher.h"
+#else
+#include "base/callback.h"
+#include "base/message_loop.h"
+#include "base/synchronization/waitable_event.h"
+#endif
+
+namespace base {
+
+class Flag;
+class AsyncWaiter;
+class AsyncCallbackTask;
+class WaitableEvent;
+
+// -----------------------------------------------------------------------------
+// This class provides a way to wait on a WaitableEvent asynchronously.
+//
+// Each instance of this object can be waiting on a single WaitableEvent. When
+// the waitable event is signaled, a callback is made in the thread of a given
+// MessageLoop. This callback can be deleted by deleting the waiter.
+//
+// Typical usage:
+//
+// class MyClass : public base::WaitableEventWatcher::Delegate {
+// public:
+// void DoStuffWhenSignaled(WaitableEvent *waitable_event) {
+// watcher_.StartWatching(waitable_event, this);
+// }
+// virtual void OnWaitableEventSignaled(WaitableEvent* waitable_event) {
+// // OK, time to do stuff!
+// }
+// private:
+// base::WaitableEventWatcher watcher_;
+// };
+//
+// In the above example, MyClass wants to "do stuff" when waitable_event
+// becomes signaled. WaitableEventWatcher makes this task easy. When MyClass
+// goes out of scope, the watcher_ will be destroyed, and there is no need to
+// worry about OnWaitableEventSignaled being called on a deleted MyClass
+// pointer.
+//
+// BEWARE: With automatically reset WaitableEvents, a signal may be lost if it
+// occurs just before a WaitableEventWatcher is deleted. There is currently no
+// safe way to stop watching an automatic reset WaitableEvent without possibly
+// missing a signal.
+//
+// NOTE: you /are/ allowed to delete the WaitableEvent while still waiting on
+// it with a Watcher. It will act as if the event was never signaled.
+// -----------------------------------------------------------------------------
+
+class BASE_EXPORT WaitableEventWatcher
+#if !defined(OS_WIN)
+ : public MessageLoop::DestructionObserver
+#endif
+{
+ public:
+
+ WaitableEventWatcher();
+ virtual ~WaitableEventWatcher();
+
+ class BASE_EXPORT Delegate {
+ public:
+ // -------------------------------------------------------------------------
+ // This is called on the MessageLoop thread when WaitableEvent has been
+ // signaled.
+ //
+ // Note: the event may not be signaled by the time that this function is
+ // called. This indicates only that it has been signaled at some point in
+ // the past.
+ // -------------------------------------------------------------------------
+ virtual void OnWaitableEventSignaled(WaitableEvent* waitable_event) = 0;
+
+ protected:
+ virtual ~Delegate() { }
+ };
+
+ // ---------------------------------------------------------------------------
+ // When @event is signaled, the given delegate is called on the thread of the
+ // current message loop when StartWatching is called. The delegate is not
+ // deleted.
+ // ---------------------------------------------------------------------------
+ bool StartWatching(WaitableEvent* event, Delegate* delegate);
+
+ // ---------------------------------------------------------------------------
+ // Cancel the current watch. Must be called from the same thread which
+ // started the watch.
+ //
+ // Does nothing if no event is being watched, nor if the watch has completed.
+ // The delegate will *not* be called for the current watch after this
+ // function returns. Since the delegate runs on the same thread as this
+ // function, it cannot be called during this function either.
+ // ---------------------------------------------------------------------------
+ void StopWatching();
+
+ // ---------------------------------------------------------------------------
+ // Return the currently watched event, or NULL if no object is currently being
+ // watched.
+ // ---------------------------------------------------------------------------
+ WaitableEvent* GetWatchedEvent();
+
+ // ---------------------------------------------------------------------------
+ // Return the delegate, or NULL if there is no delegate.
+ // ---------------------------------------------------------------------------
+ Delegate* delegate() {
+ return delegate_;
+ }
+
+ private:
+#if defined(OS_WIN)
+ // ---------------------------------------------------------------------------
+ // The helper class exists because, if WaitableEventWatcher were to inherit
+ // from ObjectWatcher::Delegate, then it couldn't also have an inner class
+ // called Delegate (at least on Windows). Thus this object exists to proxy
+ // the callback function
+ // ---------------------------------------------------------------------------
+ class ObjectWatcherHelper : public win::ObjectWatcher::Delegate {
+ public:
+ ObjectWatcherHelper(WaitableEventWatcher* watcher);
+
+ // -------------------------------------------------------------------------
+ // Implementation of ObjectWatcher::Delegate
+ // -------------------------------------------------------------------------
+ void OnObjectSignaled(HANDLE h);
+
+ private:
+ WaitableEventWatcher *const watcher_;
+ };
+
+ void OnObjectSignaled();
+
+ ObjectWatcherHelper helper_;
+ win::ObjectWatcher watcher_;
+#else
+ // ---------------------------------------------------------------------------
+ // Implementation of MessageLoop::DestructionObserver
+ // ---------------------------------------------------------------------------
+ virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
+
+ MessageLoop* message_loop_;
+ scoped_refptr<Flag> cancel_flag_;
+ AsyncWaiter* waiter_;
+ base::Closure callback_;
+ scoped_refptr<WaitableEvent::WaitableEventKernel> kernel_;
+#endif
+
+ WaitableEvent* event_;
+
+ Delegate* delegate_;
+};
+
+} // namespace base
+
+#endif // BASE_SYNCHRONIZATION_WAITABLE_EVENT_WATCHER_H_
diff --git a/src/base/synchronization/waitable_event_watcher_posix.cc b/src/base/synchronization/waitable_event_watcher_posix.cc
new file mode 100644
index 0000000..3180b4b
--- /dev/null
+++ b/src/base/synchronization/waitable_event_watcher_posix.cc
@@ -0,0 +1,270 @@
+// Copyright (c) 2012 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/synchronization/waitable_event_watcher.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/message_loop.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+
+namespace base {
+
+// -----------------------------------------------------------------------------
+// WaitableEventWatcher (async waits).
+//
+// The basic design is that we add an AsyncWaiter to the wait-list of the event.
+// That AsyncWaiter has a pointer to MessageLoop, and a Task to be posted to it.
+// The MessageLoop ends up running the task, which calls the delegate.
+//
+// Since the wait can be canceled, we have a thread-safe Flag object which is
+// set when the wait has been canceled. At each stage in the above, we check the
+// flag before going onto the next stage. Since the wait may only be canceled in
+// the MessageLoop which runs the Task, we are assured that the delegate cannot
+// be called after canceling...
+
+// -----------------------------------------------------------------------------
+// A thread-safe, reference-counted, write-once flag.
+// -----------------------------------------------------------------------------
+class Flag : public RefCountedThreadSafe<Flag> {
+ public:
+ Flag() { flag_ = false; }
+
+ void Set() {
+ AutoLock locked(lock_);
+ flag_ = true;
+ }
+
+ bool value() const {
+ AutoLock locked(lock_);
+ return flag_;
+ }
+
+ private:
+ friend class RefCountedThreadSafe<Flag>;
+ ~Flag() {}
+
+ mutable Lock lock_;
+ bool flag_;
+
+ DISALLOW_COPY_AND_ASSIGN(Flag);
+};
+
+// -----------------------------------------------------------------------------
+// This is an asynchronous waiter which posts a task to a MessageLoop when
+// fired. An AsyncWaiter may only be in a single wait-list.
+// -----------------------------------------------------------------------------
+class AsyncWaiter : public WaitableEvent::Waiter {
+ public:
+ AsyncWaiter(MessageLoop* message_loop,
+ const base::Closure& callback,
+ Flag* flag)
+ : message_loop_(message_loop),
+ callback_(callback),
+ flag_(flag) { }
+
+ virtual bool Fire(WaitableEvent* event) OVERRIDE {
+ // Post the callback if we haven't been cancelled.
+ if (!flag_->value()) {
+ message_loop_->PostTask(FROM_HERE, callback_);
+ }
+
+ // We are removed from the wait-list by the WaitableEvent itself. It only
+ // remains to delete ourselves.
+ delete this;
+
+ // We can always return true because an AsyncWaiter is never in two
+ // different wait-lists at the same time.
+ return true;
+ }
+
+ // See StopWatching for discussion
+ virtual bool Compare(void* tag) OVERRIDE {
+ return tag == flag_.get();
+ }
+
+ private:
+ MessageLoop *const message_loop_;
+ base::Closure callback_;
+ scoped_refptr<Flag> flag_;
+};
+
+// -----------------------------------------------------------------------------
+// For async waits we need to make a callback in a MessageLoop thread. We do
+// this by posting a callback, which calls the delegate and keeps track of when
+// the event is canceled.
+// -----------------------------------------------------------------------------
+void AsyncCallbackHelper(Flag* flag,
+ WaitableEventWatcher::Delegate* delegate,
+ WaitableEvent* event) {
+ // Runs in MessageLoop thread.
+ if (!flag->value()) {
+ // This is to let the WaitableEventWatcher know that the event has occured
+ // because it needs to be able to return NULL from GetWatchedObject
+ flag->Set();
+ delegate->OnWaitableEventSignaled(event);
+ }
+}
+
+WaitableEventWatcher::WaitableEventWatcher()
+ : message_loop_(NULL),
+ cancel_flag_(NULL),
+ waiter_(NULL),
+ event_(NULL),
+ delegate_(NULL) {
+}
+
+WaitableEventWatcher::~WaitableEventWatcher() {
+ StopWatching();
+}
+
+// -----------------------------------------------------------------------------
+// The Handle is how the user cancels a wait. After deleting the Handle we
+// insure that the delegate cannot be called.
+// -----------------------------------------------------------------------------
+bool WaitableEventWatcher::StartWatching
+ (WaitableEvent* event, WaitableEventWatcher::Delegate* delegate) {
+ MessageLoop *const current_ml = MessageLoop::current();
+ DCHECK(current_ml) << "Cannot create WaitableEventWatcher without a "
+ "current MessageLoop";
+
+ // A user may call StartWatching from within the callback function. In this
+ // case, we won't know that we have finished watching, expect that the Flag
+ // will have been set in AsyncCallbackHelper().
+ if (cancel_flag_.get() && cancel_flag_->value()) {
+ if (message_loop_) {
+ message_loop_->RemoveDestructionObserver(this);
+ message_loop_ = NULL;
+ }
+
+ cancel_flag_ = NULL;
+ }
+
+ DCHECK(!cancel_flag_.get()) << "StartWatching called while still watching";
+
+ cancel_flag_ = new Flag;
+ callback_ = base::Bind(&AsyncCallbackHelper, cancel_flag_, delegate, event);
+ WaitableEvent::WaitableEventKernel* kernel = event->kernel_.get();
+
+ AutoLock locked(kernel->lock_);
+
+ delegate_ = delegate;
+ event_ = event;
+
+ if (kernel->signaled_) {
+ if (!kernel->manual_reset_)
+ kernel->signaled_ = false;
+
+ // No hairpinning - we can't call the delegate directly here. We have to
+ // enqueue a task on the MessageLoop as normal.
+ current_ml->PostTask(FROM_HERE, callback_);
+ return true;
+ }
+
+ message_loop_ = current_ml;
+ current_ml->AddDestructionObserver(this);
+
+ kernel_ = kernel;
+ waiter_ = new AsyncWaiter(current_ml, callback_, cancel_flag_);
+ event->Enqueue(waiter_);
+
+ return true;
+}
+
+void WaitableEventWatcher::StopWatching() {
+ delegate_ = NULL;
+
+ if (message_loop_) {
+ message_loop_->RemoveDestructionObserver(this);
+ message_loop_ = NULL;
+ }
+
+ if (!cancel_flag_.get()) // if not currently watching...
+ return;
+
+ if (cancel_flag_->value()) {
+ // In this case, the event has fired, but we haven't figured that out yet.
+ // The WaitableEvent may have been deleted too.
+ cancel_flag_ = NULL;
+ return;
+ }
+
+ if (!kernel_.get()) {
+ // We have no kernel. This means that we never enqueued a Waiter on an
+ // event because the event was already signaled when StartWatching was
+ // called.
+ //
+ // In this case, a task was enqueued on the MessageLoop and will run.
+ // We set the flag in case the task hasn't yet run. The flag will stop the
+ // delegate getting called. If the task has run then we have the last
+ // reference to the flag and it will be deleted immedately after.
+ cancel_flag_->Set();
+ cancel_flag_ = NULL;
+ return;
+ }
+
+ AutoLock locked(kernel_->lock_);
+ // We have a lock on the kernel. No one else can signal the event while we
+ // have it.
+
+ // We have a possible ABA issue here. If Dequeue was to compare only the
+ // pointer values then it's possible that the AsyncWaiter could have been
+ // fired, freed and the memory reused for a different Waiter which was
+ // enqueued in the same wait-list. We would think that that waiter was our
+ // AsyncWaiter and remove it.
+ //
+ // To stop this, Dequeue also takes a tag argument which is passed to the
+ // virtual Compare function before the two are considered a match. So we need
+ // a tag which is good for the lifetime of this handle: the Flag. Since we
+ // have a reference to the Flag, its memory cannot be reused while this object
+ // still exists. So if we find a waiter with the correct pointer value, and
+ // which shares a Flag pointer, we have a real match.
+ if (kernel_->Dequeue(waiter_, cancel_flag_.get())) {
+ // Case 2: the waiter hasn't been signaled yet; it was still on the wait
+ // list. We've removed it, thus we can delete it and the task (which cannot
+ // have been enqueued with the MessageLoop because the waiter was never
+ // signaled)
+ delete waiter_;
+ callback_.Reset();
+ cancel_flag_ = NULL;
+ return;
+ }
+
+ // Case 3: the waiter isn't on the wait-list, thus it was signaled. It may
+ // not have run yet, so we set the flag to tell it not to bother enqueuing the
+ // task on the MessageLoop, but to delete it instead. The Waiter deletes
+ // itself once run.
+ cancel_flag_->Set();
+ cancel_flag_ = NULL;
+
+ // If the waiter has already run then the task has been enqueued. If the Task
+ // hasn't yet run, the flag will stop the delegate from getting called. (This
+ // is thread safe because one may only delete a Handle from the MessageLoop
+ // thread.)
+ //
+ // If the delegate has already been called then we have nothing to do. The
+ // task has been deleted by the MessageLoop.
+}
+
+WaitableEvent* WaitableEventWatcher::GetWatchedEvent() {
+ if (!cancel_flag_.get())
+ return NULL;
+
+ if (cancel_flag_->value())
+ return NULL;
+
+ return event_;
+}
+
+// -----------------------------------------------------------------------------
+// This is called when the MessageLoop which the callback will be run it is
+// deleted. We need to cancel the callback as if we had been deleted, but we
+// will still be deleted at some point in the future.
+// -----------------------------------------------------------------------------
+void WaitableEventWatcher::WillDestroyCurrentMessageLoop() {
+ StopWatching();
+}
+
+} // namespace base
diff --git a/src/base/synchronization/waitable_event_watcher_unittest.cc b/src/base/synchronization/waitable_event_watcher_unittest.cc
new file mode 100644
index 0000000..8744aa2
--- /dev/null
+++ b/src/base/synchronization/waitable_event_watcher_unittest.cc
@@ -0,0 +1,176 @@
+// Copyright (c) 2012 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/message_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/synchronization/waitable_event_watcher.h"
+#include "base/threading/platform_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+// The message loops on which each waitable event timer should be tested.
+const MessageLoop::Type testing_message_loops[] = {
+ MessageLoop::TYPE_DEFAULT,
+ MessageLoop::TYPE_IO,
+#if !defined(OS_IOS) // iOS does not allow direct running of the UI loop.
+ MessageLoop::TYPE_UI,
+#endif
+};
+
+const int kNumTestingMessageLoops = arraysize(testing_message_loops);
+
+class QuitDelegate : public WaitableEventWatcher::Delegate {
+ public:
+ virtual void OnWaitableEventSignaled(WaitableEvent* event) OVERRIDE {
+ MessageLoop::current()->Quit();
+ }
+};
+
+class DecrementCountDelegate : public WaitableEventWatcher::Delegate {
+ public:
+ explicit DecrementCountDelegate(int* counter) : counter_(counter) {
+ }
+ virtual void OnWaitableEventSignaled(WaitableEvent* object) OVERRIDE {
+ --(*counter_);
+ }
+ private:
+ int* counter_;
+};
+
+void RunTest_BasicSignal(MessageLoop::Type message_loop_type) {
+ MessageLoop message_loop(message_loop_type);
+
+ // A manual-reset event that is not yet signaled.
+ WaitableEvent event(true, false);
+
+ WaitableEventWatcher watcher;
+ EXPECT_TRUE(watcher.GetWatchedEvent() == NULL);
+
+ QuitDelegate delegate;
+ watcher.StartWatching(&event, &delegate);
+ EXPECT_EQ(&event, watcher.GetWatchedEvent());
+
+ event.Signal();
+
+ MessageLoop::current()->Run();
+
+ EXPECT_TRUE(watcher.GetWatchedEvent() == NULL);
+}
+
+void RunTest_BasicCancel(MessageLoop::Type message_loop_type) {
+ MessageLoop message_loop(message_loop_type);
+
+ // A manual-reset event that is not yet signaled.
+ WaitableEvent event(true, false);
+
+ WaitableEventWatcher watcher;
+
+ QuitDelegate delegate;
+ watcher.StartWatching(&event, &delegate);
+
+ watcher.StopWatching();
+}
+
+void RunTest_CancelAfterSet(MessageLoop::Type message_loop_type) {
+ MessageLoop message_loop(message_loop_type);
+
+ // A manual-reset event that is not yet signaled.
+ WaitableEvent event(true, false);
+
+ WaitableEventWatcher watcher;
+
+ int counter = 1;
+ DecrementCountDelegate delegate(&counter);
+
+ watcher.StartWatching(&event, &delegate);
+
+ event.Signal();
+
+ // Let the background thread do its business
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(30));
+
+ watcher.StopWatching();
+
+ MessageLoop::current()->RunUntilIdle();
+
+ // Our delegate should not have fired.
+ EXPECT_EQ(1, counter);
+}
+
+void RunTest_OutlivesMessageLoop(MessageLoop::Type message_loop_type) {
+ // Simulate a MessageLoop that dies before an WaitableEventWatcher. This
+ // ordinarily doesn't happen when people use the Thread class, but it can
+ // happen when people use the Singleton pattern or atexit.
+ WaitableEvent event(true, false);
+ {
+ WaitableEventWatcher watcher;
+ {
+ MessageLoop message_loop(message_loop_type);
+
+ QuitDelegate delegate;
+ watcher.StartWatching(&event, &delegate);
+ }
+ }
+}
+
+void RunTest_DeleteUnder(MessageLoop::Type message_loop_type) {
+ // Delete the WaitableEvent out from under the Watcher. This is explictly
+ // allowed by the interface.
+
+ MessageLoop message_loop(message_loop_type);
+
+ {
+ WaitableEventWatcher watcher;
+
+ WaitableEvent* event = new WaitableEvent(false, false);
+ QuitDelegate delegate;
+ watcher.StartWatching(event, &delegate);
+ delete event;
+ }
+}
+
+} // namespace
+
+//-----------------------------------------------------------------------------
+
+TEST(WaitableEventWatcherTest, BasicSignal) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_BasicSignal(testing_message_loops[i]);
+ }
+}
+
+TEST(WaitableEventWatcherTest, BasicCancel) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_BasicCancel(testing_message_loops[i]);
+ }
+}
+
+TEST(WaitableEventWatcherTest, CancelAfterSet) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_CancelAfterSet(testing_message_loops[i]);
+ }
+}
+
+TEST(WaitableEventWatcherTest, OutlivesMessageLoop) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_OutlivesMessageLoop(testing_message_loops[i]);
+ }
+}
+
+#if defined(OS_WIN)
+// Crashes sometimes on vista. http://crbug.com/62119
+#define MAYBE_DeleteUnder DISABLED_DeleteUnder
+#else
+#define MAYBE_DeleteUnder DeleteUnder
+#endif
+TEST(WaitableEventWatcherTest, MAYBE_DeleteUnder) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_DeleteUnder(testing_message_loops[i]);
+ }
+}
+
+} // namespace base
diff --git a/src/base/synchronization/waitable_event_watcher_win.cc b/src/base/synchronization/waitable_event_watcher_win.cc
new file mode 100644
index 0000000..43e3c47
--- /dev/null
+++ b/src/base/synchronization/waitable_event_watcher_win.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/synchronization/waitable_event_watcher.h"
+
+#include "base/compiler_specific.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/win/object_watcher.h"
+
+namespace base {
+
+WaitableEventWatcher::ObjectWatcherHelper::ObjectWatcherHelper(
+ WaitableEventWatcher* watcher)
+ : watcher_(watcher) {
+};
+
+void WaitableEventWatcher::ObjectWatcherHelper::OnObjectSignaled(HANDLE h) {
+ watcher_->OnObjectSignaled();
+}
+
+
+WaitableEventWatcher::WaitableEventWatcher()
+ : ALLOW_THIS_IN_INITIALIZER_LIST(helper_(this)),
+ event_(NULL),
+ delegate_(NULL) {
+}
+
+WaitableEventWatcher::~WaitableEventWatcher() {
+}
+
+bool WaitableEventWatcher::StartWatching(WaitableEvent* event,
+ Delegate* delegate) {
+ delegate_ = delegate;
+ event_ = event;
+
+ return watcher_.StartWatching(event->handle(), &helper_);
+}
+
+void WaitableEventWatcher::StopWatching() {
+ delegate_ = NULL;
+ event_ = NULL;
+ watcher_.StopWatching();
+}
+
+WaitableEvent* WaitableEventWatcher::GetWatchedEvent() {
+ return event_;
+}
+
+void WaitableEventWatcher::OnObjectSignaled() {
+ WaitableEvent* event = event_;
+ Delegate* delegate = delegate_;
+ event_ = NULL;
+ delegate_ = NULL;
+ DCHECK(event);
+
+ delegate->OnWaitableEventSignaled(event);
+}
+
+} // namespace base
diff --git a/src/base/synchronization/waitable_event_win.cc b/src/base/synchronization/waitable_event_win.cc
new file mode 100644
index 0000000..28f8fc3
--- /dev/null
+++ b/src/base/synchronization/waitable_event_win.cc
@@ -0,0 +1,102 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/synchronization/waitable_event.h"
+
+#include <math.h>
+#include <windows.h>
+
+#include "base/logging.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time.h"
+
+namespace base {
+
+WaitableEvent::WaitableEvent(bool manual_reset, bool signaled)
+ : handle_(CreateEvent(NULL, manual_reset, signaled, NULL)) {
+ // We're probably going to crash anyways if this is ever NULL, so we might as
+ // well make our stack reports more informative by crashing here.
+ CHECK(handle_);
+}
+
+WaitableEvent::WaitableEvent(HANDLE handle)
+ : handle_(handle) {
+ CHECK(handle) << "Tried to create WaitableEvent from NULL handle";
+}
+
+WaitableEvent::~WaitableEvent() {
+ CloseHandle(handle_);
+}
+
+HANDLE WaitableEvent::Release() {
+ HANDLE rv = handle_;
+ handle_ = INVALID_HANDLE_VALUE;
+ return rv;
+}
+
+void WaitableEvent::Reset() {
+ ResetEvent(handle_);
+}
+
+void WaitableEvent::Signal() {
+ SetEvent(handle_);
+}
+
+bool WaitableEvent::IsSignaled() {
+ return TimedWait(TimeDelta::FromMilliseconds(0));
+}
+
+void WaitableEvent::Wait() {
+ base::ThreadRestrictions::AssertWaitAllowed();
+ DWORD result = WaitForSingleObject(handle_, INFINITE);
+ // It is most unexpected that this should ever fail. Help consumers learn
+ // about it if it should ever fail.
+ DCHECK_EQ(WAIT_OBJECT_0, result) << "WaitForSingleObject failed";
+}
+
+bool WaitableEvent::TimedWait(const TimeDelta& max_time) {
+ base::ThreadRestrictions::AssertWaitAllowed();
+ DCHECK(max_time >= TimeDelta::FromMicroseconds(0));
+ // Be careful here. TimeDelta has a precision of microseconds, but this API
+ // is in milliseconds. If there are 5.5ms left, should the delay be 5 or 6?
+ // It should be 6 to avoid returning too early.
+ double timeout = ceil(max_time.InMillisecondsF());
+ DWORD result = WaitForSingleObject(handle_, static_cast<DWORD>(timeout));
+ switch (result) {
+ case WAIT_OBJECT_0:
+ return true;
+ case WAIT_TIMEOUT:
+ return false;
+ }
+ // It is most unexpected that this should ever fail. Help consumers learn
+ // about it if it should ever fail.
+ NOTREACHED() << "WaitForSingleObject failed";
+ return false;
+}
+
+// static
+size_t WaitableEvent::WaitMany(WaitableEvent** events, size_t count) {
+ base::ThreadRestrictions::AssertWaitAllowed();
+ HANDLE handles[MAXIMUM_WAIT_OBJECTS];
+ CHECK_LE(count, MAXIMUM_WAIT_OBJECTS)
+ << "Can only wait on " << MAXIMUM_WAIT_OBJECTS << " with WaitMany";
+
+ for (size_t i = 0; i < count; ++i)
+ handles[i] = events[i]->handle();
+
+ // The cast is safe because count is small - see the CHECK above.
+ DWORD result =
+ WaitForMultipleObjects(static_cast<DWORD>(count),
+ handles,
+ FALSE, // don't wait for all the objects
+ INFINITE); // no timeout
+ if (result >= WAIT_OBJECT_0 + count) {
+ NOTREACHED() << "WaitForMultipleObjects failed: " << GetLastError();
+ return 0;
+ }
+
+ return result - WAIT_OBJECT_0;
+}
+
+} // namespace base
diff --git a/src/base/sys_byteorder.h b/src/base/sys_byteorder.h
new file mode 100644
index 0000000..5548347
--- /dev/null
+++ b/src/base/sys_byteorder.h
@@ -0,0 +1,155 @@
+// Copyright (c) 2012 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 header defines cross-platform ByteSwap() implementations for 16, 32 and
+// 64-bit values, and NetToHostXX() / HostToNextXX() functions equivalent to
+// the traditional ntohX() and htonX() functions.
+// Use the functions defined here rather than using the platform-specific
+// functions directly.
+
+#ifndef BASE_SYS_BYTEORDER_H_
+#define BASE_SYS_BYTEORDER_H_
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+// Include headers to provide byteswap for all platforms.
+#if defined(OS_STARBOARD)
+#include "starboard/byte_swap.h"
+#elif defined(COMPILER_MSVC)
+#include <stdlib.h>
+#elif defined(OS_MACOSX)
+#include <libkern/OSByteOrder.h>
+#elif defined(OS_OPENBSD)
+#include <sys/endian.h>
+#else
+#include <byteswap.h>
+#endif
+
+
+namespace base {
+
+// Returns a value with all bytes in |x| swapped, i.e. reverses the endianness.
+inline uint16 ByteSwap(uint16 x) {
+#if defined(OS_STARBOARD)
+ return SbByteSwapU16(x);
+#elif defined(COMPILER_MSVC)
+ return _byteswap_ushort(x);
+#elif defined(OS_MACOSX)
+ return OSSwapInt16(x);
+#elif defined(OS_OPENBSD)
+ return swap16(x);
+#else
+ return bswap_16(x);
+#endif
+}
+inline uint32 ByteSwap(uint32 x) {
+#if defined(OS_STARBOARD)
+ return SbByteSwapU32(x);
+#elif defined(COMPILER_MSVC)
+ return _byteswap_ulong(x);
+#elif defined(OS_MACOSX)
+ return OSSwapInt32(x);
+#elif defined(OS_OPENBSD)
+ return swap32(x);
+#else
+ return bswap_32(x);
+#endif
+}
+inline uint64 ByteSwap(uint64 x) {
+#if defined(OS_STARBOARD)
+ return SbByteSwapU64(x);
+#elif defined(COMPILER_MSVC)
+ return _byteswap_uint64(x);
+#elif defined(OS_MACOSX)
+ return OSSwapInt64(x);
+#elif defined(OS_OPENBSD)
+ return swap64(x);
+#else
+ return bswap_64(x);
+#endif
+}
+
+// Converts the bytes in |x| from host order (endianness) to little endian, and
+// returns the result.
+inline uint16 ByteSwapToLE16(uint16 x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ return x;
+#else
+ return ByteSwap(x);
+#endif
+}
+inline uint32 ByteSwapToLE32(uint32 x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ return x;
+#else
+ return ByteSwap(x);
+#endif
+}
+inline uint64 ByteSwapToLE64(uint64 x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ return x;
+#else
+ return ByteSwap(x);
+#endif
+}
+
+// Converts the bytes in |x| from network to host order (endianness), and
+// returns the result.
+inline uint16 NetToHost16(uint16 x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ return ByteSwap(x);
+#else
+ return x;
+#endif
+}
+inline uint32 NetToHost32(uint32 x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ return ByteSwap(x);
+#else
+ return x;
+#endif
+}
+inline uint64 NetToHost64(uint64 x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ return ByteSwap(x);
+#else
+ return x;
+#endif
+}
+
+// Converts the bytes in |x| from host to network order (endianness), and
+// returns the result.
+inline uint16 HostToNet16(uint16 x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ return ByteSwap(x);
+#else
+ return x;
+#endif
+}
+inline uint32 HostToNet32(uint32 x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ return ByteSwap(x);
+#else
+ return x;
+#endif
+}
+inline uint64 HostToNet64(uint64 x) {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ return ByteSwap(x);
+#else
+ return x;
+#endif
+}
+
+} // namespace base
+
+
+#endif // BASE_SYS_BYTEORDER_H_
diff --git a/src/base/sys_info.cc b/src/base/sys_info.cc
new file mode 100644
index 0000000..fa940bf
--- /dev/null
+++ b/src/base/sys_info.cc
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 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/sys_info.h"
+
+#include "base/time.h"
+
+namespace base {
+
+// static
+int64 SysInfo::Uptime() {
+ // This code relies on an implementation detail of TimeTicks::Now() - that
+ // its return value happens to coincide with the system uptime value in
+ // microseconds, on Win/Mac/iOS/Linux/ChromeOS and Android.
+ int64 uptime_in_microseconds = TimeTicks::Now().ToInternalValue();
+ return uptime_in_microseconds / 1000;
+}
+
+} // namespace base
diff --git a/src/base/sys_info.h b/src/base/sys_info.h
new file mode 100644
index 0000000..e653590
--- /dev/null
+++ b/src/base/sys_info.h
@@ -0,0 +1,118 @@
+// Copyright (c) 2012 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_SYS_INFO_H_
+#define BASE_SYS_INFO_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "build/build_config.h"
+
+namespace base {
+
+class BASE_EXPORT SysInfo {
+ public:
+ // Return the number of logical processors/cores on the current machine.
+ static int NumberOfProcessors();
+
+ // Return the number of bytes of physical memory on the current machine.
+ static int64 AmountOfPhysicalMemory();
+
+#if !defined(OS_STARBOARD)
+ // Return the number of bytes of current available physical memory on the
+ // machine.
+ static int64 AmountOfAvailablePhysicalMemory();
+#endif
+
+ // Return the number of megabytes of physical memory on the current machine.
+ static int AmountOfPhysicalMemoryMB() {
+ return static_cast<int>(AmountOfPhysicalMemory() / 1024 / 1024);
+ }
+
+ // Return the available disk space in bytes on the volume containing |path|,
+ // or -1 on failure.
+ static int64 AmountOfFreeDiskSpace(const FilePath& path);
+
+ // Returns system uptime in milliseconds.
+ static int64 Uptime();
+
+#if !defined(OS_STARBOARD)
+ // Returns the name of the host operating system.
+ static std::string OperatingSystemName();
+
+ // Returns the version of the host operating system.
+ static std::string OperatingSystemVersion();
+#endif
+
+ // Retrieves detailed numeric values for the OS version.
+ // TODO(port): Implement a Linux version of this method and enable the
+ // corresponding unit test.
+ // DON'T USE THIS ON THE MAC OR WINDOWS to determine the current OS release
+ // for OS version-specific feature checks and workarounds. If you must use
+ // an OS version check instead of a feature check, use the base::mac::IsOS*
+ // family from base/mac/mac_util.h, or base::win::GetVersion from
+ // base/win/windows_version.h.
+ static void OperatingSystemVersionNumbers(int32* major_version,
+ int32* minor_version,
+ int32* bugfix_version);
+
+#if !defined(OS_STARBOARD)
+ // Returns the architecture of the running operating system.
+ // Exact return value may differ across platforms.
+ // e.g. a 32-bit x86 kernel on a 64-bit capable CPU will return "x86",
+ // whereas a x86-64 kernel on the same CPU will return "x86_64"
+ static std::string OperatingSystemArchitecture();
+
+ // Avoid using this. Use base/cpu.h to get information about the CPU instead.
+ // http://crbug.com/148884
+ // Returns the CPU model name of the system. If it can not be figured out,
+ // an empty string is returned.
+ static std::string CPUModelName();
+
+ // Return the smallest amount of memory (in bytes) which the VM system will
+ // allocate.
+ static size_t VMAllocationGranularity();
+#endif
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+ // Returns the maximum SysV shared memory segment size.
+ static size_t MaxSharedMemorySize();
+#endif // defined(OS_POSIX) && !defined(OS_MACOSX)
+
+#if defined(OS_CHROMEOS)
+ // Returns the name of the version entry we wish to look up in the
+ // Linux Standard Base release information file.
+ static std::string GetLinuxStandardBaseVersionKey();
+
+ // Parses /etc/lsb-release to get version information for Google Chrome OS.
+ // Declared here so it can be exposed for unit testing.
+ static void ParseLsbRelease(const std::string& lsb_release,
+ int32* major_version,
+ int32* minor_version,
+ int32* bugfix_version);
+
+ // Returns the path to the lsb-release file.
+ static FilePath GetLsbReleaseFilePath();
+#endif // defined(OS_CHROMEOS)
+
+#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
+ // Returns the Android build's codename.
+ static std::string GetAndroidBuildCodename();
+
+ // Returns the Android build ID.
+ static std::string GetAndroidBuildID();
+
+ // Returns the device's name.
+ static std::string GetDeviceName();
+
+ static int DalvikHeapSizeMB();
+#endif // defined(OS_ANDROID)
+};
+
+} // namespace base
+
+#endif // BASE_SYS_INFO_H_
diff --git a/src/base/sys_info_android.cc b/src/base/sys_info_android.cc
new file mode 100644
index 0000000..c0bbe44
--- /dev/null
+++ b/src/base/sys_info_android.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2012 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/sys_info.h"
+
+#include <sys/system_properties.h>
+
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "base/string_piece.h"
+
+namespace {
+
+// Default version of Android to fall back to when actual version numbers
+// cannot be acquired.
+// TODO(dfalcantara): Keep this reasonably up to date with the latest publicly
+// available version of Android.
+static const int kDefaultAndroidMajorVersion = 4;
+static const int kDefaultAndroidMinorVersion = 0;
+static const int kDefaultAndroidBugfixVersion = 3;
+
+// Parse out the OS version numbers from the system properties.
+void ParseOSVersionNumbers(const char* os_version_str,
+ int32 *major_version,
+ int32 *minor_version,
+ int32 *bugfix_version) {
+ if (os_version_str[0]) {
+ // Try to parse out the version numbers from the string.
+ int num_read = sscanf(os_version_str, "%d.%d.%d", major_version,
+ minor_version, bugfix_version);
+
+ if (num_read > 0) {
+ // If we don't have a full set of version numbers, make the extras 0.
+ if (num_read < 2) *minor_version = 0;
+ if (num_read < 3) *bugfix_version = 0;
+ return;
+ }
+ }
+
+ // For some reason, we couldn't parse the version number string.
+ *major_version = kDefaultAndroidMajorVersion;
+ *minor_version = kDefaultAndroidMinorVersion;
+ *bugfix_version = kDefaultAndroidBugfixVersion;
+}
+
+int ParseHeapSize(const base::StringPiece& str) {
+ const int64 KB = 1024;
+ const int64 MB = 1024 * KB;
+ const int64 GB = 1024 * MB;
+ CHECK_GT(str.size(), 0u);
+ int64 factor = 1;
+ size_t length = str.size();
+ if (str[length - 1] == 'k') {
+ factor = KB;
+ length--;
+ } else if (str[length - 1] == 'm') {
+ factor = MB;
+ length--;
+ } else if (str[length - 1] == 'g') {
+ factor = GB;
+ length--;
+ } else {
+ CHECK('0' <= str[length - 1] && str[length - 1] <= '9');
+ }
+ int64 result = 0;
+ bool parsed = base::StringToInt64(str.substr(0, length), &result);
+ CHECK(parsed);
+ result = result * factor / MB;
+ // dalvik.vm.heapsize property is writable by user,
+ // truncate it to reasonable value to avoid overflows later.
+ result = std::min<int64>(std::max<int64>(32, result), 1024);
+ return static_cast<int>(result);
+}
+
+int GetDalvikHeapSizeMB() {
+ char heap_size_str[PROP_VALUE_MAX];
+ __system_property_get("dalvik.vm.heapsize", heap_size_str);
+ return ParseHeapSize(heap_size_str);
+}
+
+} // anonymous namespace
+
+namespace base {
+
+std::string SysInfo::OperatingSystemName() {
+ return "Android";
+}
+
+std::string SysInfo::GetAndroidBuildCodename() {
+ char os_version_codename_str[PROP_VALUE_MAX];
+ __system_property_get("ro.build.version.codename", os_version_codename_str);
+ return std::string(os_version_codename_str);
+}
+
+std::string SysInfo::GetAndroidBuildID() {
+ char os_build_id_str[PROP_VALUE_MAX];
+ __system_property_get("ro.build.id", os_build_id_str);
+ return std::string(os_build_id_str);
+}
+
+std::string SysInfo::GetDeviceName() {
+ char device_model_str[PROP_VALUE_MAX];
+ __system_property_get("ro.product.model", device_model_str);
+ return std::string(device_model_str);
+}
+
+void SysInfo::OperatingSystemVersionNumbers(int32* major_version,
+ int32* minor_version,
+ int32* bugfix_version) {
+ // Read the version number string out from the properties.
+ char os_version_str[PROP_VALUE_MAX];
+ __system_property_get("ro.build.version.release", os_version_str);
+
+ // Parse out the numbers.
+ ParseOSVersionNumbers(os_version_str, major_version, minor_version,
+ bugfix_version);
+}
+
+int SysInfo::DalvikHeapSizeMB() {
+ static int heap_size = GetDalvikHeapSizeMB();
+ return heap_size;
+}
+
+} // namespace base
diff --git a/src/base/sys_info_chromeos.cc b/src/base/sys_info_chromeos.cc
new file mode 100644
index 0000000..92a3f0c
--- /dev/null
+++ b/src/base/sys_info_chromeos.cc
@@ -0,0 +1,121 @@
+// Copyright (c) 2012 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/sys_info.h"
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/string_number_conversions.h"
+#include "base/string_piece.h"
+#include "base/string_tokenizer.h"
+#include "base/threading/thread_restrictions.h"
+
+#include <execinfo.h>
+
+namespace base {
+
+static const char* kLinuxStandardBaseVersionKeys[] = {
+ "CHROMEOS_RELEASE_VERSION",
+ "GOOGLE_RELEASE",
+ "DISTRIB_RELEASE",
+ NULL
+};
+
+const char kLinuxStandardBaseReleaseFile[] = "/etc/lsb-release";
+
+struct ChromeOSVersionNumbers {
+ ChromeOSVersionNumbers()
+ : major_version(0),
+ minor_version(0),
+ bugfix_version(0),
+ parsed(false) {
+ }
+
+ int32 major_version;
+ int32 minor_version;
+ int32 bugfix_version;
+ bool parsed;
+};
+
+static base::LazyInstance<ChromeOSVersionNumbers>
+ g_chrome_os_version_numbers = LAZY_INSTANCE_INITIALIZER;
+
+// static
+void SysInfo::OperatingSystemVersionNumbers(int32* major_version,
+ int32* minor_version,
+ int32* bugfix_version) {
+ if (!g_chrome_os_version_numbers.Get().parsed) {
+ // The other implementations of SysInfo don't block on the disk.
+ // See http://code.google.com/p/chromium/issues/detail?id=60394
+ // Perhaps the caller ought to cache this?
+ // Temporary allowing while we work the bug out.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ FilePath path(kLinuxStandardBaseReleaseFile);
+ std::string contents;
+ if (file_util::ReadFileToString(path, &contents)) {
+ g_chrome_os_version_numbers.Get().parsed = true;
+ ParseLsbRelease(contents,
+ &(g_chrome_os_version_numbers.Get().major_version),
+ &(g_chrome_os_version_numbers.Get().minor_version),
+ &(g_chrome_os_version_numbers.Get().bugfix_version));
+ }
+ }
+ *major_version = g_chrome_os_version_numbers.Get().major_version;
+ *minor_version = g_chrome_os_version_numbers.Get().minor_version;
+ *bugfix_version = g_chrome_os_version_numbers.Get().bugfix_version;
+}
+
+// static
+std::string SysInfo::GetLinuxStandardBaseVersionKey() {
+ return std::string(kLinuxStandardBaseVersionKeys[0]);
+}
+
+// static
+void SysInfo::ParseLsbRelease(const std::string& lsb_release,
+ int32* major_version,
+ int32* minor_version,
+ int32* bugfix_version) {
+ size_t version_key_index = std::string::npos;
+ for (int i = 0; kLinuxStandardBaseVersionKeys[i] != NULL; ++i) {
+ version_key_index = lsb_release.find(kLinuxStandardBaseVersionKeys[i]);
+ if (std::string::npos != version_key_index) {
+ break;
+ }
+ }
+ if (std::string::npos == version_key_index) {
+ return;
+ }
+
+ size_t start_index = lsb_release.find_first_of('=', version_key_index);
+ start_index++; // Move past '='.
+ size_t length = lsb_release.find_first_of('\n', start_index) - start_index;
+ std::string version = lsb_release.substr(start_index, length);
+ StringTokenizer tokenizer(version, ".");
+ for (int i = 0; i < 3 && tokenizer.GetNext(); ++i) {
+ if (0 == i) {
+ StringToInt(StringPiece(tokenizer.token_begin(),
+ tokenizer.token_end()),
+ major_version);
+ *minor_version = *bugfix_version = 0;
+ } else if (1 == i) {
+ StringToInt(StringPiece(tokenizer.token_begin(),
+ tokenizer.token_end()),
+ minor_version);
+ } else { // 2 == i
+ StringToInt(StringPiece(tokenizer.token_begin(),
+ tokenizer.token_end()),
+ bugfix_version);
+ }
+ }
+}
+
+// static
+FilePath SysInfo::GetLsbReleaseFilePath() {
+ return FilePath(kLinuxStandardBaseReleaseFile);
+}
+
+} // namespace base
diff --git a/src/base/sys_info_freebsd.cc b/src/base/sys_info_freebsd.cc
new file mode 100644
index 0000000..832b359
--- /dev/null
+++ b/src/base/sys_info_freebsd.cc
@@ -0,0 +1,36 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/sys_info.h"
+
+#include <sys/sysctl.h>
+
+#include "base/logging.h"
+
+namespace base {
+
+int64 SysInfo::AmountOfPhysicalMemory() {
+ int pages, page_size;
+ size_t size = sizeof(pages);
+ sysctlbyname("vm.stats.vm.v_page_count", &pages, &size, NULL, 0);
+ sysctlbyname("vm.stats.vm.v_page_size", &page_size, &size, NULL, 0);
+ if (pages == -1 || page_size == -1) {
+ NOTREACHED();
+ return 0;
+ }
+ return static_cast<int64>(pages) * page_size;
+}
+
+// static
+size_t SysInfo::MaxSharedMemorySize() {
+ size_t limit;
+ size_t size = sizeof(limit);
+ if (sysctlbyname("kern.ipc.shmmax", &limit, &size, NULL, 0) < 0) {
+ NOTREACHED();
+ return 0;
+ }
+ return limit;
+}
+
+} // namespace base
diff --git a/src/base/sys_info_ios.mm b/src/base/sys_info_ios.mm
new file mode 100644
index 0000000..7863f6b
--- /dev/null
+++ b/src/base/sys_info_ios.mm
@@ -0,0 +1,90 @@
+// Copyright (c) 2012 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/sys_info.h"
+
+#import <UIKit/UIKit.h>
+#include <mach/mach.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include "base/logging.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/sys_string_conversions.h"
+
+namespace base {
+
+// static
+std::string SysInfo::OperatingSystemName() {
+ static dispatch_once_t get_system_name_once;
+ static std::string* system_name;
+ dispatch_once(&get_system_name_once, ^{
+ base::mac::ScopedNSAutoreleasePool pool;
+ system_name = new std::string(
+ SysNSStringToUTF8([[UIDevice currentDevice] systemName]));
+ });
+ // Examples of returned value: 'iPhone OS' on iPad 5.1.1
+ // and iPhone 5.1.1.
+ return *system_name;
+}
+
+// static
+std::string SysInfo::OperatingSystemVersion() {
+ static dispatch_once_t get_system_version_once;
+ static std::string* system_version;
+ dispatch_once(&get_system_version_once, ^{
+ base::mac::ScopedNSAutoreleasePool pool;
+ system_version = new std::string(
+ SysNSStringToUTF8([[UIDevice currentDevice] systemVersion]));
+ });
+ return *system_version;
+}
+
+// static
+void SysInfo::OperatingSystemVersionNumbers(int32* major_version,
+ int32* minor_version,
+ int32* bugfix_version) {
+ base::mac::ScopedNSAutoreleasePool pool;
+ std::string system_version = OperatingSystemVersion();
+ if (!system_version.empty()) {
+ // Try to parse out the version numbers from the string.
+ int num_read = sscanf(system_version.c_str(), "%d.%d.%d", major_version,
+ minor_version, bugfix_version);
+ if (num_read < 1)
+ *major_version = 0;
+ if (num_read < 2)
+ *minor_version = 0;
+ if (num_read < 3)
+ *bugfix_version = 0;
+ }
+}
+
+// static
+int64 SysInfo::AmountOfPhysicalMemory() {
+ struct host_basic_info hostinfo;
+ mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
+ base::mac::ScopedMachPort host(mach_host_self());
+ int result = host_info(host,
+ HOST_BASIC_INFO,
+ reinterpret_cast<host_info_t>(&hostinfo),
+ &count);
+ if (result != KERN_SUCCESS) {
+ NOTREACHED();
+ return 0;
+ }
+ DCHECK_EQ(HOST_BASIC_INFO_COUNT, count);
+ return static_cast<int64>(hostinfo.max_mem);
+}
+
+// static
+std::string SysInfo::CPUModelName() {
+ char name[256];
+ size_t len = arraysize(name);
+ if (sysctlbyname("machdep.cpu.brand_string", &name, &len, NULL, 0) == 0)
+ return name;
+ return std::string();
+}
+
+} // namespace base
diff --git a/src/base/sys_info_linux.cc b/src/base/sys_info_linux.cc
new file mode 100644
index 0000000..6683674
--- /dev/null
+++ b/src/base/sys_info_linux.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/sys_info.h"
+
+#include <limits>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+
+namespace base {
+
+int64 SysInfo::AmountOfPhysicalMemory() {
+ long pages = sysconf(_SC_PHYS_PAGES);
+ long page_size = sysconf(_SC_PAGE_SIZE);
+ if (pages == -1 || page_size == -1) {
+ NOTREACHED();
+ return 0;
+ }
+
+ return static_cast<int64>(pages) * page_size;
+}
+
+// static
+int64 SysInfo::AmountOfAvailablePhysicalMemory() {
+ long available_pages = sysconf(_SC_AVPHYS_PAGES);
+ long page_size = sysconf(_SC_PAGE_SIZE);
+ if (available_pages == -1 || page_size == -1) {
+ NOTREACHED();
+ return 0;
+ }
+ return static_cast<int64>(available_pages) * page_size;
+}
+
+// static
+size_t SysInfo::MaxSharedMemorySize() {
+ static int64 limit;
+ static bool limit_valid = false;
+ if (!limit_valid) {
+ std::string contents;
+ file_util::ReadFileToString(FilePath("/proc/sys/kernel/shmmax"), &contents);
+ DCHECK(!contents.empty());
+ if (!contents.empty() && contents[contents.length() - 1] == '\n') {
+ contents.erase(contents.length() - 1);
+ }
+ if (base::StringToInt64(contents, &limit)) {
+ DCHECK(limit >= 0);
+ DCHECK(static_cast<uint64>(limit) <= std::numeric_limits<size_t>::max());
+ limit_valid = true;
+ } else {
+ NOTREACHED();
+ return 0;
+ }
+ }
+ return static_cast<size_t>(limit);
+}
+
+// static
+std::string SysInfo::CPUModelName() {
+ const char kModelNamePrefix[] = "model name";
+ std::string contents;
+ file_util::ReadFileToString(FilePath("/proc/cpuinfo"), &contents);
+ DCHECK(!contents.empty());
+ if (!contents.empty()) {
+ std::istringstream iss(contents);
+ std::string line;
+ while (std::getline(iss, line)){
+ if (line.compare(0, strlen(kModelNamePrefix), kModelNamePrefix) == 0) {
+ size_t pos = line.find(": ");
+ return line.substr(pos + 2);
+ }
+ }
+ }
+ return std::string();
+}
+
+} // namespace base
diff --git a/src/base/sys_info_mac.cc b/src/base/sys_info_mac.cc
new file mode 100644
index 0000000..b444565
--- /dev/null
+++ b/src/base/sys_info_mac.cc
@@ -0,0 +1,88 @@
+// Copyright (c) 2012 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/sys_info.h"
+
+#include <ApplicationServices/ApplicationServices.h>
+#include <CoreServices/CoreServices.h>
+#include <mach/mach_host.h>
+#include <mach/mach_init.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include "base/logging.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/stringprintf.h"
+
+namespace base {
+
+// static
+std::string SysInfo::OperatingSystemName() {
+ return "Mac OS X";
+}
+
+// static
+std::string SysInfo::OperatingSystemVersion() {
+ int32 major, minor, bugfix;
+ OperatingSystemVersionNumbers(&major, &minor, &bugfix);
+ return base::StringPrintf("%d.%d.%d", major, minor, bugfix);
+}
+
+// static
+void SysInfo::OperatingSystemVersionNumbers(int32* major_version,
+ int32* minor_version,
+ int32* bugfix_version) {
+ Gestalt(gestaltSystemVersionMajor,
+ reinterpret_cast<SInt32*>(major_version));
+ Gestalt(gestaltSystemVersionMinor,
+ reinterpret_cast<SInt32*>(minor_version));
+ Gestalt(gestaltSystemVersionBugFix,
+ reinterpret_cast<SInt32*>(bugfix_version));
+}
+
+// static
+int64 SysInfo::AmountOfPhysicalMemory() {
+ struct host_basic_info hostinfo;
+ mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
+ base::mac::ScopedMachPort host(mach_host_self());
+ int result = host_info(host,
+ HOST_BASIC_INFO,
+ reinterpret_cast<host_info_t>(&hostinfo),
+ &count);
+ if (result != KERN_SUCCESS) {
+ NOTREACHED();
+ return 0;
+ }
+ DCHECK_EQ(HOST_BASIC_INFO_COUNT, count);
+ return static_cast<int64>(hostinfo.max_mem);
+}
+
+// static
+int64 SysInfo::AmountOfAvailablePhysicalMemory() {
+ base::mac::ScopedMachPort host(mach_host_self());
+ vm_statistics_data_t vm_info;
+ mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
+
+ if (host_statistics(host.get(),
+ HOST_VM_INFO,
+ reinterpret_cast<host_info_t>(&vm_info),
+ &count) != KERN_SUCCESS) {
+ NOTREACHED();
+ return 0;
+ }
+
+ return static_cast<int64>(
+ vm_info.free_count - vm_info.speculative_count) * PAGE_SIZE;
+}
+
+// static
+std::string SysInfo::CPUModelName() {
+ char name[256];
+ size_t len = arraysize(name);
+ if (sysctlbyname("machdep.cpu.brand_string", &name, &len, NULL, 0) == 0)
+ return name;
+ return std::string();
+}
+
+} // namespace base
diff --git a/src/base/sys_info_openbsd.cc b/src/base/sys_info_openbsd.cc
new file mode 100644
index 0000000..b9aec20
--- /dev/null
+++ b/src/base/sys_info_openbsd.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/sys_info.h"
+
+#include <sys/param.h>
+#include <sys/shm.h>
+#include <sys/sysctl.h>
+
+#include "base/logging.h"
+
+namespace base {
+
+int SysInfo::NumberOfProcessors() {
+ int mib[] = { CTL_HW, HW_NCPU };
+ int ncpu;
+ size_t size = sizeof(ncpu);
+ if (sysctl(mib, arraysize(mib), &ncpu, &size, NULL, 0) == -1) {
+ NOTREACHED();
+ return 1;
+ }
+ return ncpu;
+}
+
+int64 SysInfo::AmountOfPhysicalMemory() {
+ long pages = sysconf(_SC_PHYS_PAGES);
+ long page_size = sysconf(_SC_PAGESIZE);
+ if (pages == -1 || page_size == -1) {
+ NOTREACHED();
+ return 0;
+ }
+
+ return static_cast<int64>(pages) * page_size;
+}
+
+size_t SysInfo::MaxSharedMemorySize() {
+ int mib[] = { CTL_KERN, KERN_SHMINFO, KERN_SHMINFO_SHMMAX };
+ size_t limit;
+ size_t size = sizeof(limit);
+ if (sysctl(mib, arraysize(mib), &limit, &size, NULL, 0) < 0) {
+ NOTREACHED();
+ return 0;
+ }
+ return limit;
+}
+
+} // namespace base
diff --git a/src/base/sys_info_posix.cc b/src/base/sys_info_posix.cc
new file mode 100644
index 0000000..f2119cd
--- /dev/null
+++ b/src/base/sys_info_posix.cc
@@ -0,0 +1,98 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/sys_info.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/utf_string_conversions.h"
+
+#if defined(OS_ANDROID)
+#include <sys/vfs.h>
+#define statvfs statfs // Android uses a statvfs-like statfs struct and call.
+#else
+#include <sys/statvfs.h>
+#endif
+
+namespace base {
+
+#if !defined(OS_OPENBSD)
+int SysInfo::NumberOfProcessors() {
+ // It seems that sysconf returns the number of "logical" processors on both
+ // Mac and Linux. So we get the number of "online logical" processors.
+ long res = sysconf(_SC_NPROCESSORS_ONLN);
+ if (res == -1) {
+ NOTREACHED();
+ return 1;
+ }
+
+ return static_cast<int>(res);
+}
+#endif
+
+// static
+int64 SysInfo::AmountOfFreeDiskSpace(const FilePath& path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ struct statvfs stats;
+ if (statvfs(path.value().c_str(), &stats) != 0) {
+ return -1;
+ }
+ return static_cast<int64>(stats.f_bavail) * stats.f_frsize;
+}
+
+#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
+// static
+std::string SysInfo::OperatingSystemName() {
+ struct utsname info;
+ if (uname(&info) < 0) {
+ NOTREACHED();
+ return "";
+ }
+ return std::string(info.sysname);
+}
+#endif
+
+#if !defined(OS_MACOSX)
+// static
+std::string SysInfo::OperatingSystemVersion() {
+ struct utsname info;
+ if (uname(&info) < 0) {
+ NOTREACHED();
+ return "";
+ }
+ return std::string(info.release);
+}
+#endif
+
+// static
+std::string SysInfo::OperatingSystemArchitecture() {
+ struct utsname info;
+ if (uname(&info) < 0) {
+ NOTREACHED();
+ return "";
+ }
+ std::string arch(info.machine);
+ if (arch == "i386" || arch == "i486" || arch == "i586" || arch == "i686") {
+ arch = "x86";
+ } else if (arch == "amd64") {
+ arch = "x86_64";
+ }
+ return arch;
+}
+
+// static
+size_t SysInfo::VMAllocationGranularity() {
+ return getpagesize();
+}
+
+} // namespace base
diff --git a/src/base/sys_info_starboard.cc b/src/base/sys_info_starboard.cc
new file mode 100644
index 0000000..30b5f75
--- /dev/null
+++ b/src/base/sys_info_starboard.cc
@@ -0,0 +1,43 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/sys_info.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "starboard/system.h"
+
+namespace base {
+
+// static
+int SysInfo::NumberOfProcessors() {
+ return SbSystemGetNumberOfProcessors();
+}
+
+// static
+int64 SysInfo::AmountOfFreeDiskSpace(const FilePath& path) {
+ // TODO: This is referred to ONLY by disk_cache::BackendImpl, which I do
+ // not think is currently used in Cobalt. There's no need to implement this
+ // unless we want to use it for something. If not, we should remove the
+ // reference to it, and this amazing implementation.
+ NOTIMPLEMENTED();
+ return SB_INT64_C(1) * 1024 * 1024 * 1024;
+}
+
+// static
+int64 SysInfo::AmountOfPhysicalMemory() {
+ return SbSystemGetTotalMemory();
+}
+
+} // namespace base
diff --git a/src/base/sys_info_unittest.cc b/src/base/sys_info_unittest.cc
new file mode 100644
index 0000000..04404d4
--- /dev/null
+++ b/src/base/sys_info_unittest.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2012 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/file_util.h"
+#include "base/sys_info.h"
+#include "base/threading/platform_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+typedef PlatformTest SysInfoTest;
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) && !defined(__LB_SHELL__)
+TEST_F(SysInfoTest, MaxSharedMemorySize) {
+ // We aren't actually testing that it's correct, just that it's sane.
+ EXPECT_GT(base::SysInfo::MaxSharedMemorySize(), 0u);
+}
+#endif
+
+TEST_F(SysInfoTest, NumProcs) {
+ // We aren't actually testing that it's correct, just that it's sane.
+ EXPECT_GE(base::SysInfo::NumberOfProcessors(), 1);
+}
+
+TEST_F(SysInfoTest, AmountOfMem) {
+ // We aren't actually testing that it's correct, just that it's sane.
+ EXPECT_GT(base::SysInfo::AmountOfPhysicalMemory(), 0);
+ EXPECT_GT(base::SysInfo::AmountOfPhysicalMemoryMB(), 0);
+}
+
+#if !defined(__LB_SHELL__) && !defined(OS_STARBOARD)
+// Disk space queries are not available cross-platform.
+
+TEST_F(SysInfoTest, AmountOfFreeDiskSpace) {
+ // We aren't actually testing that it's correct, just that it's sane.
+ FilePath tmp_path;
+ ASSERT_TRUE(file_util::GetTempDir(&tmp_path));
+ EXPECT_GT(base::SysInfo::AmountOfFreeDiskSpace(tmp_path), 0)
+ << tmp_path.value();
+}
+#endif
+
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
+TEST_F(SysInfoTest, OperatingSystemVersionNumbers) {
+ int32 os_major_version = -1;
+ int32 os_minor_version = -1;
+ int32 os_bugfix_version = -1;
+ base::SysInfo::OperatingSystemVersionNumbers(&os_major_version,
+ &os_minor_version,
+ &os_bugfix_version);
+ EXPECT_GT(os_major_version, -1);
+ EXPECT_GT(os_minor_version, -1);
+ EXPECT_GT(os_bugfix_version, -1);
+}
+#endif
+
+TEST_F(SysInfoTest, Uptime) {
+ int64 up_time_1 = base::SysInfo::Uptime();
+ // UpTime() is implemented internally using TimeTicks::Now(), which documents
+ // system resolution as being 1-15ms. Sleep a little longer than that.
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20));
+ int64 up_time_2 = base::SysInfo::Uptime();
+ EXPECT_GT(up_time_1, 0);
+ EXPECT_GT(up_time_2, up_time_1);
+}
+
+#if defined(OS_CHROMEOS)
+TEST_F(SysInfoTest, GoogleChromeOSVersionNumbers) {
+ int32 os_major_version = -1;
+ int32 os_minor_version = -1;
+ int32 os_bugfix_version = -1;
+ std::string lsb_release("FOO=1234123.34.5\n");
+ lsb_release.append(base::SysInfo::GetLinuxStandardBaseVersionKey());
+ lsb_release.append("=1.2.3.4\n");
+ base::SysInfo::ParseLsbRelease(lsb_release,
+ &os_major_version,
+ &os_minor_version,
+ &os_bugfix_version);
+ EXPECT_EQ(1, os_major_version);
+ EXPECT_EQ(2, os_minor_version);
+ EXPECT_EQ(3, os_bugfix_version);
+}
+
+TEST_F(SysInfoTest, GoogleChromeOSVersionNumbersFirst) {
+ int32 os_major_version = -1;
+ int32 os_minor_version = -1;
+ int32 os_bugfix_version = -1;
+ std::string lsb_release(base::SysInfo::GetLinuxStandardBaseVersionKey());
+ lsb_release.append("=1.2.3.4\n");
+ lsb_release.append("FOO=1234123.34.5\n");
+ base::SysInfo::ParseLsbRelease(lsb_release,
+ &os_major_version,
+ &os_minor_version,
+ &os_bugfix_version);
+ EXPECT_EQ(1, os_major_version);
+ EXPECT_EQ(2, os_minor_version);
+ EXPECT_EQ(3, os_bugfix_version);
+}
+
+TEST_F(SysInfoTest, GoogleChromeOSNoVersionNumbers) {
+ int32 os_major_version = -1;
+ int32 os_minor_version = -1;
+ int32 os_bugfix_version = -1;
+ std::string lsb_release("FOO=1234123.34.5\n");
+ base::SysInfo::ParseLsbRelease(lsb_release,
+ &os_major_version,
+ &os_minor_version,
+ &os_bugfix_version);
+ EXPECT_EQ(-1, os_major_version);
+ EXPECT_EQ(-1, os_minor_version);
+ EXPECT_EQ(-1, os_bugfix_version);
+}
+
+#endif // OS_CHROMEOS
diff --git a/src/base/sys_info_win.cc b/src/base/sys_info_win.cc
new file mode 100644
index 0000000..98d2f7c
--- /dev/null
+++ b/src/base/sys_info_win.cc
@@ -0,0 +1,127 @@
+// Copyright (c) 2012 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/sys_info.h"
+
+#include <windows.h>
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stringprintf.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/win/windows_version.h"
+
+namespace base {
+
+// static
+int SysInfo::NumberOfProcessors() {
+ return win::OSInfo::GetInstance()->processors();
+}
+
+// static
+int64 SysInfo::AmountOfPhysicalMemory() {
+ MEMORYSTATUSEX memory_info;
+ memory_info.dwLength = sizeof(memory_info);
+ if (!GlobalMemoryStatusEx(&memory_info)) {
+ NOTREACHED();
+ return 0;
+ }
+
+ int64 rv = static_cast<int64>(memory_info.ullTotalPhys);
+ if (rv < 0)
+ rv = kint64max;
+ return rv;
+}
+
+// static
+int64 SysInfo::AmountOfAvailablePhysicalMemory() {
+ MEMORYSTATUSEX memory_info;
+ memory_info.dwLength = sizeof(memory_info);
+ if (!GlobalMemoryStatusEx(&memory_info)) {
+ NOTREACHED();
+ return 0;
+ }
+
+ int64 rv = static_cast<int64>(memory_info.ullAvailPhys);
+ if (rv < 0)
+ rv = kint64max;
+ return rv;
+}
+
+// static
+int64 SysInfo::AmountOfFreeDiskSpace(const FilePath& path) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ ULARGE_INTEGER available, total, free;
+ if (!GetDiskFreeSpaceExW(path.value().c_str(), &available, &total, &free)) {
+ return -1;
+ }
+ int64 rv = static_cast<int64>(available.QuadPart);
+ if (rv < 0)
+ rv = kint64max;
+ return rv;
+}
+
+// static
+std::string SysInfo::OperatingSystemName() {
+ return "Windows NT";
+}
+
+// static
+std::string SysInfo::OperatingSystemVersion() {
+ win::OSInfo* os_info = win::OSInfo::GetInstance();
+ win::OSInfo::VersionNumber version_number = os_info->version_number();
+ std::string version(StringPrintf("%d.%d", version_number.major,
+ version_number.minor));
+ win::OSInfo::ServicePack service_pack = os_info->service_pack();
+ if (service_pack.major != 0) {
+ version += StringPrintf(" SP%d", service_pack.major);
+ if (service_pack.minor != 0)
+ version += StringPrintf(".%d", service_pack.minor);
+ }
+ return version;
+}
+
+// TODO: Implement OperatingSystemVersionComplete, which would include
+// patchlevel/service pack number.
+// See chrome/browser/feedback/feedback_util.h, FeedbackUtil::SetOSVersion.
+
+// static
+std::string SysInfo::OperatingSystemArchitecture() {
+ win::OSInfo::WindowsArchitecture arch =
+ win::OSInfo::GetInstance()->architecture();
+ switch (arch) {
+ case win::OSInfo::X86_ARCHITECTURE:
+ return "x86";
+ case win::OSInfo::X64_ARCHITECTURE:
+ return "x86_64";
+ case win::OSInfo::IA64_ARCHITECTURE:
+ return "ia64";
+ default:
+ return "";
+ }
+}
+
+// static
+std::string SysInfo::CPUModelName() {
+ return win::OSInfo::GetInstance()->processor_model_name();
+}
+
+// static
+size_t SysInfo::VMAllocationGranularity() {
+ return win::OSInfo::GetInstance()->allocation_granularity();
+}
+
+// static
+void SysInfo::OperatingSystemVersionNumbers(int32* major_version,
+ int32* minor_version,
+ int32* bugfix_version) {
+ win::OSInfo* os_info = win::OSInfo::GetInstance();
+ *major_version = os_info->version_number().major;
+ *minor_version = os_info->version_number().minor;
+ *bugfix_version = 0;
+}
+
+} // namespace base
diff --git a/src/base/sys_string_conversions.h b/src/base/sys_string_conversions.h
new file mode 100644
index 0000000..3f7631c
--- /dev/null
+++ b/src/base/sys_string_conversions.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2012 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_SYS_STRING_CONVERSIONS_H_
+#define BASE_SYS_STRING_CONVERSIONS_H_
+
+// Provides system-dependent string type conversions for cases where it's
+// necessary to not use ICU. Generally, you should not need this in Chrome,
+// but it is used in some shared code. Dependencies should be minimal.
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/string16.h"
+#include "base/string_piece.h"
+
+#if defined(OS_MACOSX)
+#include <CoreFoundation/CoreFoundation.h>
+#ifdef __OBJC__
+@class NSString;
+#else
+class NSString;
+#endif
+#endif // OS_MACOSX
+
+namespace base {
+
+// Converts between wide and UTF-8 representations of a string. On error, the
+// result is system-dependent.
+BASE_EXPORT std::string SysWideToUTF8(const std::wstring& wide);
+BASE_EXPORT std::wstring SysUTF8ToWide(const StringPiece& utf8);
+
+// Converts between wide and the system multi-byte representations of a string.
+// DANGER: This will lose information and can change (on Windows, this can
+// change between reboots).
+BASE_EXPORT std::string SysWideToNativeMB(const std::wstring& wide);
+BASE_EXPORT std::wstring SysNativeMBToWide(const StringPiece& native_mb);
+
+// Windows-specific ------------------------------------------------------------
+
+#if defined(OS_WIN)
+
+// Converts between 8-bit and wide strings, using the given code page. The
+// code page identifier is one accepted by the Windows function
+// MultiByteToWideChar().
+BASE_EXPORT std::wstring SysMultiByteToWide(const StringPiece& mb,
+ uint32 code_page);
+BASE_EXPORT std::string SysWideToMultiByte(const std::wstring& wide,
+ uint32 code_page);
+
+#endif // defined(OS_WIN)
+
+// Mac-specific ----------------------------------------------------------------
+
+#if defined(OS_MACOSX)
+
+// Converts between STL strings and CFStringRefs/NSStrings.
+
+// Creates a string, and returns it with a refcount of 1. You are responsible
+// for releasing it. Returns NULL on failure.
+BASE_EXPORT CFStringRef SysUTF8ToCFStringRef(const std::string& utf8);
+BASE_EXPORT CFStringRef SysUTF16ToCFStringRef(const string16& utf16);
+
+// Same, but returns an autoreleased NSString.
+BASE_EXPORT NSString* SysUTF8ToNSString(const std::string& utf8);
+BASE_EXPORT NSString* SysUTF16ToNSString(const string16& utf16);
+
+// Converts a CFStringRef to an STL string. Returns an empty string on failure.
+BASE_EXPORT std::string SysCFStringRefToUTF8(CFStringRef ref);
+BASE_EXPORT string16 SysCFStringRefToUTF16(CFStringRef ref);
+
+// Same, but accepts NSString input. Converts nil NSString* to the appropriate
+// string type of length 0.
+BASE_EXPORT std::string SysNSStringToUTF8(NSString* ref);
+BASE_EXPORT string16 SysNSStringToUTF16(NSString* ref);
+
+#endif // defined(OS_MACOSX)
+
+} // namespace base
+
+#endif // BASE_SYS_STRING_CONVERSIONS_H_
diff --git a/src/base/sys_string_conversions_mac.mm b/src/base/sys_string_conversions_mac.mm
new file mode 100644
index 0000000..d1e3743
--- /dev/null
+++ b/src/base/sys_string_conversions_mac.mm
@@ -0,0 +1,187 @@
+// Copyright (c) 2012 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/sys_string_conversions.h"
+
+#import <Foundation/Foundation.h>
+
+#include <vector>
+
+#include "base/mac/foundation_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/string_piece.h"
+
+namespace base {
+
+namespace {
+
+// Convert the supplied CFString into the specified encoding, and return it as
+// an STL string of the template type. Returns an empty string on failure.
+//
+// Do not assert in this function since it is used by the asssertion code!
+template<typename StringType>
+static StringType CFStringToSTLStringWithEncodingT(CFStringRef cfstring,
+ CFStringEncoding encoding) {
+ CFIndex length = CFStringGetLength(cfstring);
+ if (length == 0)
+ return StringType();
+
+ CFRange whole_string = CFRangeMake(0, length);
+ CFIndex out_size;
+ CFIndex converted = CFStringGetBytes(cfstring,
+ whole_string,
+ encoding,
+ 0, // lossByte
+ false, // isExternalRepresentation
+ NULL, // buffer
+ 0, // maxBufLen
+ &out_size);
+ if (converted == 0 || out_size == 0)
+ return StringType();
+
+ // out_size is the number of UInt8-sized units needed in the destination.
+ // A buffer allocated as UInt8 units might not be properly aligned to
+ // contain elements of StringType::value_type. Use a container for the
+ // proper value_type, and convert out_size by figuring the number of
+ // value_type elements per UInt8. Leave room for a NUL terminator.
+ typename StringType::size_type elements =
+ out_size * sizeof(UInt8) / sizeof(typename StringType::value_type) + 1;
+
+ std::vector<typename StringType::value_type> out_buffer(elements);
+ converted = CFStringGetBytes(cfstring,
+ whole_string,
+ encoding,
+ 0, // lossByte
+ false, // isExternalRepresentation
+ reinterpret_cast<UInt8*>(&out_buffer[0]),
+ out_size,
+ NULL); // usedBufLen
+ if (converted == 0)
+ return StringType();
+
+ out_buffer[elements - 1] = '\0';
+ return StringType(&out_buffer[0], elements - 1);
+}
+
+// Given an STL string |in| with an encoding specified by |in_encoding|,
+// convert it to |out_encoding| and return it as an STL string of the
+// |OutStringType| template type. Returns an empty string on failure.
+//
+// Do not assert in this function since it is used by the asssertion code!
+template<typename InStringType, typename OutStringType>
+static OutStringType STLStringToSTLStringWithEncodingsT(
+ const InStringType& in,
+ CFStringEncoding in_encoding,
+ CFStringEncoding out_encoding) {
+ typename InStringType::size_type in_length = in.length();
+ if (in_length == 0)
+ return OutStringType();
+
+ base::mac::ScopedCFTypeRef<CFStringRef> cfstring(
+ CFStringCreateWithBytesNoCopy(NULL,
+ reinterpret_cast<const UInt8*>(in.data()),
+ in_length *
+ sizeof(typename InStringType::value_type),
+ in_encoding,
+ false,
+ kCFAllocatorNull));
+ if (!cfstring)
+ return OutStringType();
+
+ return CFStringToSTLStringWithEncodingT<OutStringType>(cfstring,
+ out_encoding);
+}
+
+// Given an STL string |in| with an encoding specified by |in_encoding|,
+// return it as a CFStringRef. Returns NULL on failure.
+template<typename StringType>
+static CFStringRef STLStringToCFStringWithEncodingsT(
+ const StringType& in,
+ CFStringEncoding in_encoding) {
+ typename StringType::size_type in_length = in.length();
+ if (in_length == 0)
+ return CFSTR("");
+
+ return CFStringCreateWithBytes(kCFAllocatorDefault,
+ reinterpret_cast<const UInt8*>(in.data()),
+ in_length *
+ sizeof(typename StringType::value_type),
+ in_encoding,
+ false);
+}
+
+// Specify the byte ordering explicitly, otherwise CFString will be confused
+// when strings don't carry BOMs, as they typically won't.
+static const CFStringEncoding kNarrowStringEncoding = kCFStringEncodingUTF8;
+#ifdef __BIG_ENDIAN__
+static const CFStringEncoding kMediumStringEncoding = kCFStringEncodingUTF16BE;
+static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32BE;
+#elif defined(__LITTLE_ENDIAN__)
+static const CFStringEncoding kMediumStringEncoding = kCFStringEncodingUTF16LE;
+static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32LE;
+#endif // __LITTLE_ENDIAN__
+
+} // namespace
+
+// Do not assert in this function since it is used by the asssertion code!
+std::string SysWideToUTF8(const std::wstring& wide) {
+ return STLStringToSTLStringWithEncodingsT<std::wstring, std::string>(
+ wide, kWideStringEncoding, kNarrowStringEncoding);
+}
+
+// Do not assert in this function since it is used by the asssertion code!
+std::wstring SysUTF8ToWide(const StringPiece& utf8) {
+ return STLStringToSTLStringWithEncodingsT<StringPiece, std::wstring>(
+ utf8, kNarrowStringEncoding, kWideStringEncoding);
+}
+
+std::string SysWideToNativeMB(const std::wstring& wide) {
+ return SysWideToUTF8(wide);
+}
+
+std::wstring SysNativeMBToWide(const StringPiece& native_mb) {
+ return SysUTF8ToWide(native_mb);
+}
+
+CFStringRef SysUTF8ToCFStringRef(const std::string& utf8) {
+ return STLStringToCFStringWithEncodingsT(utf8, kNarrowStringEncoding);
+}
+
+CFStringRef SysUTF16ToCFStringRef(const string16& utf16) {
+ return STLStringToCFStringWithEncodingsT(utf16, kMediumStringEncoding);
+}
+
+NSString* SysUTF8ToNSString(const std::string& utf8) {
+ return (NSString*)base::mac::CFTypeRefToNSObjectAutorelease(
+ SysUTF8ToCFStringRef(utf8));
+}
+
+NSString* SysUTF16ToNSString(const string16& utf16) {
+ return (NSString*)base::mac::CFTypeRefToNSObjectAutorelease(
+ SysUTF16ToCFStringRef(utf16));
+}
+
+std::string SysCFStringRefToUTF8(CFStringRef ref) {
+ return CFStringToSTLStringWithEncodingT<std::string>(ref,
+ kNarrowStringEncoding);
+}
+
+string16 SysCFStringRefToUTF16(CFStringRef ref) {
+ return CFStringToSTLStringWithEncodingT<string16>(ref,
+ kMediumStringEncoding);
+}
+
+std::string SysNSStringToUTF8(NSString* nsstring) {
+ if (!nsstring)
+ return std::string();
+ return SysCFStringRefToUTF8(reinterpret_cast<CFStringRef>(nsstring));
+}
+
+string16 SysNSStringToUTF16(NSString* nsstring) {
+ if (!nsstring)
+ return string16();
+ return SysCFStringRefToUTF16(reinterpret_cast<CFStringRef>(nsstring));
+}
+
+} // namespace base
diff --git a/src/base/sys_string_conversions_mac_unittest.mm b/src/base/sys_string_conversions_mac_unittest.mm
new file mode 100644
index 0000000..8b0ac88
--- /dev/null
+++ b/src/base/sys_string_conversions_mac_unittest.mm
@@ -0,0 +1,18 @@
+// Copyright (c) 2012 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.
+
+#import <Foundation/Foundation.h>
+
+#include "base/string16.h"
+#include "base/sys_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(SysStrings, ConversionsFromNSString) {
+ EXPECT_STREQ("Hello, world!",
+ base::SysNSStringToUTF8(@"Hello, world!").c_str());
+
+ // Conversions should be able to handle a NULL value without crashing.
+ EXPECT_STREQ("", base::SysNSStringToUTF8(nil).c_str());
+ EXPECT_EQ(string16(), base::SysNSStringToUTF16(nil));
+}
diff --git a/src/base/sys_string_conversions_posix.cc b/src/base/sys_string_conversions_posix.cc
new file mode 100644
index 0000000..73146e5
--- /dev/null
+++ b/src/base/sys_string_conversions_posix.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 2012 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/sys_string_conversions.h"
+
+#include <wchar.h>
+
+#include "base/string_piece.h"
+#include "base/utf_string_conversions.h"
+
+namespace base {
+
+std::string SysWideToUTF8(const std::wstring& wide) {
+ // In theory this should be using the system-provided conversion rather
+ // than our ICU, but this will do for now.
+ return WideToUTF8(wide);
+}
+std::wstring SysUTF8ToWide(const StringPiece& utf8) {
+ // In theory this should be using the system-provided conversion rather
+ // than our ICU, but this will do for now.
+ std::wstring out;
+ UTF8ToWide(utf8.data(), utf8.size(), &out);
+ return out;
+}
+
+#if defined(OS_CHROMEOS) || defined(OS_ANDROID) || defined(__LB_SHELL__) || \
+ defined(OS_STARBOARD)
+// TODO(port): Consider reverting the OS_ANDROID when we have wcrtomb()
+// support and a better understanding of what calls these routines.
+
+// ChromeOS always runs in UTF-8 locale.
+std::string SysWideToNativeMB(const std::wstring& wide) {
+ return WideToUTF8(wide);
+}
+
+std::wstring SysNativeMBToWide(const StringPiece& native_mb) {
+ return SysUTF8ToWide(native_mb);
+}
+
+#else
+
+std::string SysWideToNativeMB(const std::wstring& wide) {
+ mbstate_t ps;
+
+ // Calculate the number of multi-byte characters. We walk through the string
+ // without writing the output, counting the number of multi-byte characters.
+ size_t num_out_chars = 0;
+ memset(&ps, 0, sizeof(ps));
+ for (size_t i = 0; i < wide.size(); ++i) {
+ const wchar_t src = wide[i];
+ // Use a temp buffer since calling wcrtomb with an output of NULL does not
+ // calculate the output length.
+ char buf[16];
+ // Skip NULLs to avoid wcrtomb's special handling of them.
+ size_t res = src ? wcrtomb(buf, src, &ps) : 0;
+ switch (res) {
+ // Handle any errors and return an empty string.
+ case static_cast<size_t>(-1):
+ return std::string();
+ break;
+ case 0:
+ // We hit an embedded null byte, keep going.
+ ++num_out_chars;
+ break;
+ default:
+ num_out_chars += res;
+ break;
+ }
+ }
+
+ if (num_out_chars == 0)
+ return std::string();
+
+ std::string out;
+ out.resize(num_out_chars);
+
+ // We walk the input string again, with |i| tracking the index of the
+ // wide input, and |j| tracking the multi-byte output.
+ memset(&ps, 0, sizeof(ps));
+ for (size_t i = 0, j = 0; i < wide.size(); ++i) {
+ const wchar_t src = wide[i];
+ // We don't want wcrtomb to do its funkiness for embedded NULLs.
+ size_t res = src ? wcrtomb(&out[j], src, &ps) : 0;
+ switch (res) {
+ // Handle any errors and return an empty string.
+ case static_cast<size_t>(-1):
+ return std::string();
+ break;
+ case 0:
+ // We hit an embedded null byte, keep going.
+ ++j; // Output is already zeroed.
+ break;
+ default:
+ j += res;
+ break;
+ }
+ }
+
+ return out;
+}
+
+std::wstring SysNativeMBToWide(const StringPiece& native_mb) {
+ mbstate_t ps;
+
+ // Calculate the number of wide characters. We walk through the string
+ // without writing the output, counting the number of wide characters.
+ size_t num_out_chars = 0;
+ memset(&ps, 0, sizeof(ps));
+ for (size_t i = 0; i < native_mb.size(); ) {
+ const char* src = native_mb.data() + i;
+ size_t res = mbrtowc(NULL, src, native_mb.size() - i, &ps);
+ switch (res) {
+ // Handle any errors and return an empty string.
+ case static_cast<size_t>(-2):
+ case static_cast<size_t>(-1):
+ return std::wstring();
+ break;
+ case 0:
+ // We hit an embedded null byte, keep going.
+ i += 1; // Fall through.
+ default:
+ i += res;
+ ++num_out_chars;
+ break;
+ }
+ }
+
+ if (num_out_chars == 0)
+ return std::wstring();
+
+ std::wstring out;
+ out.resize(num_out_chars);
+
+ memset(&ps, 0, sizeof(ps)); // Clear the shift state.
+ // We walk the input string again, with |i| tracking the index of the
+ // multi-byte input, and |j| tracking the wide output.
+ for (size_t i = 0, j = 0; i < native_mb.size(); ++j) {
+ const char* src = native_mb.data() + i;
+ wchar_t* dst = &out[j];
+ size_t res = mbrtowc(dst, src, native_mb.size() - i, &ps);
+ switch (res) {
+ // Handle any errors and return an empty string.
+ case static_cast<size_t>(-2):
+ case static_cast<size_t>(-1):
+ return std::wstring();
+ break;
+ case 0:
+ i += 1; // Skip null byte.
+ break;
+ default:
+ i += res;
+ break;
+ }
+ }
+
+ return out;
+}
+
+#endif // OS_CHROMEOS
+
+} // namespace base
diff --git a/src/base/sys_string_conversions_shell.cc b/src/base/sys_string_conversions_shell.cc
new file mode 100644
index 0000000..504204f
--- /dev/null
+++ b/src/base/sys_string_conversions_shell.cc
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2012 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// We use the same code as posix version
+#include "sys_string_conversions_posix.cc"
diff --git a/src/base/sys_string_conversions_starboard.cc b/src/base/sys_string_conversions_starboard.cc
new file mode 100644
index 0000000..60357c9
--- /dev/null
+++ b/src/base/sys_string_conversions_starboard.cc
@@ -0,0 +1,16 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// We use the same code as the POSIX version.
+#include "sys_string_conversions_posix.cc"
diff --git a/src/base/sys_string_conversions_unittest.cc b/src/base/sys_string_conversions_unittest.cc
new file mode 100644
index 0000000..a3393ba
--- /dev/null
+++ b/src/base/sys_string_conversions_unittest.cc
@@ -0,0 +1,188 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/string_piece.h"
+#include "base/test/scoped_locale.h"
+#include "base/utf_string_conversions.h"
+#include "base/sys_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#ifdef WCHAR_T_IS_UTF32
+static const std::wstring kSysWideOldItalicLetterA = L"\x10300";
+#else
+static const std::wstring kSysWideOldItalicLetterA = L"\xd800\xdf00";
+#endif
+
+TEST(SysStrings, SysWideToUTF8) {
+ using base::SysWideToUTF8;
+ EXPECT_EQ("Hello, world", SysWideToUTF8(L"Hello, world"));
+ EXPECT_EQ("\xe4\xbd\xa0\xe5\xa5\xbd", SysWideToUTF8(L"\x4f60\x597d"));
+
+ // >16 bits
+ EXPECT_EQ("\xF0\x90\x8C\x80", SysWideToUTF8(kSysWideOldItalicLetterA));
+
+ // Error case. When Windows finds a UTF-16 character going off the end of
+ // a string, it just converts that literal value to UTF-8, even though this
+ // is invalid.
+ //
+ // This is what XP does, but Vista has different behavior, so we don't bother
+ // verifying it:
+ // EXPECT_EQ("\xE4\xBD\xA0\xED\xA0\x80zyxw",
+ // SysWideToUTF8(L"\x4f60\xd800zyxw"));
+
+ // Test embedded NULLs.
+ std::wstring wide_null(L"a");
+ wide_null.push_back(0);
+ wide_null.push_back('b');
+
+ std::string expected_null("a");
+ expected_null.push_back(0);
+ expected_null.push_back('b');
+
+ EXPECT_EQ(expected_null, SysWideToUTF8(wide_null));
+}
+
+TEST(SysStrings, SysUTF8ToWide) {
+ using base::SysUTF8ToWide;
+ EXPECT_EQ(L"Hello, world", SysUTF8ToWide("Hello, world"));
+ EXPECT_EQ(L"\x4f60\x597d", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5\xbd"));
+ // >16 bits
+ EXPECT_EQ(kSysWideOldItalicLetterA, SysUTF8ToWide("\xF0\x90\x8C\x80"));
+
+ // Error case. When Windows finds an invalid UTF-8 character, it just skips
+ // it. This seems weird because it's inconsistent with the reverse conversion.
+ //
+ // This is what XP does, but Vista has different behavior, so we don't bother
+ // verifying it:
+ // EXPECT_EQ(L"\x4f60zyxw", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5zyxw"));
+
+ // Test embedded NULLs.
+ std::string utf8_null("a");
+ utf8_null.push_back(0);
+ utf8_null.push_back('b');
+
+ std::wstring expected_null(L"a");
+ expected_null.push_back(0);
+ expected_null.push_back('b');
+
+ EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null));
+}
+
+#if defined(OS_LINUX) // Tests depend on setting a specific Linux locale.
+
+TEST(SysStrings, SysWideToNativeMB) {
+ using base::SysWideToNativeMB;
+ base::ScopedLocale locale("en_US.utf-8");
+ EXPECT_EQ("Hello, world", SysWideToNativeMB(L"Hello, world"));
+ EXPECT_EQ("\xe4\xbd\xa0\xe5\xa5\xbd", SysWideToNativeMB(L"\x4f60\x597d"));
+
+ // >16 bits
+ EXPECT_EQ("\xF0\x90\x8C\x80", SysWideToNativeMB(kSysWideOldItalicLetterA));
+
+ // Error case. When Windows finds a UTF-16 character going off the end of
+ // a string, it just converts that literal value to UTF-8, even though this
+ // is invalid.
+ //
+ // This is what XP does, but Vista has different behavior, so we don't bother
+ // verifying it:
+ // EXPECT_EQ("\xE4\xBD\xA0\xED\xA0\x80zyxw",
+ // SysWideToNativeMB(L"\x4f60\xd800zyxw"));
+
+ // Test embedded NULLs.
+ std::wstring wide_null(L"a");
+ wide_null.push_back(0);
+ wide_null.push_back('b');
+
+ std::string expected_null("a");
+ expected_null.push_back(0);
+ expected_null.push_back('b');
+
+ EXPECT_EQ(expected_null, SysWideToNativeMB(wide_null));
+}
+
+// We assume the test is running in a UTF8 locale.
+TEST(SysStrings, SysNativeMBToWide) {
+ using base::SysNativeMBToWide;
+ base::ScopedLocale locale("en_US.utf-8");
+ EXPECT_EQ(L"Hello, world", SysNativeMBToWide("Hello, world"));
+ EXPECT_EQ(L"\x4f60\x597d", SysNativeMBToWide("\xe4\xbd\xa0\xe5\xa5\xbd"));
+ // >16 bits
+ EXPECT_EQ(kSysWideOldItalicLetterA, SysNativeMBToWide("\xF0\x90\x8C\x80"));
+
+ // Error case. When Windows finds an invalid UTF-8 character, it just skips
+ // it. This seems weird because it's inconsistent with the reverse conversion.
+ //
+ // This is what XP does, but Vista has different behavior, so we don't bother
+ // verifying it:
+ // EXPECT_EQ(L"\x4f60zyxw", SysNativeMBToWide("\xe4\xbd\xa0\xe5\xa5zyxw"));
+
+ // Test embedded NULLs.
+ std::string utf8_null("a");
+ utf8_null.push_back(0);
+ utf8_null.push_back('b');
+
+ std::wstring expected_null(L"a");
+ expected_null.push_back(0);
+ expected_null.push_back('b');
+
+ EXPECT_EQ(expected_null, SysNativeMBToWide(utf8_null));
+}
+
+static const wchar_t* const kConvertRoundtripCases[] = {
+ L"Google Video",
+ // "网页 图片 资讯更多 »"
+ L"\x7f51\x9875\x0020\x56fe\x7247\x0020\x8d44\x8baf\x66f4\x591a\x0020\x00bb",
+ // "Παγκόσμιος Ιστός"
+ L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
+ L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2",
+ // "Поиск страниц на русском"
+ L"\x041f\x043e\x0438\x0441\x043a\x0020\x0441\x0442"
+ L"\x0440\x0430\x043d\x0438\x0446\x0020\x043d\x0430"
+ L"\x0020\x0440\x0443\x0441\x0441\x043a\x043e\x043c",
+ // "전체서비스"
+ L"\xc804\xccb4\xc11c\xbe44\xc2a4",
+
+ // Test characters that take more than 16 bits. This will depend on whether
+ // wchar_t is 16 or 32 bits.
+#if defined(WCHAR_T_IS_UTF16)
+ L"\xd800\xdf00",
+ // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+ L"\xd807\xdd40\xd807\xdd41\xd807\xdd42\xd807\xdd43\xd807\xdd44",
+#elif defined(WCHAR_T_IS_UTF32)
+ L"\x10300",
+ // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+ L"\x11d40\x11d41\x11d42\x11d43\x11d44",
+#endif
+};
+
+
+TEST(SysStrings, SysNativeMBAndWide) {
+ base::ScopedLocale locale("en_US.utf-8");
+ for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) {
+ std::wstring wide = kConvertRoundtripCases[i];
+ std::wstring trip = base::SysNativeMBToWide(base::SysWideToNativeMB(wide));
+ EXPECT_EQ(wide.size(), trip.size());
+ EXPECT_EQ(wide, trip);
+ }
+
+ // We assume our test is running in UTF-8, so double check through ICU.
+ for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) {
+ std::wstring wide = kConvertRoundtripCases[i];
+ std::wstring trip = base::SysNativeMBToWide(WideToUTF8(wide));
+ EXPECT_EQ(wide.size(), trip.size());
+ EXPECT_EQ(wide, trip);
+ }
+
+ for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) {
+ std::wstring wide = kConvertRoundtripCases[i];
+ std::wstring trip = UTF8ToWide(base::SysWideToNativeMB(wide));
+ EXPECT_EQ(wide.size(), trip.size());
+ EXPECT_EQ(wide, trip);
+ }
+}
+#endif // OS_LINUX
diff --git a/src/base/sys_string_conversions_win.cc b/src/base/sys_string_conversions_win.cc
new file mode 100644
index 0000000..8b2367b
--- /dev/null
+++ b/src/base/sys_string_conversions_win.cc
@@ -0,0 +1,70 @@
+// 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/sys_string_conversions.h"
+
+#include <windows.h>
+
+#include "base/string_piece.h"
+
+namespace base {
+
+// Do not assert in this function since it is used by the asssertion code!
+std::string SysWideToUTF8(const std::wstring& wide) {
+ return SysWideToMultiByte(wide, CP_UTF8);
+}
+
+// Do not assert in this function since it is used by the asssertion code!
+std::wstring SysUTF8ToWide(const StringPiece& utf8) {
+ return SysMultiByteToWide(utf8, CP_UTF8);
+}
+
+std::string SysWideToNativeMB(const std::wstring& wide) {
+ return SysWideToMultiByte(wide, CP_ACP);
+}
+
+std::wstring SysNativeMBToWide(const StringPiece& native_mb) {
+ return SysMultiByteToWide(native_mb, CP_ACP);
+}
+
+// Do not assert in this function since it is used by the asssertion code!
+std::wstring SysMultiByteToWide(const StringPiece& mb, uint32 code_page) {
+ if (mb.empty())
+ return std::wstring();
+
+ int mb_length = static_cast<int>(mb.length());
+ // Compute the length of the buffer.
+ int charcount = MultiByteToWideChar(code_page, 0,
+ mb.data(), mb_length, NULL, 0);
+ if (charcount == 0)
+ return std::wstring();
+
+ std::wstring wide;
+ wide.resize(charcount);
+ MultiByteToWideChar(code_page, 0, mb.data(), mb_length, &wide[0], charcount);
+
+ return wide;
+}
+
+// Do not assert in this function since it is used by the asssertion code!
+std::string SysWideToMultiByte(const std::wstring& wide, uint32 code_page) {
+ int wide_length = static_cast<int>(wide.length());
+ if (wide_length == 0)
+ return std::string();
+
+ // Compute the length of the buffer we'll need.
+ int charcount = WideCharToMultiByte(code_page, 0, wide.data(), wide_length,
+ NULL, 0, NULL, NULL);
+ if (charcount == 0)
+ return std::string();
+
+ std::string mb;
+ mb.resize(charcount);
+ WideCharToMultiByte(code_page, 0, wide.data(), wide_length,
+ &mb[0], charcount, NULL, NULL);
+
+ return mb;
+}
+
+} // namespace base
diff --git a/src/base/system_monitor/system_monitor.cc b/src/base/system_monitor/system_monitor.cc
new file mode 100644
index 0000000..a3581e1
--- /dev/null
+++ b/src/base/system_monitor/system_monitor.cc
@@ -0,0 +1,204 @@
+// Copyright (c) 2012 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/system_monitor/system_monitor.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/stl_util.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+
+namespace base {
+
+static SystemMonitor* g_system_monitor = NULL;
+
+#if defined(ENABLE_BATTERY_MONITORING)
+// The amount of time (in ms) to wait before running the initial
+// battery check.
+static int kDelayedBatteryCheckMs = 10 * 1000;
+#endif // defined(ENABLE_BATTERY_MONITORING)
+
+SystemMonitor::RemovableStorageInfo::RemovableStorageInfo() {
+}
+
+SystemMonitor::RemovableStorageInfo::RemovableStorageInfo(
+ const std::string& id,
+ const string16& device_name,
+ const FilePath::StringType& device_location)
+ : device_id(id),
+ name(device_name),
+ location(device_location) {
+}
+
+SystemMonitor::SystemMonitor()
+ : power_observer_list_(new ObserverListThreadSafe<PowerObserver>()),
+ devices_changed_observer_list_(
+ new ObserverListThreadSafe<DevicesChangedObserver>()),
+ battery_in_use_(false),
+ suspended_(false) {
+ DCHECK(!g_system_monitor);
+ g_system_monitor = this;
+
+ DCHECK(MessageLoop::current());
+#if defined(ENABLE_BATTERY_MONITORING)
+ delayed_battery_check_.Start(FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kDelayedBatteryCheckMs), this,
+ &SystemMonitor::BatteryCheck);
+#endif // defined(ENABLE_BATTERY_MONITORING)
+#if defined(OS_MACOSX)
+ PlatformInit();
+#endif
+}
+
+SystemMonitor::~SystemMonitor() {
+#if defined(OS_MACOSX)
+ PlatformDestroy();
+#endif
+ DCHECK_EQ(this, g_system_monitor);
+ g_system_monitor = NULL;
+}
+
+// static
+SystemMonitor* SystemMonitor::Get() {
+ return g_system_monitor;
+}
+
+void SystemMonitor::ProcessPowerMessage(PowerEvent event_id) {
+ // Suppress duplicate notifications. Some platforms may
+ // send multiple notifications of the same event.
+ switch (event_id) {
+ case POWER_STATE_EVENT:
+#if !defined(__LB_SHELL__) && !defined(OS_STARBOARD)
+ {
+ bool on_battery = IsBatteryPower();
+ if (on_battery != battery_in_use_) {
+ battery_in_use_ = on_battery;
+ NotifyPowerStateChange();
+ }
+ }
+#endif
+ break;
+ case RESUME_EVENT:
+ if (suspended_) {
+ suspended_ = false;
+ NotifyResume();
+ }
+ break;
+ case SUSPEND_EVENT:
+ if (!suspended_) {
+ suspended_ = true;
+ NotifySuspend();
+ }
+ break;
+ }
+}
+
+void SystemMonitor::ProcessDevicesChanged(DeviceType device_type) {
+ NotifyDevicesChanged(device_type);
+}
+
+void SystemMonitor::ProcessRemovableStorageAttached(
+ const std::string& id,
+ const string16& name,
+ const FilePath::StringType& location) {
+ {
+ base::AutoLock lock(removable_storage_lock_);
+ if (ContainsKey(removable_storage_map_, id)) {
+ // This can happen if our unique id scheme fails. Ignore the incoming
+ // non-unique attachment.
+ return;
+ }
+ RemovableStorageInfo info(id, name, location);
+ removable_storage_map_.insert(std::make_pair(id, info));
+ }
+ NotifyRemovableStorageAttached(id, name, location);
+}
+
+void SystemMonitor::ProcessRemovableStorageDetached(const std::string& id) {
+ {
+ base::AutoLock lock(removable_storage_lock_);
+ RemovableStorageMap::iterator it = removable_storage_map_.find(id);
+ if (it == removable_storage_map_.end())
+ return;
+ removable_storage_map_.erase(it);
+ }
+ NotifyRemovableStorageDetached(id);
+}
+
+std::vector<SystemMonitor::RemovableStorageInfo>
+SystemMonitor::GetAttachedRemovableStorage() const {
+ std::vector<RemovableStorageInfo> results;
+
+ base::AutoLock lock(removable_storage_lock_);
+ for (RemovableStorageMap::const_iterator it = removable_storage_map_.begin();
+ it != removable_storage_map_.end();
+ ++it) {
+ results.push_back(it->second);
+ }
+ return results;
+}
+
+void SystemMonitor::AddPowerObserver(PowerObserver* obs) {
+ power_observer_list_->AddObserver(obs);
+}
+
+void SystemMonitor::RemovePowerObserver(PowerObserver* obs) {
+ power_observer_list_->RemoveObserver(obs);
+}
+
+void SystemMonitor::AddDevicesChangedObserver(DevicesChangedObserver* obs) {
+ devices_changed_observer_list_->AddObserver(obs);
+}
+
+void SystemMonitor::RemoveDevicesChangedObserver(DevicesChangedObserver* obs) {
+ devices_changed_observer_list_->RemoveObserver(obs);
+}
+
+void SystemMonitor::NotifyDevicesChanged(DeviceType device_type) {
+ DVLOG(1) << "DevicesChanged with device type " << device_type;
+ devices_changed_observer_list_->Notify(
+ &DevicesChangedObserver::OnDevicesChanged, device_type);
+}
+
+void SystemMonitor::NotifyRemovableStorageAttached(
+ const std::string& id,
+ const string16& name,
+ const FilePath::StringType& location) {
+ DVLOG(1) << "RemovableStorageAttached with name " << UTF16ToUTF8(name)
+ << " and id " << id;
+ devices_changed_observer_list_->Notify(
+ &DevicesChangedObserver::OnRemovableStorageAttached, id, name, location);
+}
+
+void SystemMonitor::NotifyRemovableStorageDetached(const std::string& id) {
+ DVLOG(1) << "RemovableStorageDetached for id " << id;
+ devices_changed_observer_list_->Notify(
+ &DevicesChangedObserver::OnRemovableStorageDetached, id);
+}
+
+void SystemMonitor::NotifyPowerStateChange() {
+ DVLOG(1) << "PowerStateChange: " << (BatteryPower() ? "On" : "Off")
+ << " battery";
+ power_observer_list_->Notify(&PowerObserver::OnPowerStateChange,
+ BatteryPower());
+}
+
+void SystemMonitor::NotifySuspend() {
+ DVLOG(1) << "Power Suspending";
+ power_observer_list_->Notify(&PowerObserver::OnSuspend);
+}
+
+void SystemMonitor::NotifyResume() {
+ DVLOG(1) << "Power Resuming";
+ power_observer_list_->Notify(&PowerObserver::OnResume);
+}
+
+void SystemMonitor::BatteryCheck() {
+ ProcessPowerMessage(SystemMonitor::POWER_STATE_EVENT);
+}
+
+} // namespace base
diff --git a/src/base/system_monitor/system_monitor.h b/src/base/system_monitor/system_monitor.h
new file mode 100644
index 0000000..935fc03
--- /dev/null
+++ b/src/base/system_monitor/system_monitor.h
@@ -0,0 +1,237 @@
+// Copyright (c) 2012 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_SYSTEM_MONITOR_SYSTEM_MONITOR_H_
+#define BASE_SYSTEM_MONITOR_SYSTEM_MONITOR_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/string16.h"
+#include "base/synchronization/lock.h"
+#include "build/build_config.h"
+
+// Windows HiRes timers drain the battery faster so we need to know the battery
+// status. This isn't true for other platforms.
+#if defined(OS_WIN)
+#define ENABLE_BATTERY_MONITORING 1
+#else
+#undef ENABLE_BATTERY_MONITORING
+#endif // !OS_WIN
+
+#include "base/observer_list_threadsafe.h"
+#if defined(ENABLE_BATTERY_MONITORING)
+#include "base/timer.h"
+#endif // defined(ENABLE_BATTERY_MONITORING)
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+#include <IOKit/pwr_mgt/IOPMLib.h>
+#include <IOKit/IOMessage.h>
+#endif // OS_MACOSX && !OS_IOS
+
+#if defined(OS_IOS)
+#include <objc/runtime.h>
+#endif // OS_IOS
+
+namespace base {
+
+// Class for monitoring various system-related subsystems
+// such as power management, network status, etc.
+// TODO(mbelshe): Add support beyond just power management.
+class BASE_EXPORT SystemMonitor {
+ public:
+ // Normalized list of power events.
+ enum PowerEvent {
+ POWER_STATE_EVENT, // The Power status of the system has changed.
+ SUSPEND_EVENT, // The system is being suspended.
+ RESUME_EVENT // The system is being resumed.
+ };
+
+ // Type of devices whose change need to be monitored, such as add/remove.
+ enum DeviceType {
+ DEVTYPE_AUDIO_CAPTURE, // Audio capture device, e.g., microphone.
+ DEVTYPE_VIDEO_CAPTURE, // Video capture device, e.g., webcam.
+ DEVTYPE_UNKNOWN, // Other devices.
+ };
+
+ struct BASE_EXPORT RemovableStorageInfo {
+ RemovableStorageInfo();
+ RemovableStorageInfo(const std::string& id,
+ const string16& device_name,
+ const FilePath::StringType& device_location);
+
+ // Unique device id - persists between device attachments.
+ std::string device_id;
+
+ // Human readable removable storage device name.
+ string16 name;
+
+ // Current attached removable storage device location.
+ FilePath::StringType location;
+ };
+
+ // Create SystemMonitor. Only one SystemMonitor instance per application
+ // is allowed.
+ SystemMonitor();
+ ~SystemMonitor();
+
+ // Get the application-wide SystemMonitor (if not present, returns NULL).
+ static SystemMonitor* Get();
+
+#if defined(OS_MACOSX)
+ // Allocate system resources needed by the SystemMonitor class.
+ //
+ // This function must be called before instantiating an instance of the class
+ // and before the Sandbox is initialized.
+#if !defined(OS_IOS)
+ static void AllocateSystemIOPorts();
+#else
+ static void AllocateSystemIOPorts() {}
+#endif // OS_IOS
+#endif // OS_MACOSX
+
+ // Returns information for attached removable storage.
+ std::vector<RemovableStorageInfo> GetAttachedRemovableStorage() const;
+
+ //
+ // Power-related APIs
+ //
+
+ // Is the computer currently on battery power.
+ // Can be called on any thread.
+ bool BatteryPower() const {
+ // Using a lock here is not necessary for just a bool.
+ return battery_in_use_;
+ }
+
+ // Callbacks will be called on the thread which creates the SystemMonitor.
+ // During the callback, Add/RemoveObserver will block until the callbacks
+ // are finished. Observers should implement quick callback functions; if
+ // lengthy operations are needed, the observer should take care to invoke
+ // the operation on an appropriate thread.
+ class BASE_EXPORT PowerObserver {
+ public:
+ // Notification of a change in power status of the computer, such
+ // as from switching between battery and A/C power.
+ virtual void OnPowerStateChange(bool /*on_battery_power*/) {}
+
+ // Notification that the system is suspending.
+ virtual void OnSuspend() {}
+
+ // Notification that the system is resuming.
+ virtual void OnResume() {}
+
+ protected:
+ virtual ~PowerObserver() {}
+ };
+
+ class BASE_EXPORT DevicesChangedObserver {
+ public:
+ // Notification that the devices connected to the system have changed.
+ // This is only implemented on Windows currently.
+ virtual void OnDevicesChanged(DeviceType /*device_type*/) {}
+
+ // When a removable storage device is attached or detached, one of these
+ // two events is triggered.
+ virtual void OnRemovableStorageAttached(
+ const std::string& /*id*/,
+ const string16& /*name*/,
+ const FilePath::StringType& /*location*/) {}
+ virtual void OnRemovableStorageDetached(const std::string& /*id*/) {}
+
+ protected:
+ virtual ~DevicesChangedObserver() {}
+ };
+
+ // Add a new observer.
+ // Can be called from any thread.
+ // Must not be called from within a notification callback.
+ void AddPowerObserver(PowerObserver* obs);
+ void AddDevicesChangedObserver(DevicesChangedObserver* obs);
+
+ // Remove an existing observer.
+ // Can be called from any thread.
+ // Must not be called from within a notification callback.
+ void RemovePowerObserver(PowerObserver* obs);
+ void RemoveDevicesChangedObserver(DevicesChangedObserver* obs);
+
+ // The ProcessFoo() style methods are a broken pattern and should not
+ // be copied. Any significant addition to this class is blocked on
+ // refactoring to improve the state of affairs. See http://crbug.com/149059
+
+#if defined(OS_WIN)
+ // Windows-specific handling of a WM_POWERBROADCAST message.
+ // Embedders of this API should hook their top-level window
+ // message loop and forward WM_POWERBROADCAST through this call.
+ void ProcessWmPowerBroadcastMessage(int event_id);
+#endif
+
+ // Cross-platform handling of a power event.
+ void ProcessPowerMessage(PowerEvent event_id);
+
+ // Cross-platform handling of a device change event.
+ void ProcessDevicesChanged(DeviceType device_type);
+ void ProcessRemovableStorageAttached(const std::string& id,
+ const string16& name,
+ const FilePath::StringType& location);
+ void ProcessRemovableStorageDetached(const std::string& id);
+
+ private:
+ // Mapping of unique device id to device info tuple.
+ typedef std::map<std::string, RemovableStorageInfo> RemovableStorageMap;
+
+#if defined(OS_MACOSX)
+ void PlatformInit();
+ void PlatformDestroy();
+#endif
+
+ // Platform-specific method to check whether the system is currently
+ // running on battery power. Returns true if running on batteries,
+ // false otherwise.
+ bool IsBatteryPower();
+
+ // Checks the battery status and notifies observers if the battery
+ // status has changed.
+ void BatteryCheck();
+
+ // Functions to trigger notifications.
+ void NotifyDevicesChanged(DeviceType device_type);
+ void NotifyRemovableStorageAttached(const std::string& id,
+ const string16& name,
+ const FilePath::StringType& location);
+ void NotifyRemovableStorageDetached(const std::string& id);
+ void NotifyPowerStateChange();
+ void NotifySuspend();
+ void NotifyResume();
+
+ scoped_refptr<ObserverListThreadSafe<PowerObserver> > power_observer_list_;
+ scoped_refptr<ObserverListThreadSafe<DevicesChangedObserver> >
+ devices_changed_observer_list_;
+ bool battery_in_use_;
+ bool suspended_;
+
+#if defined(ENABLE_BATTERY_MONITORING)
+ base::OneShotTimer<SystemMonitor> delayed_battery_check_;
+#endif
+
+#if defined(OS_IOS)
+ // Holds pointers to system event notification observers.
+ std::vector<id> notification_observers_;
+#endif
+
+ // For manipulating removable_storage_map_ structure.
+ mutable base::Lock removable_storage_lock_;
+ // Map of all the attached removable storage devices.
+ RemovableStorageMap removable_storage_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(SystemMonitor);
+};
+
+} // namespace base
+
+#endif // BASE_SYSTEM_MONITOR_SYSTEM_MONITOR_H_
diff --git a/src/base/system_monitor/system_monitor_android.cc b/src/base/system_monitor/system_monitor_android.cc
new file mode 100644
index 0000000..f29b7b0
--- /dev/null
+++ b/src/base/system_monitor/system_monitor_android.cc
@@ -0,0 +1,36 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/system_monitor/system_monitor.h"
+#include "jni/SystemMonitor_jni.h"
+
+namespace base {
+
+namespace android {
+
+// Native implementation of SystemMonitor.java.
+void OnBatteryChargingChanged(JNIEnv* env, jclass clazz) {
+ SystemMonitor::Get()->ProcessPowerMessage(SystemMonitor::POWER_STATE_EVENT);
+}
+
+void OnMainActivityResumed(JNIEnv* env, jclass clazz) {
+ SystemMonitor::Get()->ProcessPowerMessage(SystemMonitor::RESUME_EVENT);
+}
+
+void OnMainActivitySuspended(JNIEnv* env, jclass clazz) {
+ SystemMonitor::Get()->ProcessPowerMessage(SystemMonitor::SUSPEND_EVENT);
+}
+
+} // namespace android
+
+bool SystemMonitor::IsBatteryPower() {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ return base::android::Java_SystemMonitor_isBatteryPower(env);
+}
+
+bool RegisterSystemMonitor(JNIEnv* env) {
+ return base::android::RegisterNativesImpl(env);
+}
+
+} // namespace base
diff --git a/src/base/system_monitor/system_monitor_android.h b/src/base/system_monitor/system_monitor_android.h
new file mode 100644
index 0000000..9f60560
--- /dev/null
+++ b/src/base/system_monitor/system_monitor_android.h
@@ -0,0 +1,17 @@
+// Copyright (c) 2012 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_SYSTEM_MONITOR_SYSTEM_MONITOR_ANDROID_H_
+#define BASE_SYSTEM_MONITOR_SYSTEM_MONITOR_ANDROID_H_
+
+#include <jni.h>
+
+namespace base {
+
+// Registers the JNI bindings for SystemMonitor.
+bool RegisterSystemMonitor(JNIEnv* env);
+
+} // namespace base
+
+#endif // BASE_SYSTEM_MONITOR_SYSTEM_MONITOR_ANDROID_H_
diff --git a/src/base/system_monitor/system_monitor_ios.mm b/src/base/system_monitor/system_monitor_ios.mm
new file mode 100644
index 0000000..f3251b6
--- /dev/null
+++ b/src/base/system_monitor/system_monitor_ios.mm
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 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/system_monitor/system_monitor.h"
+
+#import <UIKit/UIKit.h>
+
+namespace base {
+
+void SystemMonitor::PlatformInit() {
+ NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
+ id foreground =
+ [nc addObserverForName:UIApplicationWillEnterForegroundNotification
+ object:nil
+ queue:nil
+ usingBlock:^(NSNotification* notification) {
+ ProcessPowerMessage(RESUME_EVENT);
+ }];
+ id background =
+ [nc addObserverForName:UIApplicationDidEnterBackgroundNotification
+ object:nil
+ queue:nil
+ usingBlock:^(NSNotification* notification) {
+ ProcessPowerMessage(SUSPEND_EVENT);
+ }];
+ notification_observers_.push_back(foreground);
+ notification_observers_.push_back(background);
+}
+
+void SystemMonitor::PlatformDestroy() {
+ NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
+ for (std::vector<id>::iterator it = notification_observers_.begin();
+ it != notification_observers_.end(); ++it) {
+ [nc removeObserver:*it];
+ }
+ notification_observers_.clear();
+}
+
+} // namespace base
diff --git a/src/base/system_monitor/system_monitor_mac.mm b/src/base/system_monitor/system_monitor_mac.mm
new file mode 100644
index 0000000..d0dbaab
--- /dev/null
+++ b/src/base/system_monitor/system_monitor_mac.mm
@@ -0,0 +1,98 @@
+// Copyright (c) 2012 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.
+
+// Implementation based on sample code from
+// http://developer.apple.com/library/mac/#qa/qa1340/_index.html.
+
+#include "base/system_monitor/system_monitor.h"
+
+#include <IOKit/pwr_mgt/IOPMLib.h>
+#include <IOKit/IOMessage.h>
+
+namespace base {
+
+namespace {
+
+io_connect_t g_system_power_io_port = 0;
+IONotificationPortRef g_notification_port_ref = 0;
+io_object_t g_notifier_object = 0;
+
+void SystemPowerEventCallback(void*,
+ io_service_t service,
+ natural_t message_type,
+ void* message_argument) {
+ SystemMonitor* sys_monitor = SystemMonitor::Get();
+ DCHECK(sys_monitor);
+ switch (message_type) {
+ case kIOMessageSystemWillSleep:
+ sys_monitor->ProcessPowerMessage(SystemMonitor::SUSPEND_EVENT);
+ IOAllowPowerChange(g_system_power_io_port,
+ reinterpret_cast<intptr_t>(message_argument));
+ break;
+
+ case kIOMessageSystemWillPowerOn:
+ sys_monitor->ProcessPowerMessage(SystemMonitor::RESUME_EVENT);
+ break;
+ }
+}
+
+} // namespace
+
+// The reason we can't include this code in the constructor is because
+// PlatformInit() requires an active runloop and the IO port needs to be
+// allocated at sandbox initialization time, before there's a runloop.
+// See crbug.com/83783 .
+
+// static
+void SystemMonitor::AllocateSystemIOPorts() {
+ DCHECK_EQ(g_system_power_io_port, 0u);
+
+ // Notification port allocated by IORegisterForSystemPower.
+
+ g_system_power_io_port = IORegisterForSystemPower(
+ NULL, &g_notification_port_ref, SystemPowerEventCallback,
+ &g_notifier_object);
+
+ DCHECK_NE(g_system_power_io_port, 0u);
+}
+
+void SystemMonitor::PlatformInit() {
+ // Need to call AllocateSystemIOPorts() before constructing a SystemMonitor
+ // object.
+ DCHECK_NE(g_system_power_io_port, 0u);
+ if (g_system_power_io_port == 0)
+ return;
+
+ // Add the notification port to the application runloop
+ CFRunLoopAddSource(
+ CFRunLoopGetCurrent(),
+ IONotificationPortGetRunLoopSource(g_notification_port_ref),
+ kCFRunLoopCommonModes);
+}
+
+void SystemMonitor::PlatformDestroy() {
+ DCHECK_NE(g_system_power_io_port, 0u);
+ if (g_system_power_io_port == 0)
+ return;
+
+ // Remove the sleep notification port from the application runloop
+ CFRunLoopRemoveSource(
+ CFRunLoopGetCurrent(),
+ IONotificationPortGetRunLoopSource(g_notification_port_ref),
+ kCFRunLoopCommonModes);
+
+ // Deregister for system sleep notifications
+ IODeregisterForSystemPower(&g_notifier_object);
+
+ // IORegisterForSystemPower implicitly opens the Root Power Domain IOService,
+ // so we close it here.
+ IOServiceClose(g_system_power_io_port);
+
+ g_system_power_io_port = 0;
+
+ // Destroy the notification port allocated by IORegisterForSystemPower.
+ IONotificationPortDestroy(g_notification_port_ref);
+}
+
+} // namespace base
diff --git a/src/base/system_monitor/system_monitor_posix.cc b/src/base/system_monitor/system_monitor_posix.cc
new file mode 100644
index 0000000..6cf01bf
--- /dev/null
+++ b/src/base/system_monitor/system_monitor_posix.cc
@@ -0,0 +1,14 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/system_monitor/system_monitor.h"
+
+namespace base {
+
+bool SystemMonitor::IsBatteryPower() {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+} // namespace base
diff --git a/src/base/system_monitor/system_monitor_starboard.cc b/src/base/system_monitor/system_monitor_starboard.cc
new file mode 100644
index 0000000..ddd36ba
--- /dev/null
+++ b/src/base/system_monitor/system_monitor_starboard.cc
@@ -0,0 +1,15 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/system_monitor/system_monitor_posix.cc"
diff --git a/src/base/system_monitor/system_monitor_unittest.cc b/src/base/system_monitor/system_monitor_unittest.cc
new file mode 100644
index 0000000..10bd7e7
--- /dev/null
+++ b/src/base/system_monitor/system_monitor_unittest.cc
@@ -0,0 +1,202 @@
+// Copyright (c) 2012 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/system_monitor/system_monitor.h"
+
+#include "base/file_path.h"
+#include "base/message_loop.h"
+#include "base/test/mock_devices_changed_observer.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class PowerTest : public SystemMonitor::PowerObserver {
+ public:
+ PowerTest()
+ : power_state_changes_(0),
+ suspends_(0),
+ resumes_(0) {
+ }
+
+ // PowerObserver callbacks.
+ virtual void OnPowerStateChange(bool on_battery_power) OVERRIDE {
+ power_state_changes_++;
+ }
+
+ virtual void OnSuspend() OVERRIDE {
+ suspends_++;
+ }
+
+ virtual void OnResume() OVERRIDE {
+ resumes_++;
+ }
+
+ // Test status counts.
+ int power_state_changes() { return power_state_changes_; }
+ int suspends() { return suspends_; }
+ int resumes() { return resumes_; }
+
+ private:
+ int power_state_changes_; // Count of OnPowerStateChange notifications.
+ int suspends_; // Count of OnSuspend notifications.
+ int resumes_; // Count of OnResume notifications.
+};
+
+class SystemMonitorTest : public testing::Test {
+ protected:
+ SystemMonitorTest() {
+#if defined(OS_MACOSX)
+ // This needs to happen before SystemMonitor's ctor.
+ SystemMonitor::AllocateSystemIOPorts();
+#endif
+ system_monitor_.reset(new SystemMonitor);
+ }
+ virtual ~SystemMonitorTest() {}
+
+ MessageLoop message_loop_;
+ scoped_ptr<SystemMonitor> system_monitor_;
+
+ DISALLOW_COPY_AND_ASSIGN(SystemMonitorTest);
+};
+
+TEST_F(SystemMonitorTest, PowerNotifications) {
+ const int kObservers = 5;
+
+ PowerTest test[kObservers];
+ for (int index = 0; index < kObservers; ++index)
+ system_monitor_->AddPowerObserver(&test[index]);
+
+ // Send a bunch of power changes. Since the battery power hasn't
+ // actually changed, we shouldn't get notifications.
+ for (int index = 0; index < 5; index++) {
+ system_monitor_->ProcessPowerMessage(SystemMonitor::POWER_STATE_EVENT);
+ EXPECT_EQ(test[0].power_state_changes(), 0);
+ }
+
+ // Sending resume when not suspended should have no effect.
+ system_monitor_->ProcessPowerMessage(SystemMonitor::RESUME_EVENT);
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(test[0].resumes(), 0);
+
+ // Pretend we suspended.
+ system_monitor_->ProcessPowerMessage(SystemMonitor::SUSPEND_EVENT);
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(test[0].suspends(), 1);
+
+ // Send a second suspend notification. This should be suppressed.
+ system_monitor_->ProcessPowerMessage(SystemMonitor::SUSPEND_EVENT);
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(test[0].suspends(), 1);
+
+ // Pretend we were awakened.
+ system_monitor_->ProcessPowerMessage(SystemMonitor::RESUME_EVENT);
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(test[0].resumes(), 1);
+
+ // Send a duplicate resume notification. This should be suppressed.
+ system_monitor_->ProcessPowerMessage(SystemMonitor::RESUME_EVENT);
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(test[0].resumes(), 1);
+}
+
+TEST_F(SystemMonitorTest, DeviceChangeNotifications) {
+ const int kObservers = 5;
+ const string16 kDeviceName = ASCIIToUTF16("media device");
+ const std::string kDeviceId1 = "1";
+ const std::string kDeviceId2 = "2";
+
+ testing::Sequence mock_sequencer[kObservers];
+ MockDevicesChangedObserver observers[kObservers];
+ for (int index = 0; index < kObservers; ++index) {
+ system_monitor_->AddDevicesChangedObserver(&observers[index]);
+
+ EXPECT_CALL(observers[index],
+ OnDevicesChanged(SystemMonitor::DEVTYPE_UNKNOWN))
+ .Times(3)
+ .InSequence(mock_sequencer[index]);
+ EXPECT_CALL(observers[index], OnRemovableStorageAttached(kDeviceId1,
+ kDeviceName,
+ testing::_))
+ .InSequence(mock_sequencer[index]);
+ EXPECT_CALL(observers[index], OnRemovableStorageDetached(kDeviceId1))
+ .InSequence(mock_sequencer[index]);
+ EXPECT_CALL(observers[index], OnRemovableStorageDetached(kDeviceId2))
+ .Times(0).InSequence(mock_sequencer[index]);
+ }
+
+ system_monitor_->ProcessDevicesChanged(SystemMonitor::DEVTYPE_UNKNOWN);
+ message_loop_.RunUntilIdle();
+
+ system_monitor_->ProcessDevicesChanged(SystemMonitor::DEVTYPE_UNKNOWN);
+ system_monitor_->ProcessDevicesChanged(SystemMonitor::DEVTYPE_UNKNOWN);
+ message_loop_.RunUntilIdle();
+
+ system_monitor_->ProcessRemovableStorageAttached(kDeviceId1,
+ kDeviceName,
+ FILE_PATH_LITERAL("path"));
+ message_loop_.RunUntilIdle();
+
+ system_monitor_->ProcessRemovableStorageDetached(kDeviceId1);
+ system_monitor_->ProcessRemovableStorageDetached(kDeviceId2);
+ message_loop_.RunUntilIdle();
+}
+
+TEST_F(SystemMonitorTest, GetAttachedRemovableStorageEmpty) {
+ std::vector<SystemMonitor::RemovableStorageInfo> devices =
+ system_monitor_->GetAttachedRemovableStorage();
+ EXPECT_EQ(0U, devices.size());
+}
+
+TEST_F(SystemMonitorTest, GetAttachedRemovableStorageAttachDetach) {
+ const std::string kDeviceId1 = "42";
+ const string16 kDeviceName1 = ASCIIToUTF16("test");
+ const FilePath kDevicePath1(FILE_PATH_LITERAL("/testfoo"));
+ system_monitor_->ProcessRemovableStorageAttached(kDeviceId1,
+ kDeviceName1,
+ kDevicePath1.value());
+ message_loop_.RunUntilIdle();
+ std::vector<SystemMonitor::RemovableStorageInfo> devices =
+ system_monitor_->GetAttachedRemovableStorage();
+ ASSERT_EQ(1U, devices.size());
+ EXPECT_EQ(kDeviceId1, devices[0].device_id);
+ EXPECT_EQ(kDeviceName1, devices[0].name);
+ EXPECT_EQ(kDevicePath1.value(), devices[0].location);
+
+ const std::string kDeviceId2 = "44";
+ const string16 kDeviceName2 = ASCIIToUTF16("test2");
+ const FilePath kDevicePath2(FILE_PATH_LITERAL("/testbar"));
+ system_monitor_->ProcessRemovableStorageAttached(kDeviceId2,
+ kDeviceName2,
+ kDevicePath2.value());
+ message_loop_.RunUntilIdle();
+ devices = system_monitor_->GetAttachedRemovableStorage();
+ ASSERT_EQ(2U, devices.size());
+ EXPECT_EQ(kDeviceId1, devices[0].device_id);
+ EXPECT_EQ(kDeviceName1, devices[0].name);
+ EXPECT_EQ(kDevicePath1.value(), devices[0].location);
+ EXPECT_EQ(kDeviceId2, devices[1].device_id);
+ EXPECT_EQ(kDeviceName2, devices[1].name);
+ EXPECT_EQ(kDevicePath2.value(), devices[1].location);
+
+ system_monitor_->ProcessRemovableStorageDetached(kDeviceId1);
+ message_loop_.RunUntilIdle();
+ devices = system_monitor_->GetAttachedRemovableStorage();
+ ASSERT_EQ(1U, devices.size());
+ EXPECT_EQ(kDeviceId2, devices[0].device_id);
+ EXPECT_EQ(kDeviceName2, devices[0].name);
+ EXPECT_EQ(kDevicePath2.value(), devices[0].location);
+
+ system_monitor_->ProcessRemovableStorageDetached(kDeviceId2);
+ message_loop_.RunUntilIdle();
+ devices = system_monitor_->GetAttachedRemovableStorage();
+ EXPECT_EQ(0U, devices.size());
+}
+
+} // namespace
+
+} // namespace base
diff --git a/src/base/system_monitor/system_monitor_win.cc b/src/base/system_monitor/system_monitor_win.cc
new file mode 100644
index 0000000..a8cd54a
--- /dev/null
+++ b/src/base/system_monitor/system_monitor_win.cc
@@ -0,0 +1,50 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/system_monitor/system_monitor.h"
+
+namespace base {
+
+void SystemMonitor::ProcessWmPowerBroadcastMessage(int event_id) {
+ PowerEvent power_event;
+ switch (event_id) {
+ case PBT_APMPOWERSTATUSCHANGE: // The power status changed.
+ power_event = POWER_STATE_EVENT;
+ break;
+ case PBT_APMRESUMEAUTOMATIC: // Resume from suspend.
+ //case PBT_APMRESUMESUSPEND: // User-initiated resume from suspend.
+ // We don't notify for this latter event
+ // because if it occurs it is always sent as a
+ // second event after PBT_APMRESUMEAUTOMATIC.
+ power_event = RESUME_EVENT;
+ break;
+ case PBT_APMSUSPEND: // System has been suspended.
+ power_event = SUSPEND_EVENT;
+ break;
+ default:
+ return;
+
+ // Other Power Events:
+ // PBT_APMBATTERYLOW - removed in Vista.
+ // PBT_APMOEMEVENT - removed in Vista.
+ // PBT_APMQUERYSUSPEND - removed in Vista.
+ // PBT_APMQUERYSUSPENDFAILED - removed in Vista.
+ // PBT_APMRESUMECRITICAL - removed in Vista.
+ // PBT_POWERSETTINGCHANGE - user changed the power settings.
+ }
+ ProcessPowerMessage(power_event);
+}
+
+// Function to query the system to see if it is currently running on
+// battery power. Returns true if running on battery.
+bool SystemMonitor::IsBatteryPower() {
+ SYSTEM_POWER_STATUS status;
+ if (!GetSystemPowerStatus(&status)) {
+ DLOG(ERROR) << "GetSystemPowerStatus failed: " << GetLastError();
+ return false;
+ }
+ return (status.ACLineStatus == 0);
+}
+
+} // namespace base
diff --git a/src/base/task_runner.cc b/src/base/task_runner.cc
new file mode 100644
index 0000000..b0563d4
--- /dev/null
+++ b/src/base/task_runner.cc
@@ -0,0 +1,68 @@
+// Copyright (c) 2012 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/task_runner.h"
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/threading/post_task_and_reply_impl.h"
+
+namespace base {
+
+namespace {
+
+// TODO(akalin): There's only one other implementation of
+// PostTaskAndReplyImpl in WorkerPool. Investigate whether it'll be
+// possible to merge the two.
+class PostTaskAndReplyTaskRunner : public internal::PostTaskAndReplyImpl {
+ public:
+ PostTaskAndReplyTaskRunner(TaskRunner* destination);
+
+ private:
+ virtual bool PostTask(const tracked_objects::Location& from_here,
+ const Closure& task) OVERRIDE;
+
+ // Non-owning.
+ TaskRunner* destination_;
+};
+
+PostTaskAndReplyTaskRunner::PostTaskAndReplyTaskRunner(
+ TaskRunner* destination) : destination_(destination) {
+ DCHECK(destination_);
+}
+
+bool PostTaskAndReplyTaskRunner::PostTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task) {
+ return destination_->PostTask(from_here, task);
+}
+
+} // namespace
+
+bool TaskRunner::PostTask(const tracked_objects::Location& from_here,
+ const Closure& task) {
+ return PostDelayedTask(from_here, task, base::TimeDelta());
+}
+
+bool TaskRunner::PostTaskAndReply(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ const Closure& reply) {
+ return PostTaskAndReplyTaskRunner(this).PostTaskAndReply(
+ from_here, task, reply);
+}
+
+TaskRunner::TaskRunner() {}
+
+TaskRunner::~TaskRunner() {}
+
+void TaskRunner::OnDestruct() const {
+ delete this;
+}
+
+void TaskRunnerTraits::Destruct(const TaskRunner* task_runner) {
+ task_runner->OnDestruct();
+}
+
+} // namespace base
diff --git a/src/base/task_runner.h b/src/base/task_runner.h
new file mode 100644
index 0000000..4fbea4d
--- /dev/null
+++ b/src/base/task_runner.h
@@ -0,0 +1,153 @@
+// Copyright (c) 2012 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_TASK_RUNNER_H_
+#define BASE_TASK_RUNNER_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/memory/ref_counted.h"
+#include "base/time.h"
+
+namespace tracked_objects {
+class Location;
+} // namespace tracked_objects
+
+namespace base {
+
+struct TaskRunnerTraits;
+
+// A TaskRunner is an object that runs posted tasks (in the form of
+// Closure objects). The TaskRunner interface provides a way of
+// decoupling task posting from the mechanics of how each task will be
+// run. TaskRunner provides very weak guarantees as to how posted
+// tasks are run (or if they're run at all). In particular, it only
+// guarantees:
+//
+// - Posting a task will not run it synchronously. That is, no
+// Post*Task method will call task.Run() directly.
+//
+// - Increasing the delay can only delay when the task gets run.
+// That is, increasing the delay may not affect when the task gets
+// run, or it could make it run later than it normally would, but
+// it won't make it run earlier than it normally would.
+//
+// TaskRunner does not guarantee the order in which posted tasks are
+// run, whether tasks overlap, or whether they're run on a particular
+// thread. Also it does not guarantee a memory model for shared data
+// between tasks. (In other words, you should use your own
+// synchronization/locking primitives if you need to share data
+// between tasks.)
+//
+// Implementations of TaskRunner should be thread-safe in that all
+// methods must be safe to call on any thread. Ownership semantics
+// for TaskRunners are in general not clear, which is why the
+// interface itself is RefCountedThreadSafe.
+//
+// Some theoretical implementations of TaskRunner:
+//
+// - A TaskRunner that uses a thread pool to run posted tasks.
+//
+// - A TaskRunner that, for each task, spawns a non-joinable thread
+// to run that task and immediately quit.
+//
+// - A TaskRunner that stores the list of posted tasks and has a
+// method Run() that runs each runnable task in random order.
+class BASE_EXPORT TaskRunner
+ : public RefCountedThreadSafe<TaskRunner, TaskRunnerTraits> {
+ public:
+ // Posts the given task to be run. Returns true if the task may be
+ // run at some point in the future, and false if the task definitely
+ // will not be run.
+ //
+ // Equivalent to PostDelayedTask(from_here, task, 0).
+ bool PostTask(const tracked_objects::Location& from_here,
+ const Closure& task);
+
+ // Like PostTask, but tries to run the posted task only after
+ // |delay_ms| has passed.
+ //
+ // It is valid for an implementation to ignore |delay_ms|; that is,
+ // to have PostDelayedTask behave the same as PostTask.
+ virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
+ const Closure& task,
+ base::TimeDelta delay) = 0;
+
+ // Returns true if the current thread is a thread on which a task
+ // may be run, and false if no task will be run on the current
+ // thread.
+ //
+ // It is valid for an implementation to always return true, or in
+ // general to use 'true' as a default value.
+ virtual bool RunsTasksOnCurrentThread() const = 0;
+
+ // Posts |task| on the current TaskRunner. On completion, |reply|
+ // is posted to the thread that called PostTaskAndReply(). Both
+ // |task| and |reply| are guaranteed to be deleted on the thread
+ // from which PostTaskAndReply() is invoked. This allows objects
+ // that must be deleted on the originating thread to be bound into
+ // the |task| and |reply| Closures. In particular, it can be useful
+ // to use WeakPtr<> in the |reply| Closure so that the reply
+ // operation can be canceled. See the following pseudo-code:
+ //
+ // class DataBuffer : public RefCountedThreadSafe<DataBuffer> {
+ // public:
+ // // Called to add data into a buffer.
+ // void AddData(void* buf, size_t length);
+ // ...
+ // };
+ //
+ //
+ // class DataLoader : public SupportsWeakPtr<DataLoader> {
+ // public:
+ // void GetData() {
+ // scoped_refptr<DataBuffer> buffer = new DataBuffer();
+ // target_thread_.message_loop_proxy()->PostTaskAndReply(
+ // FROM_HERE,
+ // base::Bind(&DataBuffer::AddData, buffer),
+ // base::Bind(&DataLoader::OnDataReceived, AsWeakPtr(), buffer));
+ // }
+ //
+ // private:
+ // void OnDataReceived(scoped_refptr<DataBuffer> buffer) {
+ // // Do something with buffer.
+ // }
+ // };
+ //
+ //
+ // Things to notice:
+ // * Results of |task| are shared with |reply| by binding a shared argument
+ // (a DataBuffer instance).
+ // * The DataLoader object has no special thread safety.
+ // * The DataLoader object can be deleted while |task| is still running,
+ // and the reply will cancel itself safely because it is bound to a
+ // WeakPtr<>.
+ bool PostTaskAndReply(const tracked_objects::Location& from_here,
+ const Closure& task,
+ const Closure& reply);
+
+ protected:
+ friend struct TaskRunnerTraits;
+
+ // Only the Windows debug build seems to need this: see
+ // http://crbug.com/112250.
+ friend class RefCountedThreadSafe<TaskRunner, TaskRunnerTraits>;
+
+ TaskRunner();
+ virtual ~TaskRunner();
+
+ // Called when this object should be destroyed. By default simply
+ // deletes |this|, but can be overridden to do something else, like
+ // delete on a certain thread.
+ virtual void OnDestruct() const;
+};
+
+struct BASE_EXPORT TaskRunnerTraits {
+ static void Destruct(const TaskRunner* task_runner);
+};
+
+} // namespace base
+
+#endif // BASE_TASK_RUNNER_H_
diff --git a/src/base/task_runner_util.h b/src/base/task_runner_util.h
new file mode 100644
index 0000000..b6dd0f3
--- /dev/null
+++ b/src/base/task_runner_util.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2012 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_TASK_RUNNER_UTIL_H_
+#define BASE_TASK_RUNNER_UTIL_H_
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback_internal.h"
+#include "base/logging.h"
+#include "base/task_runner.h"
+
+namespace base {
+
+namespace internal {
+
+// Adapts a function that produces a result via a return value to
+// one that returns via an output parameter.
+template <typename ReturnType>
+void ReturnAsParamAdapter(const Callback<ReturnType(void)>& func,
+ ReturnType* result) {
+ *result = func.Run();
+}
+
+// Adapts a T* result to a callblack that expects a T.
+template <typename TaskReturnType, typename ReplyArgType>
+void ReplyAdapter(const Callback<void(ReplyArgType)>& callback,
+ TaskReturnType* result) {
+ // TODO(ajwong): Remove this conditional and add a DCHECK to enforce that
+ // |reply| must be non-null in PostTaskAndReplyWithResult() below after
+ // current code that relies on this API softness has been removed.
+ // http://crbug.com/162712
+ if (!callback.is_null())
+ callback.Run(CallbackForward(*result));
+}
+
+} // namespace internal
+
+// When you have these methods
+//
+// R DoWorkAndReturn();
+// void Callback(const R& result);
+//
+// and want to call them in a PostTaskAndReply kind of fashion where the
+// result of DoWorkAndReturn is passed to the Callback, you can use
+// PostTaskAndReplyWithResult as in this example:
+//
+// PostTaskAndReplyWithResult(
+// target_thread_.message_loop_proxy(),
+// FROM_HERE,
+// Bind(&DoWorkAndReturn),
+// Bind(&Callback));
+template <typename TaskReturnType, typename ReplyArgType>
+bool PostTaskAndReplyWithResult(
+ TaskRunner* task_runner,
+ const tracked_objects::Location& from_here,
+ const Callback<TaskReturnType(void)>& task,
+ const Callback<void(ReplyArgType)>& reply) {
+ TaskReturnType* result = new TaskReturnType();
+ return task_runner->PostTaskAndReply(
+ from_here,
+ base::Bind(&internal::ReturnAsParamAdapter<TaskReturnType>, task,
+ result),
+ base::Bind(&internal::ReplyAdapter<TaskReturnType, ReplyArgType>, reply,
+ base::Owned(result)));
+}
+
+} // namespace base
+
+#endif // BASE_TASK_RUNNER_UTIL_H_
diff --git a/src/base/task_runner_util_unittest.cc b/src/base/task_runner_util_unittest.cc
new file mode 100644
index 0000000..d4d3769
--- /dev/null
+++ b/src/base/task_runner_util_unittest.cc
@@ -0,0 +1,133 @@
+// Copyright (c) 2012 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/task_runner_util.h"
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+int ReturnFourtyTwo() {
+ return 42;
+}
+
+void StoreValue(int* destination, int value) {
+ *destination = value;
+}
+
+void StoreDoubleValue(double* destination, double value) {
+ *destination = value;
+}
+
+int g_foo_destruct_count = 0;
+int g_foo_free_count = 0;
+
+struct Foo {
+ ~Foo() {
+ ++g_foo_destruct_count;
+ }
+};
+
+scoped_ptr<Foo> CreateFoo() {
+ return scoped_ptr<Foo>(new Foo);
+}
+
+void ExpectFoo(scoped_ptr<Foo> foo) {
+ EXPECT_TRUE(foo.get());
+ scoped_ptr<Foo> local_foo(foo.Pass());
+ EXPECT_TRUE(local_foo.get());
+ EXPECT_FALSE(foo.get());
+}
+
+struct FreeFooFunctor {
+ void operator()(Foo* foo) const {
+ ++g_foo_free_count;
+ delete foo;
+ };
+};
+
+scoped_ptr_malloc<Foo, FreeFooFunctor> CreateScopedFoo() {
+ return scoped_ptr_malloc<Foo, FreeFooFunctor>(new Foo);
+}
+
+void ExpectScopedFoo(scoped_ptr_malloc<Foo, FreeFooFunctor> foo) {
+ EXPECT_TRUE(foo.get());
+ scoped_ptr_malloc<Foo, FreeFooFunctor> local_foo(foo.Pass());
+ EXPECT_TRUE(local_foo.get());
+ EXPECT_FALSE(foo.get());
+}
+
+} // namespace
+
+TEST(TaskRunnerHelpersTest, PostTaskAndReplyWithResult) {
+ MessageLoop message_loop;
+ int result = 0;
+
+ PostTaskAndReplyWithResult(
+ message_loop.message_loop_proxy(),
+ FROM_HERE,
+ Bind(&ReturnFourtyTwo),
+ Bind(&StoreValue, &result));
+
+ message_loop.RunUntilIdle();
+
+ EXPECT_EQ(42, result);
+}
+
+TEST(TaskRunnerHelpersTest, PostTaskAndReplyWithResultImplicitConvert) {
+ MessageLoop message_loop;
+ double result = 0;
+
+ PostTaskAndReplyWithResult(
+ message_loop.message_loop_proxy(),
+ FROM_HERE,
+ Bind(&ReturnFourtyTwo),
+ Bind(&StoreDoubleValue, &result));
+
+ message_loop.RunUntilIdle();
+
+ EXPECT_DOUBLE_EQ(42.0, result);
+}
+
+TEST(TaskRunnerHelpersTest, PostTaskAndReplyWithResultPassed) {
+ g_foo_destruct_count = 0;
+ g_foo_free_count = 0;
+
+ MessageLoop message_loop;
+
+ PostTaskAndReplyWithResult(
+ message_loop.message_loop_proxy(),
+ FROM_HERE,
+ Bind(&CreateFoo),
+ Bind(&ExpectFoo));
+
+ message_loop.RunUntilIdle();
+
+ EXPECT_EQ(1, g_foo_destruct_count);
+ EXPECT_EQ(0, g_foo_free_count);
+}
+
+TEST(TaskRunnerHelpersTest, PostTaskAndReplyWithResultPassedFreeProc) {
+ g_foo_destruct_count = 0;
+ g_foo_free_count = 0;
+
+ MessageLoop message_loop;
+
+ PostTaskAndReplyWithResult(
+ message_loop.message_loop_proxy(),
+ FROM_HERE,
+ Bind(&CreateScopedFoo),
+ Bind(&ExpectScopedFoo));
+
+ message_loop.RunUntilIdle();
+
+ EXPECT_EQ(1, g_foo_destruct_count);
+ EXPECT_EQ(1, g_foo_free_count);
+}
+
+} // namespace base
diff --git a/src/base/template_util.h b/src/base/template_util.h
new file mode 100644
index 0000000..e21d4dc
--- /dev/null
+++ b/src/base/template_util.h
@@ -0,0 +1,108 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEMPLATE_UTIL_H_
+#define BASE_TEMPLATE_UTIL_H_
+
+#include <cstddef> // For size_t.
+
+#include "build/build_config.h"
+
+namespace base {
+
+// template definitions from tr1
+
+template<class T, T v>
+struct integral_constant {
+ static const T value = v;
+ typedef T value_type;
+ typedef integral_constant<T, v> type;
+};
+
+template <class T, T v> const T integral_constant<T, v>::value;
+
+typedef integral_constant<bool, true> true_type;
+typedef integral_constant<bool, false> false_type;
+
+template <class T> struct is_pointer : false_type {};
+template <class T> struct is_pointer<T*> : true_type {};
+
+template <class T, class U> struct is_same : public false_type {};
+template <class T> struct is_same<T,T> : true_type {};
+
+template<class> struct is_array : public false_type {};
+template<class T, size_t n> struct is_array<T[n]> : public true_type {};
+template<class T> struct is_array<T[]> : public true_type {};
+
+template <class T> struct is_non_const_reference : false_type {};
+template <class T> struct is_non_const_reference<T&> : true_type {};
+template <class T> struct is_non_const_reference<const T&> : false_type {};
+
+template <class T> struct is_void : false_type {};
+template <> struct is_void<void> : true_type {};
+
+namespace internal {
+
+// Types YesType and NoType are guaranteed such that sizeof(YesType) <
+// sizeof(NoType).
+typedef char YesType;
+
+struct NoType {
+ YesType dummy[2];
+};
+
+// This class is an implementation detail for is_convertible, and you
+// don't need to know how it works to use is_convertible. For those
+// who care: we declare two different functions, one whose argument is
+// of type To and one with a variadic argument list. We give them
+// return types of different size, so we can use sizeof to trick the
+// compiler into telling us which function it would have chosen if we
+// had called it with an argument of type From. See Alexandrescu's
+// _Modern C++ Design_ for more details on this sort of trick.
+
+struct ConvertHelper {
+ template <typename To>
+ static YesType Test(To);
+
+ template <typename To>
+ static NoType Test(...);
+
+ template <typename From>
+ static From& Create();
+};
+
+// Used to determine if a type is a struct/union/class. Inspired by Boost's
+// is_class type_trait implementation.
+struct IsClassHelper {
+ template <typename C>
+ static YesType Test(void(C::*)(void));
+
+ template <typename C>
+ static NoType Test(...);
+};
+
+} // namespace internal
+
+// Inherits from true_type if From is convertible to To, false_type otherwise.
+//
+// Note that if the type is convertible, this will be a true_type REGARDLESS
+// of whether or not the conversion would emit a warning.
+template <typename From, typename To>
+struct is_convertible
+ : integral_constant<bool,
+ sizeof(internal::ConvertHelper::Test<To>(
+ internal::ConvertHelper::Create<From>())) ==
+ sizeof(internal::YesType)> {
+};
+
+template <typename T>
+struct is_class
+ : integral_constant<bool,
+ sizeof(internal::IsClassHelper::Test<T>(0)) ==
+ sizeof(internal::YesType)> {
+};
+
+} // namespace base
+
+#endif // BASE_TEMPLATE_UTIL_H_
diff --git a/src/base/template_util_unittest.cc b/src/base/template_util_unittest.cc
new file mode 100644
index 0000000..4cfa3e4
--- /dev/null
+++ b/src/base/template_util_unittest.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2012 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/template_util.h"
+
+#include "base/basictypes.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+struct AStruct {};
+class AClass {};
+enum AnEnum {};
+
+class Parent {};
+class Child : public Parent {};
+
+// is_pointer<Type>
+COMPILE_ASSERT(!is_pointer<int>::value, IsPointer);
+COMPILE_ASSERT(!is_pointer<int&>::value, IsPointer);
+COMPILE_ASSERT(is_pointer<int*>::value, IsPointer);
+COMPILE_ASSERT(is_pointer<const int*>::value, IsPointer);
+
+// is_array<Type>
+COMPILE_ASSERT(!is_array<int>::value, IsArray);
+COMPILE_ASSERT(!is_array<int*>::value, IsArray);
+COMPILE_ASSERT(!is_array<int(*)[3]>::value, IsArray);
+COMPILE_ASSERT(is_array<int[]>::value, IsArray);
+COMPILE_ASSERT(is_array<const int[]>::value, IsArray);
+COMPILE_ASSERT(is_array<int[3]>::value, IsArray);
+
+// is_non_const_reference<Type>
+COMPILE_ASSERT(!is_non_const_reference<int>::value, IsNonConstReference);
+COMPILE_ASSERT(!is_non_const_reference<const int&>::value, IsNonConstReference);
+COMPILE_ASSERT(is_non_const_reference<int&>::value, IsNonConstReference);
+
+// is_convertible<From, To>
+
+// Extra parens needed to make preprocessor macro parsing happy. Otherwise,
+// it sees the equivalent of:
+//
+// (is_convertible < Child), (Parent > ::value)
+//
+// Silly C++.
+COMPILE_ASSERT( (is_convertible<Child, Parent>::value), IsConvertible);
+COMPILE_ASSERT(!(is_convertible<Parent, Child>::value), IsConvertible);
+COMPILE_ASSERT(!(is_convertible<Parent, AStruct>::value), IsConvertible);
+COMPILE_ASSERT( (is_convertible<int, double>::value), IsConvertible);
+COMPILE_ASSERT( (is_convertible<int*, void*>::value), IsConvertible);
+COMPILE_ASSERT(!(is_convertible<void*, int*>::value), IsConvertible);
+
+// Array types are an easy corner case. Make sure to test that
+// it does indeed compile.
+COMPILE_ASSERT(!(is_convertible<int[10], double>::value), IsConvertible);
+COMPILE_ASSERT(!(is_convertible<double, int[10]>::value), IsConvertible);
+COMPILE_ASSERT( (is_convertible<int[10], int*>::value), IsConvertible);
+
+// is_same<Type1, Type2>
+COMPILE_ASSERT(!(is_same<Child, Parent>::value), IsSame);
+COMPILE_ASSERT(!(is_same<Parent, Child>::value), IsSame);
+COMPILE_ASSERT( (is_same<Parent, Parent>::value), IsSame);
+COMPILE_ASSERT( (is_same<int*, int*>::value), IsSame);
+COMPILE_ASSERT( (is_same<int, int>::value), IsSame);
+COMPILE_ASSERT( (is_same<void, void>::value), IsSame);
+COMPILE_ASSERT(!(is_same<int, double>::value), IsSame);
+
+
+// is_class<Type>
+COMPILE_ASSERT(is_class<AStruct>::value, IsClass);
+COMPILE_ASSERT(is_class<AClass>::value, IsClass);
+COMPILE_ASSERT(!is_class<AnEnum>::value, IsClass);
+COMPILE_ASSERT(!is_class<int>::value, IsClass);
+COMPILE_ASSERT(!is_class<char*>::value, IsClass);
+COMPILE_ASSERT(!is_class<int&>::value, IsClass);
+COMPILE_ASSERT(!is_class<char[3]>::value, IsClass);
+
+} // namespace
+} // namespace base
diff --git a/src/base/test/android/javatests/src/org/chromium/base/test/util/DisabledTest.java b/src/base/test/android/javatests/src/org/chromium/base/test/util/DisabledTest.java
new file mode 100644
index 0000000..c2b2ecd
--- /dev/null
+++ b/src/base/test/android/javatests/src/org/chromium/base/test/util/DisabledTest.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base.test.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation is for disabled tests.
+ * <p>
+ * Tests with this annotation will not be run on any of the normal bots.
+ * Please note that they might eventually run on a special bot.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DisabledTest {
+}
diff --git a/src/base/test/android/javatests/src/org/chromium/base/test/util/EnormousTest.java b/src/base/test/android/javatests/src/org/chromium/base/test/util/EnormousTest.java
new file mode 100644
index 0000000..9f8fa93
--- /dev/null
+++ b/src/base/test/android/javatests/src/org/chromium/base/test/util/EnormousTest.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base.test.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation is for enormous tests.
+ * <p>
+ * Examples of enormous tests are tests that depend on external web sites or
+ * tests that are long running.
+ * <p>
+ * Such tests are likely NOT reliable enough to run on tree closing bots and
+ * should only be run on FYI bots.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface EnormousTest {
+}
diff --git a/src/base/test/android/javatests/src/org/chromium/base/test/util/Feature.java b/src/base/test/android/javatests/src/org/chromium/base/test/util/Feature.java
new file mode 100644
index 0000000..aa120ea
--- /dev/null
+++ b/src/base/test/android/javatests/src/org/chromium/base/test/util/Feature.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base.test.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The java instrumentation tests are normally fairly large (in terms of
+ * dependencies), and the test suite ends up containing a large amount of
+ * tests that are not trivial to filter / group just by their names.
+ * Instead, we use this annotation: each test should be annotated as:
+ * @Feature({"Foo", "Bar"})
+ * in order for the test runner scripts to be able to filter and group
+ * them accordingly (for instance, this enable us to run all tests that exercise
+ * feature Foo).
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Feature {
+ /**
+ * @return A list of feature names.
+ */
+ public String[] value();
+}
diff --git a/src/base/test/android/javatests/src/org/chromium/base/test/util/InstrumentationUtils.java b/src/base/test/android/javatests/src/org/chromium/base/test/util/InstrumentationUtils.java
new file mode 100644
index 0000000..0dc730a
--- /dev/null
+++ b/src/base/test/android/javatests/src/org/chromium/base/test/util/InstrumentationUtils.java
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base.test.util;
+
+import android.app.Instrumentation;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
+/**
+ * Utility methods built around the android.app.Instrumentation class.
+ */
+public final class InstrumentationUtils {
+
+ private InstrumentationUtils() {
+ }
+
+ public static <R> R runOnMainSyncAndGetResult(Instrumentation instrumentation,
+ Callable<R> callable) throws Throwable {
+ FutureTask<R> task = new FutureTask<R>(callable);
+ instrumentation.runOnMainSync(task);
+ try {
+ return task.get();
+ } catch (ExecutionException e) {
+ // Unwrap the cause of the exception and re-throw it.
+ throw e.getCause();
+ }
+ }
+}
diff --git a/src/base/test/android/javatests/src/org/chromium/base/test/util/ScalableTimeout.java b/src/base/test/android/javatests/src/org/chromium/base/test/util/ScalableTimeout.java
new file mode 100644
index 0000000..a107b32
--- /dev/null
+++ b/src/base/test/android/javatests/src/org/chromium/base/test/util/ScalableTimeout.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base.test.util;
+
+/**
+ * Utility class for scaling various timeouts by a common factor.
+ * For example, to run tests under Valgrind, you might want the following:
+ * adb shell "echo 20.0 > /data/local/tmp/chrome_timeout_scale"
+ */
+public class ScalableTimeout {
+ private static Double sTimeoutScale = null;
+ private static final String PROPERTY_FILE = "/data/local/tmp/chrome_timeout_scale";
+
+ public static long ScaleTimeout(long timeout) {
+ if (sTimeoutScale == null) {
+ try {
+ char[] data = TestFileUtil.readUtf8File(PROPERTY_FILE, 32);
+ sTimeoutScale = Double.parseDouble(new String(data));
+ } catch (Exception e) {
+ // NumberFormatException, FileNotFoundException, IOException
+ sTimeoutScale = 1.0;
+ }
+ }
+ return (long)(timeout * sTimeoutScale);
+ }
+}
diff --git a/src/base/test/android/javatests/src/org/chromium/base/test/util/TestFileUtil.java b/src/base/test/android/javatests/src/org/chromium/base/test/util/TestFileUtil.java
new file mode 100644
index 0000000..1afbc11
--- /dev/null
+++ b/src/base/test/android/javatests/src/org/chromium/base/test/util/TestFileUtil.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base.test.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Arrays;
+
+/**
+ * Utility class for dealing with files for test.
+ */
+public class TestFileUtil {
+ public static void createNewHtmlFile(String name, String title, String body)
+ throws IOException {
+ File file = new File(name);
+ if (!file.createNewFile()) {
+ throw new IOException("File \"" + name + "\" already exists");
+ }
+
+ Writer writer = null;
+ try {
+ writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
+ writer.write("<html><meta charset=\"UTF-8\" />" +
+ "<head><title>" + title + "</title></head>" +
+ "<body>" +
+ (body != null ? body : "") +
+ "</body>" +
+ "</html>");
+ } finally {
+ if (writer != null) {
+ writer.close();
+ }
+ }
+ }
+
+ public static void deleteFile(String name) {
+ File file = new File(name);
+ file.delete();
+ }
+
+ /**
+ * @param fileName the file to read in.
+ * @param sizeLimit cap on the file size: will throw an exception if exceeded
+ * @return Array of chars read from the file
+ * @throws FileNotFoundException file does not exceed
+ * @throws IOException error encountered accessing the file
+ */
+ public static char[] readUtf8File(String fileName, int sizeLimit) throws
+ FileNotFoundException, IOException {
+ Reader reader = null;
+ try {
+ File f = new File(fileName);
+ if (f.length() > sizeLimit) {
+ throw new IOException("File " + fileName + " length " + f.length() +
+ " exceeds limit " + sizeLimit);
+ }
+ char[] buffer = new char[(int) f.length()];
+ reader = new InputStreamReader(new FileInputStream(f), "UTF-8");
+ int charsRead = reader.read(buffer);
+ // Debug check that we've exhausted the input stream (will fail e.g. if the
+ // file grew after we inspected its length).
+ assert !reader.ready();
+ return charsRead < buffer.length ? Arrays.copyOfRange(buffer, 0, charsRead) : buffer;
+ } finally {
+ if (reader != null) reader.close();
+ }
+ }
+}
diff --git a/src/base/test/android/javatests/src/org/chromium/base/test/util/TestThread.java b/src/base/test/android/javatests/src/org/chromium/base/test/util/TestThread.java
new file mode 100644
index 0000000..11e6afd
--- /dev/null
+++ b/src/base/test/android/javatests/src/org/chromium/base/test/util/TestThread.java
@@ -0,0 +1,143 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base.test.util;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * This class is usefull when writing instrumentation tests that exercise code that posts tasks
+ * (to the same thread).
+ * Since the test code is run in a single thread, the posted tasks are never executed.
+ * The TestThread class lets you run that code on a specific thread synchronously and flush the
+ * message loop on that thread.
+ *
+ * Example of test using this:
+ *
+ * public void testMyAwesomeClass() {
+ * TestThread testThread = new TestThread();
+ * testThread.startAndWaitForReadyState();
+ *
+ * testThread.runOnTestThreadSyncAndProcessPendingTasks(new Runnable() {
+ * @Override
+ * public void run() {
+ * MyAwesomeClass.doStuffAsync();
+ * }
+ * });
+ * // Once we get there we know doStuffAsync has been executed and all the tasks it posted.
+ * assertTrue(MyAwesomeClass.stuffWasDone());
+ * }
+ *
+ * Notes:
+ * - this is only for tasks posted to the same thread. Anyway if you were posting to a different
+ * thread, you'd probably need to set that other thread up.
+ * - this only supports tasks posted using Handler.post(), it won't work with postDelayed and
+ * postAtTime.
+ * - if your test instanciates an object and that object is the one doing the posting of tasks, you
+ * probably want to instanciate it on the test thread as it might create the Handler it posts
+ * tasks to in the constructor.
+ */
+
+public class TestThread extends Thread {
+ private Object mThreadReadyLock;
+ private AtomicBoolean mThreadReady;
+ private Handler mMainThreadHandler;
+ private Handler mTestThreadHandler;
+
+ public TestThread() {
+ mMainThreadHandler = new Handler();
+ // We can't use the AtomicBoolean as the lock or findbugs will freak out...
+ mThreadReadyLock = new Object();
+ mThreadReady = new AtomicBoolean();
+ }
+
+ @Override
+ public void run() {
+ Looper.prepare();
+ mTestThreadHandler = new Handler();
+ mTestThreadHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mThreadReadyLock) {
+ mThreadReady.set(true);
+ mThreadReadyLock.notify();
+ }
+ }
+ });
+ Looper.loop();
+ }
+
+ /**
+ * Starts this TestThread and blocks until it's ready to accept calls.
+ */
+ public void startAndWaitForReadyState() {
+ checkOnMainThread();
+ start();
+ synchronized (mThreadReadyLock) {
+ try {
+ // Note the mThreadReady and while are not really needed.
+ // There are there so findbugs don't report warnings.
+ while (!mThreadReady.get()) {
+ mThreadReadyLock.wait();
+ }
+ } catch (InterruptedException ie) {
+ System.err.println("Error starting TestThread.");
+ ie.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Runs the passed Runnable synchronously on the TestThread and returns when all pending
+ * runnables have been excuted.
+ * Should be called from the main thread.
+ */
+ public void runOnTestThreadSyncAndProcessPendingTasks(Runnable r) {
+ checkOnMainThread();
+
+ runOnTestThreadSync(r);
+
+ // Run another task, when it's done it means all pendings tasks have executed.
+ runOnTestThreadSync(null);
+ }
+
+ /**
+ * Runs the passed Runnable on the test thread and blocks until it has finished executing.
+ * Should be called from the main thread.
+ * @param r The runnable to be executed.
+ */
+ public void runOnTestThreadSync(final Runnable r) {
+ checkOnMainThread();
+ final Object lock = new Object();
+ // Task executed is not really needed since we are only on one thread, it is here to appease
+ // findbugs.
+ final AtomicBoolean taskExecuted = new AtomicBoolean();
+ mTestThreadHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (r != null) r.run();
+ synchronized (lock) {
+ taskExecuted.set(true);
+ lock.notify();
+ }
+ }
+ });
+ synchronized (lock) {
+ try {
+ while (!taskExecuted.get()) {
+ lock.wait();
+ }
+ } catch (InterruptedException ie) {
+ ie.printStackTrace();
+ }
+ }
+ }
+
+ private void checkOnMainThread() {
+ assert Looper.myLooper() == mMainThreadHandler.getLooper();
+ }
+}
diff --git a/src/base/test/android/javatests/src/org/chromium/base/test/util/UrlUtils.java b/src/base/test/android/javatests/src/org/chromium/base/test/util/UrlUtils.java
new file mode 100644
index 0000000..7c13a32
--- /dev/null
+++ b/src/base/test/android/javatests/src/org/chromium/base/test/util/UrlUtils.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2012 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.
+
+package org.chromium.base.test.util;
+
+import org.chromium.base.PathUtils;
+
+/**
+ * Collection of URL utilities.
+ */
+public class UrlUtils {
+ private final static String DATA_DIR = "/chrome/test/data/";
+
+ /**
+ * Construct a suitable URL for loading a test data file.
+ *
+ * @param path Pathname relative to external/chrome/testing/data
+ */
+ public static String getTestFileUrl(String path) {
+ return "file://" + PathUtils.getExternalStorageDirectory() + DATA_DIR + path;
+ }
+}
diff --git a/src/base/test/main_hook.cc b/src/base/test/main_hook.cc
new file mode 100644
index 0000000..f7384df
--- /dev/null
+++ b/src/base/test/main_hook.cc
@@ -0,0 +1,35 @@
+// Copyright (c) 2012 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/test/main_hook.h"
+
+#if defined(__LB_SHELL__) || defined(COBALT)
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "cobalt/deprecated/platform_delegate.h"
+#endif
+
+#if defined(__LB_SHELL__FOR_RELEASE__)
+#error You cannot build unit tests in gold builds.
+#endif
+
+#if defined(__LB_SHELL__) || defined(COBALT)
+base::AtExitManager* platform_at_exit_manager_;
+
+MainHook::MainHook(MainType main_func, int argc, char* argv[]) {
+ // TODO: MainHooks should no longer be used and in fact this
+ // class was removed in the recent version of Chromium. Any required
+ // initialization logic in tests should be done in TestSuite::Initialize().
+ CommandLine::Init(argc, argv);
+ platform_at_exit_manager_ = new base::AtExitManager();
+ cobalt::deprecated::PlatformDelegate::Init();
+}
+MainHook::~MainHook() {
+ cobalt::deprecated::PlatformDelegate::Teardown();
+ delete platform_at_exit_manager_;
+}
+#elif !defined(OS_IOS)
+MainHook::MainHook(MainType main_func, int argc, char* argv[]) {}
+MainHook::~MainHook() {}
+#endif
diff --git a/src/base/test/main_hook.h b/src/base/test/main_hook.h
new file mode 100644
index 0000000..ff34bcb
--- /dev/null
+++ b/src/base/test/main_hook.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2012 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/basictypes.h"
+
+// Provides a way of running code before gtest-based tests with access to
+// argv and argc.
+class MainHook {
+ public:
+ typedef int (*MainType)(int, char*[]);
+ MainHook(MainType main_func, int argc, char* argv[]);
+ ~MainHook();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MainHook);
+};
diff --git a/src/base/test/main_hook_ios.mm b/src/base/test/main_hook_ios.mm
new file mode 100644
index 0000000..6d7bc44
--- /dev/null
+++ b/src/base/test/main_hook_ios.mm
@@ -0,0 +1,108 @@
+// Copyright (c) 2012 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/test/main_hook.h"
+
+#import <UIKit/UIKit.h>
+
+#include "base/debug/debugger.h"
+#include "base/logging.h"
+#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/memory/scoped_nsobject.h"
+
+// Springboard will kill any iOS app that fails to check in after launch within
+// a given time. These two classes prevent this from happening.
+
+// MainHook saves the chrome main() and calls UIApplicationMain(),
+// providing an application delegate class: ChromeUnitTestDelegate. The delegate
+// listens for UIApplicationDidFinishLaunchingNotification. When the
+// notification is received, it fires main() again to have the real work done.
+
+// Example usage:
+// int main(int argc, char** argv) {
+// MainHook hook(main, argc, argv);
+// // Testing code goes here. There should be no code above MainHook. If
+// // there is, it will be run twice.
+// }
+
+// Since the executable isn't likely to be a real iOS UI, the delegate puts up a
+// window displaying the app name. If a bunch of apps using MainHook are being
+// run in a row, this provides an indication of which one is currently running.
+
+static MainHook::MainType g_main_func = NULL;
+static int g_argc;
+static char** g_argv;
+
+@interface UIApplication (Testing)
+- (void) _terminateWithStatus:(int)status;
+@end
+
+@interface ChromeUnitTestDelegate : NSObject {
+@private
+ scoped_nsobject<UIWindow> window_;
+}
+- (void)runTests;
+@end
+
+@implementation ChromeUnitTestDelegate
+
+- (BOOL)application:(UIApplication *)application
+ didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+
+ CGRect bounds = [[UIScreen mainScreen] bounds];
+
+ // Yes, this is leaked, it's just to make what's running visible.
+ window_.reset([[UIWindow alloc] initWithFrame:bounds]);
+ [window_ makeKeyAndVisible];
+
+ // Add a label with the app name.
+ UILabel* label = [[[UILabel alloc] initWithFrame:bounds] autorelease];
+ label.text = [[NSProcessInfo processInfo] processName];
+ label.textAlignment = UITextAlignmentCenter;
+ [window_ addSubview:label];
+
+ // Queue up the test run.
+ [self performSelector:@selector(runTests)
+ withObject:nil
+ afterDelay:0.1];
+ return YES;
+}
+
+- (void)runTests {
+ int exitStatus = g_main_func(g_argc, g_argv);
+
+ // If a test app is too fast, it will exit before Instruments has has a
+ // a chance to initialize and no test results will be seen.
+ // TODO(ios): crbug.com/137010 Figure out how much time is actually needed,
+ // and sleep only to make sure that much time has elapsed since launch.
+ [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
+ window_.reset();
+
+ // Use the hidden selector to try and cleanly take down the app (otherwise
+ // things can think the app crashed even on a zero exit status).
+ UIApplication* application = [UIApplication sharedApplication];
+ [application _terminateWithStatus:exitStatus];
+
+ exit(exitStatus);
+}
+
+@end
+
+#pragma mark -
+
+MainHook::MainHook(MainType main_func, int argc, char* argv[]) {
+ static bool ran_hook = false;
+ if (!ran_hook) {
+ ran_hook = true;
+
+ g_main_func = main_func;
+ g_argc = argc;
+ g_argv = argv;
+
+ base::mac::ScopedNSAutoreleasePool pool;
+ int exit_status = UIApplicationMain(argc, argv, nil,
+ @"ChromeUnitTestDelegate");
+ exit(exit_status);
+ }
+}
diff --git a/src/base/test/mock_chrome_application_mac.h b/src/base/test/mock_chrome_application_mac.h
new file mode 100644
index 0000000..5cd1d19
--- /dev/null
+++ b/src/base/test/mock_chrome_application_mac.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_MOCK_CHROME_APPLICATION_MAC_H_
+#define BASE_TEST_MOCK_CHROME_APPLICATION_MAC_H_
+
+#if defined(__OBJC__)
+
+#import <AppKit/AppKit.h>
+
+#include "base/mac/scoped_sending_event.h"
+#include "base/message_pump_mac.h"
+
+// A basic implementation of CrAppProtocol and
+// CrAppControlProtocol. This can be used in tests that need an
+// NSApplication and use a runloop, or which need a ScopedSendingEvent
+// when handling a nested event loop.
+@interface MockCrApp : NSApplication<CrAppProtocol,
+ CrAppControlProtocol> {
+ @private
+ BOOL handlingSendEvent_;
+}
+@end
+
+#endif
+
+// To be used to instantiate MockCrApp from C++ code.
+namespace mock_cr_app {
+void RegisterMockCrApp();
+} // namespace mock_cr_app
+
+#endif // BASE_TEST_MOCK_CHROME_APPLICATION_MAC_H_
diff --git a/src/base/test/mock_chrome_application_mac.mm b/src/base/test/mock_chrome_application_mac.mm
new file mode 100644
index 0000000..0890553
--- /dev/null
+++ b/src/base/test/mock_chrome_application_mac.mm
@@ -0,0 +1,44 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/mock_chrome_application_mac.h"
+
+#include "base/auto_reset.h"
+#include "base/logging.h"
+
+@implementation MockCrApp
+
++ (NSApplication*)sharedApplication {
+ NSApplication* app = [super sharedApplication];
+ DCHECK([app conformsToProtocol:@protocol(CrAppControlProtocol)])
+ << "Existing NSApp (class " << [[app className] UTF8String]
+ << ") does not conform to required protocol.";
+ DCHECK(base::MessagePumpMac::UsingCrApp())
+ << "MessagePumpMac::Create() was called before "
+ << "+[MockCrApp sharedApplication]";
+ return app;
+}
+
+- (void)sendEvent:(NSEvent*)event {
+ base::AutoReset<BOOL> scoper(&handlingSendEvent_, YES);
+ [super sendEvent:event];
+}
+
+- (void)setHandlingSendEvent:(BOOL)handlingSendEvent {
+ handlingSendEvent_ = handlingSendEvent;
+}
+
+- (BOOL)isHandlingSendEvent {
+ return handlingSendEvent_;
+}
+
+@end
+
+namespace mock_cr_app {
+
+void RegisterMockCrApp() {
+ [MockCrApp sharedApplication];
+}
+
+} // namespace mock_cr_app
diff --git a/src/base/test/mock_devices_changed_observer.cc b/src/base/test/mock_devices_changed_observer.cc
new file mode 100644
index 0000000..c05f26a
--- /dev/null
+++ b/src/base/test/mock_devices_changed_observer.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2012 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/test/mock_devices_changed_observer.h"
+
+namespace base {
+
+MockDevicesChangedObserver::MockDevicesChangedObserver() {
+}
+
+MockDevicesChangedObserver::~MockDevicesChangedObserver() {
+}
+
+} // namespace base
diff --git a/src/base/test/mock_devices_changed_observer.h b/src/base/test/mock_devices_changed_observer.h
new file mode 100644
index 0000000..1d0f3dd
--- /dev/null
+++ b/src/base/test/mock_devices_changed_observer.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 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_TEST_MOCK_DEVICES_CHANGED_OBSERVER_H_
+#define BASE_TEST_MOCK_DEVICES_CHANGED_OBSERVER_H_
+
+#include <string>
+
+#include "base/system_monitor/system_monitor.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace base {
+
+class MockDevicesChangedObserver
+ : public base::SystemMonitor::DevicesChangedObserver {
+ public:
+ MockDevicesChangedObserver();
+ ~MockDevicesChangedObserver();
+
+ MOCK_METHOD1(OnDevicesChanged,
+ void(base::SystemMonitor::DeviceType device_type));
+ MOCK_METHOD3(OnRemovableStorageAttached,
+ void(const std::string& id,
+ const string16& name,
+ const FilePath::StringType& location));
+ MOCK_METHOD1(OnRemovableStorageDetached, void(const std::string& id));
+
+ DISALLOW_COPY_AND_ASSIGN(MockDevicesChangedObserver);
+};
+
+} // namespace base
+
+#endif // BASE_TEST_MOCK_DEVICES_CHANGED_OBSERVER_H_
diff --git a/src/base/test/mock_time_provider.cc b/src/base/test/mock_time_provider.cc
new file mode 100644
index 0000000..9e5547f
--- /dev/null
+++ b/src/base/test/mock_time_provider.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "base/test/mock_time_provider.h"
+
+using ::testing::DefaultValue;
+
+namespace base {
+
+MockTimeProvider* MockTimeProvider::instance_ = NULL;
+
+MockTimeProvider::MockTimeProvider() {
+ DCHECK(!instance_) << "Only one instance of MockTimeProvider can exist";
+ DCHECK(!DefaultValue<Time>::IsSet());
+ instance_ = this;
+ DefaultValue<Time>::Set(Time::FromInternalValue(0));
+}
+
+MockTimeProvider::~MockTimeProvider() {
+ instance_ = NULL;
+ DefaultValue<Time>::Clear();
+}
+
+// static
+Time MockTimeProvider::StaticNow() {
+ return instance_->Now();
+}
+
+} // namespace base
diff --git a/src/base/test/mock_time_provider.h b/src/base/test/mock_time_provider.h
new file mode 100644
index 0000000..81240f7
--- /dev/null
+++ b/src/base/test/mock_time_provider.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A helper class used to mock out calls to the static method base::Time::Now.
+//
+// Example usage:
+//
+// typedef base::Time(TimeProvider)();
+// class StopWatch {
+// public:
+// StopWatch(TimeProvider* time_provider);
+// void Start();
+// base::TimeDelta Stop();
+// private:
+// TimeProvider* time_provider_;
+// ...
+// }
+//
+// Normally, you would instantiate a StopWatch with the real Now function:
+//
+// StopWatch watch(&base::Time::Now);
+//
+// But when testing, you want to instantiate it with
+// MockTimeProvider::StaticNow, which calls an internally mocked out member.
+// This allows you to set expectations on the Now method. For example:
+//
+// TEST_F(StopWatchTest, BasicTest) {
+// InSequence s;
+// StrictMock<MockTimeProvider> mock_time;
+// EXPECT_CALL(mock_time, Now())
+// .WillOnce(Return(Time::FromDoubleT(4)));
+// EXPECT_CALL(mock_time, Now())
+// .WillOnce(Return(Time::FromDoubleT(10)));
+//
+// StopWatch sw(&MockTimeProvider::StaticNow);
+// sw.Start(); // First call to Now.
+// TimeDelta elapsed = sw.stop(); // Second call to Now.
+// ASSERT_EQ(elapsed, TimeDelta::FromSeconds(6));
+// }
+
+#ifndef BASE_TEST_MOCK_TIME_PROVIDER_H_
+#define BASE_TEST_MOCK_TIME_PROVIDER_H_
+
+#include "base/time.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace base {
+
+class MockTimeProvider {
+ public:
+ MockTimeProvider();
+ ~MockTimeProvider();
+
+ MOCK_METHOD0(Now, Time());
+
+ static Time StaticNow();
+
+ private:
+ static MockTimeProvider* instance_;
+ DISALLOW_COPY_AND_ASSIGN(MockTimeProvider);
+};
+
+} // namespace base
+
+#endif // BASE_TEST_MOCK_TIME_PROVIDER_H_
diff --git a/src/base/test/multiprocess_test.cc b/src/base/test/multiprocess_test.cc
new file mode 100644
index 0000000..27f9970
--- /dev/null
+++ b/src/base/test/multiprocess_test.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 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/test/multiprocess_test.h"
+
+#include "base/base_switches.h"
+#include "base/command_line.h"
+
+namespace base {
+
+MultiProcessTest::MultiProcessTest() {
+}
+
+ProcessHandle MultiProcessTest::SpawnChild(const std::string& procname,
+ bool debug_on_start) {
+ FileHandleMappingVector empty_file_list;
+ return SpawnChildImpl(procname, empty_file_list, debug_on_start);
+}
+
+#if defined(OS_POSIX)
+ProcessHandle MultiProcessTest::SpawnChild(
+ const std::string& procname,
+ const FileHandleMappingVector& fds_to_map,
+ bool debug_on_start) {
+ return SpawnChildImpl(procname, fds_to_map, debug_on_start);
+}
+#endif
+
+CommandLine MultiProcessTest::MakeCmdLine(const std::string& procname,
+ bool debug_on_start) {
+ CommandLine cl(*CommandLine::ForCurrentProcess());
+ cl.AppendSwitchASCII(switches::kTestChildProcess, procname);
+ if (debug_on_start)
+ cl.AppendSwitch(switches::kDebugOnStart);
+ return cl;
+}
+
+#if !defined(OS_ANDROID)
+ProcessHandle MultiProcessTest::SpawnChildImpl(
+ const std::string& procname,
+ const FileHandleMappingVector& fds_to_map,
+ bool debug_on_start) {
+ ProcessHandle handle = kNullProcessHandle;
+ base::LaunchOptions options;
+#if defined(OS_WIN)
+ options.start_hidden = true;
+#else
+ options.fds_to_remap = &fds_to_map;
+#endif
+ base::LaunchProcess(MakeCmdLine(procname, debug_on_start), options, &handle);
+ return handle;
+}
+#endif // !defined(OS_ANDROID)
+
+} // namespace base
diff --git a/src/base/test/multiprocess_test.h b/src/base/test/multiprocess_test.h
new file mode 100644
index 0000000..7c3af33
--- /dev/null
+++ b/src/base/test/multiprocess_test.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2012 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_TEST_MULTIPROCESS_TEST_H_
+#define BASE_TEST_MULTIPROCESS_TEST_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/process.h"
+#include "base/process_util.h"
+#include "build/build_config.h"
+#include "testing/platform_test.h"
+
+class CommandLine;
+
+namespace base {
+
+// A MultiProcessTest is a test class which makes it easier to
+// write a test which requires code running out of process.
+//
+// To create a multiprocess test simply follow these steps:
+//
+// 1) Derive your test from MultiProcessTest. Example:
+//
+// class MyTest : public MultiProcessTest {
+// };
+//
+// TEST_F(MyTest, TestCaseName) {
+// ...
+// }
+//
+// 2) Create a mainline function for the child processes and include
+// testing/multiprocess_func_list.h.
+// See the declaration of the MULTIPROCESS_TEST_MAIN macro
+// in that file for an example.
+// 3) Call SpawnChild("foo"), where "foo" is the name of
+// the function you wish to run in the child processes.
+// That's it!
+class MultiProcessTest : public PlatformTest {
+ public:
+ MultiProcessTest();
+
+ protected:
+ // Run a child process.
+ // 'procname' is the name of a function which the child will
+ // execute. It must be exported from this library in order to
+ // run.
+ //
+ // Example signature:
+ // extern "C" int __declspec(dllexport) FooBar() {
+ // // do client work here
+ // }
+ //
+ // Returns the handle to the child, or NULL on failure
+ ProcessHandle SpawnChild(const std::string& procname, bool debug_on_start);
+
+#if defined(OS_POSIX)
+ // TODO(evan): see if we can delete this via more refactoring.
+ // SpawnChild() should just take a base::LaunchOptions so that we don't
+ // need multiple versions of it.
+ ProcessHandle SpawnChild(const std::string& procname,
+ const FileHandleMappingVector& fds_to_map,
+ bool debug_on_start);
+#endif
+
+ // Set up the command line used to spawn the child process.
+ virtual CommandLine MakeCmdLine(const std::string& procname,
+ bool debug_on_start);
+
+ private:
+ // Shared implementation of SpawnChild.
+ // TODO: |fds_to_map| is unused on Windows; see above TODO about
+ // further refactoring.
+ ProcessHandle SpawnChildImpl(const std::string& procname,
+ const FileHandleMappingVector& fds_to_map,
+ bool debug_on_start);
+
+ DISALLOW_COPY_AND_ASSIGN(MultiProcessTest);
+};
+
+} // namespace base
+
+#endif // BASE_TEST_MULTIPROCESS_TEST_H_
diff --git a/src/base/test/multiprocess_test_android.cc b/src/base/test/multiprocess_test_android.cc
new file mode 100644
index 0000000..a61441f
--- /dev/null
+++ b/src/base/test/multiprocess_test_android.cc
@@ -0,0 +1,62 @@
+// Copyright (c) 2012 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/test/multiprocess_test.h"
+
+#include <unistd.h>
+
+#include "base/hash_tables.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace base {
+
+// A very basic implementation for android. On Android tests can run in an APK
+// and we don't have an executable to exec*. This implementation does the bare
+// minimum to execute the method specified by procname (in the child process).
+// - |debug_on_start| is ignored.
+ProcessHandle MultiProcessTest::SpawnChildImpl(
+ const std::string& procname,
+ const FileHandleMappingVector& fds_to_remap,
+ bool debug_on_start) {
+ pid_t pid = fork();
+
+ if (pid < 0) {
+ PLOG(ERROR) << "fork";
+ return kNullProcessHandle;
+ }
+ if (pid > 0) {
+ // Parent process.
+ return pid;
+ }
+ // Child process.
+ std::hash_set<int> fds_to_keep_open;
+ for (FileHandleMappingVector::const_iterator it = fds_to_remap.begin();
+ it != fds_to_remap.end(); ++it) {
+ fds_to_keep_open.insert(it->first);
+ }
+ // Keep stdin, stdout and stderr open since this is not meant to spawn a
+ // daemon.
+ const int kFdForAndroidLogging = 3; // FD used by __android_log_write().
+ for (int fd = kFdForAndroidLogging + 1; fd < getdtablesize(); ++fd) {
+ if (fds_to_keep_open.find(fd) == fds_to_keep_open.end()) {
+ HANDLE_EINTR(close(fd));
+ }
+ }
+ for (FileHandleMappingVector::const_iterator it = fds_to_remap.begin();
+ it != fds_to_remap.end(); ++it) {
+ int old_fd = it->first;
+ int new_fd = it->second;
+ if (dup2(old_fd, new_fd) < 0) {
+ PLOG(FATAL) << "dup2";
+ }
+ HANDLE_EINTR(close(old_fd));
+ }
+ _exit(multi_process_function_list::InvokeChildProcessTest(procname));
+ return 0;
+}
+
+} // namespace base
diff --git a/src/base/test/perf_test_suite.cc b/src/base/test/perf_test_suite.cc
new file mode 100644
index 0000000..ebfe32f
--- /dev/null
+++ b/src/base/test/perf_test_suite.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/perf_test_suite.h"
+
+#include "base/command_line.h"
+#include "base/debug/debugger.h"
+#include "base/file_path.h"
+#include "base/path_service.h"
+#include "base/perftimer.h"
+#include "base/process_util.h"
+#include "base/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+PerfTestSuite::PerfTestSuite(int argc, char** argv) : TestSuite(argc, argv) {
+}
+
+void PerfTestSuite::Initialize() {
+ TestSuite::Initialize();
+
+ // Initialize the perf timer log
+ FilePath log_path =
+ CommandLine::ForCurrentProcess()->GetSwitchValuePath("log-file");
+ if (log_path.empty()) {
+ FilePath exe;
+ PathService::Get(base::FILE_EXE, &exe);
+ log_path = exe.ReplaceExtension(FILE_PATH_LITERAL("log"));
+ log_path = log_path.InsertBeforeExtension(FILE_PATH_LITERAL("_perf"));
+ }
+ ASSERT_TRUE(InitPerfLog(log_path));
+
+#if !defined(OS_STARBOARD)
+ // Raise to high priority to have more precise measurements. Since we don't
+ // aim at 1% precision, it is not necessary to run at realtime level.
+ if (!base::debug::BeingDebugged())
+ base::RaiseProcessToHighPriority();
+#endif
+}
+
+void PerfTestSuite::Shutdown() {
+ TestSuite::Shutdown();
+ FinalizePerfLog();
+}
+
+} // namespace base
diff --git a/src/base/test/perf_test_suite.h b/src/base/test/perf_test_suite.h
new file mode 100644
index 0000000..85bfc41
--- /dev/null
+++ b/src/base/test/perf_test_suite.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_PERF_TEST_SUITE_H_
+#define BASE_TEST_PERF_TEST_SUITE_H_
+
+#include "base/test/test_suite.h"
+
+namespace base {
+
+class PerfTestSuite : public TestSuite {
+ public:
+ PerfTestSuite(int argc, char** argv);
+
+ virtual void Initialize() OVERRIDE;
+ virtual void Shutdown() OVERRIDE;
+};
+
+} // namespace base
+
+#endif // BASE_TEST_PERF_TEST_SUITE_H_
diff --git a/src/base/test/run_all_perftests.cc b/src/base/test/run_all_perftests.cc
new file mode 100644
index 0000000..6d9817c
--- /dev/null
+++ b/src/base/test/run_all_perftests.cc
@@ -0,0 +1,11 @@
+// Copyright (c) 2012 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/test/main_hook.h"
+#include "base/test/perf_test_suite.h"
+
+int main(int argc, char** argv) {
+ MainHook hook(main, argc, argv);
+ return base::PerfTestSuite(argc, argv).Run();
+}
diff --git a/src/base/test/run_all_unittests.cc b/src/base/test/run_all_unittests.cc
new file mode 100644
index 0000000..1aba309
--- /dev/null
+++ b/src/base/test/run_all_unittests.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 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/test/main_hook.h"
+#include "base/test/test_suite.h"
+#include "build/build_config.h"
+
+#if !defined(OS_STARBOARD)
+int main(int argc, char** argv) {
+ MainHook hook(main, argc, argv);
+ return base::TestSuite(argc, argv).Run();
+}
+#else
+#include "starboard/event.h"
+#include "starboard/system.h"
+
+void SbEventHandle(const SbEvent* event) {
+ if (event->type == kSbEventTypeStart) {
+ SbEventStartData* data = static_cast<SbEventStartData*>(event->data);
+ MainHook hook(NULL, data->argument_count, data->argument_values);
+ SbSystemRequestStop(
+ base::TestSuite(data->argument_count, data->argument_values).Run());
+ }
+}
+#endif
diff --git a/src/base/test/scoped_locale.cc b/src/base/test/scoped_locale.cc
new file mode 100644
index 0000000..35b3fbe
--- /dev/null
+++ b/src/base/test/scoped_locale.cc
@@ -0,0 +1,23 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/scoped_locale.h"
+
+#include <locale.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+ScopedLocale::ScopedLocale(const std::string& locale) {
+ prev_locale_ = setlocale(LC_ALL, NULL);
+ EXPECT_TRUE(setlocale(LC_ALL, locale.c_str()) != NULL) <<
+ "Failed to set locale: " << locale;
+}
+
+ScopedLocale::~ScopedLocale() {
+ EXPECT_STREQ(prev_locale_.c_str(), setlocale(LC_ALL, prev_locale_.c_str()));
+}
+
+} // namespace base
diff --git a/src/base/test/scoped_locale.h b/src/base/test/scoped_locale.h
new file mode 100644
index 0000000..a9f9348
--- /dev/null
+++ b/src/base/test/scoped_locale.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_SCOPED_LOCALE_H_
+#define BASE_TEST_SCOPED_LOCALE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace base {
+
+// Sets the given |locale| on construction, and restores the previous locale
+// on destruction.
+class ScopedLocale {
+ public:
+ explicit ScopedLocale(const std::string& locale);
+ ~ScopedLocale();
+
+ private:
+ std::string prev_locale_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedLocale);
+};
+
+} // namespace base
+
+#endif // BASE_TEST_SCOPED_LOCALE_H_
diff --git a/src/base/test/scoped_path_override.cc b/src/base/test/scoped_path_override.cc
new file mode 100644
index 0000000..bb38c7f
--- /dev/null
+++ b/src/base/test/scoped_path_override.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 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/test/scoped_path_override.h"
+
+#include "base/logging.h"
+#include "base/path_service.h"
+
+namespace base {
+
+ScopedPathOverride::ScopedPathOverride(int key) : key_(key) {
+ bool result = temp_dir_.CreateUniqueTempDir();
+ CHECK(result);
+ result = PathService::Override(key, temp_dir_.path());
+ CHECK(result);
+}
+
+ScopedPathOverride::ScopedPathOverride(int key, const FilePath& dir)
+ : key_(key) {
+ bool result = PathService::Override(key, dir);
+ CHECK(result);
+}
+
+ScopedPathOverride::~ScopedPathOverride() {
+ bool result = PathService::RemoveOverride(key_);
+ CHECK(result) << "The override seems to have been removed already!";
+}
+
+} // namespace base
diff --git a/src/base/test/scoped_path_override.h b/src/base/test/scoped_path_override.h
new file mode 100644
index 0000000..3ac441c
--- /dev/null
+++ b/src/base/test/scoped_path_override.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 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_TEST_SCOPED_PATH_OVERRIDE_H_
+#define BASE_TEST_SCOPED_PATH_OVERRIDE_H_
+
+#include "base/basictypes.h"
+#include "base/files/scoped_temp_dir.h"
+
+class FilePath;
+
+namespace base {
+
+// Sets a path override on construction, and removes it when the object goes out
+// of scope. This class is intended to be used by tests that need to override
+// paths to ensure their overrides are properly handled and reverted when the
+// scope of the test is left.
+class ScopedPathOverride {
+ public:
+ // Contructor that intializes the override to a scoped temp directory.
+ explicit ScopedPathOverride(int key);
+ // Constructor that would use a path provided by the user.
+ ScopedPathOverride(int key, const FilePath& dir);
+ ~ScopedPathOverride();
+
+ private:
+ int key_;
+ ScopedTempDir temp_dir_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedPathOverride);
+};
+
+} // namespace base
+
+#endif // BASE_TEST_SCOPED_PATH_OVERRIDE_H_
diff --git a/src/base/test/sequenced_task_runner_test_template.cc b/src/base/test/sequenced_task_runner_test_template.cc
new file mode 100644
index 0000000..50487d2
--- /dev/null
+++ b/src/base/test/sequenced_task_runner_test_template.cc
@@ -0,0 +1,259 @@
+// Copyright (c) 2012 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/test/sequenced_task_runner_test_template.h"
+
+#include <ostream>
+
+#include "base/location.h"
+
+namespace base {
+
+namespace internal {
+
+TaskEvent::TaskEvent(int i, Type type)
+ : i(i), type(type) {
+}
+
+SequencedTaskTracker::SequencedTaskTracker() : next_post_i_(0) {
+}
+
+void SequencedTaskTracker::PostWrappedNonNestableTask(
+ const scoped_refptr<SequencedTaskRunner>& task_runner,
+ const Closure& task) {
+ AutoLock event_lock(lock_);
+ const int post_i = next_post_i_++;
+ Closure wrapped_task = Bind(&SequencedTaskTracker::RunTask, this,
+ task, post_i);
+ task_runner->PostNonNestableTask(FROM_HERE, wrapped_task);
+ TaskPosted(post_i);
+}
+
+void SequencedTaskTracker::PostWrappedNestableTask(
+ const scoped_refptr<SequencedTaskRunner>& task_runner,
+ const Closure& task) {
+ AutoLock event_lock(lock_);
+ const int post_i = next_post_i_++;
+ Closure wrapped_task = Bind(&SequencedTaskTracker::RunTask, this,
+ task, post_i);
+ task_runner->PostTask(FROM_HERE, wrapped_task);
+ TaskPosted(post_i);
+}
+
+void SequencedTaskTracker::PostWrappedDelayedNonNestableTask(
+ const scoped_refptr<SequencedTaskRunner>& task_runner,
+ const Closure& task,
+ TimeDelta delay) {
+ AutoLock event_lock(lock_);
+ const int post_i = next_post_i_++;
+ Closure wrapped_task = Bind(&SequencedTaskTracker::RunTask, this,
+ task, post_i);
+ task_runner->PostNonNestableDelayedTask(FROM_HERE, wrapped_task, delay);
+ TaskPosted(post_i);
+}
+
+void SequencedTaskTracker::PostNonNestableTasks(
+ const scoped_refptr<SequencedTaskRunner>& task_runner,
+ int task_count) {
+ for (int i = 0; i < task_count; ++i) {
+ PostWrappedNonNestableTask(task_runner, Closure());
+ }
+}
+
+void SequencedTaskTracker::RunTask(const Closure& task, int task_i) {
+ TaskStarted(task_i);
+ if (!task.is_null())
+ task.Run();
+ TaskEnded(task_i);
+}
+
+void SequencedTaskTracker::TaskPosted(int i) {
+ // Caller must own |lock_|.
+ events_.push_back(TaskEvent(i, TaskEvent::POST));
+}
+
+void SequencedTaskTracker::TaskStarted(int i) {
+ AutoLock lock(lock_);
+ events_.push_back(TaskEvent(i, TaskEvent::START));
+}
+
+void SequencedTaskTracker::TaskEnded(int i) {
+ AutoLock lock(lock_);
+ events_.push_back(TaskEvent(i, TaskEvent::END));
+}
+
+const std::vector<TaskEvent>&
+SequencedTaskTracker::GetTaskEvents() const {
+ return events_;
+}
+
+SequencedTaskTracker::~SequencedTaskTracker() {
+}
+
+void PrintTo(const TaskEvent& event, std::ostream* os) {
+ *os << "(i=" << event.i << ", type=";
+ switch (event.type) {
+ case TaskEvent::POST: *os << "POST"; break;
+ case TaskEvent::START: *os << "START"; break;
+ case TaskEvent::END: *os << "END"; break;
+ }
+ *os << ")";
+}
+
+namespace {
+
+// Returns the task ordinals for the task event type |type| in the order that
+// they were recorded.
+std::vector<int> GetEventTypeOrder(const std::vector<TaskEvent>& events,
+ TaskEvent::Type type) {
+ std::vector<int> tasks;
+ std::vector<TaskEvent>::const_iterator event;
+ for (event = events.begin(); event != events.end(); ++event) {
+ if (event->type == type)
+ tasks.push_back(event->i);
+ }
+ return tasks;
+}
+
+// Returns all task events for task |task_i|.
+std::vector<TaskEvent::Type> GetEventsForTask(
+ const std::vector<TaskEvent>& events,
+ int task_i) {
+ std::vector<TaskEvent::Type> task_event_orders;
+ std::vector<TaskEvent>::const_iterator event;
+ for (event = events.begin(); event != events.end(); ++event) {
+ if (event->i == task_i)
+ task_event_orders.push_back(event->type);
+ }
+ return task_event_orders;
+}
+
+// Checks that the task events for each task in |events| occur in the order
+// {POST, START, END}, and that there is only one instance of each event type
+// per task.
+::testing::AssertionResult CheckEventOrdersForEachTask(
+ const std::vector<TaskEvent>& events,
+ int task_count) {
+ std::vector<TaskEvent::Type> expected_order;
+ expected_order.push_back(TaskEvent::POST);
+ expected_order.push_back(TaskEvent::START);
+ expected_order.push_back(TaskEvent::END);
+
+ // This is O(n^2), but it runs fast enough currently so is not worth
+ // optimizing.
+ for (int i = 0; i < task_count; ++i) {
+ const std::vector<TaskEvent::Type> task_events =
+ GetEventsForTask(events, i);
+ if (task_events != expected_order) {
+ return ::testing::AssertionFailure()
+ << "Events for task " << i << " are out of order; expected: "
+ << ::testing::PrintToString(expected_order) << "; actual: "
+ << ::testing::PrintToString(task_events);
+ }
+ }
+ return ::testing::AssertionSuccess();
+}
+
+// Checks that no two tasks were running at the same time. I.e. the only
+// events allowed between the START and END of a task are the POSTs of other
+// tasks.
+::testing::AssertionResult CheckNoTaskRunsOverlap(
+ const std::vector<TaskEvent>& events) {
+ // If > -1, we're currently inside a START, END pair.
+ int current_task_i = -1;
+
+ std::vector<TaskEvent>::const_iterator event;
+ for (event = events.begin(); event != events.end(); ++event) {
+ bool spurious_event_found = false;
+
+ if (current_task_i == -1) { // Not inside a START, END pair.
+ switch (event->type) {
+ case TaskEvent::POST:
+ break;
+ case TaskEvent::START:
+ current_task_i = event->i;
+ break;
+ case TaskEvent::END:
+ spurious_event_found = true;
+ break;
+ }
+
+ } else { // Inside a START, END pair.
+ bool interleaved_task_detected = false;
+
+ switch (event->type) {
+ case TaskEvent::POST:
+ if (event->i == current_task_i)
+ spurious_event_found = true;
+ break;
+ case TaskEvent::START:
+ interleaved_task_detected = true;
+ break;
+ case TaskEvent::END:
+ if (event->i != current_task_i)
+ interleaved_task_detected = true;
+ else
+ current_task_i = -1;
+ break;
+ }
+
+ if (interleaved_task_detected) {
+ return ::testing::AssertionFailure()
+ << "Found event " << ::testing::PrintToString(*event)
+ << " between START and END events for task " << current_task_i
+ << "; event dump: " << ::testing::PrintToString(events);
+ }
+ }
+
+ if (spurious_event_found) {
+ const int event_i = event - events.begin();
+ return ::testing::AssertionFailure()
+ << "Spurious event " << ::testing::PrintToString(*event)
+ << " at position " << event_i << "; event dump: "
+ << ::testing::PrintToString(events);
+ }
+ }
+
+ return ::testing::AssertionSuccess();
+}
+
+} // namespace
+
+::testing::AssertionResult CheckNonNestableInvariants(
+ const std::vector<TaskEvent>& events,
+ int task_count) {
+ const std::vector<int> post_order =
+ GetEventTypeOrder(events, TaskEvent::POST);
+ const std::vector<int> start_order =
+ GetEventTypeOrder(events, TaskEvent::START);
+ const std::vector<int> end_order =
+ GetEventTypeOrder(events, TaskEvent::END);
+
+ if (start_order != post_order) {
+ return ::testing::AssertionFailure()
+ << "Expected START order (which equals actual POST order): \n"
+ << ::testing::PrintToString(post_order)
+ << "\n Actual START order:\n"
+ << ::testing::PrintToString(start_order);
+ }
+
+ if (end_order != post_order) {
+ return ::testing::AssertionFailure()
+ << "Expected END order (which equals actual POST order): \n"
+ << ::testing::PrintToString(post_order)
+ << "\n Actual END order:\n"
+ << ::testing::PrintToString(end_order);
+ }
+
+ const ::testing::AssertionResult result =
+ CheckEventOrdersForEachTask(events, task_count);
+ if (!result)
+ return result;
+
+ return CheckNoTaskRunsOverlap(events);
+}
+
+} // namespace internal
+
+} // namespace base
diff --git a/src/base/test/sequenced_task_runner_test_template.h b/src/base/test/sequenced_task_runner_test_template.h
new file mode 100644
index 0000000..0c8d135
--- /dev/null
+++ b/src/base/test/sequenced_task_runner_test_template.h
@@ -0,0 +1,363 @@
+// Copyright (c) 2012 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 defines tests that implementations of SequencedTaskRunner should
+// pass in order to be conformant. See task_runner_test_template.h for a
+// description of how to use the constructs in this file; these work the same.
+
+#ifndef BASE_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_
+#define BASE_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_
+
+#include <cstddef>
+#include <iosfwd>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace internal {
+
+struct TaskEvent {
+ enum Type { POST, START, END };
+ TaskEvent(int i, Type type);
+ int i;
+ Type type;
+};
+
+// Utility class used in the tests below.
+class SequencedTaskTracker : public RefCountedThreadSafe<SequencedTaskTracker> {
+ public:
+ SequencedTaskTracker();
+
+ // Posts the non-nestable task |task|, and records its post event.
+ void PostWrappedNonNestableTask(
+ const scoped_refptr<SequencedTaskRunner>& task_runner,
+ const Closure& task);
+
+ // Posts the nestable task |task|, and records its post event.
+ void PostWrappedNestableTask(
+ const scoped_refptr<SequencedTaskRunner>& task_runner,
+ const Closure& task);
+
+ // Posts the delayed non-nestable task |task|, and records its post event.
+ void PostWrappedDelayedNonNestableTask(
+ const scoped_refptr<SequencedTaskRunner>& task_runner,
+ const Closure& task,
+ TimeDelta delay);
+
+ // Posts |task_count| non-nestable tasks.
+ void PostNonNestableTasks(
+ const scoped_refptr<SequencedTaskRunner>& task_runner,
+ int task_count);
+
+ const std::vector<TaskEvent>& GetTaskEvents() const;
+
+ private:
+ friend class RefCountedThreadSafe<SequencedTaskTracker>;
+
+ ~SequencedTaskTracker();
+
+ // A task which runs |task|, recording the start and end events.
+ void RunTask(const Closure& task, int task_i);
+
+ // Records a post event for task |i|. The owner is expected to be holding
+ // |lock_| (unlike |TaskStarted| and |TaskEnded|).
+ void TaskPosted(int i);
+
+ // Records a start event for task |i|.
+ void TaskStarted(int i);
+
+ // Records a end event for task |i|.
+ void TaskEnded(int i);
+
+ // Protects events_ and next_post_i_.
+ Lock lock_;
+
+ // The events as they occurred for each task (protected by lock_).
+ std::vector<TaskEvent> events_;
+
+ // The ordinal to be used for the next task-posting task (protected by
+ // lock_).
+ int next_post_i_;
+
+ DISALLOW_COPY_AND_ASSIGN(SequencedTaskTracker);
+};
+
+void PrintTo(const TaskEvent& event, std::ostream* os);
+
+// Checks the non-nestable task invariants for all tasks in |events|.
+//
+// The invariants are:
+// 1) Events started and ended in the same order that they were posted.
+// 2) Events for an individual tasks occur in the order {POST, START, END},
+// and there is only one instance of each event type for a task.
+// 3) The only events between a task's START and END events are the POSTs of
+// other tasks. I.e. tasks were run sequentially, not interleaved.
+::testing::AssertionResult CheckNonNestableInvariants(
+ const std::vector<TaskEvent>& events,
+ int task_count);
+
+} // namespace internal
+
+template <typename TaskRunnerTestDelegate>
+class SequencedTaskRunnerTest : public testing::Test {
+ protected:
+ SequencedTaskRunnerTest()
+ : task_tracker_(new internal::SequencedTaskTracker()) {}
+
+ const scoped_refptr<internal::SequencedTaskTracker> task_tracker_;
+ TaskRunnerTestDelegate delegate_;
+};
+
+TYPED_TEST_CASE_P(SequencedTaskRunnerTest);
+
+// This test posts N non-nestable tasks in sequence, and expects them to run
+// in FIFO order, with no part of any two tasks' execution
+// overlapping. I.e. that each task starts only after the previously-posted
+// one has finished.
+TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNonNestable) {
+ const int kTaskCount = 1000;
+
+ this->delegate_.StartTaskRunner();
+ const scoped_refptr<SequencedTaskRunner> task_runner =
+ this->delegate_.GetTaskRunner();
+
+ this->task_tracker_->PostWrappedNonNestableTask(
+ task_runner, Bind(&PlatformThread::Sleep, TimeDelta::FromSeconds(1)));
+ for (int i = 1; i < kTaskCount; ++i) {
+ this->task_tracker_->PostWrappedNonNestableTask(task_runner, Closure());
+ }
+
+ this->delegate_.StopTaskRunner();
+
+ EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+ kTaskCount));
+}
+
+// This test posts N nestable tasks in sequence. It has the same expectations
+// as SequentialNonNestable because even though the tasks are nestable, they
+// will not be run nestedly in this case.
+TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNestable) {
+ const int kTaskCount = 1000;
+
+ this->delegate_.StartTaskRunner();
+ const scoped_refptr<SequencedTaskRunner> task_runner =
+ this->delegate_.GetTaskRunner();
+
+ this->task_tracker_->PostWrappedNestableTask(
+ task_runner,
+ Bind(&PlatformThread::Sleep, TimeDelta::FromSeconds(1)));
+ for (int i = 1; i < kTaskCount; ++i) {
+ this->task_tracker_->PostWrappedNestableTask(task_runner, Closure());
+ }
+
+ this->delegate_.StopTaskRunner();
+
+ EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+ kTaskCount));
+}
+
+// This test posts non-nestable tasks in order of increasing delay, and checks
+// that that the tasks are run in FIFO order and that there is no execution
+// overlap whatsoever between any two tasks.
+TYPED_TEST_P(SequencedTaskRunnerTest, SequentialDelayedNonNestable) {
+ // TODO(akalin): Remove this check (http://crbug.com/149144).
+ if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) {
+ DLOG(INFO) << "This SequencedTaskRunner doesn't handle "
+ "non-zero delays; skipping";
+ return;
+ }
+
+ const int kTaskCount = 20;
+ const int kDelayIncrementMs = 50;
+
+ this->delegate_.StartTaskRunner();
+ const scoped_refptr<SequencedTaskRunner> task_runner =
+ this->delegate_.GetTaskRunner();
+
+ for (int i = 0; i < kTaskCount; ++i) {
+ this->task_tracker_->PostWrappedDelayedNonNestableTask(
+ task_runner,
+ Closure(),
+ TimeDelta::FromMilliseconds(kDelayIncrementMs * i));
+ }
+
+ this->delegate_.StopTaskRunner();
+
+ EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+ kTaskCount));
+}
+
+// This test posts a fast, non-nestable task from within each of a number of
+// slow, non-nestable tasks and checks that they all run in the sequence they
+// were posted in and that there is no execution overlap whatsoever.
+TYPED_TEST_P(SequencedTaskRunnerTest, NonNestablePostFromNonNestableTask) {
+ const int kParentCount = 10;
+ const int kChildrenPerParent = 10;
+
+ this->delegate_.StartTaskRunner();
+ const scoped_refptr<SequencedTaskRunner> task_runner =
+ this->delegate_.GetTaskRunner();
+
+ for (int i = 0; i < kParentCount; ++i) {
+ Closure task = Bind(
+ &internal::SequencedTaskTracker::PostNonNestableTasks,
+ this->task_tracker_,
+ task_runner,
+ kChildrenPerParent);
+ this->task_tracker_->PostWrappedNonNestableTask(task_runner, task);
+ }
+
+ this->delegate_.StopTaskRunner();
+
+ EXPECT_TRUE(CheckNonNestableInvariants(
+ this->task_tracker_->GetTaskEvents(),
+ kParentCount * (kChildrenPerParent + 1)));
+}
+
+// This test posts a delayed task, and checks that the task is run later than
+// the specified time.
+TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskBasic) {
+ // TODO(akalin): Remove this check (http://crbug.com/149144).
+ if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) {
+ DLOG(INFO) << "This SequencedTaskRunner doesn't handle "
+ "non-zero delays; skipping";
+ return;
+ }
+
+ const int kTaskCount = 1;
+ const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
+
+ this->delegate_.StartTaskRunner();
+ const scoped_refptr<SequencedTaskRunner> task_runner =
+ this->delegate_.GetTaskRunner();
+
+ Time time_before_run = Time::Now();
+ this->task_tracker_->PostWrappedDelayedNonNestableTask(
+ task_runner, Closure(), kDelay);
+ this->delegate_.StopTaskRunner();
+ Time time_after_run = Time::Now();
+
+ EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+ kTaskCount));
+ EXPECT_LE(kDelay, time_after_run - time_before_run);
+}
+
+// This test posts two tasks with the same delay, and checks that the tasks are
+// run in the order in which they were posted.
+//
+// NOTE: This is actually an approximate test since the API only takes a
+// "delay" parameter, so we are not exactly simulating two tasks that get
+// posted at the exact same time. It would be nice if the API allowed us to
+// specify the desired run time.
+TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTasksSameDelay) {
+ // TODO(akalin): Remove this check (http://crbug.com/149144).
+ if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) {
+ DLOG(INFO) << "This SequencedTaskRunner doesn't handle "
+ "non-zero delays; skipping";
+ return;
+ }
+
+ const int kTaskCount = 2;
+ const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
+
+ this->delegate_.StartTaskRunner();
+ const scoped_refptr<SequencedTaskRunner> task_runner =
+ this->delegate_.GetTaskRunner();
+
+ this->task_tracker_->PostWrappedDelayedNonNestableTask(
+ task_runner, Closure(), kDelay);
+ this->task_tracker_->PostWrappedDelayedNonNestableTask(
+ task_runner, Closure(), kDelay);
+ this->delegate_.StopTaskRunner();
+
+ EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+ kTaskCount));
+}
+
+// This test posts a normal task and a delayed task, and checks that the
+// delayed task runs after the normal task even if the normal task takes
+// a long time to run.
+TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterLongTask) {
+ // TODO(akalin): Remove this check (http://crbug.com/149144).
+ if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) {
+ DLOG(INFO) << "This SequencedTaskRunner doesn't handle "
+ "non-zero delays; skipping";
+ return;
+ }
+
+ const int kTaskCount = 2;
+
+ this->delegate_.StartTaskRunner();
+ const scoped_refptr<SequencedTaskRunner> task_runner =
+ this->delegate_.GetTaskRunner();
+
+ this->task_tracker_->PostWrappedNonNestableTask(
+ task_runner, base::Bind(&PlatformThread::Sleep,
+ TimeDelta::FromMilliseconds(50)));
+ this->task_tracker_->PostWrappedDelayedNonNestableTask(
+ task_runner, Closure(), TimeDelta::FromMilliseconds(10));
+ this->delegate_.StopTaskRunner();
+
+ EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+ kTaskCount));
+}
+
+// Test that a pile of normal tasks and a delayed task run in the
+// time-to-run order.
+TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterManyLongTasks) {
+ // TODO(akalin): Remove this check (http://crbug.com/149144).
+ if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) {
+ DLOG(INFO) << "This SequencedTaskRunner doesn't handle "
+ "non-zero delays; skipping";
+ return;
+ }
+
+ const int kTaskCount = 11;
+
+ this->delegate_.StartTaskRunner();
+ const scoped_refptr<SequencedTaskRunner> task_runner =
+ this->delegate_.GetTaskRunner();
+
+ for (int i = 0; i < kTaskCount - 1; i++) {
+ this->task_tracker_->PostWrappedNonNestableTask(
+ task_runner, base::Bind(&PlatformThread::Sleep,
+ TimeDelta::FromMilliseconds(50)));
+ }
+ this->task_tracker_->PostWrappedDelayedNonNestableTask(
+ task_runner, Closure(), TimeDelta::FromMilliseconds(10));
+ this->delegate_.StopTaskRunner();
+
+ EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+ kTaskCount));
+}
+
+
+// TODO(francoisk777@gmail.com) Add a test, similiar to the above, which runs
+// some tasked nestedly (which should be implemented in the test
+// delegate). Also add, to the the test delegate, a predicate which checks
+// whether the implementation supports nested tasks.
+//
+
+REGISTER_TYPED_TEST_CASE_P(SequencedTaskRunnerTest,
+ SequentialNonNestable,
+ SequentialNestable,
+ SequentialDelayedNonNestable,
+ NonNestablePostFromNonNestableTask,
+ DelayedTaskBasic,
+ DelayedTasksSameDelay,
+ DelayedTaskAfterLongTask,
+ DelayedTaskAfterManyLongTasks);
+
+} // namespace base
+
+#endif // BASE_TASK_RUNNER_TEST_TEMPLATE_H_
diff --git a/src/base/test/sequenced_worker_pool_owner.cc b/src/base/test/sequenced_worker_pool_owner.cc
new file mode 100644
index 0000000..3d124fa
--- /dev/null
+++ b/src/base/test/sequenced_worker_pool_owner.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 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/test/sequenced_worker_pool_owner.h"
+
+#include "base/location.h"
+#include "base/message_loop.h"
+
+namespace base {
+
+SequencedWorkerPoolOwner::SequencedWorkerPoolOwner(
+ size_t max_threads,
+ const std::string& thread_name_prefix)
+ : constructor_message_loop_(MessageLoop::current()),
+ pool_(new SequencedWorkerPool(
+ max_threads, thread_name_prefix,
+ ALLOW_THIS_IN_INITIALIZER_LIST(this))),
+ has_work_call_count_(0) {}
+
+SequencedWorkerPoolOwner::~SequencedWorkerPoolOwner() {
+ pool_ = NULL;
+ MessageLoop::current()->Run();
+}
+
+const scoped_refptr<SequencedWorkerPool>& SequencedWorkerPoolOwner::pool() {
+ return pool_;
+}
+
+void SequencedWorkerPoolOwner::SetWillWaitForShutdownCallback(
+ const Closure& callback) {
+ will_wait_for_shutdown_callback_ = callback;
+}
+
+int SequencedWorkerPoolOwner::has_work_call_count() const {
+ AutoLock lock(has_work_lock_);
+ return has_work_call_count_;
+}
+
+void SequencedWorkerPoolOwner::OnHasWork() {
+ AutoLock lock(has_work_lock_);
+ ++has_work_call_count_;
+}
+
+void SequencedWorkerPoolOwner::WillWaitForShutdown() {
+ if (!will_wait_for_shutdown_callback_.is_null()) {
+ will_wait_for_shutdown_callback_.Run();
+ }
+}
+
+void SequencedWorkerPoolOwner::OnDestruct() {
+ constructor_message_loop_->PostTask(
+ FROM_HERE,
+ constructor_message_loop_->QuitClosure());
+}
+
+} // namespace base
diff --git a/src/base/test/sequenced_worker_pool_owner.h b/src/base/test/sequenced_worker_pool_owner.h
new file mode 100644
index 0000000..c6c8d67
--- /dev/null
+++ b/src/base/test/sequenced_worker_pool_owner.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 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_THREADING_SEQUENCED_WORKER_POOL_UNITTEST_H_
+#define BASE_THREADING_SEQUENCED_WORKER_POOL_UNITTEST_H_
+
+#include <cstddef>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/sequenced_worker_pool.h"
+
+class MessageLoop;
+
+namespace base {
+
+// Wrapper around SequencedWorkerPool for testing that blocks destruction
+// until the pool is actually destroyed. This is so that a
+// SequencedWorkerPool from one test doesn't outlive its test and cause
+// strange races with other tests that touch global stuff (like histograms and
+// logging). However, this requires that nothing else on this thread holds a
+// ref to the pool when the SequencedWorkerPoolOwner is destroyed.
+class SequencedWorkerPoolOwner : public SequencedWorkerPool::TestingObserver {
+ public:
+ SequencedWorkerPoolOwner(size_t max_threads,
+ const std::string& thread_name_prefix);
+
+ virtual ~SequencedWorkerPoolOwner();
+
+ // Don't change the returned pool's testing observer.
+ const scoped_refptr<SequencedWorkerPool>& pool();
+
+ // The given callback will be called on WillWaitForShutdown().
+ void SetWillWaitForShutdownCallback(const Closure& callback);
+
+ int has_work_call_count() const;
+
+ private:
+ // SequencedWorkerPool::TestingObserver implementation.
+ virtual void OnHasWork() OVERRIDE;
+ virtual void WillWaitForShutdown() OVERRIDE;
+ virtual void OnDestruct() OVERRIDE;
+
+ MessageLoop* const constructor_message_loop_;
+ scoped_refptr<SequencedWorkerPool> pool_;
+ Closure will_wait_for_shutdown_callback_;
+
+ mutable Lock has_work_lock_;
+ int has_work_call_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(SequencedWorkerPoolOwner);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_SEQUENCED_WORKER_POOL_UNITTEST_H_
diff --git a/src/base/test/task_runner_test_template.cc b/src/base/test/task_runner_test_template.cc
new file mode 100644
index 0000000..239a99f
--- /dev/null
+++ b/src/base/test/task_runner_test_template.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 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/test/task_runner_test_template.h"
+
+namespace base {
+
+namespace internal {
+
+TaskTracker::TaskTracker() {}
+
+TaskTracker::~TaskTracker() {}
+
+Closure TaskTracker::WrapTask(const Closure& task, int i) {
+ return Bind(&TaskTracker::RunTask, this, task, i);
+}
+
+void TaskTracker::RunTask(const Closure& task, int i) {
+ AutoLock lock(task_run_counts_lock_);
+ if (!task.is_null()) {
+ task.Run();
+ }
+ ++task_run_counts_[i];
+}
+
+std::map<int, int> TaskTracker::GetTaskRunCounts() const {
+ AutoLock lock(task_run_counts_lock_);
+ return task_run_counts_;
+}
+
+void ExpectRunsTasksOnCurrentThread(
+ bool expected_value,
+ const scoped_refptr<TaskRunner>& task_runner) {
+ EXPECT_EQ(expected_value, task_runner->RunsTasksOnCurrentThread());
+}
+
+} // namespace internal
+
+} // namespace base
diff --git a/src/base/test/task_runner_test_template.h b/src/base/test/task_runner_test_template.h
new file mode 100644
index 0000000..437a7d9
--- /dev/null
+++ b/src/base/test/task_runner_test_template.h
@@ -0,0 +1,215 @@
+// Copyright (c) 2012 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 defines tests that implementations of TaskRunner should
+// pass in order to be conformant. Here's how you use it to test your
+// implementation.
+//
+// Say your class is called MyTaskRunner. Then you need to define a
+// class called MyTaskRunnerTestDelegate in my_task_runner_unittest.cc
+// like this:
+//
+// class MyTaskRunnerTestDelegate {
+// public:
+// // Tasks posted to the task runner after this and before
+// // StopTaskRunner() is called is called should run successfully.
+// void StartTaskRunner() {
+// ...
+// }
+//
+// // Should return the task runner implementation. Only called
+// // after StartTaskRunner and before StopTaskRunner.
+// scoped_refptr<MyTaskRunner> GetTaskRunner() {
+// ...
+// }
+//
+// // Stop the task runner and make sure all tasks posted before
+// // this is called are run.
+// void StopTaskRunner() {
+// ...
+// }
+//
+// // Returns whether or not the task runner obeys non-zero delays.
+// bool TaskRunnerHandlesNonZeroDelays() const {
+// return true;
+// }
+// };
+//
+// The TaskRunnerTest test harness will have a member variable of
+// this delegate type and will call its functions in the various
+// tests.
+//
+// Then you simply #include this file as well as gtest.h and add the
+// following statement to my_task_runner_unittest.cc:
+//
+// INSTANTIATE_TYPED_TEST_CASE_P(
+// MyTaskRunner, TaskRunnerTest, MyTaskRunnerTestDelegate);
+//
+// Easy!
+
+#ifndef BASE_TEST_TASK_RUNNER_TEST_TEMPLATE_H_
+#define BASE_TEST_TASK_RUNNER_TEST_TEMPLATE_H_
+
+#include <cstddef>
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "base/task_runner.h"
+#include "base/threading/thread.h"
+#include "base/tracked_objects.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace internal {
+
+// Utility class that keeps track of how many times particular tasks
+// are run.
+class TaskTracker : public RefCountedThreadSafe<TaskTracker> {
+ public:
+ TaskTracker();
+
+ // Returns a closure that runs the given task and increments the run
+ // count of |i| by one. |task| may be null. It is guaranteed that
+ // only one task wrapped by a given tracker will be run at a time.
+ Closure WrapTask(const Closure& task, int i);
+
+ std::map<int, int> GetTaskRunCounts() const;
+
+ private:
+ friend class RefCountedThreadSafe<TaskTracker>;
+
+ ~TaskTracker();
+
+ void RunTask(const Closure& task, int i);
+
+ mutable Lock task_run_counts_lock_;
+ std::map<int, int> task_run_counts_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskTracker);
+};
+
+} // namespace internal
+
+template <typename TaskRunnerTestDelegate>
+class TaskRunnerTest : public testing::Test {
+ protected:
+ TaskRunnerTest() : task_tracker_(new internal::TaskTracker()) {}
+
+ const scoped_refptr<internal::TaskTracker> task_tracker_;
+ TaskRunnerTestDelegate delegate_;
+};
+
+TYPED_TEST_CASE_P(TaskRunnerTest);
+
+// We can't really test much, since TaskRunner provides very few
+// guarantees.
+
+// Post a bunch of tasks to the task runner. They should all
+// complete.
+TYPED_TEST_P(TaskRunnerTest, Basic) {
+ std::map<int, int> expected_task_run_counts;
+
+ this->delegate_.StartTaskRunner();
+ scoped_refptr<TaskRunner> task_runner = this->delegate_.GetTaskRunner();
+ // Post each ith task i+1 times.
+ for (int i = 0; i < 20; ++i) {
+ const Closure& ith_task = this->task_tracker_->WrapTask(Closure(), i);
+ for (int j = 0; j < i + 1; ++j) {
+ task_runner->PostTask(FROM_HERE, ith_task);
+ ++expected_task_run_counts[i];
+ }
+ }
+ this->delegate_.StopTaskRunner();
+
+ EXPECT_EQ(expected_task_run_counts,
+ this->task_tracker_->GetTaskRunCounts());
+}
+
+// Post a bunch of delayed tasks to the task runner. They should all
+// complete.
+TYPED_TEST_P(TaskRunnerTest, Delayed) {
+ if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) {
+ DLOG(INFO) << "This TaskRunner doesn't handle non-zero delays; skipping";
+ return;
+ }
+
+ std::map<int, int> expected_task_run_counts;
+
+ this->delegate_.StartTaskRunner();
+ scoped_refptr<TaskRunner> task_runner = this->delegate_.GetTaskRunner();
+ // Post each ith task i+1 times with delays from 0-i.
+ for (int i = 0; i < 20; ++i) {
+ const Closure& ith_task = this->task_tracker_->WrapTask(Closure(), i);
+ for (int j = 0; j < i + 1; ++j) {
+ task_runner->PostDelayedTask(
+ FROM_HERE, ith_task, base::TimeDelta::FromMilliseconds(j));
+ ++expected_task_run_counts[i];
+ }
+ }
+ this->delegate_.StopTaskRunner();
+
+ EXPECT_EQ(expected_task_run_counts,
+ this->task_tracker_->GetTaskRunCounts());
+}
+
+namespace internal {
+
+// Calls RunsTasksOnCurrentThread() on |task_runner| and expects it to
+// equal |expected_value|.
+void ExpectRunsTasksOnCurrentThread(
+ bool expected_value,
+ const scoped_refptr<TaskRunner>& task_runner);
+
+} // namespace internal
+
+// Post a bunch of tasks to the task runner as well as to a separate
+// thread, each checking the value of RunsTasksOnCurrentThread(),
+// which should return true for the tasks posted on the task runner
+// and false for the tasks posted on the separate thread.
+TYPED_TEST_P(TaskRunnerTest, RunsTasksOnCurrentThread) {
+ std::map<int, int> expected_task_run_counts;
+
+ Thread thread("Non-task-runner thread");
+ ASSERT_TRUE(thread.Start());
+ this->delegate_.StartTaskRunner();
+
+ scoped_refptr<TaskRunner> task_runner = this->delegate_.GetTaskRunner();
+ // Post each ith task i+1 times on the task runner and i+1 times on
+ // the non-task-runner thread.
+ for (int i = 0; i < 20; ++i) {
+ const Closure& ith_task_runner_task =
+ this->task_tracker_->WrapTask(
+ Bind(&internal::ExpectRunsTasksOnCurrentThread,
+ true, task_runner),
+ i);
+ const Closure& ith_non_task_runner_task =
+ this->task_tracker_->WrapTask(
+ Bind(&internal::ExpectRunsTasksOnCurrentThread,
+ false, task_runner),
+ i);
+ for (int j = 0; j < i + 1; ++j) {
+ task_runner->PostTask(FROM_HERE, ith_task_runner_task);
+ thread.message_loop()->PostTask(FROM_HERE, ith_non_task_runner_task);
+ expected_task_run_counts[i] += 2;
+ }
+ }
+
+ this->delegate_.StopTaskRunner();
+ thread.Stop();
+
+ EXPECT_EQ(expected_task_run_counts,
+ this->task_tracker_->GetTaskRunCounts());
+}
+
+REGISTER_TYPED_TEST_CASE_P(
+ TaskRunnerTest, Basic, Delayed, RunsTasksOnCurrentThread);
+
+} // namespace base
+
+#endif //#define BASE_TEST_TASK_RUNNER_TEST_TEMPLATE_H_
diff --git a/src/base/test/test_file_util.h b/src/base/test/test_file_util.h
new file mode 100644
index 0000000..6c21410
--- /dev/null
+++ b/src/base/test/test_file_util.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 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_TEST_TEST_FILE_UTIL_H_
+#define BASE_TEST_TEST_FILE_UTIL_H_
+
+// File utility functions used only by tests.
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+
+class FilePath;
+
+namespace file_util {
+
+// Wrapper over file_util::Delete. On Windows repeatedly invokes Delete in case
+// of failure to workaround Windows file locking semantics. Returns true on
+// success.
+bool DieFileDie(const FilePath& file, bool recurse);
+
+// Clear a specific file from the system cache. After this call, trying
+// to access this file will result in a cold load from the hard drive.
+bool EvictFileFromSystemCache(const FilePath& file);
+
+// Like CopyFileNoCache but recursively copies all files and subdirectories
+// in the given input directory to the output directory. Any files in the
+// destination that already exist will be overwritten.
+//
+// Returns true on success. False means there was some error copying, so the
+// state of the destination is unknown.
+bool CopyRecursiveDirNoCache(const FilePath& source_dir,
+ const FilePath& dest_dir);
+
+#if defined(OS_WIN)
+// Returns true if the volume supports Alternate Data Streams.
+bool VolumeSupportsADS(const FilePath& path);
+
+// Returns true if the ZoneIdentifier is correctly set to "Internet" (3).
+// Note that this function must be called from the same process as
+// the one that set the zone identifier. I.e. don't use it in UI/automation
+// based tests.
+bool HasInternetZoneIdentifier(const FilePath& full_path);
+#endif // defined(OS_WIN)
+
+// In general it's not reliable to convert a FilePath to a wstring and we use
+// string16 elsewhere for Unicode strings, but in tests it is frequently
+// convenient to be able to compare paths to literals like L"foobar".
+BASE_EXPORT std::wstring FilePathAsWString(const FilePath& path);
+BASE_EXPORT FilePath WStringAsFilePath(const std::wstring& path);
+
+// For testing, make the file unreadable or unwritable.
+// In POSIX, this does not apply to the root user.
+bool MakeFileUnreadable(const FilePath& path) WARN_UNUSED_RESULT;
+bool MakeFileUnwritable(const FilePath& path) WARN_UNUSED_RESULT;
+
+// Saves the current permissions for a path, and restores it on destruction.
+class PermissionRestorer {
+ public:
+ explicit PermissionRestorer(const FilePath& path);
+ ~PermissionRestorer();
+
+ private:
+ const FilePath path_;
+ void* info_; // The opaque stored permission information.
+ size_t length_; // The length of the stored permission information.
+
+ DISALLOW_COPY_AND_ASSIGN(PermissionRestorer);
+};
+
+} // namespace file_util
+
+#endif // BASE_TEST_TEST_FILE_UTIL_H_
diff --git a/src/base/test/test_file_util_linux.cc b/src/base/test/test_file_util_linux.cc
new file mode 100644
index 0000000..993750e
--- /dev/null
+++ b/src/base/test/test_file_util_linux.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_file_util.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/file_path.h"
+
+namespace file_util {
+
+bool EvictFileFromSystemCache(const FilePath& file) {
+ int fd = open(file.value().c_str(), O_RDONLY);
+ if (fd < 0)
+ return false;
+ if (fdatasync(fd) != 0)
+ return false;
+ if (posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED) != 0)
+ return false;
+ close(fd);
+ return true;
+}
+
+} // namespace file_util
diff --git a/src/base/test/test_file_util_mac.cc b/src/base/test/test_file_util_mac.cc
new file mode 100644
index 0000000..7145e51
--- /dev/null
+++ b/src/base/test/test_file_util_mac.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_file_util.h"
+
+#include <sys/mman.h>
+#include <errno.h>
+#include "base/logging.h"
+#include "base/file_util.h"
+
+namespace file_util {
+
+bool EvictFileFromSystemCache(const FilePath& file) {
+ // There aren't any really direct ways to purge a file from the UBC. From
+ // talking with Amit Singh, the safest is to mmap the file with MAP_FILE (the
+ // default) + MAP_SHARED, then do an msync to invalidate the memory. The next
+ // open should then have to load the file from disk.
+
+ int64 length;
+ if (!file_util::GetFileSize(file, &length)) {
+ DLOG(ERROR) << "failed to get size of " << file.value();
+ return false;
+ }
+
+ // When a file is empty, we do not need to evict it from cache.
+ // In fact, an attempt to map it to memory will result in error.
+ if (length == 0) {
+ DLOG(WARNING) << "file size is zero, will not attempt to map to memory";
+ return true;
+ }
+
+ file_util::MemoryMappedFile mapped_file;
+ if (!mapped_file.Initialize(file)) {
+ DLOG(WARNING) << "failed to memory map " << file.value();
+ return false;
+ }
+
+ if (msync(const_cast<uint8*>(mapped_file.data()), mapped_file.length(),
+ MS_INVALIDATE) != 0) {
+ DLOG(WARNING) << "failed to invalidate memory map of " << file.value()
+ << ", errno: " << errno;
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace file_util
diff --git a/src/base/test/test_file_util_posix.cc b/src/base/test/test_file_util_posix.cc
new file mode 100644
index 0000000..5ebf335
--- /dev/null
+++ b/src/base/test/test_file_util_posix.cc
@@ -0,0 +1,191 @@
+// Copyright (c) 2012 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/test/test_file_util.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+
+namespace file_util {
+
+namespace {
+
+// Deny |permission| on the file |path|.
+bool DenyFilePermission(const FilePath& path, mode_t permission) {
+ struct stat stat_buf;
+ if (stat(path.value().c_str(), &stat_buf) != 0)
+ return false;
+ stat_buf.st_mode &= ~permission;
+
+ int rv = HANDLE_EINTR(chmod(path.value().c_str(), stat_buf.st_mode));
+ return rv == 0;
+}
+
+// Gets a blob indicating the permission information for |path|.
+// |length| is the length of the blob. Zero on failure.
+// Returns the blob pointer, or NULL on failure.
+void* GetPermissionInfo(const FilePath& path, size_t* length) {
+ DCHECK(length);
+ *length = 0;
+
+ struct stat stat_buf;
+ if (stat(path.value().c_str(), &stat_buf) != 0)
+ return NULL;
+
+ *length = sizeof(mode_t);
+ mode_t* mode = new mode_t;
+ *mode = stat_buf.st_mode & ~S_IFMT; // Filter out file/path kind.
+
+ return mode;
+}
+
+// Restores the permission information for |path|, given the blob retrieved
+// using |GetPermissionInfo()|.
+// |info| is the pointer to the blob.
+// |length| is the length of the blob.
+// Either |info| or |length| may be NULL/0, in which case nothing happens.
+bool RestorePermissionInfo(const FilePath& path, void* info, size_t length) {
+ if (!info || (length == 0))
+ return false;
+
+ DCHECK_EQ(sizeof(mode_t), length);
+ mode_t* mode = reinterpret_cast<mode_t*>(info);
+
+ int rv = HANDLE_EINTR(chmod(path.value().c_str(), *mode));
+
+ delete mode;
+
+ return rv == 0;
+}
+
+} // namespace
+
+bool DieFileDie(const FilePath& file, bool recurse) {
+ // There is no need to workaround Windows problems on POSIX.
+ // Just pass-through.
+ return file_util::Delete(file, recurse);
+}
+
+// Mostly a verbatim copy of CopyDirectory
+bool CopyRecursiveDirNoCache(const FilePath& source_dir,
+ const FilePath& dest_dir) {
+ char top_dir[PATH_MAX];
+ if (base::strlcpy(top_dir, source_dir.value().c_str(),
+ arraysize(top_dir)) >= arraysize(top_dir)) {
+ return false;
+ }
+
+ // This function does not properly handle destinations within the source
+ FilePath real_to_path = dest_dir;
+ if (PathExists(real_to_path)) {
+ if (!AbsolutePath(&real_to_path))
+ return false;
+ } else {
+ real_to_path = real_to_path.DirName();
+ if (!AbsolutePath(&real_to_path))
+ return false;
+ }
+ if (real_to_path.value().compare(0, source_dir.value().size(),
+ source_dir.value()) == 0)
+ return false;
+
+ bool success = true;
+ int traverse_type = FileEnumerator::FILES |
+ FileEnumerator::SHOW_SYM_LINKS | FileEnumerator::DIRECTORIES;
+ FileEnumerator traversal(source_dir, true, traverse_type);
+
+ // dest_dir may not exist yet, start the loop with dest_dir
+ FileEnumerator::FindInfo info;
+ FilePath current = source_dir;
+ if (stat(source_dir.value().c_str(), &info.stat) < 0) {
+ DLOG(ERROR) << "CopyRecursiveDirNoCache() couldn't stat source directory: "
+ << source_dir.value() << " errno = " << errno;
+ success = false;
+ }
+
+ while (success && !current.empty()) {
+ // |current| is the source path, including source_dir, so paste
+ // the suffix after source_dir onto dest_dir to create the target_path.
+ std::string suffix(¤t.value().c_str()[source_dir.value().size()]);
+ // Strip the leading '/' (if any).
+ if (!suffix.empty()) {
+ DCHECK_EQ('/', suffix[0]);
+ suffix.erase(0, 1);
+ }
+ const FilePath target_path = dest_dir.Append(suffix);
+
+ if (S_ISDIR(info.stat.st_mode)) {
+ if (mkdir(target_path.value().c_str(), info.stat.st_mode & 01777) != 0 &&
+ errno != EEXIST) {
+ DLOG(ERROR) << "CopyRecursiveDirNoCache() couldn't create directory: "
+ << target_path.value() << " errno = " << errno;
+ success = false;
+ }
+ } else if (S_ISREG(info.stat.st_mode)) {
+ if (CopyFile(current, target_path)) {
+ success = EvictFileFromSystemCache(target_path);
+ DCHECK(success);
+ } else {
+ DLOG(ERROR) << "CopyRecursiveDirNoCache() couldn't create file: "
+ << target_path.value();
+ success = false;
+ }
+ } else {
+ DLOG(WARNING) << "CopyRecursiveDirNoCache() skipping non-regular file: "
+ << current.value();
+ }
+
+ current = traversal.Next();
+ traversal.GetFindInfo(&info);
+ }
+
+ return success;
+}
+
+#if !defined(OS_LINUX) && !defined(OS_MACOSX)
+bool EvictFileFromSystemCache(const FilePath& file) {
+ // There doesn't seem to be a POSIX way to cool the disk cache.
+ NOTIMPLEMENTED();
+ return false;
+}
+#endif
+
+std::wstring FilePathAsWString(const FilePath& path) {
+ return UTF8ToWide(path.value());
+}
+FilePath WStringAsFilePath(const std::wstring& path) {
+ return FilePath(WideToUTF8(path));
+}
+
+bool MakeFileUnreadable(const FilePath& path) {
+ return DenyFilePermission(path, S_IRUSR | S_IRGRP | S_IROTH);
+}
+
+bool MakeFileUnwritable(const FilePath& path) {
+ return DenyFilePermission(path, S_IWUSR | S_IWGRP | S_IWOTH);
+}
+
+PermissionRestorer::PermissionRestorer(const FilePath& path)
+ : path_(path), info_(NULL), length_(0) {
+ info_ = GetPermissionInfo(path_, &length_);
+ DCHECK(info_ != NULL);
+ DCHECK_NE(0u, length_);
+}
+
+PermissionRestorer::~PermissionRestorer() {
+ if (!RestorePermissionInfo(path_, info_, length_))
+ NOTREACHED();
+}
+
+} // namespace file_util
diff --git a/src/base/test/test_file_util_shell.cc b/src/base/test/test_file_util_shell.cc
new file mode 100644
index 0000000..9831b66
--- /dev/null
+++ b/src/base/test/test_file_util_shell.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2012 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/test/test_file_util.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+
+namespace file_util {
+
+// Deny |permission| on the file |path|.
+bool DenyFilePermission(const FilePath& path, mode_t permission) {
+ struct stat stat_buf;
+ if (stat(path.value().c_str(), &stat_buf) != 0)
+ return false;
+ stat_buf.st_mode &= ~permission;
+
+ int rv = HANDLE_EINTR(chmod(path.value().c_str(), stat_buf.st_mode));
+ return rv == 0;
+}
+
+std::wstring FilePathAsWString(const FilePath& path) {
+ return UTF8ToWide(path.value());
+}
+FilePath WStringAsFilePath(const std::wstring& path) {
+ return FilePath(WideToUTF8(path));
+}
+
+bool MakeFileUnreadable(const FilePath& path) {
+ return DenyFilePermission(path, S_IRUSR | S_IRGRP | S_IROTH);
+}
+
+bool MakeFileUnwritable(const FilePath& path) {
+ return DenyFilePermission(path, S_IWUSR | S_IWGRP | S_IWOTH);
+}
+
+} // namespace file_util
\ No newline at end of file
diff --git a/src/base/test/test_file_util_starboard.cc b/src/base/test/test_file_util_starboard.cc
new file mode 100644
index 0000000..ef4cdad
--- /dev/null
+++ b/src/base/test/test_file_util_starboard.cc
@@ -0,0 +1,134 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/test/test_file_util.h"
+
+#include <string>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "starboard/directory.h"
+#include "starboard/file.h"
+#include "starboard/system.h"
+
+namespace file_util {
+
+bool DieFileDie(const FilePath& file, bool recurse) {
+ // There is no need to workaround Windows problems on Starboard. Just
+ // pass-through.
+ return file_util::Delete(file, recurse);
+}
+
+// Mostly a verbatim copy of CopyDirectory
+bool CopyRecursiveDirNoCache(const FilePath& source_dir,
+ const FilePath& dest_dir) {
+ char top_dir[PATH_MAX];
+ if (base::strlcpy(top_dir, source_dir.value().c_str(), arraysize(top_dir)) >=
+ arraysize(top_dir)) {
+ return false;
+ }
+
+ // This function does not properly handle destinations within the source
+ FilePath real_to_path = dest_dir;
+ if (PathExists(real_to_path)) {
+ if (!AbsolutePath(&real_to_path))
+ return false;
+ } else {
+ real_to_path = real_to_path.DirName();
+ if (!AbsolutePath(&real_to_path))
+ return false;
+ }
+ if (real_to_path.value().compare(0, source_dir.value().size(),
+ source_dir.value()) == 0)
+ return false;
+
+ bool success = true;
+ int traverse_type = FileEnumerator::FILES | FileEnumerator::DIRECTORIES;
+ FileEnumerator traversal(source_dir, true, traverse_type);
+
+ // dest_dir may not exist yet, start the loop with dest_dir
+ FileEnumerator::FindInfo info;
+ FilePath current = source_dir;
+ if (!SbFileGetPathInfo(source_dir.value().c_str(), &info.sb_info)) {
+ DLOG(ERROR) << "CopyRecursiveDirNoCache() couldn't stat source directory: "
+ << source_dir.value() << " error = " << SbSystemGetLastError();
+ success = false;
+ }
+
+ while (success && !current.empty()) {
+ // |current| is the source path, including source_dir, so paste
+ // the suffix after source_dir onto dest_dir to create the target_path.
+ std::string suffix(¤t.value().c_str()[source_dir.value().size()]);
+ // Strip the leading '/' (if any).
+ if (!suffix.empty()) {
+ DCHECK_EQ('/', suffix[0]);
+ suffix.erase(0, 1);
+ }
+ const FilePath target_path = dest_dir.Append(suffix);
+
+ if (info.sb_info.is_directory) {
+ if (SbDirectoryCreate(target_path.value().c_str())) {
+ DLOG(ERROR) << "CopyRecursiveDirNoCache() couldn't create directory: "
+ << target_path.value()
+ << " error = " << SbSystemGetLastError();
+ success = false;
+ }
+ } else {
+ if (CopyFile(current, target_path)) {
+ success = true;
+ } else {
+ DLOG(ERROR) << "CopyRecursiveDirNoCache() couldn't create file: "
+ << target_path.value();
+ success = false;
+ }
+ }
+
+ current = traversal.Next();
+ traversal.GetFindInfo(&info);
+ }
+
+ return success;
+}
+
+std::wstring FilePathAsWString(const FilePath& path) {
+ return UTF8ToWide(path.value());
+}
+
+FilePath WStringAsFilePath(const std::wstring& path) {
+ return FilePath(WideToUTF8(path));
+}
+
+bool MakeFileUnreadable(const FilePath& path) {
+ NOTIMPLEMENTED();
+ return true;
+}
+
+bool MakeFileUnwritable(const FilePath& path) {
+ NOTIMPLEMENTED();
+ return true;
+}
+
+PermissionRestorer::PermissionRestorer(const FilePath& path)
+ : path_(path), info_(NULL), length_(0) {
+ NOTIMPLEMENTED();
+}
+
+PermissionRestorer::~PermissionRestorer() {
+ NOTIMPLEMENTED();
+}
+
+} // namespace file_util
diff --git a/src/base/test/test_file_util_win.cc b/src/base/test/test_file_util_win.cc
new file mode 100644
index 0000000..41d69ea
--- /dev/null
+++ b/src/base/test/test_file_util_win.cc
@@ -0,0 +1,343 @@
+// Copyright (c) 2012 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/test/test_file_util.h"
+
+#include <aclapi.h>
+#include <shlwapi.h>
+#include <windows.h>
+
+#include <vector>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/string_split.h"
+#include "base/win/scoped_handle.h"
+#include "base/threading/platform_thread.h"
+
+namespace file_util {
+
+static const ptrdiff_t kOneMB = 1024 * 1024;
+
+namespace {
+
+struct PermissionInfo {
+ PSECURITY_DESCRIPTOR security_descriptor;
+ ACL dacl;
+};
+
+// Deny |permission| on the file |path|, for the current user.
+bool DenyFilePermission(const FilePath& path, DWORD permission) {
+ PACL old_dacl;
+ PSECURITY_DESCRIPTOR security_descriptor;
+ if (GetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
+ SE_FILE_OBJECT,
+ DACL_SECURITY_INFORMATION, NULL, NULL, &old_dacl,
+ NULL, &security_descriptor) != ERROR_SUCCESS) {
+ return false;
+ }
+
+ EXPLICIT_ACCESS change;
+ change.grfAccessPermissions = permission;
+ change.grfAccessMode = DENY_ACCESS;
+ change.grfInheritance = 0;
+ change.Trustee.pMultipleTrustee = NULL;
+ change.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+ change.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
+ change.Trustee.TrusteeType = TRUSTEE_IS_USER;
+ change.Trustee.ptstrName = L"CURRENT_USER";
+
+ PACL new_dacl;
+ if (SetEntriesInAcl(1, &change, old_dacl, &new_dacl) != ERROR_SUCCESS) {
+ LocalFree(security_descriptor);
+ return false;
+ }
+
+ DWORD rc = SetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
+ SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
+ NULL, NULL, new_dacl, NULL);
+ LocalFree(security_descriptor);
+ LocalFree(new_dacl);
+
+ return rc == ERROR_SUCCESS;
+}
+
+// Gets a blob indicating the permission information for |path|.
+// |length| is the length of the blob. Zero on failure.
+// Returns the blob pointer, or NULL on failure.
+void* GetPermissionInfo(const FilePath& path, size_t* length) {
+ DCHECK(length != NULL);
+ *length = 0;
+ PACL dacl = NULL;
+ PSECURITY_DESCRIPTOR security_descriptor;
+ if (GetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
+ SE_FILE_OBJECT,
+ DACL_SECURITY_INFORMATION, NULL, NULL, &dacl,
+ NULL, &security_descriptor) != ERROR_SUCCESS) {
+ return NULL;
+ }
+ DCHECK(dacl != NULL);
+
+ *length = sizeof(PSECURITY_DESCRIPTOR) + dacl->AclSize;
+ PermissionInfo* info = reinterpret_cast<PermissionInfo*>(new char[*length]);
+ info->security_descriptor = security_descriptor;
+ memcpy(&info->dacl, dacl, dacl->AclSize);
+
+ return info;
+}
+
+// Restores the permission information for |path|, given the blob retrieved
+// using |GetPermissionInfo()|.
+// |info| is the pointer to the blob.
+// |length| is the length of the blob.
+// Either |info| or |length| may be NULL/0, in which case nothing happens.
+bool RestorePermissionInfo(const FilePath& path, void* info, size_t length) {
+ if (!info || !length)
+ return false;
+
+ PermissionInfo* perm = reinterpret_cast<PermissionInfo*>(info);
+
+ DWORD rc = SetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
+ SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
+ NULL, NULL, &perm->dacl, NULL);
+ LocalFree(perm->security_descriptor);
+
+ char* char_array = reinterpret_cast<char*>(info);
+ delete [] char_array;
+
+ return rc == ERROR_SUCCESS;
+}
+
+} // namespace
+
+bool DieFileDie(const FilePath& file, bool recurse) {
+ // It turns out that to not induce flakiness a long timeout is needed.
+ const int kIterations = 25;
+ const base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(10) /
+ kIterations;
+
+ if (!file_util::PathExists(file))
+ return true;
+
+ // Sometimes Delete fails, so try a few more times. Divide the timeout
+ // into short chunks, so that if a try succeeds, we won't delay the test
+ // for too long.
+ for (int i = 0; i < kIterations; ++i) {
+ if (file_util::Delete(file, recurse))
+ return true;
+ base::PlatformThread::Sleep(kTimeout);
+ }
+ return false;
+}
+
+bool EvictFileFromSystemCache(const FilePath& file) {
+ // Request exclusive access to the file and overwrite it with no buffering.
+ base::win::ScopedHandle file_handle(
+ CreateFile(file.value().c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL));
+ if (!file_handle)
+ return false;
+
+ // Get some attributes to restore later.
+ BY_HANDLE_FILE_INFORMATION bhi = {0};
+ CHECK(::GetFileInformationByHandle(file_handle, &bhi));
+
+ // Execute in chunks. It could be optimized. We want to do few of these since
+ // these operations will be slow without the cache.
+
+ // Allocate a buffer for the reads and the writes.
+ char* buffer = reinterpret_cast<char*>(VirtualAlloc(NULL,
+ kOneMB,
+ MEM_COMMIT | MEM_RESERVE,
+ PAGE_READWRITE));
+
+ // If the file size isn't a multiple of kOneMB, we'll need special
+ // processing.
+ bool file_is_aligned = true;
+ int total_bytes = 0;
+ DWORD bytes_read, bytes_written;
+ for (;;) {
+ bytes_read = 0;
+ ::ReadFile(file_handle, buffer, kOneMB, &bytes_read, NULL);
+ if (bytes_read == 0)
+ break;
+
+ if (bytes_read < kOneMB) {
+ // Zero out the remaining part of the buffer.
+ // WriteFile will fail if we provide a buffer size that isn't a
+ // sector multiple, so we'll have to write the entire buffer with
+ // padded zeros and then use SetEndOfFile to truncate the file.
+ ZeroMemory(buffer + bytes_read, kOneMB - bytes_read);
+ file_is_aligned = false;
+ }
+
+ // Move back to the position we just read from.
+ // Note that SetFilePointer will also fail if total_bytes isn't sector
+ // aligned, but that shouldn't happen here.
+ DCHECK((total_bytes % kOneMB) == 0);
+ SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN);
+ if (!::WriteFile(file_handle, buffer, kOneMB, &bytes_written, NULL) ||
+ bytes_written != kOneMB) {
+ BOOL freed = VirtualFree(buffer, 0, MEM_RELEASE);
+ DCHECK(freed);
+ NOTREACHED();
+ return false;
+ }
+
+ total_bytes += bytes_read;
+
+ // If this is false, then we just processed the last portion of the file.
+ if (!file_is_aligned)
+ break;
+ }
+
+ BOOL freed = VirtualFree(buffer, 0, MEM_RELEASE);
+ DCHECK(freed);
+
+ if (!file_is_aligned) {
+ // The size of the file isn't a multiple of 1 MB, so we'll have
+ // to open the file again, this time without the FILE_FLAG_NO_BUFFERING
+ // flag and use SetEndOfFile to mark EOF.
+ file_handle.Set(NULL);
+ file_handle.Set(CreateFile(file.value().c_str(), GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING, 0, NULL));
+ CHECK_NE(SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN),
+ INVALID_SET_FILE_POINTER);
+ CHECK(::SetEndOfFile(file_handle));
+ }
+
+ // Restore the file attributes.
+ CHECK(::SetFileTime(file_handle, &bhi.ftCreationTime, &bhi.ftLastAccessTime,
+ &bhi.ftLastWriteTime));
+
+ return true;
+}
+
+// Like CopyFileNoCache but recursively copies all files and subdirectories
+// in the given input directory to the output directory.
+bool CopyRecursiveDirNoCache(const FilePath& source_dir,
+ const FilePath& dest_dir) {
+ // Try to create the directory if it doesn't already exist.
+ if (!CreateDirectory(dest_dir)) {
+ if (GetLastError() != ERROR_ALREADY_EXISTS)
+ return false;
+ }
+
+ std::vector<std::wstring> files_copied;
+
+ FilePath src(source_dir.AppendASCII("*"));
+
+ WIN32_FIND_DATA fd;
+ HANDLE fh = FindFirstFile(src.value().c_str(), &fd);
+ if (fh == INVALID_HANDLE_VALUE)
+ return false;
+
+ do {
+ std::wstring cur_file(fd.cFileName);
+ if (cur_file == L"." || cur_file == L"..")
+ continue; // Skip these special entries.
+
+ FilePath cur_source_path = source_dir.Append(cur_file);
+ FilePath cur_dest_path = dest_dir.Append(cur_file);
+
+ if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ // Recursively copy a subdirectory. We stripped "." and ".." already.
+ if (!CopyRecursiveDirNoCache(cur_source_path, cur_dest_path)) {
+ FindClose(fh);
+ return false;
+ }
+ } else {
+ // Copy the file.
+ if (!::CopyFile(cur_source_path.value().c_str(),
+ cur_dest_path.value().c_str(), false)) {
+ FindClose(fh);
+ return false;
+ }
+
+ // We don't check for errors from this function, often, we are copying
+ // files that are in the repository, and they will have read-only set.
+ // This will prevent us from evicting from the cache, but these don't
+ // matter anyway.
+ EvictFileFromSystemCache(cur_dest_path);
+ }
+ } while (FindNextFile(fh, &fd));
+
+ FindClose(fh);
+ return true;
+}
+
+// Checks if the volume supports Alternate Data Streams. This is required for
+// the Zone Identifier implementation.
+bool VolumeSupportsADS(const FilePath& path) {
+ wchar_t drive[MAX_PATH] = {0};
+ wcscpy_s(drive, MAX_PATH, path.value().c_str());
+
+ if (!PathStripToRootW(drive))
+ return false;
+
+ DWORD fs_flags = 0;
+ if (!GetVolumeInformationW(drive, NULL, 0, 0, NULL, &fs_flags, NULL, 0))
+ return false;
+
+ if (fs_flags & FILE_NAMED_STREAMS)
+ return true;
+
+ return false;
+}
+
+// Return whether the ZoneIdentifier is correctly set to "Internet" (3)
+// Only returns a valid result when called from same process as the
+// one that (was supposed to have) set the zone identifier.
+bool HasInternetZoneIdentifier(const FilePath& full_path) {
+ FilePath zone_path(full_path.value() + L":Zone.Identifier");
+ std::string zone_path_contents;
+ if (!file_util::ReadFileToString(zone_path, &zone_path_contents))
+ return false;
+
+ std::vector<std::string> lines;
+ // This call also trims whitespaces, including carriage-returns (\r).
+ base::SplitString(zone_path_contents, '\n', &lines);
+
+ switch (lines.size()) {
+ case 3:
+ // optional empty line at end of file:
+ if (lines[2] != "")
+ return false;
+ // fall through:
+ case 2:
+ return lines[0] == "[ZoneTransfer]" && lines[1] == "ZoneId=3";
+ default:
+ return false;
+ }
+}
+
+std::wstring FilePathAsWString(const FilePath& path) {
+ return path.value();
+}
+FilePath WStringAsFilePath(const std::wstring& path) {
+ return FilePath(path);
+}
+
+bool MakeFileUnreadable(const FilePath& path) {
+ return DenyFilePermission(path, GENERIC_READ);
+}
+
+bool MakeFileUnwritable(const FilePath& path) {
+ return DenyFilePermission(path, GENERIC_WRITE);
+}
+
+PermissionRestorer::PermissionRestorer(const FilePath& path)
+ : path_(path), info_(NULL), length_(0) {
+ info_ = GetPermissionInfo(path_, &length_);
+ DCHECK(info_ != NULL);
+ DCHECK_NE(0u, length_);
+}
+
+PermissionRestorer::~PermissionRestorer() {
+ if (!RestorePermissionInfo(path_, info_, length_))
+ NOTREACHED();
+}
+
+} // namespace file_util
diff --git a/src/base/test/test_listener_ios.h b/src/base/test/test_listener_ios.h
new file mode 100644
index 0000000..c312250
--- /dev/null
+++ b/src/base/test/test_listener_ios.h
@@ -0,0 +1,17 @@
+// Copyright (c) 2012 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_TEST_TEST_LISTENER_IOS_H_
+#define BASE_TEST_TEST_LISTENER_IOS_H_
+
+namespace base {
+namespace test_listener_ios {
+
+// Register an IOSRunLoopListener.
+void RegisterTestEndListener();
+
+} // namespace test_listener_ios
+} // namespace base
+
+#endif // BASE_TEST_TEST_LISTENER_IOS_H_
diff --git a/src/base/test/test_listener_ios.mm b/src/base/test/test_listener_ios.mm
new file mode 100644
index 0000000..12cf5bb
--- /dev/null
+++ b/src/base/test/test_listener_ios.mm
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 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/test/test_listener_ios.h"
+
+#import <Foundation/Foundation.h>
+
+#include "base/mac/scoped_nsautorelease_pool.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// The iOS watchdog timer will kill an app that doesn't spin the main event
+// loop often enough. This uses a Gtest TestEventListener to spin the current
+// loop after each test finishes. However, if any individual test takes too
+// long, it is still possible that the app will get killed.
+
+namespace {
+
+class IOSRunLoopListener : public testing::EmptyTestEventListener {
+ public:
+ virtual void OnTestEnd(const testing::TestInfo& test_info);
+};
+
+void IOSRunLoopListener::OnTestEnd(const testing::TestInfo& test_info) {
+ base::mac::ScopedNSAutoreleasePool scoped_pool;
+
+ // At the end of the test, spin the default loop for a moment.
+ NSDate* stop_date = [NSDate dateWithTimeIntervalSinceNow:0.001];
+ [[NSRunLoop currentRunLoop] runUntilDate:stop_date];
+}
+
+} // namespace
+
+
+namespace base {
+namespace test_listener_ios {
+
+void RegisterTestEndListener() {
+ testing::TestEventListeners& listeners =
+ testing::UnitTest::GetInstance()->listeners();
+ listeners.Append(new IOSRunLoopListener);
+}
+
+} // namespace test_listener_ios
+} // namespace base
diff --git a/src/base/test/test_reg_util_win.cc b/src/base/test/test_reg_util_win.cc
new file mode 100644
index 0000000..e23c6e9
--- /dev/null
+++ b/src/base/test/test_reg_util_win.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_reg_util_win.h"
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace registry_util {
+
+const wchar_t RegistryOverrideManager::kTempTestKeyPath[] =
+ L"Software\\Chromium\\TempTestKeys";
+
+RegistryOverrideManager::ScopedRegistryKeyOverride::ScopedRegistryKeyOverride(
+ HKEY override,
+ const std::wstring& temp_name)
+ : override_(override),
+ temp_name_(temp_name) {
+ DCHECK(!temp_name_.empty());
+ std::wstring key_path(RegistryOverrideManager::kTempTestKeyPath);
+ key_path += L"\\" + temp_name_;
+ EXPECT_EQ(ERROR_SUCCESS,
+ temp_key_.Create(HKEY_CURRENT_USER, key_path.c_str(), KEY_ALL_ACCESS));
+ EXPECT_EQ(ERROR_SUCCESS,
+ ::RegOverridePredefKey(override_, temp_key_.Handle()));
+}
+
+RegistryOverrideManager::
+ ScopedRegistryKeyOverride::~ScopedRegistryKeyOverride() {
+ ::RegOverridePredefKey(override_, NULL);
+ // The temp key will be deleted via a call to DeleteAllTempKeys().
+}
+
+RegistryOverrideManager::RegistryOverrideManager() {
+ DeleteAllTempKeys();
+}
+
+RegistryOverrideManager::~RegistryOverrideManager() {
+ RemoveAllOverrides();
+}
+
+void RegistryOverrideManager::OverrideRegistry(HKEY override,
+ const std::wstring& temp_name) {
+ overrides_.push_back(new ScopedRegistryKeyOverride(override, temp_name));
+}
+
+void RegistryOverrideManager::RemoveAllOverrides() {
+ while (!overrides_.empty()) {
+ delete overrides_.back();
+ overrides_.pop_back();
+ }
+
+ DeleteAllTempKeys();
+}
+
+// static
+void RegistryOverrideManager::DeleteAllTempKeys() {
+ base::win::RegKey key;
+ if (key.Open(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS) == ERROR_SUCCESS) {
+ key.DeleteKey(kTempTestKeyPath);
+ }
+}
+
+} // namespace registry_util
diff --git a/src/base/test/test_reg_util_win.h b/src/base/test/test_reg_util_win.h
new file mode 100644
index 0000000..b71831f
--- /dev/null
+++ b/src/base/test/test_reg_util_win.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TEST_REG_UTIL_H_
+#define BASE_TEST_TEST_REG_UTIL_H_
+
+// Registry utility functions used only by tests.
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/win/registry.h"
+
+namespace registry_util {
+
+// Allows a test to easily override registry hives so that it can start from a
+// known good state, or make sure to not leave any side effects once the test
+// completes.
+class RegistryOverrideManager {
+ public:
+ // All overridden hives will be descendents of this registry path under the
+ // main HKCU hive.
+ static const wchar_t kTempTestKeyPath[];
+
+ RegistryOverrideManager();
+ ~RegistryOverrideManager();
+
+ // Override the given registry hive using a temporary key named by temp_name
+ // under the temporary test key path.
+ void OverrideRegistry(HKEY override, const std::wstring& temp_name);
+
+ // Deletes all temporary test keys used by the overrides.
+ static void DeleteAllTempKeys();
+
+ // Removes all overrides and deletes all temporary test keys used by the
+ // overrides.
+ void RemoveAllOverrides();
+
+ private:
+ // Keeps track of one override.
+ class ScopedRegistryKeyOverride {
+ public:
+ ScopedRegistryKeyOverride(HKEY override, const std::wstring& temp_name);
+ ~ScopedRegistryKeyOverride();
+
+ private:
+ HKEY override_;
+ base::win::RegKey temp_key_;
+ std::wstring temp_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedRegistryKeyOverride);
+ };
+
+ std::vector<ScopedRegistryKeyOverride*> overrides_;
+
+ DISALLOW_COPY_AND_ASSIGN(RegistryOverrideManager);
+};
+
+} // namespace registry_util
+
+#endif // BASE_TEST_TEST_REG_UTIL_H_
diff --git a/src/base/test/test_shortcut_win.cc b/src/base/test/test_shortcut_win.cc
new file mode 100644
index 0000000..f84c0d0
--- /dev/null
+++ b/src/base/test/test_shortcut_win.cc
@@ -0,0 +1,155 @@
+// Copyright (c) 2012 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/test/test_shortcut_win.h"
+
+#include <windows.h>
+#include <shlobj.h>
+#include <propkey.h>
+#include <propvarutil.h>
+
+#include "base/file_path.h"
+#include "base/string16.h"
+#include "base/utf_string_conversions.h"
+#include "base/win/scoped_comptr.h"
+#include "base/win/windows_version.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// propsys.lib is required for PropvariantTo*().
+#pragma comment(lib, "propsys.lib")
+
+namespace base {
+namespace win {
+
+namespace {
+
+// Validates |actual_path|'s LongPathName case-insensitively matches
+// |expected_path|'s LongPathName.
+void ValidatePathsAreEqual(const FilePath& expected_path,
+ const FilePath& actual_path) {
+ wchar_t long_expected_path_chars[MAX_PATH] = {0};
+ wchar_t long_actual_path_chars[MAX_PATH] = {0};
+
+ // If |expected_path| is empty confirm immediately that |actual_path| is also
+ // empty.
+ if (expected_path.empty()) {
+ EXPECT_TRUE(actual_path.empty());
+ return;
+ }
+
+ // Proceed with LongPathName matching which will also confirm the paths exist.
+ EXPECT_NE(0U, ::GetLongPathName(
+ expected_path.value().c_str(), long_expected_path_chars, MAX_PATH))
+ << "Failed to get LongPathName of " << expected_path.value();
+ EXPECT_NE(0U, ::GetLongPathName(
+ actual_path.value().c_str(), long_actual_path_chars, MAX_PATH))
+ << "Failed to get LongPathName of " << actual_path.value();
+
+ FilePath long_expected_path(long_expected_path_chars);
+ FilePath long_actual_path(long_actual_path_chars);
+ EXPECT_FALSE(long_expected_path.empty());
+ EXPECT_FALSE(long_actual_path.empty());
+
+ EXPECT_EQ(long_expected_path, long_actual_path);
+}
+
+} // namespace
+
+void ValidateShortcut(const FilePath& shortcut_path,
+ const ShortcutProperties& properties) {
+ ScopedComPtr<IShellLink> i_shell_link;
+ ScopedComPtr<IPersistFile> i_persist_file;
+
+ wchar_t read_target[MAX_PATH] = {0};
+ wchar_t read_working_dir[MAX_PATH] = {0};
+ wchar_t read_arguments[MAX_PATH] = {0};
+ wchar_t read_description[MAX_PATH] = {0};
+ wchar_t read_icon[MAX_PATH] = {0};
+ int read_icon_index = 0;
+
+ HRESULT hr;
+
+ // Initialize the shell interfaces.
+ EXPECT_TRUE(SUCCEEDED(hr = i_shell_link.CreateInstance(
+ CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER)));
+ if (FAILED(hr))
+ return;
+
+ EXPECT_TRUE(SUCCEEDED(hr = i_persist_file.QueryFrom(i_shell_link)));
+ if (FAILED(hr))
+ return;
+
+ // Load the shortcut.
+ EXPECT_TRUE(SUCCEEDED(hr = i_persist_file->Load(
+ shortcut_path.value().c_str(), 0))) << "Failed to load shortcut at "
+ << shortcut_path.value();
+ if (FAILED(hr))
+ return;
+
+ if (properties.options & ShortcutProperties::PROPERTIES_TARGET) {
+ EXPECT_TRUE(SUCCEEDED(
+ i_shell_link->GetPath(read_target, MAX_PATH, NULL, SLGP_SHORTPATH)));
+ ValidatePathsAreEqual(properties.target, FilePath(read_target));
+ }
+
+ if (properties.options & ShortcutProperties::PROPERTIES_WORKING_DIR) {
+ EXPECT_TRUE(SUCCEEDED(
+ i_shell_link->GetWorkingDirectory(read_working_dir, MAX_PATH)));
+ ValidatePathsAreEqual(properties.working_dir, FilePath(read_working_dir));
+ }
+
+ if (properties.options & ShortcutProperties::PROPERTIES_ARGUMENTS) {
+ EXPECT_TRUE(SUCCEEDED(
+ i_shell_link->GetArguments(read_arguments, MAX_PATH)));
+ EXPECT_EQ(properties.arguments, read_arguments);
+ }
+
+ if (properties.options & ShortcutProperties::PROPERTIES_DESCRIPTION) {
+ EXPECT_TRUE(SUCCEEDED(
+ i_shell_link->GetDescription(read_description, MAX_PATH)));
+ EXPECT_EQ(properties.description, read_description);
+ }
+
+ if (properties.options & ShortcutProperties::PROPERTIES_ICON) {
+ EXPECT_TRUE(SUCCEEDED(
+ i_shell_link->GetIconLocation(read_icon, MAX_PATH, &read_icon_index)));
+ ValidatePathsAreEqual(properties.icon, FilePath(read_icon));
+ EXPECT_EQ(properties.icon_index, read_icon_index);
+ }
+
+ if (GetVersion() >= VERSION_WIN7) {
+ ScopedComPtr<IPropertyStore> property_store;
+ // Note that, as mentioned on MSDN at http://goo.gl/M8h9g, if a property is
+ // not set, GetValue will return S_OK and the PROPVARIANT will be set to
+ // VT_EMPTY.
+ PROPVARIANT pv_app_id, pv_dual_mode;
+ EXPECT_TRUE(SUCCEEDED(hr = property_store.QueryFrom(i_shell_link)));
+ if (FAILED(hr))
+ return;
+ EXPECT_EQ(S_OK, property_store->GetValue(PKEY_AppUserModel_ID, &pv_app_id));
+ EXPECT_EQ(S_OK, property_store->GetValue(PKEY_AppUserModel_IsDualMode,
+ &pv_dual_mode));
+
+ // Note, as mentioned on MSDN at
+ // http://msdn.microsoft.com/library/windows/desktop/bb776559.aspx, if
+ // |pv_app_id| is a VT_EMPTY it is successfully converted to the empty
+ // string as desired.
+ wchar_t read_app_id[MAX_PATH] = {0};
+ PropVariantToString(pv_app_id, read_app_id, MAX_PATH);
+ if (properties.options & ShortcutProperties::PROPERTIES_APP_ID)
+ EXPECT_EQ(properties.app_id, read_app_id);
+
+ // Note, as mentioned on MSDN at
+ // http://msdn.microsoft.com/library/windows/desktop/bb776531.aspx, if
+ // |pv_dual_mode| is a VT_EMPTY it is successfully converted to false as
+ // desired.
+ BOOL read_dual_mode;
+ PropVariantToBoolean(pv_dual_mode, &read_dual_mode);
+ if (properties.options & ShortcutProperties::PROPERTIES_DUAL_MODE)
+ EXPECT_EQ(properties.dual_mode, static_cast<bool>(read_dual_mode));
+ }
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/test/test_shortcut_win.h b/src/base/test/test_shortcut_win.h
new file mode 100644
index 0000000..ef75713
--- /dev/null
+++ b/src/base/test/test_shortcut_win.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2012 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_TEST_TEST_SHORTCUT_WIN_H_
+#define BASE_TEST_TEST_SHORTCUT_WIN_H_
+
+#include "base/file_path.h"
+#include "base/win/shortcut.h"
+
+// Windows shortcut functions used only by tests.
+
+namespace base {
+namespace win {
+
+// Validates that a shortcut exists at |shortcut_path| with the expected
+// |properties|.
+// Logs gtest failures on failed verifications.
+void ValidateShortcut(const FilePath& shortcut_path,
+ const ShortcutProperties& properties);
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_TEST_TEST_SHORTCUT_WIN_H_
diff --git a/src/base/test/test_suite.cc b/src/base/test/test_suite.cc
new file mode 100644
index 0000000..6e0444d
--- /dev/null
+++ b/src/base/test/test_suite.cc
@@ -0,0 +1,304 @@
+// Copyright (c) 2012 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/test/test_suite.h"
+
+#include "base/at_exit.h"
+#include "base/base_paths.h"
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/debug/debug_on_start_win.h"
+#include "base/debug/debugger.h"
+#include "base/debug/stack_trace.h"
+#include "base/file_path.h"
+#include "base/i18n/icu_util.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/test_timeouts.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_nsautorelease_pool.h"
+#if defined(OS_IOS)
+#include "base/test/test_listener_ios.h"
+#else
+#include "base/test/mock_chrome_application_mac.h"
+#endif // OS_IOS
+#endif // OS_MACOSX
+
+#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
+#include "base/test/test_support_android.h"
+#endif
+
+#if defined(OS_IOS)
+#include "base/test/test_support_ios.h"
+#endif
+
+#if defined(TOOLKIT_GTK)
+#include <gtk/gtk.h>
+#endif
+
+namespace {
+
+class MaybeTestDisabler : public testing::EmptyTestEventListener {
+ public:
+ virtual void OnTestStart(const testing::TestInfo& test_info) OVERRIDE {
+ ASSERT_FALSE(TestSuite::IsMarkedMaybe(test_info))
+ << "Probably the OS #ifdefs don't include all of the necessary "
+ "platforms.\nPlease ensure that no tests have the MAYBE_ prefix "
+ "after the code is preprocessed.";
+ }
+};
+
+class TestClientInitializer : public testing::EmptyTestEventListener {
+ public:
+ TestClientInitializer()
+ : old_command_line_(CommandLine::NO_PROGRAM) {
+ }
+
+ virtual void OnTestStart(const testing::TestInfo& test_info) OVERRIDE {
+ old_command_line_ = *CommandLine::ForCurrentProcess();
+ }
+
+ virtual void OnTestEnd(const testing::TestInfo& test_info) OVERRIDE {
+ *CommandLine::ForCurrentProcess() = old_command_line_;
+ }
+
+ private:
+ CommandLine old_command_line_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestClientInitializer);
+};
+
+} // namespace
+
+const char TestSuite::kStrictFailureHandling[] = "strict_failure_handling";
+
+TestSuite::TestSuite(int argc, char** argv) : initialized_command_line_(false) {
+ PreInitialize(argc, argv, true);
+}
+
+TestSuite::TestSuite(int argc, char** argv, bool create_at_exit_manager)
+ : initialized_command_line_(false) {
+ PreInitialize(argc, argv, create_at_exit_manager);
+}
+
+TestSuite::~TestSuite() {
+ if (initialized_command_line_)
+ CommandLine::Reset();
+}
+
+void TestSuite::PreInitialize(int argc, char** argv,
+ bool create_at_exit_manager) {
+#if defined(OS_WIN)
+ testing::GTEST_FLAG(catch_exceptions) = false;
+#endif
+ base::EnableTerminationOnHeapCorruption();
+ initialized_command_line_ = CommandLine::Init(argc, argv);
+ testing::InitGoogleTest(&argc, argv);
+#if defined (__LB_LINUX__)
+ setlocale(LC_ALL, "");
+#elif defined(OS_LINUX) && defined(USE_AURA)
+ // When calling native char conversion functions (e.g wrctomb) we need to
+ // have the locale set. In the absence of such a call the "C" locale is the
+ // default. In the gtk code (below) gtk_init() implicitly sets a locale.
+ setlocale(LC_ALL, "");
+#elif defined(TOOLKIT_GTK)
+ gtk_init_check(&argc, &argv);
+#endif // defined(TOOLKIT_GTK)
+
+ // On Android, AtExitManager is created in
+ // testing/android/native_test_wrapper.cc before main() is called.
+#if defined(__LB_SHELL__) || defined(COBALT)
+ if (create_at_exit_manager) {
+ at_exit_manager_.reset(new base::ShadowingAtExitManager);
+ }
+#elif !defined(OS_ANDROID)
+ if (create_at_exit_manager)
+ at_exit_manager_.reset(new base::AtExitManager);
+#endif
+
+ // Don't add additional code to this function. Instead add it to
+ // Initialize(). See bug 6436.
+}
+
+
+// static
+bool TestSuite::IsMarkedFlaky(const testing::TestInfo& test) {
+ return strncmp(test.name(), "FLAKY_", 6) == 0;
+}
+
+// static
+bool TestSuite::IsMarkedMaybe(const testing::TestInfo& test) {
+ return strncmp(test.name(), "MAYBE_", 6) == 0;
+}
+
+// static
+bool TestSuite::ShouldIgnoreFailure(const testing::TestInfo& test) {
+ if (CommandLine::ForCurrentProcess()->HasSwitch(kStrictFailureHandling))
+ return false;
+ return IsMarkedFlaky(test);
+}
+
+// static
+bool TestSuite::NonIgnoredFailures(const testing::TestInfo& test) {
+ return test.should_run() && test.result()->Failed() &&
+ !ShouldIgnoreFailure(test);
+}
+
+int TestSuite::GetTestCount(TestMatch test_match) {
+ testing::UnitTest* instance = testing::UnitTest::GetInstance();
+ int count = 0;
+
+ for (int i = 0; i < instance->total_test_case_count(); ++i) {
+ const testing::TestCase& test_case = *instance->GetTestCase(i);
+ for (int j = 0; j < test_case.total_test_count(); ++j) {
+ if (test_match(*test_case.GetTestInfo(j))) {
+ count++;
+ }
+ }
+ }
+
+ return count;
+}
+
+void TestSuite::CatchMaybeTests() {
+ testing::TestEventListeners& listeners =
+ testing::UnitTest::GetInstance()->listeners();
+ listeners.Append(new MaybeTestDisabler);
+}
+
+void TestSuite::ResetCommandLine() {
+ testing::TestEventListeners& listeners =
+ testing::UnitTest::GetInstance()->listeners();
+ listeners.Append(new TestClientInitializer);
+}
+
+// Don't add additional code to this method. Instead add it to
+// Initialize(). See bug 6436.
+int TestSuite::Run() {
+#if defined(OS_MACOSX)
+ base::mac::ScopedNSAutoreleasePool scoped_pool;
+#endif
+
+ Initialize();
+ std::string client_func =
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kTestChildProcess);
+
+ // Check to see if we are being run as a client process.
+ if (!client_func.empty())
+ return multi_process_function_list::InvokeChildProcessTest(client_func);
+#if defined(OS_IOS)
+ base::test_listener_ios::RegisterTestEndListener();
+#endif
+ int result = RUN_ALL_TESTS();
+
+ // If there are failed tests, see if we should ignore the failures.
+ if (result != 0 && GetTestCount(&TestSuite::NonIgnoredFailures) == 0)
+ result = 0;
+
+ // Display the number of flaky tests.
+ int flaky_count = GetTestCount(&TestSuite::IsMarkedFlaky);
+ if (flaky_count) {
+ printf(" YOU HAVE %d FLAKY %s\n\n", flaky_count,
+ flaky_count == 1 ? "TEST" : "TESTS");
+ }
+
+#if defined(OS_MACOSX)
+ // This MUST happen before Shutdown() since Shutdown() tears down
+ // objects (such as NotificationService::current()) that Cocoa
+ // objects use to remove themselves as observers.
+ scoped_pool.Recycle();
+#endif
+
+ Shutdown();
+
+ return result;
+}
+
+// static
+void TestSuite::UnitTestAssertHandler(const std::string& str) {
+ RAW_LOG(FATAL, str.c_str());
+}
+
+void TestSuite::SuppressErrorDialogs() {
+#if defined(OS_WIN)
+ UINT new_flags = SEM_FAILCRITICALERRORS |
+ SEM_NOGPFAULTERRORBOX |
+ SEM_NOOPENFILEERRORBOX;
+
+ // Preserve existing error mode, as discussed at
+ // http://blogs.msdn.com/oldnewthing/archive/2004/07/27/198410.aspx
+ UINT existing_flags = SetErrorMode(new_flags);
+ SetErrorMode(existing_flags | new_flags);
+
+#if defined(_DEBUG) && defined(_HAS_EXCEPTIONS) && (_HAS_EXCEPTIONS == 1)
+ // Suppress the "Debug Assertion Failed" dialog.
+ // TODO(hbono): remove this code when gtest has it.
+ // http://groups.google.com/d/topic/googletestframework/OjuwNlXy5ac/discussion
+ _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+#endif // defined(_DEBUG) && defined(_HAS_EXCEPTIONS) && (_HAS_EXCEPTIONS == 1)
+#endif // defined(OS_WIN)
+}
+
+void TestSuite::Initialize() {
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+ // Some of the app unit tests spin runloops.
+ mock_cr_app::RegisterMockCrApp();
+#endif
+
+#if defined(OS_IOS)
+ InitIOSTestMessageLoop();
+#endif // OS_IOS
+
+#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
+ InitAndroidTest();
+#else
+ // Initialize logging.
+ FilePath exe;
+ PathService::Get(base::FILE_EXE, &exe);
+ FilePath log_filename = exe.ReplaceExtension(FILE_PATH_LITERAL("log"));
+ logging::InitLogging(
+ log_filename.value().c_str(),
+ logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG,
+ logging::LOCK_LOG_FILE,
+ logging::DELETE_OLD_LOG_FILE,
+ logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);
+ // We want process and thread IDs because we may have multiple processes.
+ // Note: temporarily enabled timestamps in an effort to catch bug 6361.
+ logging::SetLogItems(true, true, true, true);
+#endif // else defined(OS_ANDROID)
+
+ CHECK(base::debug::EnableInProcessStackDumping());
+#if defined(OS_WIN)
+ // Make sure we run with high resolution timer to minimize differences
+ // between production code and test code.
+ base::Time::EnableHighResolutionTimer(true);
+#endif // defined(OS_WIN)
+
+ // In some cases, we do not want to see standard error dialogs.
+ if (!base::debug::BeingDebugged() &&
+ !CommandLine::ForCurrentProcess()->HasSwitch("show-error-dialogs")) {
+ SuppressErrorDialogs();
+ base::debug::SetSuppressDebugUI(true);
+ logging::SetLogAssertHandler(UnitTestAssertHandler);
+ }
+
+ icu_util::Initialize();
+
+ CatchMaybeTests();
+ ResetCommandLine();
+
+ TestTimeouts::Initialize();
+}
+
+void TestSuite::Shutdown() {
+}
diff --git a/src/base/test/test_suite.h b/src/base/test/test_suite.h
new file mode 100644
index 0000000..1758f86
--- /dev/null
+++ b/src/base/test/test_suite.h
@@ -0,0 +1,100 @@
+// Copyright (c) 2012 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_TEST_TEST_SUITE_H_
+#define BASE_TEST_TEST_SUITE_H_
+
+// Defines a basic test suite framework for running gtest based tests. You can
+// instantiate this class in your main function and call its Run method to run
+// any gtest based tests that are linked into your executable.
+
+#include <string>
+
+#include "base/at_exit.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace testing {
+class TestInfo;
+}
+
+namespace base {
+
+class TestSuite {
+ public:
+ // Match function used by the GetTestCount method.
+ typedef bool (*TestMatch)(const testing::TestInfo&);
+
+ TestSuite(int argc, char** argv);
+ virtual ~TestSuite();
+
+ // Returns true if the test is marked as flaky.
+ static bool IsMarkedFlaky(const testing::TestInfo& test);
+
+ // Returns true if the test is marked as failing.
+ static bool IsMarkedFailing(const testing::TestInfo& test);
+
+ // Returns true if the test is marked as "MAYBE_".
+ // When using different prefixes depending on platform, we use MAYBE_ and
+ // preprocessor directives to replace MAYBE_ with the target prefix.
+ static bool IsMarkedMaybe(const testing::TestInfo& test);
+
+ // Returns true if the test failure should be ignored.
+ static bool ShouldIgnoreFailure(const testing::TestInfo& test);
+
+ // Returns true if the test failed and the failure shouldn't be ignored.
+ static bool NonIgnoredFailures(const testing::TestInfo& test);
+
+ // Returns the number of tests where the match function returns true.
+ int GetTestCount(TestMatch test_match);
+
+ void CatchMaybeTests();
+
+ void ResetCommandLine();
+
+ int Run();
+
+ // A command-line flag that makes a test failure always result in a non-zero
+ // process exit code.
+ static const char kStrictFailureHandling[];
+
+ protected:
+ // This constructor is only accessible to specialized test suite
+ // implementations which need to control the creation of an AtExitManager
+ // instance for the duration of the test.
+ TestSuite(int argc, char** argv, bool create_at_exit_manager);
+
+ // By default fatal log messages (e.g. from DCHECKs) result in error dialogs
+ // which gum up buildbots. Use a minimalistic assert handler which just
+ // terminates the process.
+ static void UnitTestAssertHandler(const std::string& str);
+
+ // Disable crash dialogs so that it doesn't gum up the buildbot
+ virtual void SuppressErrorDialogs();
+
+ // Override these for custom initialization and shutdown handling. Use these
+ // instead of putting complex code in your constructor/destructor.
+
+ virtual void Initialize();
+ virtual void Shutdown();
+
+ // Make sure that we setup an AtExitManager so Singleton objects will be
+ // destroyed.
+ scoped_ptr<base::AtExitManager> at_exit_manager_;
+
+ private:
+ // Basic initialization for the test suite happens here.
+ void PreInitialize(int argc, char** argv, bool create_at_exit_manager);
+
+ bool initialized_command_line_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestSuite);
+};
+
+} // namespace base
+
+// TODO(brettw) remove this. This is a temporary hack to allow WebKit to compile
+// until we can update it to use "base::" (preventing a two-sided patch).
+using base::TestSuite;
+
+#endif // BASE_TEST_TEST_SUITE_H_
diff --git a/src/base/test/test_support_android.cc b/src/base/test/test_support_android.cc
new file mode 100644
index 0000000..8b512f4
--- /dev/null
+++ b/src/base/test/test_support_android.cc
@@ -0,0 +1,194 @@
+// Copyright (c) 2012 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 <stdarg.h>
+#include <string.h>
+
+#include "base/android/path_utils.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/message_loop.h"
+#include "base/message_pump_android.h"
+#include "base/path_service.h"
+#include "base/synchronization/waitable_event.h"
+
+namespace {
+
+struct RunState {
+ RunState(base::MessagePump::Delegate* delegate, int run_depth)
+ : delegate(delegate),
+ run_depth(run_depth),
+ should_quit(false) {
+ }
+
+ base::MessagePump::Delegate* delegate;
+
+ // Used to count how many Run() invocations are on the stack.
+ int run_depth;
+
+ // Used to flag that the current Run() invocation should return ASAP.
+ bool should_quit;
+};
+
+RunState* g_state = NULL;
+
+// A singleton WaitableEvent wrapper so we avoid a busy loop in
+// MessagePumpForUIStub. Other platforms use the native event loop which blocks
+// when there are no pending messages.
+class Waitable {
+ public:
+ static Waitable* GetInstance() {
+ return Singleton<Waitable>::get();
+ }
+
+ // Signals that there are more work to do.
+ void Signal() {
+ waitable_event_.Signal();
+ }
+
+ // Blocks until more work is scheduled.
+ void Block() {
+ waitable_event_.Wait();
+ }
+
+ void Quit() {
+ g_state->should_quit = true;
+ Signal();
+ }
+
+ private:
+ friend struct DefaultSingletonTraits<Waitable>;
+
+ Waitable()
+ : waitable_event_(false, false) {
+ }
+
+ base::WaitableEvent waitable_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(Waitable);
+};
+
+// The MessagePumpForUI implementation for test purpose.
+class MessagePumpForUIStub : public base::MessagePumpForUI {
+ virtual void Start(base::MessagePump::Delegate* delegate) OVERRIDE {
+ NOTREACHED() << "The Start() method shouldn't be called in test, using"
+ " Run() method should be used.";
+ }
+
+ virtual void Run(base::MessagePump::Delegate* delegate) OVERRIDE {
+ // The following was based on message_pump_glib.cc, except we're using a
+ // WaitableEvent since there are no native message loop to use.
+ RunState state(delegate, g_state ? g_state->run_depth + 1 : 1);
+
+ RunState* previous_state = g_state;
+ g_state = &state;
+
+ bool more_work_is_plausible = true;
+
+ for (;;) {
+ if (!more_work_is_plausible) {
+ Waitable::GetInstance()->Block();
+ if (g_state->should_quit)
+ break;
+ }
+
+ more_work_is_plausible = g_state->delegate->DoWork();
+ if (g_state->should_quit)
+ break;
+
+ base::TimeTicks delayed_work_time;
+ more_work_is_plausible |=
+ g_state->delegate->DoDelayedWork(&delayed_work_time);
+ if (g_state->should_quit)
+ break;
+
+ if (more_work_is_plausible)
+ continue;
+
+ more_work_is_plausible = g_state->delegate->DoIdleWork();
+ if (g_state->should_quit)
+ break;
+
+ more_work_is_plausible |= !delayed_work_time.is_null();
+ }
+
+ g_state = previous_state;
+ }
+
+ virtual void Quit() OVERRIDE {
+ Waitable::GetInstance()->Quit();
+ }
+
+ virtual void ScheduleWork() OVERRIDE {
+ Waitable::GetInstance()->Signal();
+ }
+
+ virtual void ScheduleDelayedWork(
+ const base::TimeTicks& delayed_work_time) OVERRIDE {
+ Waitable::GetInstance()->Signal();
+ }
+
+ protected:
+ virtual ~MessagePumpForUIStub() {}
+};
+
+base::MessagePump* CreateMessagePumpForUIStub() {
+ return new MessagePumpForUIStub();
+};
+
+// Provides the test path for DIR_MODULE and DIR_ANDROID_APP_DATA.
+bool GetTestProviderPath(int key, FilePath* result) {
+ switch (key) {
+ case base::DIR_MODULE: {
+ return base::android::GetExternalStorageDirectory(result);
+ }
+ case base::DIR_ANDROID_APP_DATA: {
+ // For tests, app data is put in external storage.
+ return base::android::GetExternalStorageDirectory(result);
+ }
+ default:
+ return false;
+ }
+}
+
+void InitPathProvider(int key) {
+ FilePath path;
+ // If failed to override the key, that means the way has not been registered.
+ if (GetTestProviderPath(key, &path) && !PathService::Override(key, path))
+ PathService::RegisterProvider(&GetTestProviderPath, key, key + 1);
+}
+
+} // namespace
+
+namespace base {
+
+void InitAndroidTestLogging() {
+ logging::InitLogging(NULL,
+ logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
+ logging::DONT_LOCK_LOG_FILE,
+ logging::DELETE_OLD_LOG_FILE,
+ logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);
+ // To view log output with IDs and timestamps use "adb logcat -v threadtime".
+ logging::SetLogItems(false, // Process ID
+ false, // Thread ID
+ false, // Timestamp
+ false); // Tick count
+}
+
+void InitAndroidTestPaths() {
+ InitPathProvider(DIR_MODULE);
+ InitPathProvider(DIR_ANDROID_APP_DATA);
+}
+
+void InitAndroidTestMessageLoop() {
+ MessageLoop::InitMessagePumpForUIFactory(&CreateMessagePumpForUIStub);
+}
+
+void InitAndroidTest() {
+ InitAndroidTestLogging();
+ InitAndroidTestPaths();
+ InitAndroidTestMessageLoop();
+}
+} // namespace base
diff --git a/src/base/test/test_support_android.h b/src/base/test/test_support_android.h
new file mode 100644
index 0000000..062785e
--- /dev/null
+++ b/src/base/test/test_support_android.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 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_TEST_TEST_SUPPORT_ANDROID_H_
+#define BASE_TEST_TEST_SUPPORT_ANDROID_H_
+
+#include "base/base_export.h"
+
+namespace base {
+
+// Init logging for tests on Android. Logs will be output into Android's logcat.
+BASE_EXPORT void InitAndroidTestLogging();
+
+// Init path providers for tests on Android.
+BASE_EXPORT void InitAndroidTestPaths();
+
+// Init the message loop for tests on Android.
+BASE_EXPORT void InitAndroidTestMessageLoop();
+
+// Do all of the initializations above.
+BASE_EXPORT void InitAndroidTest();
+
+} // namespace base
+
+#endif // BASE_TEST_TEST_SUPPORT_ANDROID_H_
diff --git a/src/base/test/test_support_ios.h b/src/base/test/test_support_ios.h
new file mode 100644
index 0000000..35b5b19
--- /dev/null
+++ b/src/base/test/test_support_ios.h
@@ -0,0 +1,15 @@
+// Copyright (c) 2012 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_TEST_TEST_SUPPORT_IOS_H_
+#define BASE_TEST_TEST_SUPPORT_IOS_H_
+
+namespace base {
+
+// Init the message loop for tests on iOS.
+void InitIOSTestMessageLoop();
+
+} // namespace base
+
+#endif // BASE_TEST_TEST_SUPPORT_IOS_H_
diff --git a/src/base/test/test_support_ios.mm b/src/base/test/test_support_ios.mm
new file mode 100644
index 0000000..01b5158
--- /dev/null
+++ b/src/base/test/test_support_ios.mm
@@ -0,0 +1,23 @@
+// Copyright (c) 2012 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/message_loop.h"
+#include "base/message_pump_default.h"
+
+namespace {
+
+base::MessagePump* CreateMessagePumpForUIForTests() {
+ // A default MessagePump will do quite nicely in tests.
+ return new base::MessagePumpDefault();
+}
+
+} // namespace
+
+namespace base {
+
+void InitIOSTestMessageLoop() {
+ MessageLoop::InitMessagePumpForUIFactory(&CreateMessagePumpForUIForTests);
+}
+
+} // namespace base
diff --git a/src/base/test/test_switches.cc b/src/base/test/test_switches.cc
new file mode 100644
index 0000000..79fbad1
--- /dev/null
+++ b/src/base/test/test_switches.cc
@@ -0,0 +1,12 @@
+// Copyright (c) 2012 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/test/test_switches.h"
+
+// Time (in milliseconds) that the tests should wait before timing out.
+// TODO(phajdan.jr): Clean up the switch names.
+const char switches::kTestLargeTimeout[] = "test-large-timeout";
+const char switches::kTestTinyTimeout[] = "test-tiny-timeout";
+const char switches::kUiTestActionTimeout[] = "ui-test-action-timeout";
+const char switches::kUiTestActionMaxTimeout[] = "ui-test-action-max-timeout";
diff --git a/src/base/test/test_switches.h b/src/base/test/test_switches.h
new file mode 100644
index 0000000..a5665d4
--- /dev/null
+++ b/src/base/test/test_switches.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2012 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_TEST_TEST_SWITCHES_H_
+#define BASE_TEST_TEST_SWITCHES_H_
+
+namespace switches {
+
+// All switches in alphabetical order. The switches should be documented
+// alongside the definition of their values in the .cc file.
+extern const char kTestLargeTimeout[];
+extern const char kTestTinyTimeout[];
+extern const char kUiTestActionTimeout[];
+extern const char kUiTestActionMaxTimeout[];
+
+} // namespace switches
+
+#endif // BASE_TEST_TEST_SWITCHES_H_
diff --git a/src/base/test/test_timeouts.cc b/src/base/test/test_timeouts.cc
new file mode 100644
index 0000000..b3fb968
--- /dev/null
+++ b/src/base/test/test_timeouts.cc
@@ -0,0 +1,87 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_timeouts.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "base/test/test_switches.h"
+
+namespace {
+
+#ifdef ADDRESS_SANITIZER
+static const int kTimeoutMultiplier = 2;
+#else
+static const int kTimeoutMultiplier = 1;
+#endif
+
+// Sets value to the greatest of:
+// 1) value's current value multiplied by kTimeoutMultiplier (assuming
+// InitializeTimeout is called only once per value).
+// 2) min_value.
+// 3) the numerical value given by switch_name on the command line multiplied
+// by kTimeoutMultiplier.
+void InitializeTimeout(const char* switch_name, int min_value, int* value) {
+ DCHECK(value);
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switch_name)) {
+ std::string string_value(
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switch_name));
+ int timeout;
+ base::StringToInt(string_value, &timeout);
+ *value = std::max(*value, timeout);
+ }
+ *value *= kTimeoutMultiplier;
+ *value = std::max(*value, min_value);
+}
+
+// Sets value to the greatest of:
+// 1) value's current value multiplied by kTimeoutMultiplier.
+// 2) 0
+// 3) the numerical value given by switch_name on the command line multiplied
+// by kTimeoutMultiplier.
+void InitializeTimeout(const char* switch_name, int* value) {
+ InitializeTimeout(switch_name, 0, value);
+}
+
+} // namespace
+
+// static
+bool TestTimeouts::initialized_ = false;
+
+// The timeout values should increase in the order they appear in this block.
+// static
+int TestTimeouts::tiny_timeout_ms_ = 100;
+int TestTimeouts::action_timeout_ms_ = 10000;
+#ifndef NDEBUG
+int TestTimeouts::action_max_timeout_ms_ = 45000;
+int TestTimeouts::large_test_timeout_ms_ = 10 * 60 * 1000;
+#else
+int TestTimeouts::action_max_timeout_ms_ = 30000;
+int TestTimeouts::large_test_timeout_ms_ = 10 * 45 * 1000;
+#endif // NDEBUG
+
+// static
+void TestTimeouts::Initialize() {
+ if (initialized_) {
+ NOTREACHED();
+ return;
+ }
+ initialized_ = true;
+
+ // Note that these timeouts MUST be initialized in the correct order as
+ // per the CHECKS below.
+ InitializeTimeout(switches::kTestTinyTimeout, &tiny_timeout_ms_);
+ InitializeTimeout(switches::kUiTestActionTimeout, tiny_timeout_ms_,
+ &action_timeout_ms_);
+ InitializeTimeout(switches::kUiTestActionMaxTimeout, action_timeout_ms_,
+ &action_max_timeout_ms_);
+ InitializeTimeout(switches::kTestLargeTimeout, action_max_timeout_ms_,
+ &large_test_timeout_ms_);
+
+ // The timeout values should be increasing in the right order.
+ CHECK(tiny_timeout_ms_ <= action_timeout_ms_);
+ CHECK(action_timeout_ms_ <= action_max_timeout_ms_);
+ CHECK(action_max_timeout_ms_ <= large_test_timeout_ms_);
+}
diff --git a/src/base/test/test_timeouts.h b/src/base/test/test_timeouts.h
new file mode 100644
index 0000000..0c4587e
--- /dev/null
+++ b/src/base/test/test_timeouts.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 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_TEST_TEST_TIMEOUTS_H_
+#define BASE_TEST_TEST_TIMEOUTS_H_
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/time.h"
+
+// Returns common timeouts to use in tests. Makes it possible to adjust
+// the timeouts for different environments (like Valgrind).
+class TestTimeouts {
+ public:
+ // Initializes the timeouts. Non thread-safe. Should be called exactly once
+ // by the test suite.
+ static void Initialize();
+
+ // Timeout for actions that are expected to finish "almost instantly".
+ static base::TimeDelta tiny_timeout() {
+ DCHECK(initialized_);
+ return base::TimeDelta::FromMilliseconds(tiny_timeout_ms_);
+ }
+
+ // Timeout to wait for something to happen. If you are not sure
+ // which timeout to use, this is the one you want.
+ static base::TimeDelta action_timeout() {
+ DCHECK(initialized_);
+ return base::TimeDelta::FromMilliseconds(action_timeout_ms_);
+ }
+
+ // Timeout longer than the above, but still suitable to use
+ // multiple times in a single test. Use if the timeout above
+ // is not sufficient.
+ static base::TimeDelta action_max_timeout() {
+ DCHECK(initialized_);
+ return base::TimeDelta::FromMilliseconds(action_max_timeout_ms_);
+ }
+
+ // Timeout for a large test that may take a few minutes to run.
+ static base::TimeDelta large_test_timeout() {
+ DCHECK(initialized_);
+ return base::TimeDelta::FromMilliseconds(large_test_timeout_ms_);
+ }
+
+ private:
+ static bool initialized_;
+
+ static int tiny_timeout_ms_;
+ static int action_timeout_ms_;
+ static int action_max_timeout_ms_;
+ static int large_test_timeout_ms_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(TestTimeouts);
+};
+
+#endif // BASE_TEST_TEST_TIMEOUTS_H_
diff --git a/src/base/test/thread_test_helper.cc b/src/base/test/thread_test_helper.cc
new file mode 100644
index 0000000..a91517d
--- /dev/null
+++ b/src/base/test/thread_test_helper.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/thread_test_helper.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace base {
+
+ThreadTestHelper::ThreadTestHelper(MessageLoopProxy* target_thread)
+ : test_result_(false),
+ target_thread_(target_thread),
+ done_event_(false, false) {
+}
+
+bool ThreadTestHelper::Run() {
+ if (!target_thread_->PostTask(
+ FROM_HERE, base::Bind(&ThreadTestHelper::RunInThread, this))) {
+ return false;
+ }
+ base::ThreadRestrictions::ScopedAllowWait allow_wait;
+ done_event_.Wait();
+ return test_result_;
+}
+
+void ThreadTestHelper::RunTest() { set_test_result(true); }
+
+ThreadTestHelper::~ThreadTestHelper() {}
+
+void ThreadTestHelper::RunInThread() {
+ RunTest();
+ done_event_.Signal();
+}
+
+} // namespace base
diff --git a/src/base/test/thread_test_helper.h b/src/base/test/thread_test_helper.h
new file mode 100644
index 0000000..961982c
--- /dev/null
+++ b/src/base/test/thread_test_helper.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_THREAD_TEST_HELPER_H_
+#define BASE_TEST_THREAD_TEST_HELPER_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop_proxy.h"
+#include "base/synchronization/waitable_event.h"
+
+namespace base {
+
+// Helper class that executes code on a given thread while blocking on the
+// invoking thread. To use, derive from this class and overwrite RunTest. An
+// alternative use of this class is to use it directly. It will then block
+// until all pending tasks on a given thread have been executed.
+class ThreadTestHelper : public RefCountedThreadSafe<ThreadTestHelper> {
+ public:
+ explicit ThreadTestHelper(MessageLoopProxy* target_thread);
+
+ // True if RunTest() was successfully executed on the target thread.
+ bool Run() WARN_UNUSED_RESULT;
+
+ virtual void RunTest();
+
+ protected:
+ friend class RefCountedThreadSafe<ThreadTestHelper>;
+
+ virtual ~ThreadTestHelper();
+
+ // Use this method to store the result of RunTest().
+ void set_test_result(bool test_result) { test_result_ = test_result; }
+
+ private:
+ void RunInThread();
+
+ bool test_result_;
+ scoped_refptr<MessageLoopProxy> target_thread_;
+ WaitableEvent done_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadTestHelper);
+};
+
+} // namespace base
+
+#endif // BASE_TEST_THREAD_TEST_HELPER_H_
diff --git a/src/base/test/time_helpers.cc b/src/base/test/time_helpers.cc
new file mode 100644
index 0000000..3914dc2
--- /dev/null
+++ b/src/base/test/time_helpers.cc
@@ -0,0 +1,144 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/test/time_helpers.h"
+
+#include <unicode/ucal.h>
+#include <unicode/uloc.h>
+#include <unicode/ustring.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "base/time.h"
+
+// Negative statuses are considered warnings, so this only fails if the status
+// value is greater than zero.
+#define UCHECK(status) DCHECK_GE(U_ZERO_ERROR, status) << __FUNCTION__ << ": "
+
+namespace base {
+namespace test {
+namespace time_helpers {
+
+namespace {
+base::Time UDateToTime(UDate udate) {
+ return base::Time::UnixEpoch() +
+ base::TimeDelta::FromMicroseconds(static_cast<int64_t>(
+ udate * base::Time::kMicrosecondsPerMillisecond));
+}
+
+const char* ToMonthString(int month) {
+ switch (month) {
+ case 1:
+ return "Jan";
+ case 2:
+ return "Feb";
+ case 3:
+ return "Mar";
+ case 4:
+ return "Apr";
+ case 5:
+ return "May";
+ case 6:
+ return "Jun";
+ case 7:
+ return "Jul";
+ case 8:
+ return "Aug";
+ case 9:
+ return "Sep";
+ case 10:
+ return "Oct";
+ case 11:
+ return "Nov";
+ case 12:
+ return "Dec";
+ }
+
+ return "UNK";
+}
+} // namespace
+
+base::Time FieldsToTime(TimeZone timezone,
+ int year,
+ int month,
+ int date,
+ int hour,
+ int minute,
+ int second) {
+ UErrorCode status = U_ZERO_ERROR;
+ UChar timezone_buffer[32] = {0};
+ UChar* timezone_uname = NULL;
+ switch (timezone) {
+ case kTimeZonePacific:
+ u_strFromUTF8(timezone_buffer, arraysize(timezone_buffer), NULL,
+ "America/Los_Angeles", -1, &status);
+ timezone_uname = timezone_buffer;
+ break;
+ case kTimeZoneGMT:
+ u_strFromUTF8(timezone_buffer, arraysize(timezone_buffer), NULL,
+ "Etc/GMT", -1, &status);
+ timezone_uname = timezone_buffer;
+ case kTimeZoneLocal:
+ // Leave NULL.
+ break;
+ }
+
+ UCHECK(status);
+ UCalendar* calendar =
+ ucal_open(timezone_uname, -1, uloc_getDefault(), UCAL_DEFAULT, &status);
+ DCHECK(calendar);
+ UCHECK(status);
+ ucal_setMillis(calendar, 0, &status); // Clear the calendar.
+ UCHECK(status);
+ ucal_setDateTime(calendar, year, month - 1 + UCAL_JANUARY, date, hour, minute,
+ second, &status);
+ UCHECK(status);
+ UDate udate = ucal_getMillis(calendar, &status);
+ UCHECK(status);
+ ucal_close(calendar);
+ return UDateToTime(udate);
+}
+
+base::Time TestDateToTime(TimeZone timezone) {
+ return FieldsToTime(timezone,
+ 2007, // year
+ 10, // month
+ 15, // date
+ 12, // hour
+ 45, // minute
+ 0); // second
+}
+
+std::string TimeFormatUTC(base::Time time) {
+ Time::Exploded exploded;
+ time.UTCExplode(&exploded);
+ return base::StringPrintf("%s %02d %02d:%02d:%02d %04d",
+ ToMonthString(exploded.month),
+ exploded.day_of_month, exploded.hour,
+ exploded.minute, exploded.second, exploded.year);
+}
+
+std::string TimeFormatLocal(base::Time time) {
+ Time::Exploded exploded;
+ time.LocalExplode(&exploded);
+ return base::StringPrintf("%s %02d %02d:%02d:%02d %04d",
+ ToMonthString(exploded.month),
+ exploded.day_of_month, exploded.hour,
+ exploded.minute, exploded.second, exploded.year);
+}
+
+} // namespace time_helpers
+} // namespace test
+} // namespace base
diff --git a/src/base/test/time_helpers.h b/src/base/test/time_helpers.h
new file mode 100644
index 0000000..dad3ec6
--- /dev/null
+++ b/src/base/test/time_helpers.h
@@ -0,0 +1,68 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// These functions use ICU to generate test values based on specific and local
+// time zones.
+
+#ifndef BASE_TEST_TIME_HELPERS_H_
+#define BASE_TEST_TIME_HELPERS_H_
+
+#include <string>
+
+#include "base/time.h"
+
+namespace base {
+namespace test {
+namespace time_helpers {
+
+// Some TimeZone values that can be used in tests.
+enum TimeZone {
+ kTimeZoneGMT,
+ kTimeZoneLocal,
+ kTimeZonePacific,
+};
+
+// Converts a set of fields in a timezone to a time.
+// |timezone| is one of the enumerated timezones.
+// |year| is a full 4-digit year
+// |month| is a 1-based month (1 = January)
+// |date| is a 1-based day-of-month (first day of month is 1)
+// |hour| is the 0-based 24-hour clock hour-of-the-day
+// (midnight is 0, noon is 12, 3pm is 15)
+// |minute| is the 0-based clock minute-of-the-hour (0-59)
+// |second| is the 0-based clock second-of-the-minute (0-59)
+base::Time FieldsToTime(TimeZone timezone,
+ int year,
+ int month,
+ int date,
+ int hour,
+ int minute,
+ int second);
+
+// Defines a standard date to use in tests.
+// Mon, Oct 15 12:45:00 PDT 2007
+base::Time TestDateToTime(TimeZone timezone);
+
+// Formats the given time into a UTC something parsable by
+// base::Time::FromString().
+std::string TimeFormatUTC(base::Time time);
+
+// Formats the given time into a local something parsable by
+// base::Time::FromString().
+std::string TimeFormatLocal(base::Time time);
+
+} // namespace time_helpers
+} // namespace test
+} // namespace base
+#endif // BASE_TEST_TIME_HELPERS_H_
diff --git a/src/base/test/trace_event_analyzer.cc b/src/base/test/trace_event_analyzer.cc
new file mode 100644
index 0000000..d4ebb04
--- /dev/null
+++ b/src/base/test/trace_event_analyzer.cc
@@ -0,0 +1,966 @@
+// Copyright (c) 2012 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/test/trace_event_analyzer.h"
+
+#include <algorithm>
+#include <math.h>
+#include <set>
+
+#include "base/json/json_reader.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+
+namespace trace_analyzer {
+
+// TraceEvent
+
+TraceEvent::TraceEvent()
+ : thread(0, 0),
+ timestamp(0),
+ phase(TRACE_EVENT_PHASE_BEGIN),
+ other_event(NULL) {
+}
+
+TraceEvent::~TraceEvent() {
+}
+
+bool TraceEvent::SetFromJSON(const base::Value* event_value) {
+ if (event_value->GetType() != base::Value::TYPE_DICTIONARY) {
+ LOG(ERROR) << "Value must be TYPE_DICTIONARY";
+ return false;
+ }
+ const base::DictionaryValue* dictionary =
+ static_cast<const base::DictionaryValue*>(event_value);
+
+ std::string phase_str;
+ const base::DictionaryValue* args = NULL;
+
+ if (!dictionary->GetString("ph", &phase_str)) {
+ LOG(ERROR) << "ph is missing from TraceEvent JSON";
+ return false;
+ }
+
+ phase = *phase_str.data();
+
+ bool require_origin = (phase != TRACE_EVENT_PHASE_METADATA);
+ bool require_id = (phase == TRACE_EVENT_PHASE_ASYNC_BEGIN ||
+ phase == TRACE_EVENT_PHASE_ASYNC_STEP ||
+ phase == TRACE_EVENT_PHASE_ASYNC_END);
+
+ if (require_origin && !dictionary->GetInteger("pid", &thread.process_id)) {
+ LOG(ERROR) << "pid is missing from TraceEvent JSON";
+ return false;
+ }
+ if (require_origin && !dictionary->GetInteger("tid", &thread.thread_id)) {
+ LOG(ERROR) << "tid is missing from TraceEvent JSON";
+ return false;
+ }
+ if (require_origin && !dictionary->GetDouble("ts", ×tamp)) {
+ LOG(ERROR) << "ts is missing from TraceEvent JSON";
+ return false;
+ }
+ if (!dictionary->GetString("cat", &category)) {
+ LOG(ERROR) << "cat is missing from TraceEvent JSON";
+ return false;
+ }
+ if (!dictionary->GetString("name", &name)) {
+ LOG(ERROR) << "name is missing from TraceEvent JSON";
+ return false;
+ }
+ if (!dictionary->GetDictionary("args", &args)) {
+ LOG(ERROR) << "args is missing from TraceEvent JSON";
+ return false;
+ }
+ if (require_id && !dictionary->GetString("id", &id)) {
+ LOG(ERROR) << "id is missing from ASYNC_BEGIN/ASYNC_END TraceEvent JSON";
+ return false;
+ }
+
+ // For each argument, copy the type and create a trace_analyzer::TraceValue.
+ base::DictionaryValue::key_iterator keyi = args->begin_keys();
+ for (; keyi != args->end_keys(); ++keyi) {
+ std::string str;
+ bool boolean = false;
+ int int_num = 0;
+ double double_num = 0.0;
+ const Value* value = NULL;
+ if (args->GetWithoutPathExpansion(*keyi, &value)) {
+ if (value->GetAsString(&str))
+ arg_strings[*keyi] = str;
+ else if (value->GetAsInteger(&int_num))
+ arg_numbers[*keyi] = static_cast<double>(int_num);
+ else if (value->GetAsBoolean(&boolean))
+ arg_numbers[*keyi] = static_cast<double>(boolean ? 1 : 0);
+ else if (value->GetAsDouble(&double_num))
+ arg_numbers[*keyi] = double_num;
+ else {
+ LOG(ERROR) << "Value type of argument is not supported: " <<
+ static_cast<int>(value->GetType());
+ return false; // Invalid trace event JSON format.
+ }
+ }
+ }
+
+ return true;
+}
+
+double TraceEvent::GetAbsTimeToOtherEvent() const {
+ return fabs(other_event->timestamp - timestamp);
+}
+
+bool TraceEvent::GetArgAsString(const std::string& name,
+ std::string* arg) const {
+ std::map<std::string, std::string>::const_iterator i = arg_strings.find(name);
+ if (i != arg_strings.end()) {
+ *arg = i->second;
+ return true;
+ }
+ return false;
+}
+
+bool TraceEvent::GetArgAsNumber(const std::string& name,
+ double* arg) const {
+ std::map<std::string, double>::const_iterator i = arg_numbers.find(name);
+ if (i != arg_numbers.end()) {
+ *arg = i->second;
+ return true;
+ }
+ return false;
+}
+
+bool TraceEvent::HasStringArg(const std::string& name) const {
+ return (arg_strings.find(name) != arg_strings.end());
+}
+
+bool TraceEvent::HasNumberArg(const std::string& name) const {
+ return (arg_numbers.find(name) != arg_numbers.end());
+}
+
+std::string TraceEvent::GetKnownArgAsString(const std::string& name) const {
+ std::string arg_string;
+ if (GetArgAsString(name, &arg_string))
+ return arg_string;
+ NOTREACHED();
+ return "";
+}
+
+double TraceEvent::GetKnownArgAsDouble(const std::string& name) const {
+ double arg_double;
+ if (GetArgAsNumber(name, &arg_double))
+ return arg_double;
+ NOTREACHED();
+ return 0;
+}
+
+int TraceEvent::GetKnownArgAsInt(const std::string& name) const {
+ double arg_double;
+ if (GetArgAsNumber(name, &arg_double))
+ return static_cast<int>(arg_double);
+ NOTREACHED();
+ return 0;
+}
+
+bool TraceEvent::GetKnownArgAsBool(const std::string& name) const {
+ double arg_double;
+ if (GetArgAsNumber(name, &arg_double))
+ return (arg_double != 0.0);
+ NOTREACHED();
+ return false;
+}
+
+// QueryNode
+
+QueryNode::QueryNode(const Query& query) : query_(query) {
+}
+
+QueryNode::~QueryNode() {
+}
+
+// Query
+
+Query::Query(TraceEventMember member)
+ : type_(QUERY_EVENT_MEMBER),
+ operator_(OP_INVALID),
+ member_(member),
+ number_(0),
+ is_pattern_(false) {
+}
+
+Query::Query(TraceEventMember member, const std::string& arg_name)
+ : type_(QUERY_EVENT_MEMBER),
+ operator_(OP_INVALID),
+ member_(member),
+ number_(0),
+ string_(arg_name),
+ is_pattern_(false) {
+}
+
+Query::Query(const Query& query)
+ : type_(query.type_),
+ operator_(query.operator_),
+ left_(query.left_),
+ right_(query.right_),
+ member_(query.member_),
+ number_(query.number_),
+ string_(query.string_),
+ is_pattern_(query.is_pattern_) {
+}
+
+Query::~Query() {
+}
+
+Query Query::String(const std::string& str) {
+ return Query(str);
+}
+
+Query Query::Double(double num) {
+ return Query(num);
+}
+
+Query Query::Int(int32 num) {
+ return Query(static_cast<double>(num));
+}
+
+Query Query::Uint(uint32 num) {
+ return Query(static_cast<double>(num));
+}
+
+Query Query::Bool(bool boolean) {
+ return Query(boolean ? 1.0 : 0.0);
+}
+
+Query Query::Phase(char phase) {
+ return Query(static_cast<double>(phase));
+}
+
+Query Query::Pattern(const std::string& pattern) {
+ Query query(pattern);
+ query.is_pattern_ = true;
+ return query;
+}
+
+bool Query::Evaluate(const TraceEvent& event) const {
+ // First check for values that can convert to bool.
+
+ // double is true if != 0:
+ double bool_value = 0.0;
+ bool is_bool = GetAsDouble(event, &bool_value);
+ if (is_bool)
+ return (bool_value != 0.0);
+
+ // string is true if it is non-empty:
+ std::string str_value;
+ bool is_str = GetAsString(event, &str_value);
+ if (is_str)
+ return !str_value.empty();
+
+ DCHECK(type_ == QUERY_BOOLEAN_OPERATOR)
+ << "Invalid query: missing boolean expression";
+ DCHECK(left_.get() && (right_.get() || is_unary_operator()));
+
+ if (is_comparison_operator()) {
+ DCHECK(left().is_value() && right().is_value())
+ << "Invalid query: comparison operator used between event member and "
+ "value.";
+ bool compare_result = false;
+ if (CompareAsDouble(event, &compare_result))
+ return compare_result;
+ else if (CompareAsString(event, &compare_result))
+ return compare_result;
+ return false;
+ }
+ // It's a logical operator.
+ switch (operator_) {
+ case OP_AND:
+ return left().Evaluate(event) && right().Evaluate(event);
+ case OP_OR:
+ return left().Evaluate(event) || right().Evaluate(event);
+ case OP_NOT:
+ return !left().Evaluate(event);
+ default:
+ NOTREACHED();
+ }
+
+ NOTREACHED();
+ return false;
+}
+
+bool Query::CompareAsDouble(const TraceEvent& event, bool* result) const {
+ double lhs, rhs;
+ if (!left().GetAsDouble(event, &lhs) || !right().GetAsDouble(event, &rhs))
+ return false;
+ switch (operator_) {
+ case OP_EQ:
+ *result = (lhs == rhs);
+ return true;
+ case OP_NE:
+ *result = (lhs != rhs);
+ return true;
+ case OP_LT:
+ *result = (lhs < rhs);
+ return true;
+ case OP_LE:
+ *result = (lhs <= rhs);
+ return true;
+ case OP_GT:
+ *result = (lhs > rhs);
+ return true;
+ case OP_GE:
+ *result = (lhs >= rhs);
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+ return true;
+}
+
+bool Query::CompareAsString(const TraceEvent& event, bool* result) const {
+ std::string lhs, rhs;
+ if (!left().GetAsString(event, &lhs) || !right().GetAsString(event, &rhs))
+ return false;
+ switch (operator_) {
+ case OP_EQ:
+ if (right().is_pattern_)
+ *result = MatchPattern(lhs, rhs);
+ else if (left().is_pattern_)
+ *result = MatchPattern(rhs, lhs);
+ else
+ *result = (lhs == rhs);
+ return true;
+ case OP_NE:
+ if (right().is_pattern_)
+ *result = !MatchPattern(lhs, rhs);
+ else if (left().is_pattern_)
+ *result = !MatchPattern(rhs, lhs);
+ else
+ *result = (lhs != rhs);
+ return true;
+ case OP_LT:
+ *result = (lhs < rhs);
+ return true;
+ case OP_LE:
+ *result = (lhs <= rhs);
+ return true;
+ case OP_GT:
+ *result = (lhs > rhs);
+ return true;
+ case OP_GE:
+ *result = (lhs >= rhs);
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+ return true;
+}
+
+bool Query::EvaluateArithmeticOperator(const TraceEvent& event,
+ double* num) const {
+ DCHECK(type_ == QUERY_ARITHMETIC_OPERATOR);
+ DCHECK(left_.get() && (right_.get() || is_unary_operator()));
+
+ double lhs = 0, rhs = 0;
+ if (!left().GetAsDouble(event, &lhs))
+ return false;
+ if (!is_unary_operator() && !right().GetAsDouble(event, &rhs))
+ return false;
+
+ switch (operator_) {
+ case OP_ADD:
+ *num = lhs + rhs;
+ return true;
+ case OP_SUB:
+ *num = lhs - rhs;
+ return true;
+ case OP_MUL:
+ *num = lhs * rhs;
+ return true;
+ case OP_DIV:
+ *num = lhs / rhs;
+ return true;
+ case OP_MOD:
+ *num = static_cast<double>(static_cast<int64>(lhs) %
+ static_cast<int64>(rhs));
+ return true;
+ case OP_NEGATE:
+ *num = -lhs;
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool Query::GetAsDouble(const TraceEvent& event, double* num) const {
+ switch (type_) {
+ case QUERY_ARITHMETIC_OPERATOR:
+ return EvaluateArithmeticOperator(event, num);
+ case QUERY_EVENT_MEMBER:
+ return GetMemberValueAsDouble(event, num);
+ case QUERY_NUMBER:
+ *num = number_;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool Query::GetAsString(const TraceEvent& event, std::string* str) const {
+ switch (type_) {
+ case QUERY_EVENT_MEMBER:
+ return GetMemberValueAsString(event, str);
+ case QUERY_STRING:
+ *str = string_;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool Query::GetMemberValueAsDouble(const TraceEvent& event,
+ double* num) const {
+ DCHECK(type_ == QUERY_EVENT_MEMBER);
+
+ // This could be a request for a member of |event| or a member of |event|'s
+ // associated event. Store the target event in the_event:
+ const TraceEvent* the_event = (member_ < OTHER_PID) ?
+ &event : event.other_event;
+
+ // Request for member of associated event, but there is no associated event.
+ if (!the_event)
+ return false;
+
+ switch (member_) {
+ case EVENT_PID:
+ case OTHER_PID:
+ *num = static_cast<double>(the_event->thread.process_id);
+ return true;
+ case EVENT_TID:
+ case OTHER_TID:
+ *num = static_cast<double>(the_event->thread.thread_id);
+ return true;
+ case EVENT_TIME:
+ case OTHER_TIME:
+ *num = the_event->timestamp;
+ return true;
+ case EVENT_DURATION:
+ if (the_event->has_other_event()) {
+ *num = the_event->GetAbsTimeToOtherEvent();
+ return true;
+ }
+ return false;
+ case EVENT_PHASE:
+ case OTHER_PHASE:
+ *num = static_cast<double>(the_event->phase);
+ return true;
+ case EVENT_HAS_STRING_ARG:
+ case OTHER_HAS_STRING_ARG:
+ *num = (the_event->HasStringArg(string_) ? 1.0 : 0.0);
+ return true;
+ case EVENT_HAS_NUMBER_ARG:
+ case OTHER_HAS_NUMBER_ARG:
+ *num = (the_event->HasNumberArg(string_) ? 1.0 : 0.0);
+ return true;
+ case EVENT_ARG:
+ case OTHER_ARG: {
+ // Search for the argument name and return its value if found.
+ std::map<std::string, double>::const_iterator num_i =
+ the_event->arg_numbers.find(string_);
+ if (num_i == the_event->arg_numbers.end())
+ return false;
+ *num = num_i->second;
+ return true;
+ }
+ case EVENT_HAS_OTHER:
+ // return 1.0 (true) if the other event exists
+ *num = event.other_event ? 1.0 : 0.0;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool Query::GetMemberValueAsString(const TraceEvent& event,
+ std::string* str) const {
+ DCHECK(type_ == QUERY_EVENT_MEMBER);
+
+ // This could be a request for a member of |event| or a member of |event|'s
+ // associated event. Store the target event in the_event:
+ const TraceEvent* the_event = (member_ < OTHER_PID) ?
+ &event : event.other_event;
+
+ // Request for member of associated event, but there is no associated event.
+ if (!the_event)
+ return false;
+
+ switch (member_) {
+ case EVENT_CATEGORY:
+ case OTHER_CATEGORY:
+ *str = the_event->category;
+ return true;
+ case EVENT_NAME:
+ case OTHER_NAME:
+ *str = the_event->name;
+ return true;
+ case EVENT_ID:
+ case OTHER_ID:
+ *str = the_event->id;
+ return true;
+ case EVENT_ARG:
+ case OTHER_ARG: {
+ // Search for the argument name and return its value if found.
+ std::map<std::string, std::string>::const_iterator str_i =
+ the_event->arg_strings.find(string_);
+ if (str_i == the_event->arg_strings.end())
+ return false;
+ *str = str_i->second;
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
+Query::Query(const std::string& str)
+ : type_(QUERY_STRING),
+ operator_(OP_INVALID),
+ member_(EVENT_INVALID),
+ number_(0),
+ string_(str),
+ is_pattern_(false) {
+}
+
+Query::Query(double num)
+ : type_(QUERY_NUMBER),
+ operator_(OP_INVALID),
+ member_(EVENT_INVALID),
+ number_(num),
+ is_pattern_(false) {
+}
+const Query& Query::left() const {
+ return left_->query();
+}
+
+const Query& Query::right() const {
+ return right_->query();
+}
+
+Query Query::operator==(const Query& rhs) const {
+ return Query(*this, rhs, OP_EQ);
+}
+
+Query Query::operator!=(const Query& rhs) const {
+ return Query(*this, rhs, OP_NE);
+}
+
+Query Query::operator<(const Query& rhs) const {
+ return Query(*this, rhs, OP_LT);
+}
+
+Query Query::operator<=(const Query& rhs) const {
+ return Query(*this, rhs, OP_LE);
+}
+
+Query Query::operator>(const Query& rhs) const {
+ return Query(*this, rhs, OP_GT);
+}
+
+Query Query::operator>=(const Query& rhs) const {
+ return Query(*this, rhs, OP_GE);
+}
+
+Query Query::operator&&(const Query& rhs) const {
+ return Query(*this, rhs, OP_AND);
+}
+
+Query Query::operator||(const Query& rhs) const {
+ return Query(*this, rhs, OP_OR);
+}
+
+Query Query::operator!() const {
+ return Query(*this, OP_NOT);
+}
+
+Query Query::operator+(const Query& rhs) const {
+ return Query(*this, rhs, OP_ADD);
+}
+
+Query Query::operator-(const Query& rhs) const {
+ return Query(*this, rhs, OP_SUB);
+}
+
+Query Query::operator*(const Query& rhs) const {
+ return Query(*this, rhs, OP_MUL);
+}
+
+Query Query::operator/(const Query& rhs) const {
+ return Query(*this, rhs, OP_DIV);
+}
+
+Query Query::operator%(const Query& rhs) const {
+ return Query(*this, rhs, OP_MOD);
+}
+
+Query Query::operator-() const {
+ return Query(*this, OP_NEGATE);
+}
+
+
+Query::Query(const Query& left, const Query& right, Operator binary_op)
+ : operator_(binary_op),
+ left_(new QueryNode(left)),
+ right_(new QueryNode(right)),
+ member_(EVENT_INVALID),
+ number_(0) {
+ type_ = (binary_op < OP_ADD ?
+ QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR);
+}
+
+Query::Query(const Query& left, Operator unary_op)
+ : operator_(unary_op),
+ left_(new QueryNode(left)),
+ member_(EVENT_INVALID),
+ number_(0) {
+ type_ = (unary_op < OP_ADD ?
+ QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR);
+}
+
+namespace {
+
+// Search |events| for |query| and add matches to |output|.
+size_t FindMatchingEvents(const std::vector<TraceEvent>& events,
+ const Query& query,
+ TraceEventVector* output) {
+ for (size_t i = 0; i < events.size(); ++i) {
+ if (query.Evaluate(events[i]))
+ output->push_back(&events[i]);
+ }
+ return output->size();
+}
+
+bool ParseEventsFromJson(const std::string& json,
+ std::vector<TraceEvent>* output) {
+ scoped_ptr<base::Value> root;
+ root.reset(base::JSONReader::Read(json));
+
+ ListValue* root_list = NULL;
+ if (!root.get() || !root->GetAsList(&root_list))
+ return false;
+
+ for (size_t i = 0; i < root_list->GetSize(); ++i) {
+ Value* item = NULL;
+ if (root_list->Get(i, &item)) {
+ TraceEvent event;
+ if (event.SetFromJSON(item))
+ output->push_back(event);
+ else
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace
+
+// TraceAnalyzer
+
+TraceAnalyzer::TraceAnalyzer() : allow_assocation_changes_(true) {
+}
+
+TraceAnalyzer::~TraceAnalyzer() {
+}
+
+// static
+TraceAnalyzer* TraceAnalyzer::Create(const std::string& json_events) {
+ scoped_ptr<TraceAnalyzer> analyzer(new TraceAnalyzer());
+ if (analyzer->SetEvents(json_events))
+ return analyzer.release();
+ return NULL;
+}
+
+bool TraceAnalyzer::SetEvents(const std::string& json_events) {
+ raw_events_.clear();
+ if (!ParseEventsFromJson(json_events, &raw_events_))
+ return false;
+ std::stable_sort(raw_events_.begin(), raw_events_.end());
+ ParseMetadata();
+ return true;
+}
+
+void TraceAnalyzer::AssociateBeginEndEvents() {
+ using trace_analyzer::Query;
+
+ Query begin(Query::EventPhaseIs(TRACE_EVENT_PHASE_BEGIN));
+ Query end(Query::EventPhaseIs(TRACE_EVENT_PHASE_END));
+ Query match(Query::EventName() == Query::OtherName() &&
+ Query::EventCategory() == Query::OtherCategory() &&
+ Query::EventTid() == Query::OtherTid() &&
+ Query::EventPid() == Query::OtherPid());
+
+ AssociateEvents(begin, end, match);
+}
+
+void TraceAnalyzer::AssociateAsyncBeginEndEvents() {
+ using trace_analyzer::Query;
+
+ Query begin(
+ Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_BEGIN) ||
+ Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP));
+ Query end(Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_END) ||
+ Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP));
+ Query match(Query::EventName() == Query::OtherName() &&
+ Query::EventCategory() == Query::OtherCategory() &&
+ Query::EventId() == Query::OtherId());
+
+ AssociateEvents(begin, end, match);
+}
+
+void TraceAnalyzer::AssociateEvents(const Query& first,
+ const Query& second,
+ const Query& match) {
+ DCHECK(allow_assocation_changes_) << "AssociateEvents not allowed after "
+ "FindEvents";
+
+ // Search for matching begin/end event pairs. When a matching end is found,
+ // it is associated with the begin event.
+ std::vector<TraceEvent*> begin_stack;
+ for (size_t event_index = 0; event_index < raw_events_.size();
+ ++event_index) {
+
+ TraceEvent& this_event = raw_events_[event_index];
+
+ if (second.Evaluate(this_event)) {
+ // Search stack for matching begin, starting from end.
+ for (int stack_index = static_cast<int>(begin_stack.size()) - 1;
+ stack_index >= 0; --stack_index) {
+ TraceEvent& begin_event = *begin_stack[stack_index];
+
+ // Temporarily set other to test against the match query.
+ const TraceEvent* other_backup = begin_event.other_event;
+ begin_event.other_event = &this_event;
+ if (match.Evaluate(begin_event)) {
+ // Found a matching begin/end pair.
+ // Erase the matching begin event index from the stack.
+ begin_stack.erase(begin_stack.begin() + stack_index);
+ break;
+ }
+
+ // Not a match, restore original other and continue.
+ begin_event.other_event = other_backup;
+ }
+ }
+ // Even if this_event is a |second| event that has matched an earlier
+ // |first| event, it can still also be a |first| event and be associated
+ // with a later |second| event.
+ if (first.Evaluate(this_event)) {
+ begin_stack.push_back(&this_event);
+ }
+ }
+}
+
+void TraceAnalyzer::MergeAssociatedEventArgs() {
+ for (size_t i = 0; i < raw_events_.size(); ++i) {
+ // Merge all associated events with the first event.
+ const TraceEvent* other = raw_events_[i].other_event;
+ // Avoid looping by keeping set of encountered TraceEvents.
+ std::set<const TraceEvent*> encounters;
+ encounters.insert(&raw_events_[i]);
+ while (other && encounters.find(other) == encounters.end()) {
+ encounters.insert(other);
+ raw_events_[i].arg_numbers.insert(
+ other->arg_numbers.begin(),
+ other->arg_numbers.end());
+ raw_events_[i].arg_strings.insert(
+ other->arg_strings.begin(),
+ other->arg_strings.end());
+ other = other->other_event;
+ }
+ }
+}
+
+size_t TraceAnalyzer::FindEvents(const Query& query, TraceEventVector* output) {
+ allow_assocation_changes_ = false;
+ output->clear();
+ return FindMatchingEvents(raw_events_, query, output);
+}
+
+const TraceEvent* TraceAnalyzer::FindFirstOf(const Query& query) {
+ TraceEventVector output;
+ if (FindEvents(query, &output) > 0)
+ return output.front();
+ return NULL;
+}
+
+const TraceEvent* TraceAnalyzer::FindLastOf(const Query& query) {
+ TraceEventVector output;
+ if (FindEvents(query, &output) > 0)
+ return output.back();
+ return NULL;
+}
+
+const std::string& TraceAnalyzer::GetThreadName(
+ const TraceEvent::ProcessThreadID& thread) {
+ // If thread is not found, just add and return empty string.
+ return thread_names_[thread];
+}
+
+void TraceAnalyzer::ParseMetadata() {
+ for (size_t i = 0; i < raw_events_.size(); ++i) {
+ TraceEvent& this_event = raw_events_[i];
+ // Check for thread name metadata.
+ if (this_event.phase != TRACE_EVENT_PHASE_METADATA ||
+ this_event.name != "thread_name")
+ continue;
+ std::map<std::string, std::string>::const_iterator string_it =
+ this_event.arg_strings.find("name");
+ if (string_it != this_event.arg_strings.end())
+ thread_names_[this_event.thread] = string_it->second;
+ }
+}
+
+// TraceEventVector utility functions.
+
+bool GetRateStats(const TraceEventVector& events,
+ RateStats* stats,
+ const RateStatsOptions* options) {
+ CHECK(stats);
+ // Need at least 3 events to calculate rate stats.
+ const size_t kMinEvents = 3;
+ if (events.size() < kMinEvents) {
+ LOG(ERROR) << "Not enough events: " << events.size();
+ return false;
+ }
+
+ std::vector<double> deltas;
+ size_t num_deltas = events.size() - 1;
+ for (size_t i = 0; i < num_deltas; ++i) {
+ double delta = events.at(i + 1)->timestamp - events.at(i)->timestamp;
+ if (delta < 0.0) {
+ LOG(ERROR) << "Events are out of order";
+ return false;
+ }
+ deltas.push_back(delta);
+ }
+
+ std::sort(deltas.begin(), deltas.end());
+
+ if (options) {
+ if (options->trim_min + options->trim_max > events.size() - kMinEvents) {
+ LOG(ERROR) << "Attempt to trim too many events";
+ return false;
+ }
+ deltas.erase(deltas.begin(), deltas.begin() + options->trim_min);
+ deltas.erase(deltas.end() - options->trim_max, deltas.end());
+ }
+
+ num_deltas = deltas.size();
+ double delta_sum = 0.0;
+ for (size_t i = 0; i < num_deltas; ++i)
+ delta_sum += deltas[i];
+
+ stats->min_us = *std::min_element(deltas.begin(), deltas.end());
+ stats->max_us = *std::max_element(deltas.begin(), deltas.end());
+ stats->mean_us = delta_sum / static_cast<double>(num_deltas);
+
+ double sum_mean_offsets_squared = 0.0;
+ for (size_t i = 0; i < num_deltas; ++i) {
+ double offset = fabs(deltas[i] - stats->mean_us);
+ sum_mean_offsets_squared += offset * offset;
+ }
+ stats->standard_deviation_us =
+ sum_mean_offsets_squared / static_cast<double>(num_deltas - 1);
+
+ return true;
+}
+
+bool FindFirstOf(const TraceEventVector& events,
+ const Query& query,
+ size_t position,
+ size_t* return_index) {
+ CHECK(return_index);
+ for (size_t i = position; i < events.size(); ++i) {
+ if (query.Evaluate(*events.at(i))) {
+ *return_index = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool FindLastOf(const TraceEventVector& events,
+ const Query& query,
+ size_t position,
+ size_t* return_index) {
+ CHECK(return_index);
+ if (events.empty())
+ return false;
+ position = (position < events.size()) ? position : events.size() - 1;
+ for (;;) {
+ if (query.Evaluate(*events.at(position))) {
+ *return_index = position;
+ return true;
+ }
+ if (position == 0)
+ return false;
+ --position;
+ }
+ return false;
+}
+
+bool FindClosest(const TraceEventVector& events,
+ const Query& query,
+ size_t position,
+ size_t* return_closest,
+ size_t* return_second_closest) {
+ CHECK(return_closest);
+ if (events.empty() || position >= events.size())
+ return false;
+ size_t closest = events.size();
+ size_t second_closest = events.size();
+ for (size_t i = 0; i < events.size(); ++i) {
+ if (!query.Evaluate(*events.at(i)))
+ continue;
+ if (closest == events.size()) {
+ closest = i;
+ continue;
+ }
+ if (fabs(events.at(i)->timestamp - events.at(position)->timestamp) <
+ fabs(events.at(closest)->timestamp - events.at(position)->timestamp)) {
+ second_closest = closest;
+ closest = i;
+ } else if (second_closest == events.size()) {
+ second_closest = i;
+ }
+ }
+
+ if (closest < events.size() &&
+ (!return_second_closest || second_closest < events.size())) {
+ *return_closest = closest;
+ if (return_second_closest)
+ *return_second_closest = second_closest;
+ return true;
+ }
+
+ return false;
+}
+
+size_t CountMatches(const TraceEventVector& events,
+ const Query& query,
+ size_t begin_position,
+ size_t end_position) {
+ if (begin_position >= events.size())
+ return 0u;
+ end_position = (end_position < events.size()) ? end_position : events.size();
+ size_t count = 0u;
+ for (size_t i = begin_position; i < end_position; ++i) {
+ if (query.Evaluate(*events.at(i)))
+ ++count;
+ }
+ return count;
+}
+
+} // namespace trace_analyzer
diff --git a/src/base/test/trace_event_analyzer.h b/src/base/test/trace_event_analyzer.h
new file mode 100644
index 0000000..ddd1a00
--- /dev/null
+++ b/src/base/test/trace_event_analyzer.h
@@ -0,0 +1,684 @@
+// Copyright (c) 2012 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.
+
+// Use trace_analyzer::Query and trace_analyzer::TraceAnalyzer to search for
+// specific trace events that were generated by the trace_event.h API.
+//
+// Basic procedure:
+// - Get trace events JSON string from base::debug::TraceLog.
+// - Create TraceAnalyzer with JSON string.
+// - Call TraceAnalyzer::AssociateBeginEndEvents (optional).
+// - Call TraceAnalyzer::AssociateEvents (zero or more times).
+// - Call TraceAnalyzer::FindEvents with queries to find specific events.
+//
+// A Query is a boolean expression tree that evaluates to true or false for a
+// given trace event. Queries can be combined into a tree using boolean,
+// arithmetic and comparison operators that refer to data of an individual trace
+// event.
+//
+// The events are returned as trace_analyzer::TraceEvent objects.
+// TraceEvent contains a single trace event's data, as well as a pointer to
+// a related trace event. The related trace event is typically the matching end
+// of a begin event or the matching begin of an end event.
+//
+// The following examples use this basic setup code to construct TraceAnalyzer
+// with the json trace string retrieved from TraceLog and construct an event
+// vector for retrieving events:
+//
+// TraceAnalyzer analyzer(json_events);
+// TraceEventVector events;
+//
+// EXAMPLE 1: Find events named "my_event".
+//
+// analyzer.FindEvents(Query(EVENT_NAME) == "my_event", &events);
+//
+// EXAMPLE 2: Find begin events named "my_event" with duration > 1 second.
+//
+// Query q = (Query(EVENT_NAME) == Query::String("my_event") &&
+// Query(EVENT_PHASE) == Query::Phase(TRACE_EVENT_PHASE_BEGIN) &&
+// Query(EVENT_DURATION) > Query::Double(1000000.0));
+// analyzer.FindEvents(q, &events);
+//
+// EXAMPLE 3: Associating event pairs across threads.
+//
+// If the test needs to analyze something that starts and ends on different
+// threads, the test needs to use INSTANT events. The typical procedure is to
+// specify the same unique ID as a TRACE_EVENT argument on both the start and
+// finish INSTANT events. Then use the following procedure to associate those
+// events.
+//
+// Step 1: instrument code with custom begin/end trace events.
+// [Thread 1 tracing code]
+// TRACE_EVENT_INSTANT1("test_latency", "timing1_begin", "id", 3);
+// [Thread 2 tracing code]
+// TRACE_EVENT_INSTANT1("test_latency", "timing1_end", "id", 3);
+//
+// Step 2: associate these custom begin/end pairs.
+// Query begin(Query(EVENT_NAME) == Query::String("timing1_begin"));
+// Query end(Query(EVENT_NAME) == Query::String("timing1_end"));
+// Query match(Query(EVENT_ARG, "id") == Query(OTHER_ARG, "id"));
+// analyzer.AssociateEvents(begin, end, match);
+//
+// Step 3: search for "timing1_begin" events with existing other event.
+// Query q = (Query(EVENT_NAME) == Query::String("timing1_begin") &&
+// Query(EVENT_HAS_OTHER));
+// analyzer.FindEvents(q, &events);
+//
+// Step 4: analyze events, such as checking durations.
+// for (size_t i = 0; i < events.size(); ++i) {
+// double duration;
+// EXPECT_TRUE(events[i].GetAbsTimeToOtherEvent(&duration));
+// EXPECT_LT(duration, 1000000.0/60.0); // expect less than 1/60 second.
+// }
+
+
+#ifndef BASE_TEST_TRACE_EVENT_ANALYZER_H_
+#define BASE_TEST_TRACE_EVENT_ANALYZER_H_
+
+#include <map>
+
+#include "base/debug/trace_event.h"
+#include "base/memory/ref_counted.h"
+
+namespace base {
+class Value;
+}
+
+namespace trace_analyzer {
+class QueryNode;
+
+// trace_analyzer::TraceEvent is a more convenient form of the
+// base::debug::TraceEvent class to make tracing-based tests easier to write.
+struct TraceEvent {
+ // ProcessThreadID contains a Process ID and Thread ID.
+ struct ProcessThreadID {
+ ProcessThreadID() : process_id(0), thread_id(0) {}
+ ProcessThreadID(int process_id, int thread_id)
+ : process_id(process_id), thread_id(thread_id) {}
+ bool operator< (const ProcessThreadID& rhs) const {
+ if (process_id != rhs.process_id)
+ return process_id < rhs.process_id;
+ return thread_id < rhs.thread_id;
+ }
+ int process_id;
+ int thread_id;
+ };
+
+ TraceEvent();
+ ~TraceEvent();
+
+ bool SetFromJSON(const base::Value* event_value) WARN_UNUSED_RESULT;
+
+ bool operator< (const TraceEvent& rhs) const {
+ return timestamp < rhs.timestamp;
+ }
+
+ bool has_other_event() const { return other_event != NULL; }
+
+ // Returns absolute duration in microseconds between this event and other
+ // event. Must have already verified that other_event exists by
+ // Query(EVENT_HAS_OTHER) or by calling has_other_event().
+ double GetAbsTimeToOtherEvent() const;
+
+ // Return the argument value if it exists and it is a string.
+ bool GetArgAsString(const std::string& name, std::string* arg) const;
+ // Return the argument value if it exists and it is a number.
+ bool GetArgAsNumber(const std::string& name, double* arg) const;
+
+ // Check if argument exists and is string.
+ bool HasStringArg(const std::string& name) const;
+ // Check if argument exists and is number (double, int or bool).
+ bool HasNumberArg(const std::string& name) const;
+
+ // Get known existing arguments as specific types.
+ // Useful when you have already queried the argument with
+ // Query(HAS_NUMBER_ARG) or Query(HAS_STRING_ARG).
+ std::string GetKnownArgAsString(const std::string& name) const;
+ double GetKnownArgAsDouble(const std::string& name) const;
+ int GetKnownArgAsInt(const std::string& name) const;
+ bool GetKnownArgAsBool(const std::string& name) const;
+
+ // Process ID and Thread ID.
+ ProcessThreadID thread;
+
+ // Time since epoch in microseconds.
+ // Stored as double to match its JSON representation.
+ double timestamp;
+
+ char phase;
+
+ std::string category;
+
+ std::string name;
+
+ std::string id;
+
+ // All numbers and bool values from TraceEvent args are cast to double.
+ // bool becomes 1.0 (true) or 0.0 (false).
+ std::map<std::string, double> arg_numbers;
+
+ std::map<std::string, std::string> arg_strings;
+
+ // The other event associated with this event (or NULL).
+ const TraceEvent* other_event;
+};
+
+typedef std::vector<const TraceEvent*> TraceEventVector;
+
+class Query {
+ public:
+ Query(const Query& query);
+
+ ~Query();
+
+ ////////////////////////////////////////////////////////////////
+ // Query literal values
+
+ // Compare with the given string.
+ static Query String(const std::string& str);
+
+ // Compare with the given number.
+ static Query Double(double num);
+ static Query Int(int32 num);
+ static Query Uint(uint32 num);
+
+ // Compare with the given bool.
+ static Query Bool(bool boolean);
+
+ // Compare with the given phase.
+ static Query Phase(char phase);
+
+ // Compare with the given string pattern. Only works with == and != operators.
+ // Example: Query(EVENT_NAME) == Query::Pattern("MyEvent*")
+ static Query Pattern(const std::string& pattern);
+
+ ////////////////////////////////////////////////////////////////
+ // Query event members
+
+ static Query EventPid() { return Query(EVENT_PID); }
+
+ static Query EventTid() { return Query(EVENT_TID); }
+
+ // Return the timestamp of the event in microseconds since epoch.
+ static Query EventTime() { return Query(EVENT_TIME); }
+
+ // Return the absolute time between event and other event in microseconds.
+ // Only works if Query::EventHasOther() == true.
+ static Query EventDuration() { return Query(EVENT_DURATION); }
+
+ static Query EventPhase() { return Query(EVENT_PHASE); }
+
+ static Query EventCategory() { return Query(EVENT_CATEGORY); }
+
+ static Query EventName() { return Query(EVENT_NAME); }
+
+ static Query EventId() { return Query(EVENT_ID); }
+
+ static Query EventPidIs(int process_id) {
+ return Query(EVENT_PID) == Query::Int(process_id);
+ }
+
+ static Query EventTidIs(int thread_id) {
+ return Query(EVENT_TID) == Query::Int(thread_id);
+ }
+
+ static Query EventThreadIs(const TraceEvent::ProcessThreadID& thread) {
+ return EventPidIs(thread.process_id) && EventTidIs(thread.thread_id);
+ }
+
+ static Query EventTimeIs(double timestamp) {
+ return Query(EVENT_TIME) == Query::Double(timestamp);
+ }
+
+ static Query EventDurationIs(double duration) {
+ return Query(EVENT_DURATION) == Query::Double(duration);
+ }
+
+ static Query EventPhaseIs(char phase) {
+ return Query(EVENT_PHASE) == Query::Phase(phase);
+ }
+
+ static Query EventCategoryIs(const std::string& category) {
+ return Query(EVENT_CATEGORY) == Query::String(category);
+ }
+
+ static Query EventNameIs(const std::string& name) {
+ return Query(EVENT_NAME) == Query::String(name);
+ }
+
+ static Query EventIdIs(const std::string& id) {
+ return Query(EVENT_ID) == Query::String(id);
+ }
+
+ // Evaluates to true if arg exists and is a string.
+ static Query EventHasStringArg(const std::string& arg_name) {
+ return Query(EVENT_HAS_STRING_ARG, arg_name);
+ }
+
+ // Evaluates to true if arg exists and is a number.
+ // Number arguments include types double, int and bool.
+ static Query EventHasNumberArg(const std::string& arg_name) {
+ return Query(EVENT_HAS_NUMBER_ARG, arg_name);
+ }
+
+ // Evaluates to arg value (string or number).
+ static Query EventArg(const std::string& arg_name) {
+ return Query(EVENT_ARG, arg_name);
+ }
+
+ // Return true if associated event exists.
+ static Query EventHasOther() { return Query(EVENT_HAS_OTHER); }
+
+ // Access the associated other_event's members:
+
+ static Query OtherPid() { return Query(OTHER_PID); }
+
+ static Query OtherTid() { return Query(OTHER_TID); }
+
+ static Query OtherTime() { return Query(OTHER_TIME); }
+
+ static Query OtherPhase() { return Query(OTHER_PHASE); }
+
+ static Query OtherCategory() { return Query(OTHER_CATEGORY); }
+
+ static Query OtherName() { return Query(OTHER_NAME); }
+
+ static Query OtherId() { return Query(OTHER_ID); }
+
+ static Query OtherPidIs(int process_id) {
+ return Query(OTHER_PID) == Query::Int(process_id);
+ }
+
+ static Query OtherTidIs(int thread_id) {
+ return Query(OTHER_TID) == Query::Int(thread_id);
+ }
+
+ static Query OtherThreadIs(const TraceEvent::ProcessThreadID& thread) {
+ return OtherPidIs(thread.process_id) && OtherTidIs(thread.thread_id);
+ }
+
+ static Query OtherTimeIs(double timestamp) {
+ return Query(OTHER_TIME) == Query::Double(timestamp);
+ }
+
+ static Query OtherPhaseIs(char phase) {
+ return Query(OTHER_PHASE) == Query::Phase(phase);
+ }
+
+ static Query OtherCategoryIs(const std::string& category) {
+ return Query(OTHER_CATEGORY) == Query::String(category);
+ }
+
+ static Query OtherNameIs(const std::string& name) {
+ return Query(OTHER_NAME) == Query::String(name);
+ }
+
+ static Query OtherIdIs(const std::string& id) {
+ return Query(OTHER_ID) == Query::String(id);
+ }
+
+ // Evaluates to true if arg exists and is a string.
+ static Query OtherHasStringArg(const std::string& arg_name) {
+ return Query(OTHER_HAS_STRING_ARG, arg_name);
+ }
+
+ // Evaluates to true if arg exists and is a number.
+ // Number arguments include types double, int and bool.
+ static Query OtherHasNumberArg(const std::string& arg_name) {
+ return Query(OTHER_HAS_NUMBER_ARG, arg_name);
+ }
+
+ // Evaluates to arg value (string or number).
+ static Query OtherArg(const std::string& arg_name) {
+ return Query(OTHER_ARG, arg_name);
+ }
+
+ ////////////////////////////////////////////////////////////////
+ // Common queries:
+
+ // Find BEGIN events that have a corresponding END event.
+ static Query MatchBeginWithEnd() {
+ return (Query(EVENT_PHASE) == Query::Phase(TRACE_EVENT_PHASE_BEGIN)) &&
+ Query(EVENT_HAS_OTHER);
+ }
+
+ // Find ASYNC_BEGIN events that have a corresponding ASYNC_END event.
+ static Query MatchAsyncBeginWithNext() {
+ return (Query(EVENT_PHASE) ==
+ Query::Phase(TRACE_EVENT_PHASE_ASYNC_BEGIN)) &&
+ Query(EVENT_HAS_OTHER);
+ }
+
+ // Find BEGIN events of given |name| which also have associated END events.
+ static Query MatchBeginName(const std::string& name) {
+ return (Query(EVENT_NAME) == name) && MatchBeginWithEnd();
+ }
+
+ // Match given Process ID and Thread ID.
+ static Query MatchThread(const TraceEvent::ProcessThreadID& thread) {
+ return (Query(EVENT_PID) == Query::Int(thread.process_id)) &&
+ (Query(EVENT_TID) == Query::Int(thread.thread_id));
+ }
+
+ // Match event pair that spans multiple threads.
+ static Query MatchCrossThread() {
+ return (Query(EVENT_PID) != Query(OTHER_PID)) ||
+ (Query(EVENT_TID) != Query(OTHER_TID));
+ }
+
+ ////////////////////////////////////////////////////////////////
+ // Operators:
+
+ // Boolean operators:
+ Query operator==(const Query& rhs) const;
+ Query operator!=(const Query& rhs) const;
+ Query operator< (const Query& rhs) const;
+ Query operator<=(const Query& rhs) const;
+ Query operator> (const Query& rhs) const;
+ Query operator>=(const Query& rhs) const;
+ Query operator&&(const Query& rhs) const;
+ Query operator||(const Query& rhs) const;
+ Query operator!() const;
+
+ // Arithmetic operators:
+ // Following operators are applied to double arguments:
+ Query operator+(const Query& rhs) const;
+ Query operator-(const Query& rhs) const;
+ Query operator*(const Query& rhs) const;
+ Query operator/(const Query& rhs) const;
+ Query operator-() const;
+ // Mod operates on int64 args (doubles are casted to int64 beforehand):
+ Query operator%(const Query& rhs) const;
+
+ // Return true if the given event matches this query tree.
+ // This is a recursive method that walks the query tree.
+ bool Evaluate(const TraceEvent& event) const;
+
+ private:
+ enum TraceEventMember {
+ EVENT_INVALID,
+ EVENT_PID,
+ EVENT_TID,
+ EVENT_TIME,
+ EVENT_DURATION,
+ EVENT_PHASE,
+ EVENT_CATEGORY,
+ EVENT_NAME,
+ EVENT_ID,
+ EVENT_HAS_STRING_ARG,
+ EVENT_HAS_NUMBER_ARG,
+ EVENT_ARG,
+ EVENT_HAS_OTHER,
+ OTHER_PID,
+ OTHER_TID,
+ OTHER_TIME,
+ OTHER_PHASE,
+ OTHER_CATEGORY,
+ OTHER_NAME,
+ OTHER_ID,
+ OTHER_HAS_STRING_ARG,
+ OTHER_HAS_NUMBER_ARG,
+ OTHER_ARG,
+ };
+
+ enum Operator {
+ OP_INVALID,
+ // Boolean operators:
+ OP_EQ,
+ OP_NE,
+ OP_LT,
+ OP_LE,
+ OP_GT,
+ OP_GE,
+ OP_AND,
+ OP_OR,
+ OP_NOT,
+ // Arithmetic operators:
+ OP_ADD,
+ OP_SUB,
+ OP_MUL,
+ OP_DIV,
+ OP_MOD,
+ OP_NEGATE
+ };
+
+ enum QueryType {
+ QUERY_BOOLEAN_OPERATOR,
+ QUERY_ARITHMETIC_OPERATOR,
+ QUERY_EVENT_MEMBER,
+ QUERY_NUMBER,
+ QUERY_STRING
+ };
+
+ // Compare with the given member.
+ Query(TraceEventMember member);
+
+ // Compare with the given member argument value.
+ Query(TraceEventMember member, const std::string& arg_name);
+
+ // Compare with the given string.
+ Query(const std::string& str);
+
+ // Compare with the given number.
+ Query(double num);
+
+ // Construct a boolean Query that returns (left <binary_op> right).
+ Query(const Query& left, const Query& right, Operator binary_op);
+
+ // Construct a boolean Query that returns (<binary_op> left).
+ Query(const Query& left, Operator unary_op);
+
+ // Try to compare left_ against right_ based on operator_.
+ // If either left or right does not convert to double, false is returned.
+ // Otherwise, true is returned and |result| is set to the comparison result.
+ bool CompareAsDouble(const TraceEvent& event, bool* result) const;
+
+ // Try to compare left_ against right_ based on operator_.
+ // If either left or right does not convert to string, false is returned.
+ // Otherwise, true is returned and |result| is set to the comparison result.
+ bool CompareAsString(const TraceEvent& event, bool* result) const;
+
+ // Attempt to convert this Query to a double. On success, true is returned
+ // and the double value is stored in |num|.
+ bool GetAsDouble(const TraceEvent& event, double* num) const;
+
+ // Attempt to convert this Query to a string. On success, true is returned
+ // and the string value is stored in |str|.
+ bool GetAsString(const TraceEvent& event, std::string* str) const;
+
+ // Evaluate this Query as an arithmetic operator on left_ and right_.
+ bool EvaluateArithmeticOperator(const TraceEvent& event,
+ double* num) const;
+
+ // For QUERY_EVENT_MEMBER Query: attempt to get the double value of the Query.
+ bool GetMemberValueAsDouble(const TraceEvent& event, double* num) const;
+
+ // For QUERY_EVENT_MEMBER Query: attempt to get the string value of the Query.
+ bool GetMemberValueAsString(const TraceEvent& event, std::string* num) const;
+
+ // Does this Query represent a value?
+ bool is_value() const { return type_ != QUERY_BOOLEAN_OPERATOR; }
+
+ bool is_unary_operator() const {
+ return operator_ == OP_NOT || operator_ == OP_NEGATE;
+ }
+
+ bool is_comparison_operator() const {
+ return operator_ != OP_INVALID && operator_ < OP_AND;
+ }
+
+ const Query& left() const;
+ const Query& right() const;
+
+ QueryType type_;
+ Operator operator_;
+ scoped_refptr<QueryNode> left_;
+ scoped_refptr<QueryNode> right_;
+ TraceEventMember member_;
+ double number_;
+ std::string string_;
+ bool is_pattern_;
+};
+
+// Implementation detail:
+// QueryNode allows Query to store a ref-counted query tree.
+class QueryNode : public base::RefCounted<QueryNode> {
+ public:
+ explicit QueryNode(const Query& query);
+ const Query& query() const { return query_; }
+
+ private:
+ friend class base::RefCounted<QueryNode>;
+ ~QueryNode();
+
+ Query query_;
+};
+
+// TraceAnalyzer helps tests search for trace events.
+class TraceAnalyzer {
+ public:
+ ~TraceAnalyzer();
+
+ // Use trace events from JSON string generated by tracing API.
+ // Returns non-NULL if the JSON is successfully parsed.
+ static TraceAnalyzer* Create(const std::string& json_events)
+ WARN_UNUSED_RESULT;
+
+ // Associate BEGIN and END events with each other. This allows Query(OTHER_*)
+ // to access the associated event and enables Query(EVENT_DURATION).
+ // An end event will match the most recent begin event with the same name,
+ // category, process ID and thread ID. This matches what is shown in
+ // about:tracing. After association, the BEGIN event will point to the
+ // matching END event, but the END event will not point to the BEGIN event.
+ void AssociateBeginEndEvents();
+
+ // Associate ASYNC_BEGIN, ASYNC_STEP and ASYNC_END events with each other.
+ // An ASYNC_END event will match the most recent ASYNC_BEGIN or ASYNC_STEP
+ // event with the same name, category, and ID. This creates a singly linked
+ // list of ASYNC_BEGIN->ASYNC_STEP...->ASYNC_END.
+ void AssociateAsyncBeginEndEvents();
+
+ // AssociateEvents can be used to customize event associations by setting the
+ // other_event member of TraceEvent. This should be used to associate two
+ // INSTANT events.
+ //
+ // The assumptions are:
+ // - |first| events occur before |second| events.
+ // - the closest matching |second| event is the correct match.
+ //
+ // |first| - Eligible |first| events match this query.
+ // |second| - Eligible |second| events match this query.
+ // |match| - This query is run on the |first| event. The OTHER_* EventMember
+ // queries will point to an eligible |second| event. The query
+ // should evaluate to true if the |first|/|second| pair is a match.
+ //
+ // When a match is found, the pair will be associated by having the first
+ // event's other_event member point to the other. AssociateEvents does not
+ // clear previous associations, so it is possible to associate multiple pairs
+ // of events by calling AssociateEvents more than once with different queries.
+ //
+ // NOTE: AssociateEvents will overwrite existing other_event associations if
+ // the queries pass for events that already had a previous association.
+ //
+ // After calling any Find* method, it is not allowed to call AssociateEvents
+ // again.
+ void AssociateEvents(const Query& first,
+ const Query& second,
+ const Query& match);
+
+ // For each event, copy its arguments to the other_event argument map. If
+ // argument name already exists, it will not be overwritten.
+ void MergeAssociatedEventArgs();
+
+ // Find all events that match query and replace output vector.
+ size_t FindEvents(const Query& query, TraceEventVector* output);
+
+ // Find first event that matches query or NULL if not found.
+ const TraceEvent* FindFirstOf(const Query& query);
+
+ // Find last event that matches query or NULL if not found.
+ const TraceEvent* FindLastOf(const Query& query);
+
+ const std::string& GetThreadName(const TraceEvent::ProcessThreadID& thread);
+
+ private:
+ TraceAnalyzer();
+
+ bool SetEvents(const std::string& json_events) WARN_UNUSED_RESULT;
+
+ // Read metadata (thread names, etc) from events.
+ void ParseMetadata();
+
+ std::map<TraceEvent::ProcessThreadID, std::string> thread_names_;
+ std::vector<TraceEvent> raw_events_;
+ bool allow_assocation_changes_;
+
+ DISALLOW_COPY_AND_ASSIGN(TraceAnalyzer);
+};
+
+// Utility functions for TraceEventVector.
+
+struct RateStats {
+ double min_us;
+ double max_us;
+ double mean_us;
+ double standard_deviation_us;
+};
+
+struct RateStatsOptions {
+ RateStatsOptions() : trim_min(0u), trim_max(0u) {}
+ // After the times between events are sorted, the number of specified elements
+ // will be trimmed before calculating the RateStats. This is useful in cases
+ // where extreme outliers are tolerable and should not skew the overall
+ // average.
+ size_t trim_min; // Trim this many minimum times.
+ size_t trim_max; // Trim this many maximum times.
+};
+
+// Calculate min/max/mean and standard deviation from the times between
+// adjacent events.
+bool GetRateStats(const TraceEventVector& events,
+ RateStats* stats,
+ const RateStatsOptions* options);
+
+// Starting from |position|, find the first event that matches |query|.
+// Returns true if found, false otherwise.
+bool FindFirstOf(const TraceEventVector& events,
+ const Query& query,
+ size_t position,
+ size_t* return_index);
+
+// Starting from |position|, find the last event that matches |query|.
+// Returns true if found, false otherwise.
+bool FindLastOf(const TraceEventVector& events,
+ const Query& query,
+ size_t position,
+ size_t* return_index);
+
+// Find the closest events to |position| in time that match |query|.
+// return_second_closest may be NULL. Closeness is determined by comparing
+// with the event timestamp.
+// Returns true if found, false otherwise. If both return parameters are
+// requested, both must be found for a successful result.
+bool FindClosest(const TraceEventVector& events,
+ const Query& query,
+ size_t position,
+ size_t* return_closest,
+ size_t* return_second_closest);
+
+// Count matches, inclusive of |begin_position|, exclusive of |end_position|.
+size_t CountMatches(const TraceEventVector& events,
+ const Query& query,
+ size_t begin_position,
+ size_t end_position);
+
+// Count all matches.
+static inline size_t CountMatches(const TraceEventVector& events,
+ const Query& query) {
+ return CountMatches(events, query, 0u, events.size());
+}
+
+} // namespace trace_analyzer
+
+#endif // BASE_TEST_TRACE_EVENT_ANALYZER_H_
diff --git a/src/base/test/trace_event_analyzer_unittest.cc b/src/base/test/trace_event_analyzer_unittest.cc
new file mode 100644
index 0000000..eca2b70
--- /dev/null
+++ b/src/base/test/trace_event_analyzer_unittest.cc
@@ -0,0 +1,825 @@
+// Copyright (c) 2012 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/bind.h"
+#include "base/debug/trace_event_unittest.h"
+#include "base/test/trace_event_analyzer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace trace_analyzer {
+
+namespace {
+
+class TraceEventAnalyzerTest : public testing::Test {
+ public:
+ void ManualSetUp();
+ void OnTraceDataCollected(
+ const scoped_refptr<base::RefCountedString>& json_events_str);
+ void BeginTracing();
+ void EndTracing();
+
+ base::debug::TraceResultBuffer::SimpleOutput output_;
+ base::debug::TraceResultBuffer buffer_;
+};
+
+void TraceEventAnalyzerTest::ManualSetUp() {
+ base::debug::TraceLog::Resurrect();
+ ASSERT_TRUE(base::debug::TraceLog::GetInstance());
+ buffer_.SetOutputCallback(output_.GetCallback());
+ output_.json_output.clear();
+}
+
+void TraceEventAnalyzerTest::OnTraceDataCollected(
+ const scoped_refptr<base::RefCountedString>& json_events_str) {
+ buffer_.AddFragment(json_events_str->data());
+}
+
+void TraceEventAnalyzerTest::BeginTracing() {
+ output_.json_output.clear();
+ buffer_.Start();
+ base::debug::TraceLog::GetInstance()->SetEnabled(true);
+}
+
+void TraceEventAnalyzerTest::EndTracing() {
+ base::debug::TraceLog::GetInstance()->SetEnabled(false);
+ base::debug::TraceLog::GetInstance()->Flush(
+ base::Bind(&TraceEventAnalyzerTest::OnTraceDataCollected,
+ base::Unretained(this)));
+ buffer_.Finish();
+}
+
+} // namespace
+
+TEST_F(TraceEventAnalyzerTest, NoEvents) {
+ ManualSetUp();
+
+ // Create an empty JSON event string:
+ buffer_.Start();
+ buffer_.Finish();
+
+ scoped_ptr<TraceAnalyzer>
+ analyzer(TraceAnalyzer::Create(output_.json_output));
+ ASSERT_TRUE(analyzer.get());
+
+ // Search for all events and verify that nothing is returned.
+ TraceEventVector found;
+ analyzer->FindEvents(Query::Bool(true), &found);
+ EXPECT_EQ(0u, found.size());
+}
+
+TEST_F(TraceEventAnalyzerTest, TraceEvent) {
+ ManualSetUp();
+
+ int int_num = 2;
+ double double_num = 3.5;
+ const char* str = "the string";
+
+ TraceEvent event;
+ event.arg_numbers["false"] = 0.0;
+ event.arg_numbers["true"] = 1.0;
+ event.arg_numbers["int"] = static_cast<double>(int_num);
+ event.arg_numbers["double"] = double_num;
+ event.arg_strings["string"] = str;
+
+ ASSERT_TRUE(event.HasNumberArg("false"));
+ ASSERT_TRUE(event.HasNumberArg("true"));
+ ASSERT_TRUE(event.HasNumberArg("int"));
+ ASSERT_TRUE(event.HasNumberArg("double"));
+ ASSERT_TRUE(event.HasStringArg("string"));
+ ASSERT_FALSE(event.HasNumberArg("notfound"));
+ ASSERT_FALSE(event.HasStringArg("notfound"));
+
+ EXPECT_FALSE(event.GetKnownArgAsBool("false"));
+ EXPECT_TRUE(event.GetKnownArgAsBool("true"));
+ EXPECT_EQ(int_num, event.GetKnownArgAsInt("int"));
+ EXPECT_EQ(double_num, event.GetKnownArgAsDouble("double"));
+ EXPECT_STREQ(str, event.GetKnownArgAsString("string").c_str());
+}
+
+TEST_F(TraceEventAnalyzerTest, QueryEventMember) {
+ ManualSetUp();
+
+ TraceEvent event;
+ event.thread.process_id = 3;
+ event.thread.thread_id = 4;
+ event.timestamp = 1.5;
+ event.phase = TRACE_EVENT_PHASE_BEGIN;
+ event.category = "category";
+ event.name = "name";
+ event.id = "1";
+ event.arg_numbers["num"] = 7.0;
+ event.arg_strings["str"] = "the string";
+
+ // Other event with all different members:
+ TraceEvent other;
+ other.thread.process_id = 5;
+ other.thread.thread_id = 6;
+ other.timestamp = 2.5;
+ other.phase = TRACE_EVENT_PHASE_END;
+ other.category = "category2";
+ other.name = "name2";
+ other.id = "2";
+ other.arg_numbers["num2"] = 8.0;
+ other.arg_strings["str2"] = "the string 2";
+
+ event.other_event = &other;
+ ASSERT_TRUE(event.has_other_event());
+ double duration = event.GetAbsTimeToOtherEvent();
+
+ Query event_pid = Query::EventPidIs(event.thread.process_id);
+ Query event_tid = Query::EventTidIs(event.thread.thread_id);
+ Query event_time = Query::EventTimeIs(event.timestamp);
+ Query event_duration = Query::EventDurationIs(duration);
+ Query event_phase = Query::EventPhaseIs(event.phase);
+ Query event_category = Query::EventCategoryIs(event.category);
+ Query event_name = Query::EventNameIs(event.name);
+ Query event_id = Query::EventIdIs(event.id);
+ Query event_has_arg1 = Query::EventHasNumberArg("num");
+ Query event_has_arg2 = Query::EventHasStringArg("str");
+ Query event_arg1 =
+ (Query::EventArg("num") == Query::Double(event.arg_numbers["num"]));
+ Query event_arg2 =
+ (Query::EventArg("str") == Query::String(event.arg_strings["str"]));
+ Query event_has_other = Query::EventHasOther();
+ Query other_pid = Query::OtherPidIs(other.thread.process_id);
+ Query other_tid = Query::OtherTidIs(other.thread.thread_id);
+ Query other_time = Query::OtherTimeIs(other.timestamp);
+ Query other_phase = Query::OtherPhaseIs(other.phase);
+ Query other_category = Query::OtherCategoryIs(other.category);
+ Query other_name = Query::OtherNameIs(other.name);
+ Query other_id = Query::OtherIdIs(other.id);
+ Query other_has_arg1 = Query::OtherHasNumberArg("num2");
+ Query other_has_arg2 = Query::OtherHasStringArg("str2");
+ Query other_arg1 =
+ (Query::OtherArg("num2") == Query::Double(other.arg_numbers["num2"]));
+ Query other_arg2 =
+ (Query::OtherArg("str2") == Query::String(other.arg_strings["str2"]));
+
+ EXPECT_TRUE(event_pid.Evaluate(event));
+ EXPECT_TRUE(event_tid.Evaluate(event));
+ EXPECT_TRUE(event_time.Evaluate(event));
+ EXPECT_TRUE(event_duration.Evaluate(event));
+ EXPECT_TRUE(event_phase.Evaluate(event));
+ EXPECT_TRUE(event_category.Evaluate(event));
+ EXPECT_TRUE(event_name.Evaluate(event));
+ EXPECT_TRUE(event_id.Evaluate(event));
+ EXPECT_TRUE(event_has_arg1.Evaluate(event));
+ EXPECT_TRUE(event_has_arg2.Evaluate(event));
+ EXPECT_TRUE(event_arg1.Evaluate(event));
+ EXPECT_TRUE(event_arg2.Evaluate(event));
+ EXPECT_TRUE(event_has_other.Evaluate(event));
+ EXPECT_TRUE(other_pid.Evaluate(event));
+ EXPECT_TRUE(other_tid.Evaluate(event));
+ EXPECT_TRUE(other_time.Evaluate(event));
+ EXPECT_TRUE(other_phase.Evaluate(event));
+ EXPECT_TRUE(other_category.Evaluate(event));
+ EXPECT_TRUE(other_name.Evaluate(event));
+ EXPECT_TRUE(other_id.Evaluate(event));
+ EXPECT_TRUE(other_has_arg1.Evaluate(event));
+ EXPECT_TRUE(other_has_arg2.Evaluate(event));
+ EXPECT_TRUE(other_arg1.Evaluate(event));
+ EXPECT_TRUE(other_arg2.Evaluate(event));
+
+ // Evaluate event queries against other to verify the queries fail when the
+ // event members are wrong.
+ EXPECT_FALSE(event_pid.Evaluate(other));
+ EXPECT_FALSE(event_tid.Evaluate(other));
+ EXPECT_FALSE(event_time.Evaluate(other));
+ EXPECT_FALSE(event_duration.Evaluate(other));
+ EXPECT_FALSE(event_phase.Evaluate(other));
+ EXPECT_FALSE(event_category.Evaluate(other));
+ EXPECT_FALSE(event_name.Evaluate(other));
+ EXPECT_FALSE(event_id.Evaluate(other));
+ EXPECT_FALSE(event_has_arg1.Evaluate(other));
+ EXPECT_FALSE(event_has_arg2.Evaluate(other));
+ EXPECT_FALSE(event_arg1.Evaluate(other));
+ EXPECT_FALSE(event_arg2.Evaluate(other));
+ EXPECT_FALSE(event_has_other.Evaluate(other));
+}
+
+TEST_F(TraceEventAnalyzerTest, BooleanOperators) {
+ ManualSetUp();
+
+ BeginTracing();
+ {
+ TRACE_EVENT_INSTANT1("cat1", "name1", "num", 1);
+ TRACE_EVENT_INSTANT1("cat1", "name2", "num", 2);
+ TRACE_EVENT_INSTANT1("cat2", "name3", "num", 3);
+ TRACE_EVENT_INSTANT1("cat2", "name4", "num", 4);
+ }
+ EndTracing();
+
+ scoped_ptr<TraceAnalyzer>
+ analyzer(TraceAnalyzer::Create(output_.json_output));
+ ASSERT_TRUE(!!analyzer.get());
+
+ TraceEventVector found;
+
+ // ==
+
+ analyzer->FindEvents(Query::EventCategory() == Query::String("cat1"), &found);
+ ASSERT_EQ(2u, found.size());
+ EXPECT_STREQ("name1", found[0]->name.c_str());
+ EXPECT_STREQ("name2", found[1]->name.c_str());
+
+ analyzer->FindEvents(Query::EventArg("num") == Query::Int(2), &found);
+ ASSERT_EQ(1u, found.size());
+ EXPECT_STREQ("name2", found[0]->name.c_str());
+
+ // !=
+
+ analyzer->FindEvents(Query::EventCategory() != Query::String("cat1"), &found);
+ ASSERT_EQ(2u, found.size());
+ EXPECT_STREQ("name3", found[0]->name.c_str());
+ EXPECT_STREQ("name4", found[1]->name.c_str());
+
+ analyzer->FindEvents(Query::EventArg("num") != Query::Int(2), &found);
+ ASSERT_EQ(3u, found.size());
+ EXPECT_STREQ("name1", found[0]->name.c_str());
+ EXPECT_STREQ("name3", found[1]->name.c_str());
+ EXPECT_STREQ("name4", found[2]->name.c_str());
+
+ // <
+ analyzer->FindEvents(Query::EventArg("num") < Query::Int(2), &found);
+ ASSERT_EQ(1u, found.size());
+ EXPECT_STREQ("name1", found[0]->name.c_str());
+
+ // <=
+ analyzer->FindEvents(Query::EventArg("num") <= Query::Int(2), &found);
+ ASSERT_EQ(2u, found.size());
+ EXPECT_STREQ("name1", found[0]->name.c_str());
+ EXPECT_STREQ("name2", found[1]->name.c_str());
+
+ // >
+ analyzer->FindEvents(Query::EventArg("num") > Query::Int(3), &found);
+ ASSERT_EQ(1u, found.size());
+ EXPECT_STREQ("name4", found[0]->name.c_str());
+
+ // >=
+ analyzer->FindEvents(Query::EventArg("num") >= Query::Int(4), &found);
+ ASSERT_EQ(1u, found.size());
+ EXPECT_STREQ("name4", found[0]->name.c_str());
+
+ // &&
+ analyzer->FindEvents(Query::EventName() != Query::String("name1") &&
+ Query::EventArg("num") < Query::Int(3), &found);
+ ASSERT_EQ(1u, found.size());
+ EXPECT_STREQ("name2", found[0]->name.c_str());
+
+ // ||
+ analyzer->FindEvents(Query::EventName() == Query::String("name1") ||
+ Query::EventArg("num") == Query::Int(3), &found);
+ ASSERT_EQ(2u, found.size());
+ EXPECT_STREQ("name1", found[0]->name.c_str());
+ EXPECT_STREQ("name3", found[1]->name.c_str());
+
+ // !
+ analyzer->FindEvents(!(Query::EventName() == Query::String("name1") ||
+ Query::EventArg("num") == Query::Int(3)), &found);
+ ASSERT_EQ(2u, found.size());
+ EXPECT_STREQ("name2", found[0]->name.c_str());
+ EXPECT_STREQ("name4", found[1]->name.c_str());
+}
+
+TEST_F(TraceEventAnalyzerTest, ArithmeticOperators) {
+ ManualSetUp();
+
+ BeginTracing();
+ {
+ // These events are searched for:
+ TRACE_EVENT_INSTANT2("cat1", "math1", "a", 10, "b", 5);
+ TRACE_EVENT_INSTANT2("cat1", "math2", "a", 10, "b", 10);
+ // Extra events that never match, for noise:
+ TRACE_EVENT_INSTANT2("noise", "math3", "a", 1, "b", 3);
+ TRACE_EVENT_INSTANT2("noise", "math4", "c", 10, "d", 5);
+ }
+ EndTracing();
+
+ scoped_ptr<TraceAnalyzer>
+ analyzer(TraceAnalyzer::Create(output_.json_output));
+ ASSERT_TRUE(analyzer.get());
+
+ TraceEventVector found;
+
+ // Verify that arithmetic operators function:
+
+ // +
+ analyzer->FindEvents(Query::EventArg("a") + Query::EventArg("b") ==
+ Query::Int(20), &found);
+ EXPECT_EQ(1u, found.size());
+ EXPECT_STREQ("math2", found.front()->name.c_str());
+
+ // -
+ analyzer->FindEvents(Query::EventArg("a") - Query::EventArg("b") ==
+ Query::Int(5), &found);
+ EXPECT_EQ(1u, found.size());
+ EXPECT_STREQ("math1", found.front()->name.c_str());
+
+ // *
+ analyzer->FindEvents(Query::EventArg("a") * Query::EventArg("b") ==
+ Query::Int(50), &found);
+ EXPECT_EQ(1u, found.size());
+ EXPECT_STREQ("math1", found.front()->name.c_str());
+
+ // /
+ analyzer->FindEvents(Query::EventArg("a") / Query::EventArg("b") ==
+ Query::Int(2), &found);
+ EXPECT_EQ(1u, found.size());
+ EXPECT_STREQ("math1", found.front()->name.c_str());
+
+ // %
+ analyzer->FindEvents(Query::EventArg("a") % Query::EventArg("b") ==
+ Query::Int(0), &found);
+ EXPECT_EQ(2u, found.size());
+
+ // - (negate)
+ analyzer->FindEvents(-Query::EventArg("b") == Query::Int(-10), &found);
+ EXPECT_EQ(1u, found.size());
+ EXPECT_STREQ("math2", found.front()->name.c_str());
+}
+
+TEST_F(TraceEventAnalyzerTest, StringPattern) {
+ ManualSetUp();
+
+ BeginTracing();
+ {
+ TRACE_EVENT_INSTANT0("cat1", "name1");
+ TRACE_EVENT_INSTANT0("cat1", "name2");
+ TRACE_EVENT_INSTANT0("cat1", "no match");
+ TRACE_EVENT_INSTANT0("cat1", "name3x");
+ }
+ EndTracing();
+
+ scoped_ptr<TraceAnalyzer>
+ analyzer(TraceAnalyzer::Create(output_.json_output));
+ ASSERT_TRUE(analyzer.get());
+
+ TraceEventVector found;
+
+ analyzer->FindEvents(Query::EventName() == Query::Pattern("name?"), &found);
+ ASSERT_EQ(2u, found.size());
+ EXPECT_STREQ("name1", found[0]->name.c_str());
+ EXPECT_STREQ("name2", found[1]->name.c_str());
+
+ analyzer->FindEvents(Query::EventName() == Query::Pattern("name*"), &found);
+ ASSERT_EQ(3u, found.size());
+ EXPECT_STREQ("name1", found[0]->name.c_str());
+ EXPECT_STREQ("name2", found[1]->name.c_str());
+ EXPECT_STREQ("name3x", found[2]->name.c_str());
+
+ analyzer->FindEvents(Query::EventName() != Query::Pattern("name*"), &found);
+ ASSERT_EQ(1u, found.size());
+ EXPECT_STREQ("no match", found[0]->name.c_str());
+}
+
+// Test that duration queries work.
+TEST_F(TraceEventAnalyzerTest, Duration) {
+ ManualSetUp();
+
+ const base::TimeDelta kSleepTime = base::TimeDelta::FromMilliseconds(200);
+ // We will search for events that have a duration of greater than 90% of the
+ // sleep time, so that there is no flakiness.
+ int duration_cutoff_us = (kSleepTime.InMicroseconds() * 9) / 10;
+
+ BeginTracing();
+ {
+ TRACE_EVENT0("cat1", "name1"); // found by duration query
+ TRACE_EVENT0("noise", "name2"); // not searched for, just noise
+ {
+ TRACE_EVENT0("cat2", "name3"); // found by duration query
+ TRACE_EVENT_INSTANT0("noise", "name4"); // not searched for, just noise
+ base::debug::HighResSleepForTraceTest(kSleepTime);
+ TRACE_EVENT0("cat2", "name5"); // not found (duration too short)
+ }
+ }
+ EndTracing();
+
+ scoped_ptr<TraceAnalyzer>
+ analyzer(TraceAnalyzer::Create(output_.json_output));
+ ASSERT_TRUE(analyzer.get());
+ analyzer->AssociateBeginEndEvents();
+
+ TraceEventVector found;
+ analyzer->FindEvents(
+ Query::MatchBeginWithEnd() &&
+ Query::EventDuration() > Query::Int(duration_cutoff_us) &&
+ (Query::EventCategory() == Query::String("cat1") ||
+ Query::EventCategory() == Query::String("cat2") ||
+ Query::EventCategory() == Query::String("cat3")),
+ &found);
+ ASSERT_EQ(2u, found.size());
+ EXPECT_STREQ("name1", found[0]->name.c_str());
+ EXPECT_STREQ("name3", found[1]->name.c_str());
+}
+
+// Test AssociateBeginEndEvents
+TEST_F(TraceEventAnalyzerTest, BeginEndAssocations) {
+ ManualSetUp();
+
+ BeginTracing();
+ {
+ TRACE_EVENT_END0("cat1", "name1"); // does not match out of order begin
+ TRACE_EVENT0("cat1", "name2");
+ TRACE_EVENT_INSTANT0("cat1", "name3");
+ TRACE_EVENT_BEGIN0("cat1", "name1");
+ }
+ EndTracing();
+
+ scoped_ptr<TraceAnalyzer>
+ analyzer(TraceAnalyzer::Create(output_.json_output));
+ ASSERT_TRUE(analyzer.get());
+ analyzer->AssociateBeginEndEvents();
+
+ TraceEventVector found;
+ analyzer->FindEvents(Query::MatchBeginWithEnd(), &found);
+ ASSERT_EQ(1u, found.size());
+ EXPECT_STREQ("name2", found[0]->name.c_str());
+}
+
+// Test MergeAssociatedEventArgs
+TEST_F(TraceEventAnalyzerTest, MergeAssociatedEventArgs) {
+ ManualSetUp();
+
+ const char* arg_string = "arg_string";
+ BeginTracing();
+ {
+ TRACE_EVENT_BEGIN0("cat1", "name1");
+ TRACE_EVENT_END1("cat1", "name1", "arg", arg_string);
+ }
+ EndTracing();
+
+ scoped_ptr<TraceAnalyzer>
+ analyzer(TraceAnalyzer::Create(output_.json_output));
+ ASSERT_TRUE(analyzer.get());
+ analyzer->AssociateBeginEndEvents();
+
+ TraceEventVector found;
+ analyzer->FindEvents(Query::MatchBeginName("name1"), &found);
+ ASSERT_EQ(1u, found.size());
+ std::string arg_actual;
+ EXPECT_FALSE(found[0]->GetArgAsString("arg", &arg_actual));
+
+ analyzer->MergeAssociatedEventArgs();
+ EXPECT_TRUE(found[0]->GetArgAsString("arg", &arg_actual));
+ EXPECT_STREQ(arg_string, arg_actual.c_str());
+}
+
+// Test AssociateAsyncBeginEndEvents
+TEST_F(TraceEventAnalyzerTest, AsyncBeginEndAssocations) {
+ ManualSetUp();
+
+ BeginTracing();
+ {
+ TRACE_EVENT_ASYNC_END0("cat1", "name1", 0xA); // no match / out of order
+ TRACE_EVENT_ASYNC_BEGIN0("cat1", "name1", 0xB);
+ TRACE_EVENT_ASYNC_BEGIN0("cat1", "name1", 0xC);
+ TRACE_EVENT_INSTANT0("cat1", "name1"); // noise
+ TRACE_EVENT0("cat1", "name1"); // noise
+ TRACE_EVENT_ASYNC_END0("cat1", "name1", 0xB);
+ TRACE_EVENT_ASYNC_END0("cat1", "name1", 0xC);
+ TRACE_EVENT_ASYNC_BEGIN0("cat1", "name1", 0xA); // no match / out of order
+ }
+ EndTracing();
+
+ scoped_ptr<TraceAnalyzer>
+ analyzer(TraceAnalyzer::Create(output_.json_output));
+ ASSERT_TRUE(analyzer.get());
+ analyzer->AssociateAsyncBeginEndEvents();
+
+ TraceEventVector found;
+ analyzer->FindEvents(Query::MatchAsyncBeginWithNext(), &found);
+ ASSERT_EQ(2u, found.size());
+ EXPECT_STRCASEEQ("B", found[0]->id.c_str());
+ EXPECT_STRCASEEQ("C", found[1]->id.c_str());
+}
+
+// Test AssociateAsyncBeginEndEvents
+TEST_F(TraceEventAnalyzerTest, AsyncBeginEndAssocationsWithSteps) {
+ ManualSetUp();
+
+ BeginTracing();
+ {
+ TRACE_EVENT_ASYNC_STEP0("c", "n", 0xA, "s1");
+ TRACE_EVENT_ASYNC_END0("c", "n", 0xA);
+ TRACE_EVENT_ASYNC_BEGIN0("c", "n", 0xB);
+ TRACE_EVENT_ASYNC_BEGIN0("c", "n", 0xC);
+ TRACE_EVENT_ASYNC_STEP0("c", "n", 0xB, "s1");
+ TRACE_EVENT_ASYNC_STEP0("c", "n", 0xC, "s1");
+ TRACE_EVENT_ASYNC_STEP1("c", "n", 0xC, "s2", "a", 1);
+ TRACE_EVENT_ASYNC_END0("c", "n", 0xB);
+ TRACE_EVENT_ASYNC_END0("c", "n", 0xC);
+ TRACE_EVENT_ASYNC_BEGIN0("c", "n", 0xA);
+ TRACE_EVENT_ASYNC_STEP0("c", "n", 0xA, "s2");
+ }
+ EndTracing();
+
+ scoped_ptr<TraceAnalyzer>
+ analyzer(TraceAnalyzer::Create(output_.json_output));
+ ASSERT_TRUE(analyzer.get());
+ analyzer->AssociateAsyncBeginEndEvents();
+
+ TraceEventVector found;
+ analyzer->FindEvents(Query::MatchAsyncBeginWithNext(), &found);
+ ASSERT_EQ(3u, found.size());
+
+ EXPECT_STRCASEEQ("B", found[0]->id.c_str());
+ EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_STEP, found[0]->other_event->phase);
+ EXPECT_TRUE(found[0]->other_event->other_event);
+ EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_END,
+ found[0]->other_event->other_event->phase);
+
+ EXPECT_STRCASEEQ("C", found[1]->id.c_str());
+ EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_STEP, found[1]->other_event->phase);
+ EXPECT_TRUE(found[1]->other_event->other_event);
+ EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_STEP,
+ found[1]->other_event->other_event->phase);
+ double arg_actual = 0;
+ EXPECT_TRUE(found[1]->other_event->other_event->GetArgAsNumber(
+ "a", &arg_actual));
+ EXPECT_EQ(1.0, arg_actual);
+ EXPECT_TRUE(found[1]->other_event->other_event->other_event);
+ EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_END,
+ found[1]->other_event->other_event->other_event->phase);
+
+ EXPECT_STRCASEEQ("A", found[2]->id.c_str());
+ EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_STEP, found[2]->other_event->phase);
+}
+
+// Test that the TraceAnalyzer custom associations work.
+TEST_F(TraceEventAnalyzerTest, CustomAssociations) {
+ ManualSetUp();
+
+ // Add events that begin/end in pipelined ordering with unique ID parameter
+ // to match up the begin/end pairs.
+ BeginTracing();
+ {
+ TRACE_EVENT_INSTANT1("cat1", "end", "id", 1); // no begin match
+ TRACE_EVENT_INSTANT1("cat2", "begin", "id", 2); // end is cat4
+ TRACE_EVENT_INSTANT1("cat3", "begin", "id", 3); // end is cat5
+ TRACE_EVENT_INSTANT1("cat4", "end", "id", 2);
+ TRACE_EVENT_INSTANT1("cat5", "end", "id", 3);
+ TRACE_EVENT_INSTANT1("cat6", "begin", "id", 1); // no end match
+ }
+ EndTracing();
+
+ scoped_ptr<TraceAnalyzer>
+ analyzer(TraceAnalyzer::Create(output_.json_output));
+ ASSERT_TRUE(analyzer.get());
+
+ // begin, end, and match queries to find proper begin/end pairs.
+ Query begin(Query::EventName() == Query::String("begin"));
+ Query end(Query::EventName() == Query::String("end"));
+ Query match(Query::EventArg("id") == Query::OtherArg("id"));
+ analyzer->AssociateEvents(begin, end, match);
+
+ TraceEventVector found;
+
+ // cat1 has no other_event.
+ analyzer->FindEvents(Query::EventCategory() == Query::String("cat1") &&
+ Query::EventHasOther(), &found);
+ EXPECT_EQ(0u, found.size());
+
+ // cat1 has no other_event.
+ analyzer->FindEvents(Query::EventCategory() == Query::String("cat1") &&
+ !Query::EventHasOther(), &found);
+ EXPECT_EQ(1u, found.size());
+
+ // cat6 has no other_event.
+ analyzer->FindEvents(Query::EventCategory() == Query::String("cat6") &&
+ !Query::EventHasOther(), &found);
+ EXPECT_EQ(1u, found.size());
+
+ // cat2 and cat4 are associated.
+ analyzer->FindEvents(Query::EventCategory() == Query::String("cat2") &&
+ Query::OtherCategory() == Query::String("cat4"), &found);
+ EXPECT_EQ(1u, found.size());
+
+ // cat4 and cat2 are not associated.
+ analyzer->FindEvents(Query::EventCategory() == Query::String("cat4") &&
+ Query::OtherCategory() == Query::String("cat2"), &found);
+ EXPECT_EQ(0u, found.size());
+
+ // cat3 and cat5 are associated.
+ analyzer->FindEvents(Query::EventCategory() == Query::String("cat3") &&
+ Query::OtherCategory() == Query::String("cat5"), &found);
+ EXPECT_EQ(1u, found.size());
+
+ // cat5 and cat3 are not associated.
+ analyzer->FindEvents(Query::EventCategory() == Query::String("cat5") &&
+ Query::OtherCategory() == Query::String("cat3"), &found);
+ EXPECT_EQ(0u, found.size());
+}
+
+// Verify that Query literals and types are properly casted.
+TEST_F(TraceEventAnalyzerTest, Literals) {
+ ManualSetUp();
+
+ // Since these queries don't refer to the event data, the dummy event below
+ // will never be accessed.
+ TraceEvent dummy;
+ char char_num = 5;
+ short short_num = -5;
+ EXPECT_TRUE((Query::Double(5.0) == Query::Int(char_num)).Evaluate(dummy));
+ EXPECT_TRUE((Query::Double(-5.0) == Query::Int(short_num)).Evaluate(dummy));
+ EXPECT_TRUE((Query::Double(1.0) == Query::Uint(1u)).Evaluate(dummy));
+ EXPECT_TRUE((Query::Double(1.0) == Query::Int(1)).Evaluate(dummy));
+ EXPECT_TRUE((Query::Double(-1.0) == Query::Int(-1)).Evaluate(dummy));
+ EXPECT_TRUE((Query::Double(1.0) == Query::Double(1.0f)).Evaluate(dummy));
+ EXPECT_TRUE((Query::Bool(true) == Query::Int(1)).Evaluate(dummy));
+ EXPECT_TRUE((Query::Bool(false) == Query::Int(0)).Evaluate(dummy));
+ EXPECT_TRUE((Query::Bool(true) == Query::Double(1.0f)).Evaluate(dummy));
+ EXPECT_TRUE((Query::Bool(false) == Query::Double(0.0f)).Evaluate(dummy));
+}
+
+// Test GetRateStats.
+TEST_F(TraceEventAnalyzerTest, RateStats) {
+ std::vector<TraceEvent> events;
+ events.reserve(100);
+ TraceEventVector event_ptrs;
+ TraceEvent event;
+ event.timestamp = 0.0;
+ double little_delta = 1.0;
+ double big_delta = 10.0;
+ double tiny_delta = 0.1;
+ RateStats stats;
+ RateStatsOptions options;
+
+ // Insert 10 events, each apart by little_delta.
+ for (int i = 0; i < 10; ++i) {
+ event.timestamp += little_delta;
+ events.push_back(event);
+ event_ptrs.push_back(&events.back());
+ }
+
+ ASSERT_TRUE(GetRateStats(event_ptrs, &stats, NULL));
+ EXPECT_EQ(little_delta, stats.mean_us);
+ EXPECT_EQ(little_delta, stats.min_us);
+ EXPECT_EQ(little_delta, stats.max_us);
+ EXPECT_EQ(0.0, stats.standard_deviation_us);
+
+ // Add an event apart by big_delta.
+ event.timestamp += big_delta;
+ events.push_back(event);
+ event_ptrs.push_back(&events.back());
+
+ ASSERT_TRUE(GetRateStats(event_ptrs, &stats, NULL));
+ EXPECT_LT(little_delta, stats.mean_us);
+ EXPECT_EQ(little_delta, stats.min_us);
+ EXPECT_EQ(big_delta, stats.max_us);
+ EXPECT_LT(0.0, stats.standard_deviation_us);
+
+ // Trim off the biggest delta and verify stats.
+ options.trim_min = 0;
+ options.trim_max = 1;
+ ASSERT_TRUE(GetRateStats(event_ptrs, &stats, &options));
+ EXPECT_EQ(little_delta, stats.mean_us);
+ EXPECT_EQ(little_delta, stats.min_us);
+ EXPECT_EQ(little_delta, stats.max_us);
+ EXPECT_EQ(0.0, stats.standard_deviation_us);
+
+ // Add an event apart by tiny_delta.
+ event.timestamp += tiny_delta;
+ events.push_back(event);
+ event_ptrs.push_back(&events.back());
+
+ // Trim off both the biggest and tiniest delta and verify stats.
+ options.trim_min = 1;
+ options.trim_max = 1;
+ ASSERT_TRUE(GetRateStats(event_ptrs, &stats, &options));
+ EXPECT_EQ(little_delta, stats.mean_us);
+ EXPECT_EQ(little_delta, stats.min_us);
+ EXPECT_EQ(little_delta, stats.max_us);
+ EXPECT_EQ(0.0, stats.standard_deviation_us);
+
+ // Verify smallest allowed number of events.
+ TraceEventVector few_event_ptrs;
+ few_event_ptrs.push_back(&event);
+ few_event_ptrs.push_back(&event);
+ ASSERT_FALSE(GetRateStats(few_event_ptrs, &stats, NULL));
+ few_event_ptrs.push_back(&event);
+ ASSERT_TRUE(GetRateStats(few_event_ptrs, &stats, NULL));
+
+ // Trim off more than allowed and verify failure.
+ options.trim_min = 0;
+ options.trim_max = 1;
+ ASSERT_FALSE(GetRateStats(few_event_ptrs, &stats, &options));
+}
+
+// Test FindFirstOf and FindLastOf.
+TEST_F(TraceEventAnalyzerTest, FindOf) {
+ size_t num_events = 100;
+ size_t index = 0;
+ TraceEventVector event_ptrs;
+ EXPECT_FALSE(FindFirstOf(event_ptrs, Query::Bool(true), 0, &index));
+ EXPECT_FALSE(FindFirstOf(event_ptrs, Query::Bool(true), 10, &index));
+ EXPECT_FALSE(FindLastOf(event_ptrs, Query::Bool(true), 0, &index));
+ EXPECT_FALSE(FindLastOf(event_ptrs, Query::Bool(true), 10, &index));
+
+ std::vector<TraceEvent> events;
+ events.resize(num_events);
+ for (size_t i = 0; i < events.size(); ++i)
+ event_ptrs.push_back(&events[i]);
+ size_t bam_index = num_events/2;
+ events[bam_index].name = "bam";
+ Query query_bam = Query::EventName() == Query::String(events[bam_index].name);
+
+ // FindFirstOf
+ EXPECT_FALSE(FindFirstOf(event_ptrs, Query::Bool(false), 0, &index));
+ EXPECT_TRUE(FindFirstOf(event_ptrs, Query::Bool(true), 0, &index));
+ EXPECT_EQ(0u, index);
+ EXPECT_TRUE(FindFirstOf(event_ptrs, Query::Bool(true), 5, &index));
+ EXPECT_EQ(5u, index);
+
+ EXPECT_FALSE(FindFirstOf(event_ptrs, query_bam, bam_index + 1, &index));
+ EXPECT_TRUE(FindFirstOf(event_ptrs, query_bam, 0, &index));
+ EXPECT_EQ(bam_index, index);
+ EXPECT_TRUE(FindFirstOf(event_ptrs, query_bam, bam_index, &index));
+ EXPECT_EQ(bam_index, index);
+
+ // FindLastOf
+ EXPECT_FALSE(FindLastOf(event_ptrs, Query::Bool(false), 1000, &index));
+ EXPECT_TRUE(FindLastOf(event_ptrs, Query::Bool(true), 1000, &index));
+ EXPECT_EQ(num_events - 1, index);
+ EXPECT_TRUE(FindLastOf(event_ptrs, Query::Bool(true), num_events - 5,
+ &index));
+ EXPECT_EQ(num_events - 5, index);
+
+ EXPECT_FALSE(FindLastOf(event_ptrs, query_bam, bam_index - 1, &index));
+ EXPECT_TRUE(FindLastOf(event_ptrs, query_bam, num_events, &index));
+ EXPECT_EQ(bam_index, index);
+ EXPECT_TRUE(FindLastOf(event_ptrs, query_bam, bam_index, &index));
+ EXPECT_EQ(bam_index, index);
+}
+
+// Test FindClosest.
+TEST_F(TraceEventAnalyzerTest, FindClosest) {
+ size_t index_1 = 0;
+ size_t index_2 = 0;
+ TraceEventVector event_ptrs;
+ EXPECT_FALSE(FindClosest(event_ptrs, Query::Bool(true), 0,
+ &index_1, &index_2));
+
+ size_t num_events = 5;
+ std::vector<TraceEvent> events;
+ events.resize(num_events);
+ for (size_t i = 0; i < events.size(); ++i) {
+ // timestamps go up exponentially so the lower index is always closer in
+ // time than the higher index.
+ events[i].timestamp = static_cast<double>(i) * static_cast<double>(i);
+ event_ptrs.push_back(&events[i]);
+ }
+ events[0].name = "one";
+ events[2].name = "two";
+ events[4].name = "three";
+ Query query_named = Query::EventName() != Query::String("");
+ Query query_one = Query::EventName() == Query::String("one");
+
+ // Only one event matches query_one, so two closest can't be found.
+ EXPECT_FALSE(FindClosest(event_ptrs, query_one, 0, &index_1, &index_2));
+
+ EXPECT_TRUE(FindClosest(event_ptrs, query_one, 3, &index_1, NULL));
+ EXPECT_EQ(0u, index_1);
+
+ EXPECT_TRUE(FindClosest(event_ptrs, query_named, 1, &index_1, &index_2));
+ EXPECT_EQ(0u, index_1);
+ EXPECT_EQ(2u, index_2);
+
+ EXPECT_TRUE(FindClosest(event_ptrs, query_named, 4, &index_1, &index_2));
+ EXPECT_EQ(4u, index_1);
+ EXPECT_EQ(2u, index_2);
+
+ EXPECT_TRUE(FindClosest(event_ptrs, query_named, 3, &index_1, &index_2));
+ EXPECT_EQ(2u, index_1);
+ EXPECT_EQ(0u, index_2);
+}
+
+// Test CountMatches.
+TEST_F(TraceEventAnalyzerTest, CountMatches) {
+ TraceEventVector event_ptrs;
+ EXPECT_EQ(0u, CountMatches(event_ptrs, Query::Bool(true), 0, 10));
+
+ size_t num_events = 5;
+ size_t num_named = 3;
+ std::vector<TraceEvent> events;
+ events.resize(num_events);
+ for (size_t i = 0; i < events.size(); ++i)
+ event_ptrs.push_back(&events[i]);
+ events[0].name = "one";
+ events[2].name = "two";
+ events[4].name = "three";
+ Query query_named = Query::EventName() != Query::String("");
+ Query query_one = Query::EventName() == Query::String("one");
+
+ EXPECT_EQ(0u, CountMatches(event_ptrs, Query::Bool(false)));
+ EXPECT_EQ(num_events, CountMatches(event_ptrs, Query::Bool(true)));
+ EXPECT_EQ(num_events - 1, CountMatches(event_ptrs, Query::Bool(true),
+ 1, num_events));
+ EXPECT_EQ(1u, CountMatches(event_ptrs, query_one));
+ EXPECT_EQ(num_events - 1, CountMatches(event_ptrs, !query_one));
+ EXPECT_EQ(num_named, CountMatches(event_ptrs, query_named));
+}
+
+
+} // namespace trace_analyzer
diff --git a/src/base/test/values_test_util.cc b/src/base/test/values_test_util.cc
new file mode 100644
index 0000000..c36e7b2
--- /dev/null
+++ b/src/base/test/values_test_util.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2012 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/test/values_test_util.h"
+
+#include "base/json/json_reader.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_number_conversions.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+void ExpectDictBooleanValue(bool expected_value,
+ const DictionaryValue& value,
+ const std::string& key) {
+ bool boolean_value = false;
+ EXPECT_TRUE(value.GetBoolean(key, &boolean_value)) << key;
+ EXPECT_EQ(expected_value, boolean_value) << key;
+}
+
+void ExpectDictDictionaryValue(const DictionaryValue& expected_value,
+ const DictionaryValue& value,
+ const std::string& key) {
+ const DictionaryValue* dict_value = NULL;
+ EXPECT_TRUE(value.GetDictionary(key, &dict_value)) << key;
+ EXPECT_TRUE(Value::Equals(dict_value, &expected_value)) << key;
+}
+
+void ExpectDictIntegerValue(int expected_value,
+ const DictionaryValue& value,
+ const std::string& key) {
+ int integer_value = 0;
+ EXPECT_TRUE(value.GetInteger(key, &integer_value)) << key;
+ EXPECT_EQ(expected_value, integer_value) << key;
+}
+
+void ExpectDictListValue(const ListValue& expected_value,
+ const DictionaryValue& value,
+ const std::string& key) {
+ const ListValue* list_value = NULL;
+ EXPECT_TRUE(value.GetList(key, &list_value)) << key;
+ EXPECT_TRUE(Value::Equals(list_value, &expected_value)) << key;
+}
+
+void ExpectDictStringValue(const std::string& expected_value,
+ const DictionaryValue& value,
+ const std::string& key) {
+ std::string string_value;
+ EXPECT_TRUE(value.GetString(key, &string_value)) << key;
+ EXPECT_EQ(expected_value, string_value) << key;
+}
+
+void ExpectStringValue(const std::string& expected_str,
+ StringValue* actual) {
+ scoped_ptr<StringValue> scoped_actual(actual);
+ std::string actual_str;
+ EXPECT_TRUE(scoped_actual->GetAsString(&actual_str));
+ EXPECT_EQ(expected_str, actual_str);
+}
+
+namespace test {
+
+scoped_ptr<Value> ParseJson(base::StringPiece json) {
+ std::string error_msg;
+ scoped_ptr<Value> result(base::JSONReader::ReadAndReturnError(
+ json, base::JSON_ALLOW_TRAILING_COMMAS,
+ NULL, &error_msg));
+ if (!result) {
+ ADD_FAILURE() << "Failed to parse \"" << json << "\": " << error_msg;
+ result.reset(Value::CreateNullValue());
+ }
+ return result.Pass();
+}
+
+} // namespace test
+} // namespace base
diff --git a/src/base/test/values_test_util.h b/src/base/test/values_test_util.h
new file mode 100644
index 0000000..ef4710d
--- /dev/null
+++ b/src/base/test/values_test_util.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 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_TEST_VALUES_TEST_UTIL_H_
+#define BASE_TEST_VALUES_TEST_UTIL_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/string_piece.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+class StringValue;
+class Value;
+
+// All the functions below expect that the value for the given key in
+// the given dictionary equals the given expected value.
+
+void ExpectDictBooleanValue(bool expected_value,
+ const DictionaryValue& value,
+ const std::string& key);
+
+void ExpectDictDictionaryValue(const DictionaryValue& expected_value,
+ const DictionaryValue& value,
+ const std::string& key);
+
+void ExpectDictIntegerValue(int expected_value,
+ const DictionaryValue& value,
+ const std::string& key);
+
+void ExpectDictListValue(const ListValue& expected_value,
+ const DictionaryValue& value,
+ const std::string& key);
+
+void ExpectDictStringValue(const std::string& expected_value,
+ const DictionaryValue& value,
+ const std::string& key);
+
+// Takes ownership of |actual|.
+void ExpectStringValue(const std::string& expected_str,
+ StringValue* actual);
+
+namespace test {
+
+// Parses |json| as JSON, allowing trailing commas, and returns the
+// resulting value. If the json fails to parse, causes an EXPECT
+// failure and returns the Null Value (but never a NULL pointer).
+scoped_ptr<Value> ParseJson(base::StringPiece json);
+
+} // namespace test
+} // namespace base
+
+#endif // BASE_TEST_VALUES_TEST_UTIL_H_
diff --git a/src/base/third_party/dmg_fp/LICENSE b/src/base/third_party/dmg_fp/LICENSE
new file mode 100644
index 0000000..716f1ef
--- /dev/null
+++ b/src/base/third_party/dmg_fp/LICENSE
@@ -0,0 +1,18 @@
+/****************************************************************
+ *
+ * The author of this software is David M. Gay.
+ *
+ * Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ ***************************************************************/
diff --git a/src/base/third_party/dmg_fp/README.chromium b/src/base/third_party/dmg_fp/README.chromium
new file mode 100644
index 0000000..10db408
--- /dev/null
+++ b/src/base/third_party/dmg_fp/README.chromium
@@ -0,0 +1,22 @@
+Name: David M. Gay's floating point routines
+URL: http://www.netlib.org/fp/
+License: MIT-like
+
+Original dtoa.c file can be found at <http://www.netlib.org/fp/dtoa.c>.
+Original g_fmt.c file can be found at <http://www.netlib.org/fp/g_fmt.c>.
+
+List of changes made to original code:
+ - wrapped functions in dmg_fp namespace
+ - renamed .c files to .cc
+ - added dmg_fp.h header
+ - added #define IEEE_8087 to dtoa.cc
+ - added #define NO_HEX_FP to dtoa.cc
+ - made some minor changes to allow clean compilation under g++ -Wall, see
+ gcc_warnings.patch.
+ - made some minor changes to build on 64-bit, see gcc_64_bit.patch.
+ - made minor changes for -Wextra for Mac build, see mac_wextra.patch
+ - crash fix for running with reduced CPU float precision, see
+ float_precision_crash.patch and crbug.com/123157
+ - Fix for 'warning C4703: potentially uninitialized local pointer variable'
+ in VS2012.
+
diff --git a/src/base/third_party/dmg_fp/dmg_fp.h b/src/base/third_party/dmg_fp/dmg_fp.h
new file mode 100644
index 0000000..4795397
--- /dev/null
+++ b/src/base/third_party/dmg_fp/dmg_fp.h
@@ -0,0 +1,30 @@
+// 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.
+
+#ifndef THIRD_PARTY_DMG_FP_H_
+#define THIRD_PARTY_DMG_FP_H_
+
+namespace dmg_fp {
+
+// Return a nearest machine number to the input decimal
+// string (or set errno to ERANGE). With IEEE arithmetic, ties are
+// broken by the IEEE round-even rule. Otherwise ties are broken by
+// biased rounding (add half and chop).
+double strtod(const char* s00, char** se);
+
+// Convert double to ASCII string. For meaning of parameters
+// see dtoa.cc file.
+char* dtoa(double d, int mode, int ndigits,
+ int* decpt, int* sign, char** rve);
+
+// Must be used to free values returned by dtoa.
+void freedtoa(char* s);
+
+// Store the closest decimal approximation to x in b (null terminated).
+// Returns a pointer to b. It is sufficient for |b| to be 32 characters.
+char* g_fmt(char* b, double x);
+
+} // namespace dmg_fp
+
+#endif // THIRD_PARTY_DMG_FP_H_
diff --git a/src/base/third_party/dmg_fp/dtoa.cc b/src/base/third_party/dmg_fp/dtoa.cc
new file mode 100644
index 0000000..1339d3f
--- /dev/null
+++ b/src/base/third_party/dmg_fp/dtoa.cc
@@ -0,0 +1,4219 @@
+/****************************************************************
+ *
+ * The author of this software is David M. Gay.
+ *
+ * Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ ***************************************************************/
+
+/* Please send bug reports to David M. Gay (dmg at acm dot org,
+ * with " at " changed at "@" and " dot " changed to "."). */
+
+/* On a machine with IEEE extended-precision registers, it is
+ * necessary to specify double-precision (53-bit) rounding precision
+ * before invoking strtod or dtoa. If the machine uses (the equivalent
+ * of) Intel 80x87 arithmetic, the call
+ * _control87(PC_53, MCW_PC);
+ * does this with many compilers. Whether this or another call is
+ * appropriate depends on the compiler; for this to work, it may be
+ * necessary to #include "float.h" or another system-dependent header
+ * file.
+ */
+
+/* strtod for IEEE-, VAX-, and IBM-arithmetic machines.
+ *
+ * This strtod returns a nearest machine number to the input decimal
+ * string (or sets errno to ERANGE). With IEEE arithmetic, ties are
+ * broken by the IEEE round-even rule. Otherwise ties are broken by
+ * biased rounding (add half and chop).
+ *
+ * Inspired loosely by William D. Clinger's paper "How to Read Floating
+ * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101].
+ *
+ * Modifications:
+ *
+ * 1. We only require IEEE, IBM, or VAX double-precision
+ * arithmetic (not IEEE double-extended).
+ * 2. We get by with floating-point arithmetic in a case that
+ * Clinger missed -- when we're computing d * 10^n
+ * for a small integer d and the integer n is not too
+ * much larger than 22 (the maximum integer k for which
+ * we can represent 10^k exactly), we may be able to
+ * compute (d*10^k) * 10^(e-k) with just one roundoff.
+ * 3. Rather than a bit-at-a-time adjustment of the binary
+ * result in the hard case, we use floating-point
+ * arithmetic to determine the adjustment to within
+ * one bit; only in really hard cases do we need to
+ * compute a second residual.
+ * 4. Because of 3., we don't need a large table of powers of 10
+ * for ten-to-e (just some small tables, e.g. of 10^k
+ * for 0 <= k <= 22).
+ */
+
+/*
+ * #define IEEE_8087 for IEEE-arithmetic machines where the least
+ * significant byte has the lowest address.
+ * #define IEEE_MC68k for IEEE-arithmetic machines where the most
+ * significant byte has the lowest address.
+ * #define Long int on machines with 32-bit ints and 64-bit longs.
+ * #define IBM for IBM mainframe-style floating-point arithmetic.
+ * #define VAX for VAX-style floating-point arithmetic (D_floating).
+ * #define No_leftright to omit left-right logic in fast floating-point
+ * computation of dtoa.
+ * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3
+ * and strtod and dtoa should round accordingly. Unless Trust_FLT_ROUNDS
+ * is also #defined, fegetround() will be queried for the rounding mode.
+ * Note that both FLT_ROUNDS and fegetround() are specified by the C99
+ * standard (and are specified to be consistent, with fesetround()
+ * affecting the value of FLT_ROUNDS), but that some (Linux) systems
+ * do not work correctly in this regard, so using fegetround() is more
+ * portable than using FLT_FOUNDS directly.
+ * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3
+ * and Honor_FLT_ROUNDS is not #defined.
+ * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines
+ * that use extended-precision instructions to compute rounded
+ * products and quotients) with IBM.
+ * #define ROUND_BIASED for IEEE-format with biased rounding.
+ * #define Inaccurate_Divide for IEEE-format with correctly rounded
+ * products but inaccurate quotients, e.g., for Intel i860.
+ * #define NO_LONG_LONG on machines that do not have a "long long"
+ * integer type (of >= 64 bits). On such machines, you can
+ * #define Just_16 to store 16 bits per 32-bit Long when doing
+ * high-precision integer arithmetic. Whether this speeds things
+ * up or slows things down depends on the machine and the number
+ * being converted. If long long is available and the name is
+ * something other than "long long", #define Llong to be the name,
+ * and if "unsigned Llong" does not work as an unsigned version of
+ * Llong, #define #ULLong to be the corresponding unsigned type.
+ * #define KR_headers for old-style C function headers.
+ * #define Bad_float_h if your system lacks a float.h or if it does not
+ * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP,
+ * FLT_RADIX, FLT_ROUNDS, and DBL_MAX.
+ * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n)
+ * if memory is available and otherwise does something you deem
+ * appropriate. If MALLOC is undefined, malloc will be invoked
+ * directly -- and assumed always to succeed. Similarly, if you
+ * want something other than the system's free() to be called to
+ * recycle memory acquired from MALLOC, #define FREE to be the
+ * name of the alternate routine. (FREE or free is only called in
+ * pathological cases, e.g., in a dtoa call after a dtoa return in
+ * mode 3 with thousands of digits requested.)
+ * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making
+ * memory allocations from a private pool of memory when possible.
+ * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes,
+ * unless #defined to be a different length. This default length
+ * suffices to get rid of MALLOC calls except for unusual cases,
+ * such as decimal-to-binary conversion of a very long string of
+ * digits. The longest string dtoa can return is about 751 bytes
+ * long. For conversions by strtod of strings of 800 digits and
+ * all dtoa conversions in single-threaded executions with 8-byte
+ * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte
+ * pointers, PRIVATE_MEM >= 7112 appears adequate.
+ * #define NO_INFNAN_CHECK if you do not wish to have INFNAN_CHECK
+ * #defined automatically on IEEE systems. On such systems,
+ * when INFNAN_CHECK is #defined, strtod checks
+ * for Infinity and NaN (case insensitively). On some systems
+ * (e.g., some HP systems), it may be necessary to #define NAN_WORD0
+ * appropriately -- to the most significant word of a quiet NaN.
+ * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.)
+ * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined,
+ * strtod also accepts (case insensitively) strings of the form
+ * NaN(x), where x is a string of hexadecimal digits and spaces;
+ * if there is only one string of hexadecimal digits, it is taken
+ * for the 52 fraction bits of the resulting NaN; if there are two
+ * or more strings of hex digits, the first is for the high 20 bits,
+ * the second and subsequent for the low 32 bits, with intervening
+ * white space ignored; but if this results in none of the 52
+ * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0
+ * and NAN_WORD1 are used instead.
+ * #define MULTIPLE_THREADS if the system offers preemptively scheduled
+ * multiple threads. In this case, you must provide (or suitably
+ * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed
+ * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed
+ * in pow5mult, ensures lazy evaluation of only one copy of high
+ * powers of 5; omitting this lock would introduce a small
+ * probability of wasting memory, but would otherwise be harmless.)
+ * You must also invoke freedtoa(s) to free the value s returned by
+ * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined.
+ * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that
+ * avoids underflows on inputs whose result does not underflow.
+ * If you #define NO_IEEE_Scale on a machine that uses IEEE-format
+ * floating-point numbers and flushes underflows to zero rather
+ * than implementing gradual underflow, then you must also #define
+ * Sudden_Underflow.
+ * #define USE_LOCALE to use the current locale's decimal_point value.
+ * #define SET_INEXACT if IEEE arithmetic is being used and extra
+ * computation should be done to set the inexact flag when the
+ * result is inexact and avoid setting inexact when the result
+ * is exact. In this case, dtoa.c must be compiled in
+ * an environment, perhaps provided by #include "dtoa.c" in a
+ * suitable wrapper, that defines two functions,
+ * int get_inexact(void);
+ * void clear_inexact(void);
+ * such that get_inexact() returns a nonzero value if the
+ * inexact bit is already set, and clear_inexact() sets the
+ * inexact bit to 0. When SET_INEXACT is #defined, strtod
+ * also does extra computations to set the underflow and overflow
+ * flags when appropriate (i.e., when the result is tiny and
+ * inexact or when it is a numeric value rounded to +-infinity).
+ * #define NO_ERRNO if strtod should not assign errno = ERANGE when
+ * the result overflows to +-Infinity or underflows to 0.
+ * #define NO_HEX_FP to omit recognition of hexadecimal floating-point
+ * values by strtod.
+ * #define NO_STRTOD_BIGCOMP (on IEEE-arithmetic systems only for now)
+ * to disable logic for "fast" testing of very long input strings
+ * to strtod. This testing proceeds by initially truncating the
+ * input string, then if necessary comparing the whole string with
+ * a decimal expansion to decide close cases. This logic is only
+ * used for input more than STRTOD_DIGLIM digits long (default 40).
+ */
+
+#if defined(ARCH_CPU_BIG_ENDIAN)
+#define IEEE_MC68k
+#else
+#define IEEE_8087
+#endif
+#define NO_HEX_FP
+
+#ifndef Long
+#if __LP64__
+#define Long int
+#else
+#define Long long
+#endif
+#endif
+#ifndef ULong
+typedef unsigned Long ULong;
+#endif
+
+#ifdef DEBUG
+#include "stdio.h"
+#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);}
+#endif
+
+#if defined(STARBOARD)
+#include "starboard/memory.h"
+#define MALLOC SbMemoryAllocate
+#define FREE SbMemoryFree
+#else
+#include "stdlib.h"
+#include "string.h"
+#endif
+
+#ifdef USE_LOCALE
+#include "locale.h"
+#endif
+
+#ifdef Honor_FLT_ROUNDS
+#ifndef Trust_FLT_ROUNDS
+#include <fenv.h>
+#endif
+#endif
+
+#ifdef MALLOC
+#ifdef KR_headers
+extern char *MALLOC();
+#else
+extern void *MALLOC(size_t);
+#endif
+#else
+#define MALLOC malloc
+#endif
+
+#ifndef Omit_Private_Memory
+#ifndef PRIVATE_MEM
+#define PRIVATE_MEM 2304
+#endif
+#define PRIVATE_mem ((unsigned)((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)))
+static double private_mem[PRIVATE_mem], *pmem_next = private_mem;
+#endif
+
+#undef IEEE_Arith
+#undef Avoid_Underflow
+#ifdef IEEE_MC68k
+#define IEEE_Arith
+#endif
+#ifdef IEEE_8087
+#define IEEE_Arith
+#endif
+
+#ifdef IEEE_Arith
+#ifndef NO_INFNAN_CHECK
+#undef INFNAN_CHECK
+#define INFNAN_CHECK
+#endif
+#else
+#undef INFNAN_CHECK
+#define NO_STRTOD_BIGCOMP
+#endif
+
+#include "errno.h"
+
+#ifdef Bad_float_h
+
+#ifdef IEEE_Arith
+#define DBL_DIG 15
+#define DBL_MAX_10_EXP 308
+#define DBL_MAX_EXP 1024
+#define FLT_RADIX 2
+#endif /*IEEE_Arith*/
+
+#ifdef IBM
+#define DBL_DIG 16
+#define DBL_MAX_10_EXP 75
+#define DBL_MAX_EXP 63
+#define FLT_RADIX 16
+#define DBL_MAX 7.2370055773322621e+75
+#endif
+
+#ifdef VAX
+#define DBL_DIG 16
+#define DBL_MAX_10_EXP 38
+#define DBL_MAX_EXP 127
+#define FLT_RADIX 2
+#define DBL_MAX 1.7014118346046923e+38
+#endif
+
+#ifndef LONG_MAX
+#define LONG_MAX 2147483647
+#endif
+
+#else /* ifndef Bad_float_h */
+#include "float.h"
+#endif /* Bad_float_h */
+
+#ifndef __MATH_H__
+#include "math.h"
+#endif
+
+namespace dmg_fp {
+
+#ifndef CONST
+#ifdef KR_headers
+#define CONST /* blank */
+#else
+#define CONST const
+#endif
+#endif
+
+#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1
+Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined.
+#endif
+
+typedef union { double d; ULong L[2]; } U;
+
+#ifdef IEEE_8087
+#define word0(x) (x)->L[1]
+#define word1(x) (x)->L[0]
+#else
+#define word0(x) (x)->L[0]
+#define word1(x) (x)->L[1]
+#endif
+#define dval(x) (x)->d
+
+#ifndef STRTOD_DIGLIM
+#define STRTOD_DIGLIM 40
+#endif
+
+#ifdef DIGLIM_DEBUG
+extern int strtod_diglim;
+#else
+#define strtod_diglim STRTOD_DIGLIM
+#endif
+
+/* The following definition of Storeinc is appropriate for MIPS processors.
+ * An alternative that might be better on some machines is
+ * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff)
+ */
+#if defined(IEEE_8087) + defined(VAX)
+#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \
+((unsigned short *)a)[0] = (unsigned short)c, a++)
+#else
+#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \
+((unsigned short *)a)[1] = (unsigned short)c, a++)
+#endif
+
+/* #define P DBL_MANT_DIG */
+/* Ten_pmax = floor(P*log(2)/log(5)) */
+/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */
+/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */
+/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */
+
+#ifdef IEEE_Arith
+#define Exp_shift 20
+#define Exp_shift1 20
+#define Exp_msk1 0x100000
+#define Exp_msk11 0x100000
+#define Exp_mask 0x7ff00000
+#define P 53
+#define Nbits 53
+#define Bias 1023
+#define Emax 1023
+#define Emin (-1022)
+#define Exp_1 0x3ff00000
+#define Exp_11 0x3ff00000
+#define Ebits 11
+#define Frac_mask 0xfffff
+#define Frac_mask1 0xfffff
+#define Ten_pmax 22
+#define Bletch 0x10
+#define Bndry_mask 0xfffff
+#define Bndry_mask1 0xfffff
+#define LSB 1
+#define Sign_bit 0x80000000
+#define Log2P 1
+#define Tiny0 0
+#define Tiny1 1
+#define Quick_max 14
+#define Int_max 14
+#ifndef NO_IEEE_Scale
+#define Avoid_Underflow
+#ifdef Flush_Denorm /* debugging option */
+#undef Sudden_Underflow
+#endif
+#endif
+
+#ifndef Flt_Rounds
+#ifdef FLT_ROUNDS
+#define Flt_Rounds FLT_ROUNDS
+#else
+#define Flt_Rounds 1
+#endif
+#endif /*Flt_Rounds*/
+
+#ifdef Honor_FLT_ROUNDS
+#undef Check_FLT_ROUNDS
+#define Check_FLT_ROUNDS
+#else
+#define Rounding Flt_Rounds
+#endif
+
+#else /* ifndef IEEE_Arith */
+#undef Check_FLT_ROUNDS
+#undef Honor_FLT_ROUNDS
+#undef SET_INEXACT
+#undef Sudden_Underflow
+#define Sudden_Underflow
+#ifdef IBM
+#undef Flt_Rounds
+#define Flt_Rounds 0
+#define Exp_shift 24
+#define Exp_shift1 24
+#define Exp_msk1 0x1000000
+#define Exp_msk11 0x1000000
+#define Exp_mask 0x7f000000
+#define P 14
+#define Nbits 56
+#define Bias 65
+#define Emax 248
+#define Emin (-260)
+#define Exp_1 0x41000000
+#define Exp_11 0x41000000
+#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */
+#define Frac_mask 0xffffff
+#define Frac_mask1 0xffffff
+#define Bletch 4
+#define Ten_pmax 22
+#define Bndry_mask 0xefffff
+#define Bndry_mask1 0xffffff
+#define LSB 1
+#define Sign_bit 0x80000000
+#define Log2P 4
+#define Tiny0 0x100000
+#define Tiny1 0
+#define Quick_max 14
+#define Int_max 15
+#else /* VAX */
+#undef Flt_Rounds
+#define Flt_Rounds 1
+#define Exp_shift 23
+#define Exp_shift1 7
+#define Exp_msk1 0x80
+#define Exp_msk11 0x800000
+#define Exp_mask 0x7f80
+#define P 56
+#define Nbits 56
+#define Bias 129
+#define Emax 126
+#define Emin (-129)
+#define Exp_1 0x40800000
+#define Exp_11 0x4080
+#define Ebits 8
+#define Frac_mask 0x7fffff
+#define Frac_mask1 0xffff007f
+#define Ten_pmax 24
+#define Bletch 2
+#define Bndry_mask 0xffff007f
+#define Bndry_mask1 0xffff007f
+#define LSB 0x10000
+#define Sign_bit 0x8000
+#define Log2P 1
+#define Tiny0 0x80
+#define Tiny1 0
+#define Quick_max 15
+#define Int_max 15
+#endif /* IBM, VAX */
+#endif /* IEEE_Arith */
+
+#ifndef IEEE_Arith
+#define ROUND_BIASED
+#endif
+
+#ifdef RND_PRODQUOT
+#define rounded_product(a,b) a = rnd_prod(a, b)
+#define rounded_quotient(a,b) a = rnd_quot(a, b)
+#ifdef KR_headers
+extern double rnd_prod(), rnd_quot();
+#else
+extern double rnd_prod(double, double), rnd_quot(double, double);
+#endif
+#else
+#define rounded_product(a,b) a *= b
+#define rounded_quotient(a,b) a /= b
+#endif
+
+#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1))
+#define Big1 0xffffffff
+
+#ifndef Pack_32
+#define Pack_32
+#endif
+
+typedef struct BCinfo BCinfo;
+ struct
+BCinfo { int dp0, dp1, dplen, dsign, e0, inexact, nd, nd0, rounding, scale, uflchk; };
+
+#ifdef KR_headers
+#define FFFFFFFF ((((unsigned long)0xffff)<<16)|(unsigned long)0xffff)
+#else
+#define FFFFFFFF 0xffffffffUL
+#endif
+
+#ifdef NO_LONG_LONG
+#undef ULLong
+#ifdef Just_16
+#undef Pack_32
+/* When Pack_32 is not defined, we store 16 bits per 32-bit Long.
+ * This makes some inner loops simpler and sometimes saves work
+ * during multiplications, but it often seems to make things slightly
+ * slower. Hence the default is now to store 32 bits per Long.
+ */
+#endif
+#else /* long long available */
+#ifndef Llong
+#define Llong long long
+#endif
+#ifndef ULLong
+#define ULLong unsigned Llong
+#endif
+#endif /* NO_LONG_LONG */
+
+#ifndef MULTIPLE_THREADS
+#define ACQUIRE_DTOA_LOCK(n) /*nothing*/
+#define FREE_DTOA_LOCK(n) /*nothing*/
+#endif
+
+#define Kmax 7
+
+double strtod(const char *s00, char **se);
+char *dtoa(double d, int mode, int ndigits,
+ int *decpt, int *sign, char **rve);
+
+ struct
+Bigint {
+ struct Bigint *next;
+ int k, maxwds, sign, wds;
+ ULong x[1];
+ };
+
+ typedef struct Bigint Bigint;
+
+ static Bigint *freelist[Kmax+1];
+
+ static Bigint *
+Balloc
+#ifdef KR_headers
+ (k) int k;
+#else
+ (int k)
+#endif
+{
+ int x;
+ Bigint *rv;
+#ifndef Omit_Private_Memory
+ unsigned int len;
+#endif
+
+ ACQUIRE_DTOA_LOCK(0);
+ /* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0), */
+ /* but this case seems very unlikely. */
+ if (k <= Kmax && (rv = freelist[k]))
+ freelist[k] = rv->next;
+ else {
+ x = 1 << k;
+#ifdef Omit_Private_Memory
+ rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong));
+#else
+ len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1)
+ /sizeof(double);
+ if (k <= Kmax && pmem_next - private_mem + len <= PRIVATE_mem) {
+ rv = (Bigint*)pmem_next;
+ pmem_next += len;
+ }
+ else
+ rv = (Bigint*)MALLOC(len*sizeof(double));
+#endif
+ rv->k = k;
+ rv->maxwds = x;
+ }
+ FREE_DTOA_LOCK(0);
+ rv->sign = rv->wds = 0;
+ return rv;
+ }
+
+ static void
+Bfree
+#ifdef KR_headers
+ (v) Bigint *v;
+#else
+ (Bigint *v)
+#endif
+{
+ if (v) {
+ if (v->k > Kmax)
+#ifdef FREE
+ FREE((void*)v);
+#else
+ free((void*)v);
+#endif
+ else {
+ ACQUIRE_DTOA_LOCK(0);
+ v->next = freelist[v->k];
+ freelist[v->k] = v;
+ FREE_DTOA_LOCK(0);
+ }
+ }
+ }
+
+#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \
+y->wds*sizeof(Long) + 2*sizeof(int))
+
+ static Bigint *
+multadd
+#ifdef KR_headers
+ (b, m, a) Bigint *b; int m, a;
+#else
+ (Bigint *b, int m, int a) /* multiply by m and add a */
+#endif
+{
+ int i, wds;
+#ifdef ULLong
+ ULong *x;
+ ULLong carry, y;
+#else
+ ULong carry, *x, y;
+#ifdef Pack_32
+ ULong xi, z;
+#endif
+#endif
+ Bigint *b1;
+
+ wds = b->wds;
+ x = b->x;
+ i = 0;
+ carry = a;
+ do {
+#ifdef ULLong
+ y = *x * (ULLong)m + carry;
+ carry = y >> 32;
+ *x++ = y & FFFFFFFF;
+#else
+#ifdef Pack_32
+ xi = *x;
+ y = (xi & 0xffff) * m + carry;
+ z = (xi >> 16) * m + (y >> 16);
+ carry = z >> 16;
+ *x++ = (z << 16) + (y & 0xffff);
+#else
+ y = *x * m + carry;
+ carry = y >> 16;
+ *x++ = y & 0xffff;
+#endif
+#endif
+ }
+ while(++i < wds);
+ if (carry) {
+ if (wds >= b->maxwds) {
+ b1 = Balloc(b->k+1);
+ Bcopy(b1, b);
+ Bfree(b);
+ b = b1;
+ }
+ b->x[wds++] = carry;
+ b->wds = wds;
+ }
+ return b;
+ }
+
+ static Bigint *
+s2b
+#ifdef KR_headers
+ (s, nd0, nd, y9, dplen) CONST char *s; int nd0, nd, dplen; ULong y9;
+#else
+ (CONST char *s, int nd0, int nd, ULong y9, int dplen)
+#endif
+{
+ Bigint *b;
+ int i, k;
+ Long x, y;
+
+ x = (nd + 8) / 9;
+ for(k = 0, y = 1; x > y; y <<= 1, k++) ;
+#ifdef Pack_32
+ b = Balloc(k);
+ b->x[0] = y9;
+ b->wds = 1;
+#else
+ b = Balloc(k+1);
+ b->x[0] = y9 & 0xffff;
+ b->wds = (b->x[1] = y9 >> 16) ? 2 : 1;
+#endif
+
+ i = 9;
+ if (9 < nd0) {
+ s += 9;
+ do b = multadd(b, 10, *s++ - '0');
+ while(++i < nd0);
+ s += dplen;
+ }
+ else
+ s += dplen + 9;
+ for(; i < nd; i++)
+ b = multadd(b, 10, *s++ - '0');
+ return b;
+ }
+
+ static int
+hi0bits
+#ifdef KR_headers
+ (x) ULong x;
+#else
+ (ULong x)
+#endif
+{
+ int k = 0;
+
+ if (!(x & 0xffff0000)) {
+ k = 16;
+ x <<= 16;
+ }
+ if (!(x & 0xff000000)) {
+ k += 8;
+ x <<= 8;
+ }
+ if (!(x & 0xf0000000)) {
+ k += 4;
+ x <<= 4;
+ }
+ if (!(x & 0xc0000000)) {
+ k += 2;
+ x <<= 2;
+ }
+ if (!(x & 0x80000000)) {
+ k++;
+ if (!(x & 0x40000000))
+ return 32;
+ }
+ return k;
+ }
+
+ static int
+lo0bits
+#ifdef KR_headers
+ (y) ULong *y;
+#else
+ (ULong *y)
+#endif
+{
+ int k;
+ ULong x = *y;
+
+ if (x & 7) {
+ if (x & 1)
+ return 0;
+ if (x & 2) {
+ *y = x >> 1;
+ return 1;
+ }
+ *y = x >> 2;
+ return 2;
+ }
+ k = 0;
+ if (!(x & 0xffff)) {
+ k = 16;
+ x >>= 16;
+ }
+ if (!(x & 0xff)) {
+ k += 8;
+ x >>= 8;
+ }
+ if (!(x & 0xf)) {
+ k += 4;
+ x >>= 4;
+ }
+ if (!(x & 0x3)) {
+ k += 2;
+ x >>= 2;
+ }
+ if (!(x & 1)) {
+ k++;
+ x >>= 1;
+ if (!x)
+ return 32;
+ }
+ *y = x;
+ return k;
+ }
+
+ static Bigint *
+i2b
+#ifdef KR_headers
+ (i) int i;
+#else
+ (int i)
+#endif
+{
+ Bigint *b;
+
+ b = Balloc(1);
+ b->x[0] = i;
+ b->wds = 1;
+ return b;
+ }
+
+ static Bigint *
+mult
+#ifdef KR_headers
+ (a, b) Bigint *a, *b;
+#else
+ (Bigint *a, Bigint *b)
+#endif
+{
+ Bigint *c;
+ int k, wa, wb, wc;
+ ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0;
+ ULong y;
+#ifdef ULLong
+ ULLong carry, z;
+#else
+ ULong carry, z;
+#ifdef Pack_32
+ ULong z2;
+#endif
+#endif
+
+ if (a->wds < b->wds) {
+ c = a;
+ a = b;
+ b = c;
+ }
+ k = a->k;
+ wa = a->wds;
+ wb = b->wds;
+ wc = wa + wb;
+ if (wc > a->maxwds)
+ k++;
+ c = Balloc(k);
+ for(x = c->x, xa = x + wc; x < xa; x++)
+ *x = 0;
+ xa = a->x;
+ xae = xa + wa;
+ xb = b->x;
+ xbe = xb + wb;
+ xc0 = c->x;
+#ifdef ULLong
+ for(; xb < xbe; xc0++) {
+ if ((y = *xb++)) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ do {
+ z = *x++ * (ULLong)y + *xc + carry;
+ carry = z >> 32;
+ *xc++ = z & FFFFFFFF;
+ }
+ while(x < xae);
+ *xc = carry;
+ }
+ }
+#else
+#ifdef Pack_32
+ for(; xb < xbe; xb++, xc0++) {
+ if (y = *xb & 0xffff) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ do {
+ z = (*x & 0xffff) * y + (*xc & 0xffff) + carry;
+ carry = z >> 16;
+ z2 = (*x++ >> 16) * y + (*xc >> 16) + carry;
+ carry = z2 >> 16;
+ Storeinc(xc, z2, z);
+ }
+ while(x < xae);
+ *xc = carry;
+ }
+ if (y = *xb >> 16) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ z2 = *xc;
+ do {
+ z = (*x & 0xffff) * y + (*xc >> 16) + carry;
+ carry = z >> 16;
+ Storeinc(xc, z, z2);
+ z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry;
+ carry = z2 >> 16;
+ }
+ while(x < xae);
+ *xc = z2;
+ }
+ }
+#else
+ for(; xb < xbe; xc0++) {
+ if (y = *xb++) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ do {
+ z = *x++ * y + *xc + carry;
+ carry = z >> 16;
+ *xc++ = z & 0xffff;
+ }
+ while(x < xae);
+ *xc = carry;
+ }
+ }
+#endif
+#endif
+ for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ;
+ c->wds = wc;
+ return c;
+ }
+
+ static Bigint *p5s;
+
+ static Bigint *
+pow5mult
+#ifdef KR_headers
+ (b, k) Bigint *b; int k;
+#else
+ (Bigint *b, int k)
+#endif
+{
+ Bigint *b1, *p5, *p51;
+ int i;
+ static int p05[3] = { 5, 25, 125 };
+
+ if ((i = k & 3))
+ b = multadd(b, p05[i-1], 0);
+
+ if (!(k >>= 2))
+ return b;
+ if (!(p5 = p5s)) {
+ /* first time */
+#ifdef MULTIPLE_THREADS
+ ACQUIRE_DTOA_LOCK(1);
+ if (!(p5 = p5s)) {
+ p5 = p5s = i2b(625);
+ p5->next = 0;
+ }
+ FREE_DTOA_LOCK(1);
+#else
+ p5 = p5s = i2b(625);
+ p5->next = 0;
+#endif
+ }
+ for(;;) {
+ if (k & 1) {
+ b1 = mult(b, p5);
+ Bfree(b);
+ b = b1;
+ }
+ if (!(k >>= 1))
+ break;
+ if (!(p51 = p5->next)) {
+#ifdef MULTIPLE_THREADS
+ ACQUIRE_DTOA_LOCK(1);
+ if (!(p51 = p5->next)) {
+ p51 = p5->next = mult(p5,p5);
+ p51->next = 0;
+ }
+ FREE_DTOA_LOCK(1);
+#else
+ p51 = p5->next = mult(p5,p5);
+ p51->next = 0;
+#endif
+ }
+ p5 = p51;
+ }
+ return b;
+ }
+
+ static Bigint *
+lshift
+#ifdef KR_headers
+ (b, k) Bigint *b; int k;
+#else
+ (Bigint *b, int k)
+#endif
+{
+ int i, k1, n, n1;
+ Bigint *b1;
+ ULong *x, *x1, *xe, z;
+
+#ifdef Pack_32
+ n = k >> 5;
+#else
+ n = k >> 4;
+#endif
+ k1 = b->k;
+ n1 = n + b->wds + 1;
+ for(i = b->maxwds; n1 > i; i <<= 1)
+ k1++;
+ b1 = Balloc(k1);
+ x1 = b1->x;
+ for(i = 0; i < n; i++)
+ *x1++ = 0;
+ x = b->x;
+ xe = x + b->wds;
+#ifdef Pack_32
+ if (k &= 0x1f) {
+ k1 = 32 - k;
+ z = 0;
+ do {
+ *x1++ = *x << k | z;
+ z = *x++ >> k1;
+ }
+ while(x < xe);
+ if ((*x1 = z))
+ ++n1;
+ }
+#else
+ if (k &= 0xf) {
+ k1 = 16 - k;
+ z = 0;
+ do {
+ *x1++ = *x << k & 0xffff | z;
+ z = *x++ >> k1;
+ }
+ while(x < xe);
+ if (*x1 = z)
+ ++n1;
+ }
+#endif
+ else do
+ *x1++ = *x++;
+ while(x < xe);
+ b1->wds = n1 - 1;
+ Bfree(b);
+ return b1;
+ }
+
+ static int
+cmp
+#ifdef KR_headers
+ (a, b) Bigint *a, *b;
+#else
+ (Bigint *a, Bigint *b)
+#endif
+{
+ ULong *xa, *xa0, *xb, *xb0;
+ int i, j;
+
+ i = a->wds;
+ j = b->wds;
+#ifdef DEBUG
+ if (i > 1 && !a->x[i-1])
+ Bug("cmp called with a->x[a->wds-1] == 0");
+ if (j > 1 && !b->x[j-1])
+ Bug("cmp called with b->x[b->wds-1] == 0");
+#endif
+ if (i -= j)
+ return i;
+ xa0 = a->x;
+ xa = xa0 + j;
+ xb0 = b->x;
+ xb = xb0 + j;
+ for(;;) {
+ if (*--xa != *--xb)
+ return *xa < *xb ? -1 : 1;
+ if (xa <= xa0)
+ break;
+ }
+ return 0;
+ }
+
+ static Bigint *
+diff
+#ifdef KR_headers
+ (a, b) Bigint *a, *b;
+#else
+ (Bigint *a, Bigint *b)
+#endif
+{
+ Bigint *c;
+ int i, wa, wb;
+ ULong *xa, *xae, *xb, *xbe, *xc;
+#ifdef ULLong
+ ULLong borrow, y;
+#else
+ ULong borrow, y;
+#ifdef Pack_32
+ ULong z;
+#endif
+#endif
+
+ i = cmp(a,b);
+ if (!i) {
+ c = Balloc(0);
+ c->wds = 1;
+ c->x[0] = 0;
+ return c;
+ }
+ if (i < 0) {
+ c = a;
+ a = b;
+ b = c;
+ i = 1;
+ }
+ else
+ i = 0;
+ c = Balloc(a->k);
+ c->sign = i;
+ wa = a->wds;
+ xa = a->x;
+ xae = xa + wa;
+ wb = b->wds;
+ xb = b->x;
+ xbe = xb + wb;
+ xc = c->x;
+ borrow = 0;
+#ifdef ULLong
+ do {
+ y = (ULLong)*xa++ - *xb++ - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *xc++ = y & FFFFFFFF;
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = *xa++ - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *xc++ = y & FFFFFFFF;
+ }
+#else
+#ifdef Pack_32
+ do {
+ y = (*xa & 0xffff) - (*xb & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*xa++ >> 16) - (*xb++ >> 16) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(xc, z, y);
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = (*xa & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*xa++ >> 16) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(xc, z, y);
+ }
+#else
+ do {
+ y = *xa++ - *xb++ - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *xc++ = y & 0xffff;
+ }
+ while(xb < xbe);
+ while(xa < xae) {
+ y = *xa++ - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *xc++ = y & 0xffff;
+ }
+#endif
+#endif
+ while(!*--xc)
+ wa--;
+ c->wds = wa;
+ return c;
+ }
+
+ static double
+ulp
+#ifdef KR_headers
+ (x) U *x;
+#else
+ (U *x)
+#endif
+{
+ Long L;
+ U u;
+
+ L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1;
+#ifndef Avoid_Underflow
+#ifndef Sudden_Underflow
+ if (L > 0) {
+#endif
+#endif
+#ifdef IBM
+ L |= Exp_msk1 >> 4;
+#endif
+ word0(&u) = L;
+ word1(&u) = 0;
+#ifndef Avoid_Underflow
+#ifndef Sudden_Underflow
+ }
+ else {
+ L = -L >> Exp_shift;
+ if (L < Exp_shift) {
+ word0(&u) = 0x80000 >> L;
+ word1(&u) = 0;
+ }
+ else {
+ word0(&u) = 0;
+ L -= Exp_shift;
+ word1(&u) = L >= 31 ? 1 : 1 << 31 - L;
+ }
+ }
+#endif
+#endif
+ return dval(&u);
+ }
+
+ static double
+b2d
+#ifdef KR_headers
+ (a, e) Bigint *a; int *e;
+#else
+ (Bigint *a, int *e)
+#endif
+{
+ ULong *xa, *xa0, w, y, z;
+ int k;
+ U d;
+#ifdef VAX
+ ULong d0, d1;
+#else
+#define d0 word0(&d)
+#define d1 word1(&d)
+#endif
+
+ xa0 = a->x;
+ xa = xa0 + a->wds;
+ y = *--xa;
+#ifdef DEBUG
+ if (!y) Bug("zero y in b2d");
+#endif
+ k = hi0bits(y);
+ *e = 32 - k;
+#ifdef Pack_32
+ if (k < Ebits) {
+ d0 = Exp_1 | y >> (Ebits - k);
+ w = xa > xa0 ? *--xa : 0;
+ d1 = y << ((32-Ebits) + k) | w >> (Ebits - k);
+ goto ret_d;
+ }
+ z = xa > xa0 ? *--xa : 0;
+ if (k -= Ebits) {
+ d0 = Exp_1 | y << k | z >> (32 - k);
+ y = xa > xa0 ? *--xa : 0;
+ d1 = z << k | y >> (32 - k);
+ }
+ else {
+ d0 = Exp_1 | y;
+ d1 = z;
+ }
+#else
+ if (k < Ebits + 16) {
+ z = xa > xa0 ? *--xa : 0;
+ d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k;
+ w = xa > xa0 ? *--xa : 0;
+ y = xa > xa0 ? *--xa : 0;
+ d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k;
+ goto ret_d;
+ }
+ z = xa > xa0 ? *--xa : 0;
+ w = xa > xa0 ? *--xa : 0;
+ k -= Ebits + 16;
+ d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k;
+ y = xa > xa0 ? *--xa : 0;
+ d1 = w << k + 16 | y << k;
+#endif
+ ret_d:
+#ifdef VAX
+ word0(&d) = d0 >> 16 | d0 << 16;
+ word1(&d) = d1 >> 16 | d1 << 16;
+#else
+#undef d0
+#undef d1
+#endif
+ return dval(&d);
+ }
+
+ static Bigint *
+d2b
+#ifdef KR_headers
+ (d, e, bits) U *d; int *e, *bits;
+#else
+ (U *d, int *e, int *bits)
+#endif
+{
+ Bigint *b;
+ int de, k;
+ ULong *x, y, z;
+#ifndef Sudden_Underflow
+ int i;
+#endif
+#ifdef VAX
+ ULong d0, d1;
+ d0 = word0(d) >> 16 | word0(d) << 16;
+ d1 = word1(d) >> 16 | word1(d) << 16;
+#else
+#define d0 word0(d)
+#define d1 word1(d)
+#endif
+
+#ifdef Pack_32
+ b = Balloc(1);
+#else
+ b = Balloc(2);
+#endif
+ x = b->x;
+
+ z = d0 & Frac_mask;
+ d0 &= 0x7fffffff; /* clear sign bit, which we ignore */
+#ifdef Sudden_Underflow
+ de = (int)(d0 >> Exp_shift);
+#ifndef IBM
+ z |= Exp_msk11;
+#endif
+#else
+ if ((de = (int)(d0 >> Exp_shift)))
+ z |= Exp_msk1;
+#endif
+#ifdef Pack_32
+ if ((y = d1)) {
+ if ((k = lo0bits(&y))) {
+ x[0] = y | z << (32 - k);
+ z >>= k;
+ }
+ else
+ x[0] = y;
+#ifndef Sudden_Underflow
+ i =
+#endif
+ b->wds = (x[1] = z) ? 2 : 1;
+ }
+ else {
+ k = lo0bits(&z);
+ x[0] = z;
+#ifndef Sudden_Underflow
+ i =
+#endif
+ b->wds = 1;
+ k += 32;
+ }
+#else
+ if (y = d1) {
+ if (k = lo0bits(&y))
+ if (k >= 16) {
+ x[0] = y | z << 32 - k & 0xffff;
+ x[1] = z >> k - 16 & 0xffff;
+ x[2] = z >> k;
+ i = 2;
+ }
+ else {
+ x[0] = y & 0xffff;
+ x[1] = y >> 16 | z << 16 - k & 0xffff;
+ x[2] = z >> k & 0xffff;
+ x[3] = z >> k+16;
+ i = 3;
+ }
+ else {
+ x[0] = y & 0xffff;
+ x[1] = y >> 16;
+ x[2] = z & 0xffff;
+ x[3] = z >> 16;
+ i = 3;
+ }
+ }
+ else {
+#ifdef DEBUG
+ if (!z)
+ Bug("Zero passed to d2b");
+#endif
+ k = lo0bits(&z);
+ if (k >= 16) {
+ x[0] = z;
+ i = 0;
+ }
+ else {
+ x[0] = z & 0xffff;
+ x[1] = z >> 16;
+ i = 1;
+ }
+ k += 32;
+ }
+ while(!x[i])
+ --i;
+ b->wds = i + 1;
+#endif
+#ifndef Sudden_Underflow
+ if (de) {
+#endif
+#ifdef IBM
+ *e = (de - Bias - (P-1) << 2) + k;
+ *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask);
+#else
+ *e = de - Bias - (P-1) + k;
+ *bits = P - k;
+#endif
+#ifndef Sudden_Underflow
+ }
+ else {
+ *e = de - Bias - (P-1) + 1 + k;
+#ifdef Pack_32
+ *bits = 32*i - hi0bits(x[i-1]);
+#else
+ *bits = (i+2)*16 - hi0bits(x[i]);
+#endif
+ }
+#endif
+ return b;
+ }
+#undef d0
+#undef d1
+
+ static double
+ratio
+#ifdef KR_headers
+ (a, b) Bigint *a, *b;
+#else
+ (Bigint *a, Bigint *b)
+#endif
+{
+ U da, db;
+ int k, ka, kb;
+
+ dval(&da) = b2d(a, &ka);
+ dval(&db) = b2d(b, &kb);
+#ifdef Pack_32
+ k = ka - kb + 32*(a->wds - b->wds);
+#else
+ k = ka - kb + 16*(a->wds - b->wds);
+#endif
+#ifdef IBM
+ if (k > 0) {
+ word0(&da) += (k >> 2)*Exp_msk1;
+ if (k &= 3)
+ dval(&da) *= 1 << k;
+ }
+ else {
+ k = -k;
+ word0(&db) += (k >> 2)*Exp_msk1;
+ if (k &= 3)
+ dval(&db) *= 1 << k;
+ }
+#else
+ if (k > 0)
+ word0(&da) += k*Exp_msk1;
+ else {
+ k = -k;
+ word0(&db) += k*Exp_msk1;
+ }
+#endif
+ return dval(&da) / dval(&db);
+ }
+
+ static CONST double
+tens[] = {
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+ 1e20, 1e21, 1e22
+#ifdef VAX
+ , 1e23, 1e24
+#endif
+ };
+
+ static CONST double
+#ifdef IEEE_Arith
+bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 };
+static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128,
+#ifdef Avoid_Underflow
+ 9007199254740992.*9007199254740992.e-256
+ /* = 2^106 * 1e-256 */
+#else
+ 1e-256
+#endif
+ };
+/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */
+/* flag unnecessarily. It leads to a song and dance at the end of strtod. */
+#define Scale_Bit 0x10
+#define n_bigtens 5
+#else
+#ifdef IBM
+bigtens[] = { 1e16, 1e32, 1e64 };
+static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 };
+#define n_bigtens 3
+#else
+bigtens[] = { 1e16, 1e32 };
+static CONST double tinytens[] = { 1e-16, 1e-32 };
+#define n_bigtens 2
+#endif
+#endif
+
+#undef Need_Hexdig
+#ifdef INFNAN_CHECK
+#ifndef No_Hex_NaN
+#define Need_Hexdig
+#endif
+#endif
+
+#ifndef Need_Hexdig
+#ifndef NO_HEX_FP
+#define Need_Hexdig
+#endif
+#endif
+
+#ifdef Need_Hexdig /*{*/
+static unsigned char hexdig[256];
+
+ static void
+#ifdef KR_headers
+htinit(h, s, inc) unsigned char *h; unsigned char *s; int inc;
+#else
+htinit(unsigned char *h, unsigned char *s, int inc)
+#endif
+{
+ int i, j;
+ for(i = 0; (j = s[i]) !=0; i++)
+ h[j] = i + inc;
+ }
+
+ static void
+#ifdef KR_headers
+hexdig_init()
+#else
+hexdig_init(void)
+#endif
+{
+#define USC (unsigned char *)
+ htinit(hexdig, USC "0123456789", 0x10);
+ htinit(hexdig, USC "abcdef", 0x10 + 10);
+ htinit(hexdig, USC "ABCDEF", 0x10 + 10);
+ }
+#endif /* } Need_Hexdig */
+
+#ifdef INFNAN_CHECK
+
+#ifndef NAN_WORD0
+#define NAN_WORD0 0x7ff80000
+#endif
+
+#ifndef NAN_WORD1
+#define NAN_WORD1 0
+#endif
+
+ static int
+match
+#ifdef KR_headers
+ (sp, t) char **sp, *t;
+#else
+ (CONST char **sp, CONST char *t)
+#endif
+{
+ int c, d;
+ CONST char *s = *sp;
+
+ while((d = *t++)) {
+ if ((c = *++s) >= 'A' && c <= 'Z')
+ c += 'a' - 'A';
+ if (c != d)
+ return 0;
+ }
+ *sp = s + 1;
+ return 1;
+ }
+
+#ifndef No_Hex_NaN
+ static void
+hexnan
+#ifdef KR_headers
+ (rvp, sp) U *rvp; CONST char **sp;
+#else
+ (U *rvp, CONST char **sp)
+#endif
+{
+ ULong c, x[2];
+ CONST char *s;
+ int c1, havedig, udx0, xshift;
+
+ if (!hexdig['0'])
+ hexdig_init();
+ x[0] = x[1] = 0;
+ havedig = xshift = 0;
+ udx0 = 1;
+ s = *sp;
+ /* allow optional initial 0x or 0X */
+ while((c = *(CONST unsigned char*)(s+1)) && c <= ' ')
+ ++s;
+ if (s[1] == '0' && (s[2] == 'x' || s[2] == 'X'))
+ s += 2;
+ while((c = *(CONST unsigned char*)++s)) {
+ if ((c1 = hexdig[c]))
+ c = c1 & 0xf;
+ else if (c <= ' ') {
+ if (udx0 && havedig) {
+ udx0 = 0;
+ xshift = 1;
+ }
+ continue;
+ }
+#ifdef GDTOA_NON_PEDANTIC_NANCHECK
+ else if (/*(*/ c == ')' && havedig) {
+ *sp = s + 1;
+ break;
+ }
+ else
+ return; /* invalid form: don't change *sp */
+#else
+ else {
+ do {
+ if (/*(*/ c == ')') {
+ *sp = s + 1;
+ break;
+ }
+ } while((c = *++s));
+ break;
+ }
+#endif
+ havedig = 1;
+ if (xshift) {
+ xshift = 0;
+ x[0] = x[1];
+ x[1] = 0;
+ }
+ if (udx0)
+ x[0] = (x[0] << 4) | (x[1] >> 28);
+ x[1] = (x[1] << 4) | c;
+ }
+ if ((x[0] &= 0xfffff) || x[1]) {
+ word0(rvp) = Exp_mask | x[0];
+ word1(rvp) = x[1];
+ }
+ }
+#endif /*No_Hex_NaN*/
+#endif /* INFNAN_CHECK */
+
+#ifdef Pack_32
+#define ULbits 32
+#define kshift 5
+#define kmask 31
+#else
+#define ULbits 16
+#define kshift 4
+#define kmask 15
+#endif
+#ifndef NO_HEX_FP /*{*/
+
+ static void
+#ifdef KR_headers
+rshift(b, k) Bigint *b; int k;
+#else
+rshift(Bigint *b, int k)
+#endif
+{
+ ULong *x, *x1, *xe, y;
+ int n;
+
+ x = x1 = b->x;
+ n = k >> kshift;
+ if (n < b->wds) {
+ xe = x + b->wds;
+ x += n;
+ if (k &= kmask) {
+ n = 32 - k;
+ y = *x++ >> k;
+ while(x < xe) {
+ *x1++ = (y | (*x << n)) & 0xffffffff;
+ y = *x++ >> k;
+ }
+ if ((*x1 = y) !=0)
+ x1++;
+ }
+ else
+ while(x < xe)
+ *x1++ = *x++;
+ }
+ if ((b->wds = x1 - b->x) == 0)
+ b->x[0] = 0;
+ }
+
+ static ULong
+#ifdef KR_headers
+any_on(b, k) Bigint *b; int k;
+#else
+any_on(Bigint *b, int k)
+#endif
+{
+ int n, nwds;
+ ULong *x, *x0, x1, x2;
+
+ x = b->x;
+ nwds = b->wds;
+ n = k >> kshift;
+ if (n > nwds)
+ n = nwds;
+ else if (n < nwds && (k &= kmask)) {
+ x1 = x2 = x[n];
+ x1 >>= k;
+ x1 <<= k;
+ if (x1 != x2)
+ return 1;
+ }
+ x0 = x;
+ x += n;
+ while(x > x0)
+ if (*--x)
+ return 1;
+ return 0;
+ }
+
+enum { /* rounding values: same as FLT_ROUNDS */
+ Round_zero = 0,
+ Round_near = 1,
+ Round_up = 2,
+ Round_down = 3
+ };
+
+ static Bigint *
+#ifdef KR_headers
+increment(b) Bigint *b;
+#else
+increment(Bigint *b)
+#endif
+{
+ ULong *x, *xe;
+ Bigint *b1;
+
+ x = b->x;
+ xe = x + b->wds;
+ do {
+ if (*x < (ULong)0xffffffffL) {
+ ++*x;
+ return b;
+ }
+ *x++ = 0;
+ } while(x < xe);
+ {
+ if (b->wds >= b->maxwds) {
+ b1 = Balloc(b->k+1);
+ Bcopy(b1,b);
+ Bfree(b);
+ b = b1;
+ }
+ b->x[b->wds++] = 1;
+ }
+ return b;
+ }
+
+ void
+#ifdef KR_headers
+gethex(sp, rvp, rounding, sign)
+ CONST char **sp; U *rvp; int rounding, sign;
+#else
+gethex( CONST char **sp, U *rvp, int rounding, int sign)
+#endif
+{
+ Bigint *b;
+ CONST unsigned char *decpt, *s0, *s, *s1;
+ Long e, e1;
+ ULong L, lostbits, *x;
+ int big, denorm, esign, havedig, k, n, nbits, up, zret;
+#ifdef IBM
+ int j;
+#endif
+ enum {
+#ifdef IEEE_Arith /*{{*/
+ emax = 0x7fe - Bias - P + 1,
+ emin = Emin - P + 1
+#else /*}{*/
+ emin = Emin - P,
+#ifdef VAX
+ emax = 0x7ff - Bias - P + 1
+#endif
+#ifdef IBM
+ emax = 0x7f - Bias - P
+#endif
+#endif /*}}*/
+ };
+#ifdef USE_LOCALE
+ int i;
+#ifdef NO_LOCALE_CACHE
+ const unsigned char *decimalpoint = (unsigned char*)
+ localeconv()->decimal_point;
+#else
+ const unsigned char *decimalpoint;
+ static unsigned char *decimalpoint_cache;
+ if (!(s0 = decimalpoint_cache)) {
+ s0 = (unsigned char*)localeconv()->decimal_point;
+ if ((decimalpoint_cache = (unsigned char*)
+ MALLOC(strlen((CONST char*)s0) + 1))) {
+ strcpy((char*)decimalpoint_cache, (CONST char*)s0);
+ s0 = decimalpoint_cache;
+ }
+ }
+ decimalpoint = s0;
+#endif
+#endif
+
+ if (!hexdig['0'])
+ hexdig_init();
+ havedig = 0;
+ s0 = *(CONST unsigned char **)sp + 2;
+ while(s0[havedig] == '0')
+ havedig++;
+ s0 += havedig;
+ s = s0;
+ decpt = 0;
+ zret = 0;
+ e = 0;
+ if (hexdig[*s])
+ havedig++;
+ else {
+ zret = 1;
+#ifdef USE_LOCALE
+ for(i = 0; decimalpoint[i]; ++i) {
+ if (s[i] != decimalpoint[i])
+ goto pcheck;
+ }
+ decpt = s += i;
+#else
+ if (*s != '.')
+ goto pcheck;
+ decpt = ++s;
+#endif
+ if (!hexdig[*s])
+ goto pcheck;
+ while(*s == '0')
+ s++;
+ if (hexdig[*s])
+ zret = 0;
+ havedig = 1;
+ s0 = s;
+ }
+ while(hexdig[*s])
+ s++;
+#ifdef USE_LOCALE
+ if (*s == *decimalpoint && !decpt) {
+ for(i = 1; decimalpoint[i]; ++i) {
+ if (s[i] != decimalpoint[i])
+ goto pcheck;
+ }
+ decpt = s += i;
+#else
+ if (*s == '.' && !decpt) {
+ decpt = ++s;
+#endif
+ while(hexdig[*s])
+ s++;
+ }/*}*/
+ if (decpt)
+ e = -(((Long)(s-decpt)) << 2);
+ pcheck:
+ s1 = s;
+ big = esign = 0;
+ switch(*s) {
+ case 'p':
+ case 'P':
+ switch(*++s) {
+ case '-':
+ esign = 1;
+ /* no break */
+ case '+':
+ s++;
+ }
+ if ((n = hexdig[*s]) == 0 || n > 0x19) {
+ s = s1;
+ break;
+ }
+ e1 = n - 0x10;
+ while((n = hexdig[*++s]) !=0 && n <= 0x19) {
+ if (e1 & 0xf8000000)
+ big = 1;
+ e1 = 10*e1 + n - 0x10;
+ }
+ if (esign)
+ e1 = -e1;
+ e += e1;
+ }
+ *sp = (char*)s;
+ if (!havedig)
+ *sp = (char*)s0 - 1;
+ if (zret)
+ goto retz1;
+ if (big) {
+ if (esign) {
+#ifdef IEEE_Arith
+ switch(rounding) {
+ case Round_up:
+ if (sign)
+ break;
+ goto ret_tiny;
+ case Round_down:
+ if (!sign)
+ break;
+ goto ret_tiny;
+ }
+#endif
+ goto retz;
+#ifdef IEEE_Arith
+ ret_tiny:
+#ifndef NO_ERRNO
+ errno = ERANGE;
+#endif
+ word0(rvp) = 0;
+ word1(rvp) = 1;
+ return;
+#endif /* IEEE_Arith */
+ }
+ switch(rounding) {
+ case Round_near:
+ goto ovfl1;
+ case Round_up:
+ if (!sign)
+ goto ovfl1;
+ goto ret_big;
+ case Round_down:
+ if (sign)
+ goto ovfl1;
+ goto ret_big;
+ }
+ ret_big:
+ word0(rvp) = Big0;
+ word1(rvp) = Big1;
+ return;
+ }
+ n = s1 - s0 - 1;
+ for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1)
+ k++;
+ b = Balloc(k);
+ x = b->x;
+ n = 0;
+ L = 0;
+#ifdef USE_LOCALE
+ for(i = 0; decimalpoint[i+1]; ++i);
+#endif
+ while(s1 > s0) {
+#ifdef USE_LOCALE
+ if (*--s1 == decimalpoint[i]) {
+ s1 -= i;
+ continue;
+ }
+#else
+ if (*--s1 == '.')
+ continue;
+#endif
+ if (n == ULbits) {
+ *x++ = L;
+ L = 0;
+ n = 0;
+ }
+ L |= (hexdig[*s1] & 0x0f) << n;
+ n += 4;
+ }
+ *x++ = L;
+ b->wds = n = x - b->x;
+ n = ULbits*n - hi0bits(L);
+ nbits = Nbits;
+ lostbits = 0;
+ x = b->x;
+ if (n > nbits) {
+ n -= nbits;
+ if (any_on(b,n)) {
+ lostbits = 1;
+ k = n - 1;
+ if (x[k>>kshift] & 1 << (k & kmask)) {
+ lostbits = 2;
+ if (k > 0 && any_on(b,k))
+ lostbits = 3;
+ }
+ }
+ rshift(b, n);
+ e += n;
+ }
+ else if (n < nbits) {
+ n = nbits - n;
+ b = lshift(b, n);
+ e -= n;
+ x = b->x;
+ }
+ if (e > Emax) {
+ ovfl:
+ Bfree(b);
+ ovfl1:
+#ifndef NO_ERRNO
+ errno = ERANGE;
+#endif
+ word0(rvp) = Exp_mask;
+ word1(rvp) = 0;
+ return;
+ }
+ denorm = 0;
+ if (e < emin) {
+ denorm = 1;
+ n = emin - e;
+ if (n >= nbits) {
+#ifdef IEEE_Arith /*{*/
+ switch (rounding) {
+ case Round_near:
+ if (n == nbits && (n < 2 || any_on(b,n-1)))
+ goto ret_tiny;
+ break;
+ case Round_up:
+ if (!sign)
+ goto ret_tiny;
+ break;
+ case Round_down:
+ if (sign)
+ goto ret_tiny;
+ }
+#endif /* } IEEE_Arith */
+ Bfree(b);
+ retz:
+#ifndef NO_ERRNO
+ errno = ERANGE;
+#endif
+ retz1:
+ rvp->d = 0.;
+ return;
+ }
+ k = n - 1;
+ if (lostbits)
+ lostbits = 1;
+ else if (k > 0)
+ lostbits = any_on(b,k);
+ if (x[k>>kshift] & 1 << (k & kmask))
+ lostbits |= 2;
+ nbits -= n;
+ rshift(b,n);
+ e = emin;
+ }
+ if (lostbits) {
+ up = 0;
+ switch(rounding) {
+ case Round_zero:
+ break;
+ case Round_near:
+ if (lostbits & 2
+ && (lostbits & 1) | (x[0] & 1))
+ up = 1;
+ break;
+ case Round_up:
+ up = 1 - sign;
+ break;
+ case Round_down:
+ up = sign;
+ }
+ if (up) {
+ k = b->wds;
+ b = increment(b);
+ x = b->x;
+ if (denorm) {
+#if 0
+ if (nbits == Nbits - 1
+ && x[nbits >> kshift] & 1 << (nbits & kmask))
+ denorm = 0; /* not currently used */
+#endif
+ }
+ else if (b->wds > k
+ || ((n = nbits & kmask) !=0
+ && hi0bits(x[k-1]) < 32-n)) {
+ rshift(b,1);
+ if (++e > Emax)
+ goto ovfl;
+ }
+ }
+ }
+#ifdef IEEE_Arith
+ if (denorm)
+ word0(rvp) = b->wds > 1 ? b->x[1] & ~0x100000 : 0;
+ else
+ word0(rvp) = (b->x[1] & ~0x100000) | ((e + 0x3ff + 52) << 20);
+ word1(rvp) = b->x[0];
+#endif
+#ifdef IBM
+ if ((j = e & 3)) {
+ k = b->x[0] & ((1 << j) - 1);
+ rshift(b,j);
+ if (k) {
+ switch(rounding) {
+ case Round_up:
+ if (!sign)
+ increment(b);
+ break;
+ case Round_down:
+ if (sign)
+ increment(b);
+ break;
+ case Round_near:
+ j = 1 << (j-1);
+ if (k & j && ((k & (j-1)) | lostbits))
+ increment(b);
+ }
+ }
+ }
+ e >>= 2;
+ word0(rvp) = b->x[1] | ((e + 65 + 13) << 24);
+ word1(rvp) = b->x[0];
+#endif
+#ifdef VAX
+ /* The next two lines ignore swap of low- and high-order 2 bytes. */
+ /* word0(rvp) = (b->x[1] & ~0x800000) | ((e + 129 + 55) << 23); */
+ /* word1(rvp) = b->x[0]; */
+ word0(rvp) = ((b->x[1] & ~0x800000) >> 16) | ((e + 129 + 55) << 7) | (b->x[1] << 16);
+ word1(rvp) = (b->x[0] >> 16) | (b->x[0] << 16);
+#endif
+ Bfree(b);
+ }
+#endif /*}!NO_HEX_FP*/
+
+ static int
+#ifdef KR_headers
+dshift(b, p2) Bigint *b; int p2;
+#else
+dshift(Bigint *b, int p2)
+#endif
+{
+ int rv = hi0bits(b->x[b->wds-1]) - 4;
+ if (p2 > 0)
+ rv -= p2;
+ return rv & kmask;
+ }
+
+ static int
+quorem
+#ifdef KR_headers
+ (b, S) Bigint *b, *S;
+#else
+ (Bigint *b, Bigint *S)
+#endif
+{
+ int n;
+ ULong *bx, *bxe, q, *sx, *sxe;
+#ifdef ULLong
+ ULLong borrow, carry, y, ys;
+#else
+ ULong borrow, carry, y, ys;
+#ifdef Pack_32
+ ULong si, z, zs;
+#endif
+#endif
+
+ n = S->wds;
+#ifdef DEBUG
+ /*debug*/ if (b->wds > n)
+ /*debug*/ Bug("oversize b in quorem");
+#endif
+ if (b->wds < n)
+ return 0;
+ sx = S->x;
+ sxe = sx + --n;
+ bx = b->x;
+ bxe = bx + n;
+ q = *bxe / (*sxe + 1); /* ensure q <= true quotient */
+#ifdef DEBUG
+ /*debug*/ if (q > 9)
+ /*debug*/ Bug("oversized quotient in quorem");
+#endif
+ if (q) {
+ borrow = 0;
+ carry = 0;
+ do {
+#ifdef ULLong
+ ys = *sx++ * (ULLong)q + carry;
+ carry = ys >> 32;
+ y = *bx - (ys & FFFFFFFF) - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *bx++ = y & FFFFFFFF;
+#else
+#ifdef Pack_32
+ si = *sx++;
+ ys = (si & 0xffff) * q + carry;
+ zs = (si >> 16) * q + (ys >> 16);
+ carry = zs >> 16;
+ y = (*bx & 0xffff) - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*bx >> 16) - (zs & 0xffff) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(bx, z, y);
+#else
+ ys = *sx++ * q + carry;
+ carry = ys >> 16;
+ y = *bx - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *bx++ = y & 0xffff;
+#endif
+#endif
+ }
+ while(sx <= sxe);
+ if (!*bxe) {
+ bx = b->x;
+ while(--bxe > bx && !*bxe)
+ --n;
+ b->wds = n;
+ }
+ }
+ if (cmp(b, S) >= 0) {
+ q++;
+ borrow = 0;
+ carry = 0;
+ bx = b->x;
+ sx = S->x;
+ do {
+#ifdef ULLong
+ ys = *sx++ + carry;
+ carry = ys >> 32;
+ y = *bx - (ys & FFFFFFFF) - borrow;
+ borrow = y >> 32 & (ULong)1;
+ *bx++ = y & FFFFFFFF;
+#else
+#ifdef Pack_32
+ si = *sx++;
+ ys = (si & 0xffff) + carry;
+ zs = (si >> 16) + (ys >> 16);
+ carry = zs >> 16;
+ y = (*bx & 0xffff) - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*bx >> 16) - (zs & 0xffff) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(bx, z, y);
+#else
+ ys = *sx++ + carry;
+ carry = ys >> 16;
+ y = *bx - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ *bx++ = y & 0xffff;
+#endif
+#endif
+ }
+ while(sx <= sxe);
+ bx = b->x;
+ bxe = bx + n;
+ if (!*bxe) {
+ while(--bxe > bx && !*bxe)
+ --n;
+ b->wds = n;
+ }
+ }
+ return q;
+ }
+
+#ifndef NO_STRTOD_BIGCOMP
+
+ static void
+bigcomp
+#ifdef KR_headers
+ (rv, s0, bc)
+ U *rv; CONST char *s0; BCinfo *bc;
+#else
+ (U *rv, CONST char *s0, BCinfo *bc)
+#endif
+{
+ Bigint *b, *d;
+ int b2, bbits, d2, dd, dig, dsign, i, j, nd, nd0, p2, p5, speccase;
+
+ dsign = bc->dsign;
+ nd = bc->nd;
+ nd0 = bc->nd0;
+ p5 = nd + bc->e0 - 1;
+ dd = speccase = 0;
+#ifndef Sudden_Underflow
+ if (rv->d == 0.) { /* special case: value near underflow-to-zero */
+ /* threshold was rounded to zero */
+ b = i2b(1);
+ p2 = Emin - P + 1;
+ bbits = 1;
+#ifdef Avoid_Underflow
+ word0(rv) = (P+2) << Exp_shift;
+#else
+ word1(rv) = 1;
+#endif
+ i = 0;
+#ifdef Honor_FLT_ROUNDS
+ if (bc->rounding == 1)
+#endif
+ {
+ speccase = 1;
+ --p2;
+ dsign = 0;
+ goto have_i;
+ }
+ }
+ else
+#endif
+ b = d2b(rv, &p2, &bbits);
+#ifdef Avoid_Underflow
+ p2 -= bc->scale;
+#endif
+ /* floor(log2(rv)) == bbits - 1 + p2 */
+ /* Check for denormal case. */
+ i = P - bbits;
+ if (i > (j = P - Emin - 1 + p2)) {
+#ifdef Sudden_Underflow
+ Bfree(b);
+ b = i2b(1);
+ p2 = Emin;
+ i = P - 1;
+#ifdef Avoid_Underflow
+ word0(rv) = (1 + bc->scale) << Exp_shift;
+#else
+ word0(rv) = Exp_msk1;
+#endif
+ word1(rv) = 0;
+#else
+ i = j;
+#endif
+ }
+#ifdef Honor_FLT_ROUNDS
+ if (bc->rounding != 1) {
+ if (i > 0)
+ b = lshift(b, i);
+ if (dsign)
+ b = increment(b);
+ }
+ else
+#endif
+ {
+ b = lshift(b, ++i);
+ b->x[0] |= 1;
+ }
+#ifndef Sudden_Underflow
+ have_i:
+#endif
+ p2 -= p5 + i;
+ d = i2b(1);
+ /* Arrange for convenient computation of quotients:
+ * shift left if necessary so divisor has 4 leading 0 bits.
+ */
+ if (p5 > 0)
+ d = pow5mult(d, p5);
+ else if (p5 < 0)
+ b = pow5mult(b, -p5);
+ if (p2 > 0) {
+ b2 = p2;
+ d2 = 0;
+ }
+ else {
+ b2 = 0;
+ d2 = -p2;
+ }
+ i = dshift(d, d2);
+ if ((b2 += i) > 0)
+ b = lshift(b, b2);
+ if ((d2 += i) > 0)
+ d = lshift(d, d2);
+
+ /* Now b/d = exactly half-way between the two floating-point values */
+ /* on either side of the input string. Compute first digit of b/d. */
+
+ if (!(dig = quorem(b,d))) {
+ b = multadd(b, 10, 0); /* very unlikely */
+ dig = quorem(b,d);
+ }
+
+ /* Compare b/d with s0 */
+
+ for(i = 0; i < nd0; ) {
+ if ((dd = s0[i++] - '0' - dig))
+ goto ret;
+ if (!b->x[0] && b->wds == 1) {
+ if (i < nd)
+ dd = 1;
+ goto ret;
+ }
+ b = multadd(b, 10, 0);
+ dig = quorem(b,d);
+ }
+ for(j = bc->dp1; i++ < nd;) {
+ if ((dd = s0[j++] - '0' - dig))
+ goto ret;
+ if (!b->x[0] && b->wds == 1) {
+ if (i < nd)
+ dd = 1;
+ goto ret;
+ }
+ b = multadd(b, 10, 0);
+ dig = quorem(b,d);
+ }
+ if (b->x[0] || b->wds > 1)
+ dd = -1;
+ ret:
+ Bfree(b);
+ Bfree(d);
+#ifdef Honor_FLT_ROUNDS
+ if (bc->rounding != 1) {
+ if (dd < 0) {
+ if (bc->rounding == 0) {
+ if (!dsign)
+ goto retlow1;
+ }
+ else if (dsign)
+ goto rethi1;
+ }
+ else if (dd > 0) {
+ if (bc->rounding == 0) {
+ if (dsign)
+ goto rethi1;
+ goto ret1;
+ }
+ if (!dsign)
+ goto rethi1;
+ dval(rv) += 2.*ulp(rv);
+ }
+ else {
+ bc->inexact = 0;
+ if (dsign)
+ goto rethi1;
+ }
+ }
+ else
+#endif
+ if (speccase) {
+ if (dd <= 0)
+ rv->d = 0.;
+ }
+ else if (dd < 0) {
+ if (!dsign) /* does not happen for round-near */
+retlow1:
+ dval(rv) -= ulp(rv);
+ }
+ else if (dd > 0) {
+ if (dsign) {
+ rethi1:
+ dval(rv) += ulp(rv);
+ }
+ }
+ else {
+ /* Exact half-way case: apply round-even rule. */
+ if (word1(rv) & 1) {
+ if (dsign)
+ goto rethi1;
+ goto retlow1;
+ }
+ }
+
+#ifdef Honor_FLT_ROUNDS
+ ret1:
+#endif
+ return;
+ }
+#endif /* NO_STRTOD_BIGCOMP */
+
+ double
+strtod
+#ifdef KR_headers
+ (s00, se) CONST char *s00; char **se;
+#else
+ (CONST char *s00, char **se)
+#endif
+{
+ int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, e, e1;
+ int esign, i, j, k, nd, nd0, nf, nz, nz0, sign;
+ CONST char *s, *s0, *s1;
+ double aadj, aadj1;
+ Long L;
+ U aadj2, adj, rv, rv0;
+ ULong y, z;
+ BCinfo bc;
+ Bigint *bb, *bb1, *bd, *bd0, *bs, *delta;
+#ifdef SET_INEXACT
+ int oldinexact;
+#endif
+#ifdef Honor_FLT_ROUNDS /*{*/
+#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */
+ bc.rounding = Flt_Rounds;
+#else /*}{*/
+ bc.rounding = 1;
+ switch(fegetround()) {
+ case FE_TOWARDZERO: bc.rounding = 0; break;
+ case FE_UPWARD: bc.rounding = 2; break;
+ case FE_DOWNWARD: bc.rounding = 3;
+ }
+#endif /*}}*/
+#endif /*}*/
+#ifdef USE_LOCALE
+ CONST char *s2;
+#endif
+
+ sign = nz0 = nz = bc.dplen = bc.uflchk = 0;
+ dval(&rv) = 0.;
+ for(s = s00;;s++) switch(*s) {
+ case '-':
+ sign = 1;
+ /* no break */
+ case '+':
+ if (*++s)
+ goto break2;
+ /* no break */
+ case 0:
+ goto ret0;
+ case '\t':
+ case '\n':
+ case '\v':
+ case '\f':
+ case '\r':
+ case ' ':
+ continue;
+ default:
+ goto break2;
+ }
+ break2:
+ if (*s == '0') {
+#ifndef NO_HEX_FP /*{*/
+ switch(s[1]) {
+ case 'x':
+ case 'X':
+#ifdef Honor_FLT_ROUNDS
+ gethex(&s, &rv, bc.rounding, sign);
+#else
+ gethex(&s, &rv, 1, sign);
+#endif
+ goto ret;
+ }
+#endif /*}*/
+ nz0 = 1;
+ while(*++s == '0') ;
+ if (!*s)
+ goto ret;
+ }
+ s0 = s;
+ y = z = 0;
+ for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++)
+ if (nd < 9)
+ y = 10*y + c - '0';
+ else if (nd < 16)
+ z = 10*z + c - '0';
+ nd0 = nd;
+ bc.dp0 = bc.dp1 = s - s0;
+#ifdef USE_LOCALE
+ s1 = localeconv()->decimal_point;
+ if (c == *s1) {
+ c = '.';
+ if (*++s1) {
+ s2 = s;
+ for(;;) {
+ if (*++s2 != *s1) {
+ c = 0;
+ break;
+ }
+ if (!*++s1) {
+ s = s2;
+ break;
+ }
+ }
+ }
+ }
+#endif
+ if (c == '.') {
+ c = *++s;
+ bc.dp1 = s - s0;
+ bc.dplen = bc.dp1 - bc.dp0;
+ if (!nd) {
+ for(; c == '0'; c = *++s)
+ nz++;
+ if (c > '0' && c <= '9') {
+ s0 = s;
+ nf += nz;
+ nz = 0;
+ goto have_dig;
+ }
+ goto dig_done;
+ }
+ for(; c >= '0' && c <= '9'; c = *++s) {
+ have_dig:
+ nz++;
+ if (c -= '0') {
+ nf += nz;
+ for(i = 1; i < nz; i++)
+ if (nd++ < 9)
+ y *= 10;
+ else if (nd <= DBL_DIG + 1)
+ z *= 10;
+ if (nd++ < 9)
+ y = 10*y + c;
+ else if (nd <= DBL_DIG + 1)
+ z = 10*z + c;
+ nz = 0;
+ }
+ }
+ }
+ dig_done:
+ e = 0;
+ if (c == 'e' || c == 'E') {
+ if (!nd && !nz && !nz0) {
+ goto ret0;
+ }
+ s00 = s;
+ esign = 0;
+ switch(c = *++s) {
+ case '-':
+ esign = 1;
+ case '+':
+ c = *++s;
+ }
+ if (c >= '0' && c <= '9') {
+ while(c == '0')
+ c = *++s;
+ if (c > '0' && c <= '9') {
+ L = c - '0';
+ s1 = s;
+ while((c = *++s) >= '0' && c <= '9')
+ L = 10*L + c - '0';
+ if (s - s1 > 8 || L > 19999)
+ /* Avoid confusion from exponents
+ * so large that e might overflow.
+ */
+ e = 19999; /* safe for 16 bit ints */
+ else
+ e = (int)L;
+ if (esign)
+ e = -e;
+ }
+ else
+ e = 0;
+ }
+ else
+ s = s00;
+ }
+ if (!nd) {
+ if (!nz && !nz0) {
+#ifdef INFNAN_CHECK
+ /* Check for Nan and Infinity */
+ if (!bc.dplen)
+ switch(c) {
+ case 'i':
+ case 'I':
+ if (match(&s,"nf")) {
+ --s;
+ if (!match(&s,"inity"))
+ ++s;
+ word0(&rv) = 0x7ff00000;
+ word1(&rv) = 0;
+ goto ret;
+ }
+ break;
+ case 'n':
+ case 'N':
+ if (match(&s, "an")) {
+ word0(&rv) = NAN_WORD0;
+ word1(&rv) = NAN_WORD1;
+#ifndef No_Hex_NaN
+ if (*s == '(') /*)*/
+ hexnan(&rv, &s);
+#endif
+ goto ret;
+ }
+ }
+#endif /* INFNAN_CHECK */
+ ret0:
+ s = s00;
+ sign = 0;
+ }
+ goto ret;
+ }
+ bc.e0 = e1 = e -= nf;
+
+ /* Now we have nd0 digits, starting at s0, followed by a
+ * decimal point, followed by nd-nd0 digits. The number we're
+ * after is the integer represented by those digits times
+ * 10**e */
+
+ if (!nd0)
+ nd0 = nd;
+ k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1;
+ dval(&rv) = y;
+ if (k > 9) {
+#ifdef SET_INEXACT
+ if (k > DBL_DIG)
+ oldinexact = get_inexact();
+#endif
+ dval(&rv) = tens[k - 9] * dval(&rv) + z;
+ }
+ bd0 = 0;
+ if (nd <= DBL_DIG
+#ifndef RND_PRODQUOT
+#ifndef Honor_FLT_ROUNDS
+ && Flt_Rounds == 1
+#endif
+#endif
+ ) {
+ if (!e)
+ goto ret;
+ if (e > 0) {
+ if (e <= Ten_pmax) {
+#ifdef VAX
+ goto vax_ovfl_check;
+#else
+#ifdef Honor_FLT_ROUNDS
+ /* round correctly FLT_ROUNDS = 2 or 3 */
+ if (sign) {
+ rv.d = -rv.d;
+ sign = 0;
+ }
+#endif
+ /* rv = */ rounded_product(dval(&rv), tens[e]);
+ goto ret;
+#endif
+ }
+ i = DBL_DIG - nd;
+ if (e <= Ten_pmax + i) {
+ /* A fancier test would sometimes let us do
+ * this for larger i values.
+ */
+#ifdef Honor_FLT_ROUNDS
+ /* round correctly FLT_ROUNDS = 2 or 3 */
+ if (sign) {
+ rv.d = -rv.d;
+ sign = 0;
+ }
+#endif
+ e -= i;
+ dval(&rv) *= tens[i];
+#ifdef VAX
+ /* VAX exponent range is so narrow we must
+ * worry about overflow here...
+ */
+ vax_ovfl_check:
+ word0(&rv) -= P*Exp_msk1;
+ /* rv = */ rounded_product(dval(&rv), tens[e]);
+ if ((word0(&rv) & Exp_mask)
+ > Exp_msk1*(DBL_MAX_EXP+Bias-1-P))
+ goto ovfl;
+ word0(&rv) += P*Exp_msk1;
+#else
+ /* rv = */ rounded_product(dval(&rv), tens[e]);
+#endif
+ goto ret;
+ }
+ }
+#ifndef Inaccurate_Divide
+ else if (e >= -Ten_pmax) {
+#ifdef Honor_FLT_ROUNDS
+ /* round correctly FLT_ROUNDS = 2 or 3 */
+ if (sign) {
+ rv.d = -rv.d;
+ sign = 0;
+ }
+#endif
+ /* rv = */ rounded_quotient(dval(&rv), tens[-e]);
+ goto ret;
+ }
+#endif
+ }
+ e1 += nd - k;
+
+#ifdef IEEE_Arith
+#ifdef SET_INEXACT
+ bc.inexact = 1;
+ if (k <= DBL_DIG)
+ oldinexact = get_inexact();
+#endif
+#ifdef Avoid_Underflow
+ bc.scale = 0;
+#endif
+#ifdef Honor_FLT_ROUNDS
+ if (bc.rounding >= 2) {
+ if (sign)
+ bc.rounding = bc.rounding == 2 ? 0 : 2;
+ else
+ if (bc.rounding != 2)
+ bc.rounding = 0;
+ }
+#endif
+#endif /*IEEE_Arith*/
+
+ /* Get starting approximation = rv * 10**e1 */
+
+ if (e1 > 0) {
+ if ((i = e1 & 15))
+ dval(&rv) *= tens[i];
+ if (e1 &= ~15) {
+ if (e1 > DBL_MAX_10_EXP) {
+ ovfl:
+#ifndef NO_ERRNO
+ errno = ERANGE;
+#endif
+ /* Can't trust HUGE_VAL */
+#ifdef IEEE_Arith
+#ifdef Honor_FLT_ROUNDS
+ switch(bc.rounding) {
+ case 0: /* toward 0 */
+ case 3: /* toward -infinity */
+ word0(&rv) = Big0;
+ word1(&rv) = Big1;
+ break;
+ default:
+ word0(&rv) = Exp_mask;
+ word1(&rv) = 0;
+ }
+#else /*Honor_FLT_ROUNDS*/
+ word0(&rv) = Exp_mask;
+ word1(&rv) = 0;
+#endif /*Honor_FLT_ROUNDS*/
+#ifdef SET_INEXACT
+ /* set overflow bit */
+ dval(&rv0) = 1e300;
+ dval(&rv0) *= dval(&rv0);
+#endif
+#else /*IEEE_Arith*/
+ word0(&rv) = Big0;
+ word1(&rv) = Big1;
+#endif /*IEEE_Arith*/
+ goto ret;
+ }
+ e1 >>= 4;
+ for(j = 0; e1 > 1; j++, e1 >>= 1)
+ if (e1 & 1)
+ dval(&rv) *= bigtens[j];
+ /* The last multiplication could overflow. */
+ word0(&rv) -= P*Exp_msk1;
+ dval(&rv) *= bigtens[j];
+ if ((z = word0(&rv) & Exp_mask)
+ > Exp_msk1*(DBL_MAX_EXP+Bias-P))
+ goto ovfl;
+ if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) {
+ /* set to largest number */
+ /* (Can't trust DBL_MAX) */
+ word0(&rv) = Big0;
+ word1(&rv) = Big1;
+ }
+ else
+ word0(&rv) += P*Exp_msk1;
+ }
+ }
+ else if (e1 < 0) {
+ e1 = -e1;
+ if ((i = e1 & 15))
+ dval(&rv) /= tens[i];
+ if (e1 >>= 4) {
+ if (e1 >= 1 << n_bigtens)
+ goto undfl;
+#ifdef Avoid_Underflow
+ if (e1 & Scale_Bit)
+ bc.scale = 2*P;
+ for(j = 0; e1 > 0; j++, e1 >>= 1)
+ if (e1 & 1)
+ dval(&rv) *= tinytens[j];
+ if (bc.scale && (j = 2*P + 1 - ((word0(&rv) & Exp_mask)
+ >> Exp_shift)) > 0) {
+ /* scaled rv is denormal; clear j low bits */
+ if (j >= 32) {
+ word1(&rv) = 0;
+ if (j >= 53)
+ word0(&rv) = (P+2)*Exp_msk1;
+ else
+ word0(&rv) &= 0xffffffff << (j-32);
+ }
+ else
+ word1(&rv) &= 0xffffffff << j;
+ }
+#else
+ for(j = 0; e1 > 1; j++, e1 >>= 1)
+ if (e1 & 1)
+ dval(&rv) *= tinytens[j];
+ /* The last multiplication could underflow. */
+ dval(&rv0) = dval(&rv);
+ dval(&rv) *= tinytens[j];
+ if (!dval(&rv)) {
+ dval(&rv) = 2.*dval(&rv0);
+ dval(&rv) *= tinytens[j];
+#endif
+ if (!dval(&rv)) {
+ undfl:
+ dval(&rv) = 0.;
+#ifndef NO_ERRNO
+ errno = ERANGE;
+#endif
+ goto ret;
+ }
+#ifndef Avoid_Underflow
+ word0(&rv) = Tiny0;
+ word1(&rv) = Tiny1;
+ /* The refinement below will clean
+ * this approximation up.
+ */
+ }
+#endif
+ }
+ }
+
+ /* Now the hard part -- adjusting rv to the correct value.*/
+
+ /* Put digits into bd: true value = bd * 10^e */
+
+ bc.nd = nd;
+#ifndef NO_STRTOD_BIGCOMP
+ bc.nd0 = nd0; /* Only needed if nd > strtod_diglim, but done here */
+ /* to silence an erroneous warning about bc.nd0 */
+ /* possibly not being initialized. */
+ if (nd > strtod_diglim) {
+ /* ASSERT(strtod_diglim >= 18); 18 == one more than the */
+ /* minimum number of decimal digits to distinguish double values */
+ /* in IEEE arithmetic. */
+ i = j = 18;
+ if (i > nd0)
+ j += bc.dplen;
+ for(;;) {
+ if (--j <= bc.dp1 && j >= bc.dp0)
+ j = bc.dp0 - 1;
+ if (s0[j] != '0')
+ break;
+ --i;
+ }
+ e += nd - i;
+ nd = i;
+ if (nd0 > nd)
+ nd0 = nd;
+ if (nd < 9) { /* must recompute y */
+ y = 0;
+ for(i = 0; i < nd0; ++i)
+ y = 10*y + s0[i] - '0';
+ for(j = bc.dp1; i < nd; ++i)
+ y = 10*y + s0[j++] - '0';
+ }
+ }
+#endif
+ bd0 = s2b(s0, nd0, nd, y, bc.dplen);
+
+ for(;;) {
+ bd = Balloc(bd0->k);
+ Bcopy(bd, bd0);
+ bb = d2b(&rv, &bbe, &bbbits); /* rv = bb * 2^bbe */
+ bs = i2b(1);
+
+ if (e >= 0) {
+ bb2 = bb5 = 0;
+ bd2 = bd5 = e;
+ }
+ else {
+ bb2 = bb5 = -e;
+ bd2 = bd5 = 0;
+ }
+ if (bbe >= 0)
+ bb2 += bbe;
+ else
+ bd2 -= bbe;
+ bs2 = bb2;
+#ifdef Honor_FLT_ROUNDS
+ if (bc.rounding != 1)
+ bs2++;
+#endif
+#ifdef Avoid_Underflow
+ j = bbe - bc.scale;
+ i = j + bbbits - 1; /* logb(rv) */
+ if (i < Emin) /* denormal */
+ j += P - Emin;
+ else
+ j = P + 1 - bbbits;
+#else /*Avoid_Underflow*/
+#ifdef Sudden_Underflow
+#ifdef IBM
+ j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3);
+#else
+ j = P + 1 - bbbits;
+#endif
+#else /*Sudden_Underflow*/
+ j = bbe;
+ i = j + bbbits - 1; /* logb(rv) */
+ if (i < Emin) /* denormal */
+ j += P - Emin;
+ else
+ j = P + 1 - bbbits;
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ bb2 += j;
+ bd2 += j;
+#ifdef Avoid_Underflow
+ bd2 += bc.scale;
+#endif
+ i = bb2 < bd2 ? bb2 : bd2;
+ if (i > bs2)
+ i = bs2;
+ if (i > 0) {
+ bb2 -= i;
+ bd2 -= i;
+ bs2 -= i;
+ }
+ if (bb5 > 0) {
+ bs = pow5mult(bs, bb5);
+ bb1 = mult(bs, bb);
+ Bfree(bb);
+ bb = bb1;
+ }
+ if (bb2 > 0)
+ bb = lshift(bb, bb2);
+ if (bd5 > 0)
+ bd = pow5mult(bd, bd5);
+ if (bd2 > 0)
+ bd = lshift(bd, bd2);
+ if (bs2 > 0)
+ bs = lshift(bs, bs2);
+ delta = diff(bb, bd);
+ bc.dsign = delta->sign;
+ delta->sign = 0;
+ i = cmp(delta, bs);
+#ifndef NO_STRTOD_BIGCOMP
+ if (bc.nd > nd && i <= 0) {
+ if (bc.dsign)
+ break; /* Must use bigcomp(). */
+#ifdef Honor_FLT_ROUNDS
+ if (bc.rounding != 1) {
+ if (i < 0)
+ break;
+ }
+ else
+#endif
+ {
+ bc.nd = nd;
+ i = -1; /* Discarded digits make delta smaller. */
+ }
+ }
+#endif
+#ifdef Honor_FLT_ROUNDS
+ if (bc.rounding != 1) {
+ if (i < 0) {
+ /* Error is less than an ulp */
+ if (!delta->x[0] && delta->wds <= 1) {
+ /* exact */
+#ifdef SET_INEXACT
+ bc.inexact = 0;
+#endif
+ break;
+ }
+ if (bc.rounding) {
+ if (bc.dsign) {
+ adj.d = 1.;
+ goto apply_adj;
+ }
+ }
+ else if (!bc.dsign) {
+ adj.d = -1.;
+ if (!word1(&rv)
+ && !(word0(&rv) & Frac_mask)) {
+ y = word0(&rv) & Exp_mask;
+#ifdef Avoid_Underflow
+ if (!bc.scale || y > 2*P*Exp_msk1)
+#else
+ if (y)
+#endif
+ {
+ delta = lshift(delta,Log2P);
+ if (cmp(delta, bs) <= 0)
+ adj.d = -0.5;
+ }
+ }
+ apply_adj:
+#ifdef Avoid_Underflow
+ if (bc.scale && (y = word0(&rv) & Exp_mask)
+ <= 2*P*Exp_msk1)
+ word0(&adj) += (2*P+1)*Exp_msk1 - y;
+#else
+#ifdef Sudden_Underflow
+ if ((word0(&rv) & Exp_mask) <=
+ P*Exp_msk1) {
+ word0(&rv) += P*Exp_msk1;
+ dval(&rv) += adj.d*ulp(dval(&rv));
+ word0(&rv) -= P*Exp_msk1;
+ }
+ else
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ dval(&rv) += adj.d*ulp(&rv);
+ }
+ break;
+ }
+ adj.d = ratio(delta, bs);
+ if (adj.d < 1.)
+ adj.d = 1.;
+ if (adj.d <= 0x7ffffffe) {
+ /* adj = rounding ? ceil(adj) : floor(adj); */
+ y = adj.d;
+ if (y != adj.d) {
+ if (!((bc.rounding>>1) ^ bc.dsign))
+ y++;
+ adj.d = y;
+ }
+ }
+#ifdef Avoid_Underflow
+ if (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1)
+ word0(&adj) += (2*P+1)*Exp_msk1 - y;
+#else
+#ifdef Sudden_Underflow
+ if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) {
+ word0(&rv) += P*Exp_msk1;
+ adj.d *= ulp(dval(&rv));
+ if (bc.dsign)
+ dval(&rv) += adj.d;
+ else
+ dval(&rv) -= adj.d;
+ word0(&rv) -= P*Exp_msk1;
+ goto cont;
+ }
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ adj.d *= ulp(&rv);
+ if (bc.dsign) {
+ if (word0(&rv) == Big0 && word1(&rv) == Big1)
+ goto ovfl;
+ dval(&rv) += adj.d;
+ }
+ else
+ dval(&rv) -= adj.d;
+ goto cont;
+ }
+#endif /*Honor_FLT_ROUNDS*/
+
+ if (i < 0) {
+ /* Error is less than half an ulp -- check for
+ * special case of mantissa a power of two.
+ */
+ if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask
+#ifdef IEEE_Arith
+#ifdef Avoid_Underflow
+ || (word0(&rv) & Exp_mask) <= (2*P+1)*Exp_msk1
+#else
+ || (word0(&rv) & Exp_mask) <= Exp_msk1
+#endif
+#endif
+ ) {
+#ifdef SET_INEXACT
+ if (!delta->x[0] && delta->wds <= 1)
+ bc.inexact = 0;
+#endif
+ break;
+ }
+ if (!delta->x[0] && delta->wds <= 1) {
+ /* exact result */
+#ifdef SET_INEXACT
+ bc.inexact = 0;
+#endif
+ break;
+ }
+ delta = lshift(delta,Log2P);
+ if (cmp(delta, bs) > 0)
+ goto drop_down;
+ break;
+ }
+ if (i == 0) {
+ /* exactly half-way between */
+ if (bc.dsign) {
+ if ((word0(&rv) & Bndry_mask1) == Bndry_mask1
+ && word1(&rv) == (
+#ifdef Avoid_Underflow
+ (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1)
+ ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) :
+#endif
+ 0xffffffff)) {
+ /*boundary case -- increment exponent*/
+ word0(&rv) = (word0(&rv) & Exp_mask)
+ + Exp_msk1
+#ifdef IBM
+ | Exp_msk1 >> 4
+#endif
+ ;
+ word1(&rv) = 0;
+#ifdef Avoid_Underflow
+ bc.dsign = 0;
+#endif
+ break;
+ }
+ }
+ else if (!(word0(&rv) & Bndry_mask) && !word1(&rv)) {
+ drop_down:
+ /* boundary case -- decrement exponent */
+#ifdef Sudden_Underflow /*{{*/
+ L = word0(&rv) & Exp_mask;
+#ifdef IBM
+ if (L < Exp_msk1)
+#else
+#ifdef Avoid_Underflow
+ if (L <= (bc.scale ? (2*P+1)*Exp_msk1 : Exp_msk1))
+#else
+ if (L <= Exp_msk1)
+#endif /*Avoid_Underflow*/
+#endif /*IBM*/
+ {
+ if (bc.nd >nd) {
+ bc.uflchk = 1;
+ break;
+ }
+ goto undfl;
+ }
+ L -= Exp_msk1;
+#else /*Sudden_Underflow}{*/
+#ifdef Avoid_Underflow
+ if (bc.scale) {
+ L = word0(&rv) & Exp_mask;
+ if (L <= (2*P+1)*Exp_msk1) {
+ if (L > (P+2)*Exp_msk1)
+ /* round even ==> */
+ /* accept rv */
+ break;
+ /* rv = smallest denormal */
+ if (bc.nd >nd) {
+ bc.uflchk = 1;
+ break;
+ }
+ goto undfl;
+ }
+ }
+#endif /*Avoid_Underflow*/
+ L = (word0(&rv) & Exp_mask) - Exp_msk1;
+#endif /*Sudden_Underflow}}*/
+ word0(&rv) = L | Bndry_mask1;
+ word1(&rv) = 0xffffffff;
+#ifdef IBM
+ goto cont;
+#else
+ break;
+#endif
+ }
+#ifndef ROUND_BIASED
+ if (!(word1(&rv) & LSB))
+ break;
+#endif
+ if (bc.dsign)
+ dval(&rv) += ulp(&rv);
+#ifndef ROUND_BIASED
+ else {
+ dval(&rv) -= ulp(&rv);
+#ifndef Sudden_Underflow
+ if (!dval(&rv)) {
+ if (bc.nd >nd) {
+ bc.uflchk = 1;
+ break;
+ }
+ goto undfl;
+ }
+#endif
+ }
+#ifdef Avoid_Underflow
+ bc.dsign = 1 - bc.dsign;
+#endif
+#endif
+ break;
+ }
+ if ((aadj = ratio(delta, bs)) <= 2.) {
+ if (bc.dsign)
+ aadj = aadj1 = 1.;
+ else if (word1(&rv) || word0(&rv) & Bndry_mask) {
+#ifndef Sudden_Underflow
+ if (word1(&rv) == Tiny1 && !word0(&rv)) {
+ if (bc.nd >nd) {
+ bc.uflchk = 1;
+ break;
+ }
+ goto undfl;
+ }
+#endif
+ aadj = 1.;
+ aadj1 = -1.;
+ }
+ else {
+ /* special case -- power of FLT_RADIX to be */
+ /* rounded down... */
+
+ if (aadj < 2./FLT_RADIX)
+ aadj = 1./FLT_RADIX;
+ else
+ aadj *= 0.5;
+ aadj1 = -aadj;
+ }
+ }
+ else {
+ aadj *= 0.5;
+ aadj1 = bc.dsign ? aadj : -aadj;
+#ifdef Check_FLT_ROUNDS
+ switch(bc.rounding) {
+ case 2: /* towards +infinity */
+ aadj1 -= 0.5;
+ break;
+ case 0: /* towards 0 */
+ case 3: /* towards -infinity */
+ aadj1 += 0.5;
+ }
+#else
+ if (Flt_Rounds == 0)
+ aadj1 += 0.5;
+#endif /*Check_FLT_ROUNDS*/
+ }
+ y = word0(&rv) & Exp_mask;
+
+ /* Check for overflow */
+
+ if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) {
+ dval(&rv0) = dval(&rv);
+ word0(&rv) -= P*Exp_msk1;
+ adj.d = aadj1 * ulp(&rv);
+ dval(&rv) += adj.d;
+ if ((word0(&rv) & Exp_mask) >=
+ Exp_msk1*(DBL_MAX_EXP+Bias-P)) {
+ if (word0(&rv0) == Big0 && word1(&rv0) == Big1)
+ goto ovfl;
+ word0(&rv) = Big0;
+ word1(&rv) = Big1;
+ goto cont;
+ }
+ else
+ word0(&rv) += P*Exp_msk1;
+ }
+ else {
+#ifdef Avoid_Underflow
+ if (bc.scale && y <= 2*P*Exp_msk1) {
+ if (aadj <= 0x7fffffff) {
+ if ((z = aadj) <= 0)
+ z = 1;
+ aadj = z;
+ aadj1 = bc.dsign ? aadj : -aadj;
+ }
+ dval(&aadj2) = aadj1;
+ word0(&aadj2) += (2*P+1)*Exp_msk1 - y;
+ aadj1 = dval(&aadj2);
+ }
+ adj.d = aadj1 * ulp(&rv);
+ dval(&rv) += adj.d;
+#else
+#ifdef Sudden_Underflow
+ if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) {
+ dval(&rv0) = dval(&rv);
+ word0(&rv) += P*Exp_msk1;
+ adj.d = aadj1 * ulp(&rv);
+ dval(&rv) += adj.d;
+#ifdef IBM
+ if ((word0(&rv) & Exp_mask) < P*Exp_msk1)
+#else
+ if ((word0(&rv) & Exp_mask) <= P*Exp_msk1)
+#endif
+ {
+ if (word0(&rv0) == Tiny0
+ && word1(&rv0) == Tiny1) {
+ if (bc.nd >nd) {
+ bc.uflchk = 1;
+ break;
+ }
+ goto undfl;
+ }
+ word0(&rv) = Tiny0;
+ word1(&rv) = Tiny1;
+ goto cont;
+ }
+ else
+ word0(&rv) -= P*Exp_msk1;
+ }
+ else {
+ adj.d = aadj1 * ulp(&rv);
+ dval(&rv) += adj.d;
+ }
+#else /*Sudden_Underflow*/
+ /* Compute adj so that the IEEE rounding rules will
+ * correctly round rv + adj in some half-way cases.
+ * If rv * ulp(rv) is denormalized (i.e.,
+ * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid
+ * trouble from bits lost to denormalization;
+ * example: 1.2e-307 .
+ */
+ if (y <= (P-1)*Exp_msk1 && aadj > 1.) {
+ aadj1 = (double)(int)(aadj + 0.5);
+ if (!bc.dsign)
+ aadj1 = -aadj1;
+ }
+ adj.d = aadj1 * ulp(&rv);
+ dval(&rv) += adj.d;
+#endif /*Sudden_Underflow*/
+#endif /*Avoid_Underflow*/
+ }
+ z = word0(&rv) & Exp_mask;
+#ifndef SET_INEXACT
+ if (bc.nd == nd) {
+#ifdef Avoid_Underflow
+ if (!bc.scale)
+#endif
+ if (y == z) {
+ /* Can we stop now? */
+ L = (Long)aadj;
+ aadj -= L;
+ /* The tolerances below are conservative. */
+ if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask) {
+ if (aadj < .4999999 || aadj > .5000001)
+ break;
+ }
+ else if (aadj < .4999999/FLT_RADIX)
+ break;
+ }
+ }
+#endif
+ cont:
+ Bfree(bb);
+ Bfree(bd);
+ Bfree(bs);
+ Bfree(delta);
+ }
+ Bfree(bb);
+ Bfree(bd);
+ Bfree(bs);
+ Bfree(bd0);
+ Bfree(delta);
+#ifndef NO_STRTOD_BIGCOMP
+ if (bc.nd > nd)
+ bigcomp(&rv, s0, &bc);
+#endif
+#ifdef SET_INEXACT
+ if (bc.inexact) {
+ if (!oldinexact) {
+ word0(&rv0) = Exp_1 + (70 << Exp_shift);
+ word1(&rv0) = 0;
+ dval(&rv0) += 1.;
+ }
+ }
+ else if (!oldinexact)
+ clear_inexact();
+#endif
+#ifdef Avoid_Underflow
+ if (bc.scale) {
+ word0(&rv0) = Exp_1 - 2*P*Exp_msk1;
+ word1(&rv0) = 0;
+ dval(&rv) *= dval(&rv0);
+#ifndef NO_ERRNO
+ /* try to avoid the bug of testing an 8087 register value */
+#ifdef IEEE_Arith
+ if (!(word0(&rv) & Exp_mask))
+#else
+ if (word0(&rv) == 0 && word1(&rv) == 0)
+#endif
+ errno = ERANGE;
+#endif
+ }
+#endif /* Avoid_Underflow */
+#ifdef SET_INEXACT
+ if (bc.inexact && !(word0(&rv) & Exp_mask)) {
+ /* set underflow bit */
+ dval(&rv0) = 1e-300;
+ dval(&rv0) *= dval(&rv0);
+ }
+#endif
+ ret:
+ if (se)
+ *se = (char *)s;
+ return sign ? -dval(&rv) : dval(&rv);
+ }
+
+#ifndef MULTIPLE_THREADS
+ static char *dtoa_result;
+#endif
+
+ static char *
+#ifdef KR_headers
+rv_alloc(i) int i;
+#else
+rv_alloc(int i)
+#endif
+{
+ int j, k, *r;
+
+ j = sizeof(ULong);
+ for(k = 0;
+ sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= (size_t)i;
+ j <<= 1)
+ k++;
+ r = (int*)Balloc(k);
+ *r = k;
+ return
+#ifndef MULTIPLE_THREADS
+ dtoa_result =
+#endif
+ (char *)(r+1);
+ }
+
+ static char *
+#ifdef KR_headers
+nrv_alloc(s, rve, n) char *s, **rve; int n;
+#else
+nrv_alloc(CONST char *s, char **rve, int n)
+#endif
+{
+ char *rv, *t;
+
+ t = rv = rv_alloc(n);
+ while((*t = *s++)) t++;
+ if (rve)
+ *rve = t;
+ return rv;
+ }
+
+/* freedtoa(s) must be used to free values s returned by dtoa
+ * when MULTIPLE_THREADS is #defined. It should be used in all cases,
+ * but for consistency with earlier versions of dtoa, it is optional
+ * when MULTIPLE_THREADS is not defined.
+ */
+
+ void
+#ifdef KR_headers
+freedtoa(s) char *s;
+#else
+freedtoa(char *s)
+#endif
+{
+ Bigint *b = (Bigint *)((int *)s - 1);
+ b->maxwds = 1 << (b->k = *(int*)b);
+ Bfree(b);
+#ifndef MULTIPLE_THREADS
+ if (s == dtoa_result)
+ dtoa_result = 0;
+#endif
+ }
+
+/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string.
+ *
+ * Inspired by "How to Print Floating-Point Numbers Accurately" by
+ * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126].
+ *
+ * Modifications:
+ * 1. Rather than iterating, we use a simple numeric overestimate
+ * to determine k = floor(log10(d)). We scale relevant
+ * quantities using O(log2(k)) rather than O(k) multiplications.
+ * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't
+ * try to generate digits strictly left to right. Instead, we
+ * compute with fewer bits and propagate the carry if necessary
+ * when rounding the final digit up. This is often faster.
+ * 3. Under the assumption that input will be rounded nearest,
+ * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22.
+ * That is, we allow equality in stopping tests when the
+ * round-nearest rule will give the same floating-point value
+ * as would satisfaction of the stopping test with strict
+ * inequality.
+ * 4. We remove common factors of powers of 2 from relevant
+ * quantities.
+ * 5. When converting floating-point integers less than 1e16,
+ * we use floating-point arithmetic rather than resorting
+ * to multiple-precision integers.
+ * 6. When asked to produce fewer than 15 digits, we first try
+ * to get by with floating-point arithmetic; we resort to
+ * multiple-precision integer arithmetic only if we cannot
+ * guarantee that the floating-point calculation has given
+ * the correctly rounded result. For k requested digits and
+ * "uniformly" distributed input, the probability is
+ * something like 10^(k-15) that we must resort to the Long
+ * calculation.
+ */
+
+ char *
+dtoa
+#ifdef KR_headers
+ (dd, mode, ndigits, decpt, sign, rve)
+ double dd; int mode, ndigits, *decpt, *sign; char **rve;
+#else
+ (double dd, int mode, int ndigits, int *decpt, int *sign, char **rve)
+#endif
+{
+ /* Arguments ndigits, decpt, sign are similar to those
+ of ecvt and fcvt; trailing zeros are suppressed from
+ the returned string. If not null, *rve is set to point
+ to the end of the return value. If d is +-Infinity or NaN,
+ then *decpt is set to 9999.
+
+ mode:
+ 0 ==> shortest string that yields d when read in
+ and rounded to nearest.
+ 1 ==> like 0, but with Steele & White stopping rule;
+ e.g. with IEEE P754 arithmetic , mode 0 gives
+ 1e23 whereas mode 1 gives 9.999999999999999e22.
+ 2 ==> max(1,ndigits) significant digits. This gives a
+ return value similar to that of ecvt, except
+ that trailing zeros are suppressed.
+ 3 ==> through ndigits past the decimal point. This
+ gives a return value similar to that from fcvt,
+ except that trailing zeros are suppressed, and
+ ndigits can be negative.
+ 4,5 ==> similar to 2 and 3, respectively, but (in
+ round-nearest mode) with the tests of mode 0 to
+ possibly return a shorter string that rounds to d.
+ With IEEE arithmetic and compilation with
+ -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same
+ as modes 2 and 3 when FLT_ROUNDS != 1.
+ 6-9 ==> Debugging modes similar to mode - 4: don't try
+ fast floating-point estimate (if applicable).
+
+ Values of mode other than 0-9 are treated as mode 0.
+
+ Sufficient space is allocated to the return value
+ to hold the suppressed trailing zeros.
+ */
+
+ int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1,
+ j, j1, k, k0, k_check, leftright, m2, m5, s2, s5,
+ spec_case, try_quick;
+ Long L;
+#ifndef Sudden_Underflow
+ int denorm;
+ ULong x;
+#endif
+ Bigint *b, *b1, *delta, *mlo = NULL, *mhi, *S;
+ U d2, eps, u;
+ double ds;
+ char *s, *s0;
+#ifdef SET_INEXACT
+ int inexact, oldinexact;
+#endif
+#ifdef Honor_FLT_ROUNDS /*{*/
+ int Rounding;
+#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */
+ Rounding = Flt_Rounds;
+#else /*}{*/
+ Rounding = 1;
+ switch(fegetround()) {
+ case FE_TOWARDZERO: Rounding = 0; break;
+ case FE_UPWARD: Rounding = 2; break;
+ case FE_DOWNWARD: Rounding = 3;
+ }
+#endif /*}}*/
+#endif /*}*/
+
+#ifndef MULTIPLE_THREADS
+ if (dtoa_result) {
+ freedtoa(dtoa_result);
+ dtoa_result = 0;
+ }
+#endif
+
+ u.d = dd;
+ if (word0(&u) & Sign_bit) {
+ /* set sign for everything, including 0's and NaNs */
+ *sign = 1;
+ word0(&u) &= ~Sign_bit; /* clear sign bit */
+ }
+ else
+ *sign = 0;
+
+#if defined(IEEE_Arith) + defined(VAX)
+#ifdef IEEE_Arith
+ if ((word0(&u) & Exp_mask) == Exp_mask)
+#else
+ if (word0(&u) == 0x8000)
+#endif
+ {
+ /* Infinity or NaN */
+ *decpt = 9999;
+#ifdef IEEE_Arith
+ if (!word1(&u) && !(word0(&u) & 0xfffff))
+ return nrv_alloc("Infinity", rve, 8);
+#endif
+ return nrv_alloc("NaN", rve, 3);
+ }
+#endif
+#ifdef IBM
+ dval(&u) += 0; /* normalize */
+#endif
+ if (!dval(&u)) {
+ *decpt = 1;
+ return nrv_alloc("0", rve, 1);
+ }
+
+#ifdef SET_INEXACT
+ try_quick = oldinexact = get_inexact();
+ inexact = 1;
+#endif
+#ifdef Honor_FLT_ROUNDS
+ if (Rounding >= 2) {
+ if (*sign)
+ Rounding = Rounding == 2 ? 0 : 2;
+ else
+ if (Rounding != 2)
+ Rounding = 0;
+ }
+#endif
+
+ b = d2b(&u, &be, &bbits);
+#ifdef Sudden_Underflow
+ i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1));
+#else
+ if ((i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1)))) {
+#endif
+ dval(&d2) = dval(&u);
+ word0(&d2) &= Frac_mask1;
+ word0(&d2) |= Exp_11;
+#ifdef IBM
+ if (j = 11 - hi0bits(word0(&d2) & Frac_mask))
+ dval(&d2) /= 1 << j;
+#endif
+
+ /* log(x) ~=~ log(1.5) + (x-1.5)/1.5
+ * log10(x) = log(x) / log(10)
+ * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10))
+ * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2)
+ *
+ * This suggests computing an approximation k to log10(d) by
+ *
+ * k = (i - Bias)*0.301029995663981
+ * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 );
+ *
+ * We want k to be too large rather than too small.
+ * The error in the first-order Taylor series approximation
+ * is in our favor, so we just round up the constant enough
+ * to compensate for any error in the multiplication of
+ * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077,
+ * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14,
+ * adding 1e-13 to the constant term more than suffices.
+ * Hence we adjust the constant term to 0.1760912590558.
+ * (We could get a more accurate k by invoking log10,
+ * but this is probably not worthwhile.)
+ */
+
+ i -= Bias;
+#ifdef IBM
+ i <<= 2;
+ i += j;
+#endif
+#ifndef Sudden_Underflow
+ denorm = 0;
+ }
+ else {
+ /* d is denormalized */
+
+ i = bbits + be + (Bias + (P-1) - 1);
+ x = i > 32 ? word0(&u) << (64 - i) | word1(&u) >> (i - 32)
+ : word1(&u) << (32 - i);
+ dval(&d2) = x;
+ word0(&d2) -= 31*Exp_msk1; /* adjust exponent */
+ i -= (Bias + (P-1) - 1) + 1;
+ denorm = 1;
+ }
+#endif
+ ds = (dval(&d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981;
+ k = (int)ds;
+ if (ds < 0. && ds != k)
+ k--; /* want k = floor(ds) */
+ k_check = 1;
+ if (k >= 0 && k <= Ten_pmax) {
+ if (dval(&u) < tens[k])
+ k--;
+ k_check = 0;
+ }
+ j = bbits - i - 1;
+ if (j >= 0) {
+ b2 = 0;
+ s2 = j;
+ }
+ else {
+ b2 = -j;
+ s2 = 0;
+ }
+ if (k >= 0) {
+ b5 = 0;
+ s5 = k;
+ s2 += k;
+ }
+ else {
+ b2 -= k;
+ b5 = -k;
+ s5 = 0;
+ }
+ if (mode < 0 || mode > 9)
+ mode = 0;
+
+#ifndef SET_INEXACT
+#ifdef Check_FLT_ROUNDS
+ try_quick = Rounding == 1;
+#else
+ try_quick = 1;
+#endif
+#endif /*SET_INEXACT*/
+
+ if (mode > 5) {
+ mode -= 4;
+ try_quick = 0;
+ }
+ leftright = 1;
+ ilim = ilim1 = -1; /* Values for cases 0 and 1; done here to */
+ /* silence erroneous "gcc -Wall" warning. */
+ switch(mode) {
+ case 0:
+ case 1:
+ i = 18;
+ ndigits = 0;
+ break;
+ case 2:
+ leftright = 0;
+ /* no break */
+ case 4:
+ if (ndigits <= 0)
+ ndigits = 1;
+ ilim = ilim1 = i = ndigits;
+ break;
+ case 3:
+ leftright = 0;
+ /* no break */
+ case 5:
+ i = ndigits + k + 1;
+ ilim = i;
+ ilim1 = i - 1;
+ if (i <= 0)
+ i = 1;
+ }
+ s = s0 = rv_alloc(i);
+
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1 && Rounding != 1)
+ leftright = 0;
+#endif
+
+ if (ilim >= 0 && ilim <= Quick_max && try_quick) {
+
+ /* Try to get by with floating-point arithmetic. */
+
+ i = 0;
+ dval(&d2) = dval(&u);
+ k0 = k;
+ ilim0 = ilim;
+ ieps = 2; /* conservative */
+ if (k > 0) {
+ ds = tens[k&0xf];
+ j = k >> 4;
+ if (j & Bletch) {
+ /* prevent overflows */
+ j &= Bletch - 1;
+ dval(&u) /= bigtens[n_bigtens-1];
+ ieps++;
+ }
+ for(; j; j >>= 1, i++)
+ if (j & 1) {
+ ieps++;
+ ds *= bigtens[i];
+ }
+ dval(&u) /= ds;
+ }
+ else if ((j1 = -k)) {
+ dval(&u) *= tens[j1 & 0xf];
+ for(j = j1 >> 4; j; j >>= 1, i++)
+ if (j & 1) {
+ ieps++;
+ dval(&u) *= bigtens[i];
+ }
+ }
+ if (k_check && dval(&u) < 1. && ilim > 0) {
+ if (ilim1 <= 0)
+ goto fast_failed;
+ ilim = ilim1;
+ k--;
+ dval(&u) *= 10.;
+ ieps++;
+ }
+ dval(&eps) = ieps*dval(&u) + 7.;
+ word0(&eps) -= (P-1)*Exp_msk1;
+ if (ilim == 0) {
+ S = mhi = 0;
+ dval(&u) -= 5.;
+ if (dval(&u) > dval(&eps))
+ goto one_digit;
+ if (dval(&u) < -dval(&eps))
+ goto no_digits;
+ goto fast_failed;
+ }
+#ifndef No_leftright
+ if (leftright) {
+ /* Use Steele & White method of only
+ * generating digits needed.
+ */
+ dval(&eps) = 0.5/tens[ilim-1] - dval(&eps);
+ for(i = 0;;) {
+ L = dval(&u);
+ dval(&u) -= L;
+ *s++ = '0' + (int)L;
+ if (dval(&u) < dval(&eps))
+ goto ret1;
+ if (1. - dval(&u) < dval(&eps))
+ goto bump_up;
+ if (++i >= ilim)
+ break;
+ dval(&eps) *= 10.;
+ dval(&u) *= 10.;
+ }
+ }
+ else {
+#endif
+ /* Generate ilim digits, then fix them up. */
+ dval(&eps) *= tens[ilim-1];
+ for(i = 1;; i++, dval(&u) *= 10.) {
+ L = (Long)(dval(&u));
+ if (!(dval(&u) -= L))
+ ilim = i;
+ *s++ = '0' + (int)L;
+ if (i == ilim) {
+ if (dval(&u) > 0.5 + dval(&eps))
+ goto bump_up;
+ else if (dval(&u) < 0.5 - dval(&eps)) {
+ while(*--s == '0') {}
+ s++;
+ goto ret1;
+ }
+ break;
+ }
+ }
+#ifndef No_leftright
+ }
+#endif
+ fast_failed:
+ s = s0;
+ dval(&u) = dval(&d2);
+ k = k0;
+ ilim = ilim0;
+ }
+
+ /* Do we have a "small" integer? */
+
+ if (be >= 0 && k <= Int_max) {
+ /* Yes. */
+ ds = tens[k];
+ if (ndigits < 0 && ilim <= 0) {
+ S = mhi = 0;
+ if (ilim < 0 || dval(&u) <= 5*ds)
+ goto no_digits;
+ goto one_digit;
+ }
+ for(i = 1; i <= k + 1; i++, dval(&u) *= 10.) {
+ L = (Long)(dval(&u) / ds);
+ dval(&u) -= L*ds;
+#ifdef Check_FLT_ROUNDS
+ /* If FLT_ROUNDS == 2, L will usually be high by 1 */
+ if (dval(&u) < 0) {
+ L--;
+ dval(&u) += ds;
+ }
+#endif
+ *s++ = '0' + (int)L;
+ if (!dval(&u)) {
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ break;
+ }
+ if (i == ilim) {
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1)
+ switch(Rounding) {
+ case 0: goto ret1;
+ case 2: goto bump_up;
+ }
+#endif
+ dval(&u) += dval(&u);
+ if (dval(&u) > ds || (dval(&u) == ds && L & 1)) {
+ bump_up:
+ while(*--s == '9')
+ if (s == s0) {
+ k++;
+ *s = '0';
+ break;
+ }
+ ++*s++;
+ }
+ break;
+ }
+ }
+ goto ret1;
+ }
+
+ m2 = b2;
+ m5 = b5;
+ mhi = mlo = 0;
+ if (leftright) {
+ i =
+#ifndef Sudden_Underflow
+ denorm ? be + (Bias + (P-1) - 1 + 1) :
+#endif
+#ifdef IBM
+ 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3);
+#else
+ 1 + P - bbits;
+#endif
+ b2 += i;
+ s2 += i;
+ mhi = i2b(1);
+ }
+ if (m2 > 0 && s2 > 0) {
+ i = m2 < s2 ? m2 : s2;
+ b2 -= i;
+ m2 -= i;
+ s2 -= i;
+ }
+ if (b5 > 0) {
+ if (leftright) {
+ if (m5 > 0) {
+ mhi = pow5mult(mhi, m5);
+ b1 = mult(mhi, b);
+ Bfree(b);
+ b = b1;
+ }
+ if ((j = b5 - m5))
+ b = pow5mult(b, j);
+ }
+ else
+ b = pow5mult(b, b5);
+ }
+ S = i2b(1);
+ if (s5 > 0)
+ S = pow5mult(S, s5);
+
+ /* Check for special case that d is a normalized power of 2. */
+
+ spec_case = 0;
+ if ((mode < 2 || leftright)
+#ifdef Honor_FLT_ROUNDS
+ && Rounding == 1
+#endif
+ ) {
+ if (!word1(&u) && !(word0(&u) & Bndry_mask)
+#ifndef Sudden_Underflow
+ && word0(&u) & (Exp_mask & ~Exp_msk1)
+#endif
+ ) {
+ /* The special case */
+ b2 += Log2P;
+ s2 += Log2P;
+ spec_case = 1;
+ }
+ }
+
+ /* Arrange for convenient computation of quotients:
+ * shift left if necessary so divisor has 4 leading 0 bits.
+ *
+ * Perhaps we should just compute leading 28 bits of S once
+ * and for all and pass them and a shift to quorem, so it
+ * can do shifts and ors to compute the numerator for q.
+ */
+#ifdef Pack_32
+ if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f))
+ i = 32 - i;
+#define iInc 28
+#else
+ if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf)
+ i = 16 - i;
+#define iInc 12
+#endif
+ i = dshift(S, s2);
+ b2 += i;
+ m2 += i;
+ s2 += i;
+ if (b2 > 0)
+ b = lshift(b, b2);
+ if (s2 > 0)
+ S = lshift(S, s2);
+ if (k_check) {
+ if (cmp(b,S) < 0) {
+ k--;
+ b = multadd(b, 10, 0); /* we botched the k estimate */
+ if (leftright)
+ mhi = multadd(mhi, 10, 0);
+ ilim = ilim1;
+ }
+ }
+ if (ilim <= 0 && (mode == 3 || mode == 5)) {
+ if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) {
+ /* no digits, fcvt style */
+ no_digits:
+ k = -1 - ndigits;
+ goto ret;
+ }
+ one_digit:
+ *s++ = '1';
+ k++;
+ goto ret;
+ }
+ if (leftright) {
+ if (m2 > 0)
+ mhi = lshift(mhi, m2);
+
+ /* Compute mlo -- check for special case
+ * that d is a normalized power of 2.
+ */
+
+ mlo = mhi;
+ if (spec_case) {
+ mhi = Balloc(mhi->k);
+ Bcopy(mhi, mlo);
+ mhi = lshift(mhi, Log2P);
+ }
+
+ for(i = 1;;i++) {
+ dig = quorem(b,S) + '0';
+ /* Do we yet have the shortest decimal string
+ * that will round to d?
+ */
+ j = cmp(b, mlo);
+ delta = diff(S, mhi);
+ j1 = delta->sign ? 1 : cmp(b, delta);
+ Bfree(delta);
+#ifndef ROUND_BIASED
+ if (j1 == 0 && mode != 1 && !(word1(&u) & 1)
+#ifdef Honor_FLT_ROUNDS
+ && Rounding >= 1
+#endif
+ ) {
+ if (dig == '9')
+ goto round_9_up;
+ if (j > 0)
+ dig++;
+#ifdef SET_INEXACT
+ else if (!b->x[0] && b->wds <= 1)
+ inexact = 0;
+#endif
+ *s++ = dig;
+ goto ret;
+ }
+#endif
+ if (j < 0 || (j == 0 && mode != 1
+#ifndef ROUND_BIASED
+ && !(word1(&u) & 1)
+#endif
+ )) {
+ if (!b->x[0] && b->wds <= 1) {
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ goto accept_dig;
+ }
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1)
+ switch(Rounding) {
+ case 0: goto accept_dig;
+ case 2: goto keep_dig;
+ }
+#endif /*Honor_FLT_ROUNDS*/
+ if (j1 > 0) {
+ b = lshift(b, 1);
+ j1 = cmp(b, S);
+ if ((j1 > 0 || (j1 == 0 && dig & 1))
+ && dig++ == '9')
+ goto round_9_up;
+ }
+ accept_dig:
+ *s++ = dig;
+ goto ret;
+ }
+ if (j1 > 0) {
+#ifdef Honor_FLT_ROUNDS
+ if (!Rounding)
+ goto accept_dig;
+#endif
+ if (dig == '9') { /* possible if i == 1 */
+ round_9_up:
+ *s++ = '9';
+ goto roundoff;
+ }
+ *s++ = dig + 1;
+ goto ret;
+ }
+#ifdef Honor_FLT_ROUNDS
+ keep_dig:
+#endif
+ *s++ = dig;
+ if (i == ilim)
+ break;
+ b = multadd(b, 10, 0);
+ if (mlo == mhi)
+ mlo = mhi = multadd(mhi, 10, 0);
+ else {
+ mlo = multadd(mlo, 10, 0);
+ mhi = multadd(mhi, 10, 0);
+ }
+ }
+ }
+ else
+ for(i = 1;; i++) {
+ *s++ = dig = quorem(b,S) + '0';
+ if (!b->x[0] && b->wds <= 1) {
+#ifdef SET_INEXACT
+ inexact = 0;
+#endif
+ goto ret;
+ }
+ if (i >= ilim)
+ break;
+ b = multadd(b, 10, 0);
+ }
+
+ /* Round off last digit */
+
+#ifdef Honor_FLT_ROUNDS
+ switch(Rounding) {
+ case 0: goto trimzeros;
+ case 2: goto roundoff;
+ }
+#endif
+ b = lshift(b, 1);
+ j = cmp(b, S);
+ if (j > 0 || (j == 0 && dig & 1)) {
+ roundoff:
+ while(*--s == '9')
+ if (s == s0) {
+ k++;
+ *s++ = '1';
+ goto ret;
+ }
+ ++*s++;
+ }
+ else {
+#ifdef Honor_FLT_ROUNDS
+ trimzeros:
+#endif
+ while(*--s == '0') {}
+ s++;
+ }
+ ret:
+ Bfree(S);
+ if (mhi) {
+ if (mlo && mlo != mhi)
+ Bfree(mlo);
+ Bfree(mhi);
+ }
+ ret1:
+#ifdef SET_INEXACT
+ if (inexact) {
+ if (!oldinexact) {
+ word0(&u) = Exp_1 + (70 << Exp_shift);
+ word1(&u) = 0;
+ dval(&u) += 1.;
+ }
+ }
+ else if (!oldinexact)
+ clear_inexact();
+#endif
+ Bfree(b);
+ *s = 0;
+ *decpt = k + 1;
+ if (rve)
+ *rve = s;
+ return s0;
+ }
+
+} // namespace dmg_fp
diff --git a/src/base/third_party/dmg_fp/dtoa_wrapper.cc b/src/base/third_party/dmg_fp/dtoa_wrapper.cc
new file mode 100644
index 0000000..c314c59
--- /dev/null
+++ b/src/base/third_party/dmg_fp/dtoa_wrapper.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2012 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.
+//
+// The purpose of this file is to supply the macro definintions necessary
+// to make third_party/dmg_fp/dtoa.cc threadsafe.
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/synchronization/lock.h"
+
+// We need two locks because they're sometimes grabbed at the same time.
+// A single lock would lead to an attempted recursive grab.
+static base::LazyInstance<base::Lock>::Leaky
+ dtoa_lock_0 = LAZY_INSTANCE_INITIALIZER;
+static base::LazyInstance<base::Lock>::Leaky
+ dtoa_lock_1 = LAZY_INSTANCE_INITIALIZER;
+
+/*
+ * This define and the code below is to trigger thread-safe behavior
+ * in dtoa.cc, per this comment from the file:
+ *
+ * #define MULTIPLE_THREADS if the system offers preemptively scheduled
+ * multiple threads. In this case, you must provide (or suitably
+ * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed
+ * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed
+ * in pow5mult, ensures lazy evaluation of only one copy of high
+ * powers of 5; omitting this lock would introduce a small
+ * probability of wasting memory, but would otherwise be harmless.)
+ * You must also invoke freedtoa(s) to free the value s returned by
+ * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined.
+ */
+#define MULTIPLE_THREADS
+
+inline static void ACQUIRE_DTOA_LOCK(size_t n) {
+ DCHECK(n < 2);
+ base::Lock* lock = n == 0 ? dtoa_lock_0.Pointer() : dtoa_lock_1.Pointer();
+ lock->Acquire();
+}
+
+inline static void FREE_DTOA_LOCK(size_t n) {
+ DCHECK(n < 2);
+ base::Lock* lock = n == 0 ? dtoa_lock_0.Pointer() : dtoa_lock_1.Pointer();
+ lock->Release();
+}
+
+#include "base/third_party/dmg_fp/dtoa.cc"
diff --git a/src/base/third_party/dmg_fp/float_precision_crash.patch b/src/base/third_party/dmg_fp/float_precision_crash.patch
new file mode 100644
index 0000000..df64e7e
--- /dev/null
+++ b/src/base/third_party/dmg_fp/float_precision_crash.patch
@@ -0,0 +1,13 @@
+diff --git a/base/third_party/dmg_fp/dtoa.cc b/base/third_party/dmg_fp/dtoa.cc
+index 3f7e794..3312fa4 100644
+--- dtoa.cc
++++ dtoa.cc
+@@ -3891,7 +3891,7 @@ dtoa
+ goto no_digits;
+ goto one_digit;
+ }
+- for(i = 1;; i++, dval(&u) *= 10.) {
++ for(i = 1; i <= k + 1; i++, dval(&u) *= 10.) {
+ L = (Long)(dval(&u) / ds);
+ dval(&u) -= L*ds;
+ #ifdef Check_FLT_ROUNDS
diff --git a/src/base/third_party/dmg_fp/g_fmt.cc b/src/base/third_party/dmg_fp/g_fmt.cc
new file mode 100644
index 0000000..d864eb7
--- /dev/null
+++ b/src/base/third_party/dmg_fp/g_fmt.cc
@@ -0,0 +1,102 @@
+/****************************************************************
+ *
+ * The author of this software is David M. Gay.
+ *
+ * Copyright (c) 1991, 1996 by Lucent Technologies.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ ***************************************************************/
+
+/* g_fmt(buf,x) stores the closest decimal approximation to x in buf;
+ * it suffices to declare buf
+ * char buf[32];
+ */
+
+#include "dmg_fp.h"
+
+namespace dmg_fp {
+
+ char *
+g_fmt(register char *b, double x)
+{
+ register int i, k;
+ register char *s;
+ int decpt, j, sign;
+ char *b0, *s0, *se;
+
+ b0 = b;
+#ifdef IGNORE_ZERO_SIGN
+ if (!x) {
+ *b++ = '0';
+ *b = 0;
+ goto done;
+ }
+#endif
+ s = s0 = dtoa(x, 0, 0, &decpt, &sign, &se);
+ if (sign)
+ *b++ = '-';
+ if (decpt == 9999) /* Infinity or Nan */ {
+ while((*b++ = *s++)) {}
+ goto done0;
+ }
+ if (decpt <= -4 || decpt > se - s + 5) {
+ *b++ = *s++;
+ if (*s) {
+ *b++ = '.';
+ while((*b = *s++))
+ b++;
+ }
+ *b++ = 'e';
+ /* sprintf(b, "%+.2d", decpt - 1); */
+ if (--decpt < 0) {
+ *b++ = '-';
+ decpt = -decpt;
+ }
+ else
+ *b++ = '+';
+ for(j = 2, k = 10; 10*k <= decpt; j++, k *= 10) {}
+ for(;;) {
+ i = decpt / k;
+ *b++ = i + '0';
+ if (--j <= 0)
+ break;
+ decpt -= i*k;
+ decpt *= 10;
+ }
+ *b = 0;
+ }
+ else if (decpt <= 0) {
+ *b++ = '.';
+ for(; decpt < 0; decpt++)
+ *b++ = '0';
+ while((*b++ = *s++)) {}
+ }
+ else {
+ while((*b = *s++)) {
+ b++;
+ if (--decpt == 0 && *s)
+ *b++ = '.';
+ }
+ for(; decpt > 0; decpt--)
+ *b++ = '0';
+ *b = 0;
+ }
+ done0:
+ freedtoa(s0);
+#ifdef IGNORE_ZERO_SIGN
+ done:
+#endif
+ return b0;
+ }
+
+} // namespace dmg_fp
diff --git a/src/base/third_party/dmg_fp/gcc_64_bit.patch b/src/base/third_party/dmg_fp/gcc_64_bit.patch
new file mode 100644
index 0000000..ab943c0
--- /dev/null
+++ b/src/base/third_party/dmg_fp/gcc_64_bit.patch
@@ -0,0 +1,25 @@
+Index: dtoa.cc
+--- dtoa.cc (old copy)
++++ dtoa.cc (working copy)
+@@ -183,8 +183,12 @@
+ #define NO_HEX_FP
+
+ #ifndef Long
++#if __LP64__
++#define Long int
++#else
+ #define Long long
+ #endif
++#endif
+ #ifndef ULong
+ typedef unsigned Long ULong;
+ #endif
+@@ -221,7 +225,7 @@ extern void *MALLOC(size_t);
+ #ifndef PRIVATE_MEM
+ #define PRIVATE_MEM 2304
+ #endif
+-#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double))
++#define PRIVATE_mem ((unsigned)((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)))
+ static double private_mem[PRIVATE_mem], *pmem_next = private_mem;
+ #endif
+
diff --git a/src/base/third_party/dmg_fp/gcc_warnings.patch b/src/base/third_party/dmg_fp/gcc_warnings.patch
new file mode 100644
index 0000000..4262237
--- /dev/null
+++ b/src/base/third_party/dmg_fp/gcc_warnings.patch
@@ -0,0 +1,126 @@
+Index: dtoa.cc
+--- dtoa.cc (old copy)
++++ dtoa.cc (working copy)
+@@ -179,6 +179,9 @@
+ * used for input more than STRTOD_DIGLIM digits long (default 40).
+ */
+
++#define IEEE_8087
++#define NO_HEX_FP
++
+ #ifndef Long
+ #define Long long
+ #endif
+@@ -280,9 +283,7 @@
+ #include "math.h"
+ #endif
+
+-#ifdef __cplusplus
+-extern "C" {
+-#endif
++namespace dmg_fp {
+
+ #ifndef CONST
+ #ifdef KR_headers
+@@ -511,11 +512,9 @@
+
+ #define Kmax 7
+
+-#ifdef __cplusplus
+-extern "C" double strtod(const char *s00, char **se);
+-extern "C" char *dtoa(double d, int mode, int ndigits,
++double strtod(const char *s00, char **se);
++char *dtoa(double d, int mode, int ndigits,
+ int *decpt, int *sign, char **rve);
+-#endif
+
+ struct
+ Bigint {
+@@ -1527,7 +1526,7 @@
+ #ifdef KR_headers
+ (sp, t) char **sp, *t;
+ #else
+- (CONST char **sp, char *t)
++ (CONST char **sp, CONST char *t)
+ #endif
+ {
+ int c, d;
+@@ -2234,7 +2234,7 @@ bigcomp
+ nd = bc->nd;
+ nd0 = bc->nd0;
+ p5 = nd + bc->e0 - 1;
+- speccase = 0;
++ dd = speccase = 0;
+ #ifndef Sudden_Underflow
+ if (rv->d == 0.) { /* special case: value near underflow-to-zero */
+ /* threshold was rounded to zero */
+@@ -3431,7 +3430,7 @@
+
+ j = sizeof(ULong);
+ for(k = 0;
+- sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= i;
++ sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= (size_t)i;
+ j <<= 1)
+ k++;
+ r = (int*)Balloc(k);
+@@ -3447,7 +3446,7 @@
+ #ifdef KR_headers
+ nrv_alloc(s, rve, n) char *s, **rve; int n;
+ #else
+-nrv_alloc(char *s, char **rve, int n)
++nrv_alloc(CONST char *s, char **rve, int n)
+ #endif
+ {
+ char *rv, *t;
+@@ -4202,6 +4201,5 @@
+ *rve = s;
+ return s0;
+ }
+-#ifdef __cplusplus
+-}
+-#endif
++
++} // namespace dmg_fp
+Index: g_fmt.cc
+--- g_fmt.cc (old copy)
++++ g_fmt.cc (new copy)
+@@ -46,14 +46,14 @@ g_fmt(register char *b, double x)
+ if (sign)
+ *b++ = '-';
+ if (decpt == 9999) /* Infinity or Nan */ {
+- while(*b++ = *s++);
++ while((*b++ = *s++));
+ goto done0;
+ }
+ if (decpt <= -4 || decpt > se - s + 5) {
+ *b++ = *s++;
+ if (*s) {
+ *b++ = '.';
+- while(*b = *s++)
++ while((*b = *s++))
+ b++;
+ }
+ *b++ = 'e';
+@@ -79,10 +79,10 @@ g_fmt(register char *b, double x)
+ *b++ = '.';
+ for(; decpt < 0; decpt++)
+ *b++ = '0';
+- while(*b++ = *s++);
++ while((*b++ = *s++));
+ }
+ else {
+- while(*b = *s++) {
++ while((*b = *s++)) {
+ b++;
+ if (--decpt == 0 && *s)
+ *b++ = '.';
+@@ -93,7 +93,9 @@ g_fmt(register char *b, double x)
+ }
+ done0:
+ freedtoa(s0);
++#ifdef IGNORE_ZERO_SIGN
+ done:
++#endif
+ return b0;
+ }
+
diff --git a/src/base/third_party/dmg_fp/mac_wextra.patch b/src/base/third_party/dmg_fp/mac_wextra.patch
new file mode 100644
index 0000000..15340f2
--- /dev/null
+++ b/src/base/third_party/dmg_fp/mac_wextra.patch
@@ -0,0 +1,53 @@
+Index: g_fmt.cc
+===================================================================
+--- g_fmt.cc (revision 49784)
++++ g_fmt.cc (working copy)
+@@ -46,7 +46,7 @@
+ if (sign)
+ *b++ = '-';
+ if (decpt == 9999) /* Infinity or Nan */ {
+- while((*b++ = *s++));
++ while((*b++ = *s++)) {}
+ goto done0;
+ }
+ if (decpt <= -4 || decpt > se - s + 5) {
+@@ -64,7 +64,7 @@
+ }
+ else
+ *b++ = '+';
+- for(j = 2, k = 10; 10*k <= decpt; j++, k *= 10);
++ for(j = 2, k = 10; 10*k <= decpt; j++, k *= 10) {}
+ for(;;) {
+ i = decpt / k;
+ *b++ = i + '0';
+@@ -79,7 +79,7 @@
+ *b++ = '.';
+ for(; decpt < 0; decpt++)
+ *b++ = '0';
+- while((*b++ = *s++));
++ while((*b++ = *s++)) {}
+ }
+ else {
+ while((*b = *s++)) {
+Index: dtoa.cc
+===================================================================
+--- dtoa.cc (revision 49784)
++++ dtoa.cc (working copy)
+@@ -3863,7 +3863,7 @@
+ if (dval(&u) > 0.5 + dval(&eps))
+ goto bump_up;
+ else if (dval(&u) < 0.5 - dval(&eps)) {
+- while(*--s == '0');
++ while(*--s == '0') {}
+ s++;
+ goto ret1;
+ }
+@@ -4176,7 +4176,7 @@
+ #ifdef Honor_FLT_ROUNDS
+ trimzeros:
+ #endif
+- while(*--s == '0');
++ while(*--s == '0') {}
+ s++;
+ }
+ ret:
diff --git a/src/base/third_party/dmg_fp/win_vs2012.patch b/src/base/third_party/dmg_fp/win_vs2012.patch
new file mode 100644
index 0000000..f3f599c
--- /dev/null
+++ b/src/base/third_party/dmg_fp/win_vs2012.patch
@@ -0,0 +1,13 @@
+Index: dtoa.cc
+===================================================================
+--- dtoa.cc (revision 161424)
++++ dtoa.cc (working copy)
+@@ -3569,7 +3569,7 @@
+ int denorm;
+ ULong x;
+ #endif
+- Bigint *b, *b1, *delta, *mlo, *mhi, *S;
++ Bigint *b, *b1, *delta, *mlo = NULL, *mhi, *S;
+ U d2, eps, u;
+ double ds;
+ char *s, *s0;
diff --git a/src/base/third_party/dynamic_annotations/LICENSE b/src/base/third_party/dynamic_annotations/LICENSE
new file mode 100644
index 0000000..5c581a9
--- /dev/null
+++ b/src/base/third_party/dynamic_annotations/LICENSE
@@ -0,0 +1,28 @@
+/* Copyright (c) 2008-2009, Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 THE COPYRIGHT
+ * OWNER 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.
+ *
+ * ---
+ * Author: Kostya Serebryany
+ */
diff --git a/src/base/third_party/dynamic_annotations/README.chromium b/src/base/third_party/dynamic_annotations/README.chromium
new file mode 100644
index 0000000..dc8bdef
--- /dev/null
+++ b/src/base/third_party/dynamic_annotations/README.chromium
@@ -0,0 +1,13 @@
+Name: dynamic annotations
+URL: http://code.google.com/p/data-race-test/wiki/DynamicAnnotations
+Version: 4384
+License: BSD
+
+One header and one source file (dynamic_annotations.h and dynamic_annotations.c)
+in this directory define runtime macros useful for annotating synchronization
+utilities and benign data races so data race detectors can handle Chromium code
+with better precision.
+
+These files were taken from
+http://code.google.com/p/data-race-test/source/browse/?#svn/trunk/dynamic_annotations
+The files are covered under BSD license as described within the files.
diff --git a/src/base/third_party/dynamic_annotations/dynamic_annotations.c b/src/base/third_party/dynamic_annotations/dynamic_annotations.c
new file mode 100644
index 0000000..5a13979
--- /dev/null
+++ b/src/base/third_party/dynamic_annotations/dynamic_annotations.c
@@ -0,0 +1,273 @@
+/* Copyright (c) 2011, Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+#if defined(_MSC_VER) && !defined(COBALT_WIN)
+# include <windows.h>
+#endif
+
+#ifdef __cplusplus
+# error "This file should be built as pure C to avoid name mangling"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+
+#ifdef __GNUC__
+/* valgrind.h uses gcc extensions so it won't build with other compilers */
+# include "base/third_party/valgrind/valgrind.h"
+#endif
+
+/* Compiler-based ThreadSanitizer defines
+ DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL = 1
+ and provides its own definitions of the functions. */
+
+#ifndef DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL
+# define DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL 0
+#endif
+
+/* Each function is empty and called (via a macro) only in debug mode.
+ The arguments are captured by dynamic tools at runtime. */
+
+#if DYNAMIC_ANNOTATIONS_ENABLED == 1 \
+ && DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL == 0
+
+/* Identical code folding(-Wl,--icf=all) countermeasures.
+ This makes all Annotate* functions different, which prevents the linker from
+ folding them. */
+#ifdef __COUNTER__
+#define DYNAMIC_ANNOTATIONS_IMPL \
+ volatile short lineno = (__LINE__ << 8) + __COUNTER__; (void)lineno;
+#else
+#define DYNAMIC_ANNOTATIONS_IMPL \
+ volatile short lineno = (__LINE__ << 8); (void)lineno;
+#endif
+
+/* WARNING: always add new annotations to the end of the list.
+ Otherwise, lineno (see above) numbers for different Annotate* functions may
+ conflict. */
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockCreate)(
+ const char *file, int line, const volatile void *lock)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockDestroy)(
+ const char *file, int line, const volatile void *lock)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockAcquired)(
+ const char *file, int line, const volatile void *lock, long is_w)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockReleased)(
+ const char *file, int line, const volatile void *lock, long is_w)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierInit)(
+ const char *file, int line, const volatile void *barrier, long count,
+ long reinitialization_allowed)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitBefore)(
+ const char *file, int line, const volatile void *barrier)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitAfter)(
+ const char *file, int line, const volatile void *barrier)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierDestroy)(
+ const char *file, int line, const volatile void *barrier)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarWait)(
+ const char *file, int line, const volatile void *cv,
+ const volatile void *lock)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignal)(
+ const char *file, int line, const volatile void *cv)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignalAll)(
+ const char *file, int line, const volatile void *cv)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateHappensBefore)(
+ const char *file, int line, const volatile void *obj)
+{DYNAMIC_ANNOTATIONS_IMPL};
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateHappensAfter)(
+ const char *file, int line, const volatile void *obj)
+{DYNAMIC_ANNOTATIONS_IMPL};
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotatePublishMemoryRange)(
+ const char *file, int line, const volatile void *address, long size)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateUnpublishMemoryRange)(
+ const char *file, int line, const volatile void *address, long size)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQCreate)(
+ const char *file, int line, const volatile void *pcq)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQDestroy)(
+ const char *file, int line, const volatile void *pcq)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQPut)(
+ const char *file, int line, const volatile void *pcq)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQGet)(
+ const char *file, int line, const volatile void *pcq)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateNewMemory)(
+ const char *file, int line, const volatile void *mem, long size)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateExpectRace)(
+ const char *file, int line, const volatile void *mem,
+ const char *description)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushExpectedRaces)(
+ const char *file, int line)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRace)(
+ const char *file, int line, const volatile void *mem,
+ const char *description)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRaceSized)(
+ const char *file, int line, const volatile void *mem, long size,
+ const char *description)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsUsedAsCondVar)(
+ const char *file, int line, const volatile void *mu)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsNotPHB)(
+ const char *file, int line, const volatile void *mu)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateTraceMemory)(
+ const char *file, int line, const volatile void *arg)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateThreadName)(
+ const char *file, int line, const char *name)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsBegin)(
+ const char *file, int line)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsEnd)(
+ const char *file, int line)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesBegin)(
+ const char *file, int line)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesEnd)(
+ const char *file, int line)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncBegin)(
+ const char *file, int line)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncEnd)(
+ const char *file, int line)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateEnableRaceDetection)(
+ const char *file, int line, int enable)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateNoOp)(
+ const char *file, int line, const volatile void *arg)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushState)(
+ const char *file, int line)
+{DYNAMIC_ANNOTATIONS_IMPL}
+
+#endif /* DYNAMIC_ANNOTATIONS_ENABLED == 1
+ && DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL == 0 */
+
+#if DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND == 1 \
+ && DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL == 0
+static int GetRunningOnValgrind(void) {
+#ifdef RUNNING_ON_VALGRIND
+ if (RUNNING_ON_VALGRIND) return 1;
+#endif
+#if defined(__LB_SHELL__) || defined(STARBOARD)
+ return 0;
+#else
+
+#ifndef _MSC_VER
+ char *running_on_valgrind_str = getenv("RUNNING_ON_VALGRIND");
+ if (running_on_valgrind_str) {
+ return strcmp(running_on_valgrind_str, "0") != 0;
+ }
+#else
+ /* Visual Studio issues warnings if we use getenv,
+ * so we use GetEnvironmentVariableA instead.
+ */
+ char value[100] = "1";
+ int res = GetEnvironmentVariableA("RUNNING_ON_VALGRIND",
+ value, sizeof(value));
+ /* value will remain "1" if res == 0 or res >= sizeof(value). The latter
+ * can happen only if the given value is long, in this case it can't be "0".
+ */
+ if (res > 0 && strcmp(value, "0") != 0)
+ return 1;
+#endif
+ return 0;
+#endif
+}
+
+/* See the comments in dynamic_annotations.h */
+int RunningOnValgrind(void) {
+ static volatile int running_on_valgrind = -1;
+ /* C doesn't have thread-safe initialization of statics, and we
+ don't want to depend on pthread_once here, so hack it. */
+ int local_running_on_valgrind = running_on_valgrind;
+ if (local_running_on_valgrind == -1)
+ running_on_valgrind = local_running_on_valgrind = GetRunningOnValgrind();
+ return local_running_on_valgrind;
+}
+
+#endif /* DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND == 1
+ && DYNAMIC_ANNOTATIONS_EXTERNAL_IMPL == 0 */
diff --git a/src/base/third_party/dynamic_annotations/dynamic_annotations.gyp b/src/base/third_party/dynamic_annotations/dynamic_annotations.gyp
new file mode 100644
index 0000000..1f1dc49
--- /dev/null
+++ b/src/base/third_party/dynamic_annotations/dynamic_annotations.gyp
@@ -0,0 +1,47 @@
+# Copyright (c) 2011 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'dynamic_annotations',
+ 'type': 'static_library',
+ 'toolsets': ['host', 'target'],
+ 'include_dirs': [
+ '../../../',
+ ],
+ 'sources': [
+ 'dynamic_annotations.c',
+ 'dynamic_annotations.h',
+ '../valgrind/valgrind.h',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS == "win"', {
+ 'targets': [
+ {
+ 'target_name': 'dynamic_annotations_win64',
+ 'type': 'static_library',
+ # We can't use dynamic_annotations target for win64 build since it is
+ # a 32-bit library.
+ # TODO(gregoryd): merge with dynamic_annotations when
+ # the win32/64 targets are merged.
+ 'include_dirs': [
+ '../../../',
+ ],
+ 'sources': [
+ 'dynamic_annotations.c',
+ 'dynamic_annotations.h',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ },
+ ],
+ }],
+ ],
+}
diff --git a/src/base/third_party/dynamic_annotations/dynamic_annotations.h b/src/base/third_party/dynamic_annotations/dynamic_annotations.h
new file mode 100644
index 0000000..f7403bc
--- /dev/null
+++ b/src/base/third_party/dynamic_annotations/dynamic_annotations.h
@@ -0,0 +1,602 @@
+/* Copyright (c) 2011, Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 THE COPYRIGHT
+ * OWNER 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.
+ */
+
+/* This file defines dynamic annotations for use with dynamic analysis
+ tool such as valgrind, PIN, etc.
+
+ Dynamic annotation is a source code annotation that affects
+ the generated code (that is, the annotation is not a comment).
+ Each such annotation is attached to a particular
+ instruction and/or to a particular object (address) in the program.
+
+ The annotations that should be used by users are macros in all upper-case
+ (e.g., ANNOTATE_NEW_MEMORY).
+
+ Actual implementation of these macros may differ depending on the
+ dynamic analysis tool being used.
+
+ See http://code.google.com/p/data-race-test/ for more information.
+
+ This file supports the following dynamic analysis tools:
+ - None (DYNAMIC_ANNOTATIONS_ENABLED is not defined or zero).
+ Macros are defined empty.
+ - ThreadSanitizer, Helgrind, DRD (DYNAMIC_ANNOTATIONS_ENABLED is 1).
+ Macros are defined as calls to non-inlinable empty functions
+ that are intercepted by Valgrind. */
+
+#ifndef __DYNAMIC_ANNOTATIONS_H__
+#define __DYNAMIC_ANNOTATIONS_H__
+
+#ifndef DYNAMIC_ANNOTATIONS_PREFIX
+# define DYNAMIC_ANNOTATIONS_PREFIX
+#endif
+
+#ifndef DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND
+# define DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND 1
+#endif
+
+#ifdef DYNAMIC_ANNOTATIONS_WANT_ATTRIBUTE_WEAK
+# ifdef __GNUC__
+# define DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK __attribute__((weak))
+# else
+/* TODO(glider): for Windows support we may want to change this macro in order
+ to prepend __declspec(selectany) to the annotations' declarations. */
+# error weak annotations are not supported for your compiler
+# endif
+#else
+# define DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK
+#endif
+
+/* The following preprocessor magic prepends the value of
+ DYNAMIC_ANNOTATIONS_PREFIX to annotation function names. */
+#define DYNAMIC_ANNOTATIONS_GLUE0(A, B) A##B
+#define DYNAMIC_ANNOTATIONS_GLUE(A, B) DYNAMIC_ANNOTATIONS_GLUE0(A, B)
+#define DYNAMIC_ANNOTATIONS_NAME(name) \
+ DYNAMIC_ANNOTATIONS_GLUE(DYNAMIC_ANNOTATIONS_PREFIX, name)
+
+#ifndef DYNAMIC_ANNOTATIONS_ENABLED
+# define DYNAMIC_ANNOTATIONS_ENABLED 0
+#endif
+
+#if DYNAMIC_ANNOTATIONS_ENABLED != 0
+
+ /* -------------------------------------------------------------
+ Annotations useful when implementing condition variables such as CondVar,
+ using conditional critical sections (Await/LockWhen) and when constructing
+ user-defined synchronization mechanisms.
+
+ The annotations ANNOTATE_HAPPENS_BEFORE() and ANNOTATE_HAPPENS_AFTER() can
+ be used to define happens-before arcs in user-defined synchronization
+ mechanisms: the race detector will infer an arc from the former to the
+ latter when they share the same argument pointer.
+
+ Example 1 (reference counting):
+
+ void Unref() {
+ ANNOTATE_HAPPENS_BEFORE(&refcount_);
+ if (AtomicDecrementByOne(&refcount_) == 0) {
+ ANNOTATE_HAPPENS_AFTER(&refcount_);
+ delete this;
+ }
+ }
+
+ Example 2 (message queue):
+
+ void MyQueue::Put(Type *e) {
+ MutexLock lock(&mu_);
+ ANNOTATE_HAPPENS_BEFORE(e);
+ PutElementIntoMyQueue(e);
+ }
+
+ Type *MyQueue::Get() {
+ MutexLock lock(&mu_);
+ Type *e = GetElementFromMyQueue();
+ ANNOTATE_HAPPENS_AFTER(e);
+ return e;
+ }
+
+ Note: when possible, please use the existing reference counting and message
+ queue implementations instead of inventing new ones. */
+
+ /* Report that wait on the condition variable at address "cv" has succeeded
+ and the lock at address "lock" is held. */
+ #define ANNOTATE_CONDVAR_LOCK_WAIT(cv, lock) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarWait)(__FILE__, __LINE__, cv, lock)
+
+ /* Report that wait on the condition variable at "cv" has succeeded. Variant
+ w/o lock. */
+ #define ANNOTATE_CONDVAR_WAIT(cv) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarWait)(__FILE__, __LINE__, cv, NULL)
+
+ /* Report that we are about to signal on the condition variable at address
+ "cv". */
+ #define ANNOTATE_CONDVAR_SIGNAL(cv) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignal)(__FILE__, __LINE__, cv)
+
+ /* Report that we are about to signal_all on the condition variable at address
+ "cv". */
+ #define ANNOTATE_CONDVAR_SIGNAL_ALL(cv) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignalAll)(__FILE__, __LINE__, cv)
+
+ /* Annotations for user-defined synchronization mechanisms. */
+ #define ANNOTATE_HAPPENS_BEFORE(obj) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateHappensBefore)(__FILE__, __LINE__, obj)
+ #define ANNOTATE_HAPPENS_AFTER(obj) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateHappensAfter)(__FILE__, __LINE__, obj)
+
+ /* DEPRECATED. Don't use it. */
+ #define ANNOTATE_PUBLISH_MEMORY_RANGE(pointer, size) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotatePublishMemoryRange)(__FILE__, __LINE__, \
+ pointer, size)
+
+ /* DEPRECATED. Don't use it. */
+ #define ANNOTATE_UNPUBLISH_MEMORY_RANGE(pointer, size) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateUnpublishMemoryRange)(__FILE__, __LINE__, \
+ pointer, size)
+
+ /* DEPRECATED. Don't use it. */
+ #define ANNOTATE_SWAP_MEMORY_RANGE(pointer, size) \
+ do { \
+ ANNOTATE_UNPUBLISH_MEMORY_RANGE(pointer, size); \
+ ANNOTATE_PUBLISH_MEMORY_RANGE(pointer, size); \
+ } while (0)
+
+ /* Instruct the tool to create a happens-before arc between mu->Unlock() and
+ mu->Lock(). This annotation may slow down the race detector and hide real
+ races. Normally it is used only when it would be difficult to annotate each
+ of the mutex's critical sections individually using the annotations above.
+ This annotation makes sense only for hybrid race detectors. For pure
+ happens-before detectors this is a no-op. For more details see
+ http://code.google.com/p/data-race-test/wiki/PureHappensBeforeVsHybrid . */
+ #define ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(mu) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsUsedAsCondVar)(__FILE__, __LINE__, \
+ mu)
+
+ /* Opposite to ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX.
+ Instruct the tool to NOT create h-b arcs between Unlock and Lock, even in
+ pure happens-before mode. For a hybrid mode this is a no-op. */
+ #define ANNOTATE_NOT_HAPPENS_BEFORE_MUTEX(mu) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsNotPHB)(__FILE__, __LINE__, mu)
+
+ /* Deprecated. Use ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX. */
+ #define ANNOTATE_MUTEX_IS_USED_AS_CONDVAR(mu) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsUsedAsCondVar)(__FILE__, __LINE__, \
+ mu)
+
+ /* -------------------------------------------------------------
+ Annotations useful when defining memory allocators, or when memory that
+ was protected in one way starts to be protected in another. */
+
+ /* Report that a new memory at "address" of size "size" has been allocated.
+ This might be used when the memory has been retrieved from a free list and
+ is about to be reused, or when a the locking discipline for a variable
+ changes. */
+ #define ANNOTATE_NEW_MEMORY(address, size) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateNewMemory)(__FILE__, __LINE__, address, \
+ size)
+
+ /* -------------------------------------------------------------
+ Annotations useful when defining FIFO queues that transfer data between
+ threads. */
+
+ /* Report that the producer-consumer queue (such as ProducerConsumerQueue) at
+ address "pcq" has been created. The ANNOTATE_PCQ_* annotations
+ should be used only for FIFO queues. For non-FIFO queues use
+ ANNOTATE_HAPPENS_BEFORE (for put) and ANNOTATE_HAPPENS_AFTER (for get). */
+ #define ANNOTATE_PCQ_CREATE(pcq) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQCreate)(__FILE__, __LINE__, pcq)
+
+ /* Report that the queue at address "pcq" is about to be destroyed. */
+ #define ANNOTATE_PCQ_DESTROY(pcq) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQDestroy)(__FILE__, __LINE__, pcq)
+
+ /* Report that we are about to put an element into a FIFO queue at address
+ "pcq". */
+ #define ANNOTATE_PCQ_PUT(pcq) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQPut)(__FILE__, __LINE__, pcq)
+
+ /* Report that we've just got an element from a FIFO queue at address
+ "pcq". */
+ #define ANNOTATE_PCQ_GET(pcq) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQGet)(__FILE__, __LINE__, pcq)
+
+ /* -------------------------------------------------------------
+ Annotations that suppress errors. It is usually better to express the
+ program's synchronization using the other annotations, but these can
+ be used when all else fails. */
+
+ /* Report that we may have a benign race at "pointer", with size
+ "sizeof(*(pointer))". "pointer" must be a non-void* pointer. Insert at the
+ point where "pointer" has been allocated, preferably close to the point
+ where the race happens. See also ANNOTATE_BENIGN_RACE_STATIC. */
+ #define ANNOTATE_BENIGN_RACE(pointer, description) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRaceSized)(__FILE__, __LINE__, \
+ pointer, sizeof(*(pointer)), description)
+
+ /* Same as ANNOTATE_BENIGN_RACE(address, description), but applies to
+ the memory range [address, address+size). */
+ #define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRaceSized)(__FILE__, __LINE__, \
+ address, size, description)
+
+ /* Request the analysis tool to ignore all reads in the current thread
+ until ANNOTATE_IGNORE_READS_END is called.
+ Useful to ignore intentional racey reads, while still checking
+ other reads and all writes.
+ See also ANNOTATE_UNPROTECTED_READ. */
+ #define ANNOTATE_IGNORE_READS_BEGIN() \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsBegin)(__FILE__, __LINE__)
+
+ /* Stop ignoring reads. */
+ #define ANNOTATE_IGNORE_READS_END() \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsEnd)(__FILE__, __LINE__)
+
+ /* Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore writes. */
+ #define ANNOTATE_IGNORE_WRITES_BEGIN() \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesBegin)(__FILE__, __LINE__)
+
+ /* Stop ignoring writes. */
+ #define ANNOTATE_IGNORE_WRITES_END() \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesEnd)(__FILE__, __LINE__)
+
+ /* Start ignoring all memory accesses (reads and writes). */
+ #define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \
+ do {\
+ ANNOTATE_IGNORE_READS_BEGIN();\
+ ANNOTATE_IGNORE_WRITES_BEGIN();\
+ }while(0)\
+
+ /* Stop ignoring all memory accesses. */
+ #define ANNOTATE_IGNORE_READS_AND_WRITES_END() \
+ do {\
+ ANNOTATE_IGNORE_WRITES_END();\
+ ANNOTATE_IGNORE_READS_END();\
+ }while(0)\
+
+ /* Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore synchronization events:
+ RWLOCK* and CONDVAR*. */
+ #define ANNOTATE_IGNORE_SYNC_BEGIN() \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncBegin)(__FILE__, __LINE__)
+
+ /* Stop ignoring sync events. */
+ #define ANNOTATE_IGNORE_SYNC_END() \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncEnd)(__FILE__, __LINE__)
+
+
+ /* Enable (enable!=0) or disable (enable==0) race detection for all threads.
+ This annotation could be useful if you want to skip expensive race analysis
+ during some period of program execution, e.g. during initialization. */
+ #define ANNOTATE_ENABLE_RACE_DETECTION(enable) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateEnableRaceDetection)(__FILE__, __LINE__, \
+ enable)
+
+ /* -------------------------------------------------------------
+ Annotations useful for debugging. */
+
+ /* Request to trace every access to "address". */
+ #define ANNOTATE_TRACE_MEMORY(address) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateTraceMemory)(__FILE__, __LINE__, address)
+
+ /* Report the current thread name to a race detector. */
+ #define ANNOTATE_THREAD_NAME(name) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateThreadName)(__FILE__, __LINE__, name)
+
+ /* -------------------------------------------------------------
+ Annotations useful when implementing locks. They are not
+ normally needed by modules that merely use locks.
+ The "lock" argument is a pointer to the lock object. */
+
+ /* Report that a lock has been created at address "lock". */
+ #define ANNOTATE_RWLOCK_CREATE(lock) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockCreate)(__FILE__, __LINE__, lock)
+
+ /* Report that the lock at address "lock" is about to be destroyed. */
+ #define ANNOTATE_RWLOCK_DESTROY(lock) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockDestroy)(__FILE__, __LINE__, lock)
+
+ /* Report that the lock at address "lock" has been acquired.
+ is_w=1 for writer lock, is_w=0 for reader lock. */
+ #define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockAcquired)(__FILE__, __LINE__, lock, \
+ is_w)
+
+ /* Report that the lock at address "lock" is about to be released. */
+ #define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockReleased)(__FILE__, __LINE__, lock, \
+ is_w)
+
+ /* -------------------------------------------------------------
+ Annotations useful when implementing barriers. They are not
+ normally needed by modules that merely use barriers.
+ The "barrier" argument is a pointer to the barrier object. */
+
+ /* Report that the "barrier" has been initialized with initial "count".
+ If 'reinitialization_allowed' is true, initialization is allowed to happen
+ multiple times w/o calling barrier_destroy() */
+ #define ANNOTATE_BARRIER_INIT(barrier, count, reinitialization_allowed) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierInit)(__FILE__, __LINE__, barrier, \
+ count, reinitialization_allowed)
+
+ /* Report that we are about to enter barrier_wait("barrier"). */
+ #define ANNOTATE_BARRIER_WAIT_BEFORE(barrier) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitBefore)(__FILE__, __LINE__, \
+ barrier)
+
+ /* Report that we just exited barrier_wait("barrier"). */
+ #define ANNOTATE_BARRIER_WAIT_AFTER(barrier) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitAfter)(__FILE__, __LINE__, \
+ barrier)
+
+ /* Report that the "barrier" has been destroyed. */
+ #define ANNOTATE_BARRIER_DESTROY(barrier) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierDestroy)(__FILE__, __LINE__, \
+ barrier)
+
+ /* -------------------------------------------------------------
+ Annotations useful for testing race detectors. */
+
+ /* Report that we expect a race on the variable at "address".
+ Use only in unit tests for a race detector. */
+ #define ANNOTATE_EXPECT_RACE(address, description) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateExpectRace)(__FILE__, __LINE__, address, \
+ description)
+
+ #define ANNOTATE_FLUSH_EXPECTED_RACES() \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushExpectedRaces)(__FILE__, __LINE__)
+
+ /* A no-op. Insert where you like to test the interceptors. */
+ #define ANNOTATE_NO_OP(arg) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateNoOp)(__FILE__, __LINE__, arg)
+
+ /* Force the race detector to flush its state. The actual effect depends on
+ * the implementation of the detector. */
+ #define ANNOTATE_FLUSH_STATE() \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushState)(__FILE__, __LINE__)
+
+
+#else /* DYNAMIC_ANNOTATIONS_ENABLED == 0 */
+
+ #define ANNOTATE_RWLOCK_CREATE(lock) /* empty */
+ #define ANNOTATE_RWLOCK_DESTROY(lock) /* empty */
+ #define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) /* empty */
+ #define ANNOTATE_RWLOCK_RELEASED(lock, is_w) /* empty */
+ #define ANNOTATE_BARRIER_INIT(barrier, count, reinitialization_allowed) /* */
+ #define ANNOTATE_BARRIER_WAIT_BEFORE(barrier) /* empty */
+ #define ANNOTATE_BARRIER_WAIT_AFTER(barrier) /* empty */
+ #define ANNOTATE_BARRIER_DESTROY(barrier) /* empty */
+ #define ANNOTATE_CONDVAR_LOCK_WAIT(cv, lock) /* empty */
+ #define ANNOTATE_CONDVAR_WAIT(cv) /* empty */
+ #define ANNOTATE_CONDVAR_SIGNAL(cv) /* empty */
+ #define ANNOTATE_CONDVAR_SIGNAL_ALL(cv) /* empty */
+ #define ANNOTATE_HAPPENS_BEFORE(obj) /* empty */
+ #define ANNOTATE_HAPPENS_AFTER(obj) /* empty */
+ #define ANNOTATE_PUBLISH_MEMORY_RANGE(address, size) /* empty */
+ #define ANNOTATE_UNPUBLISH_MEMORY_RANGE(address, size) /* empty */
+ #define ANNOTATE_SWAP_MEMORY_RANGE(address, size) /* empty */
+ #define ANNOTATE_PCQ_CREATE(pcq) /* empty */
+ #define ANNOTATE_PCQ_DESTROY(pcq) /* empty */
+ #define ANNOTATE_PCQ_PUT(pcq) /* empty */
+ #define ANNOTATE_PCQ_GET(pcq) /* empty */
+ #define ANNOTATE_NEW_MEMORY(address, size) /* empty */
+ #define ANNOTATE_EXPECT_RACE(address, description) /* empty */
+ #define ANNOTATE_FLUSH_EXPECTED_RACES(address, description) /* empty */
+ #define ANNOTATE_BENIGN_RACE(address, description) /* empty */
+ #define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) /* empty */
+ #define ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(mu) /* empty */
+ #define ANNOTATE_MUTEX_IS_USED_AS_CONDVAR(mu) /* empty */
+ #define ANNOTATE_TRACE_MEMORY(arg) /* empty */
+ #define ANNOTATE_THREAD_NAME(name) /* empty */
+ #define ANNOTATE_IGNORE_READS_BEGIN() /* empty */
+ #define ANNOTATE_IGNORE_READS_END() /* empty */
+ #define ANNOTATE_IGNORE_WRITES_BEGIN() /* empty */
+ #define ANNOTATE_IGNORE_WRITES_END() /* empty */
+ #define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() /* empty */
+ #define ANNOTATE_IGNORE_READS_AND_WRITES_END() /* empty */
+ #define ANNOTATE_IGNORE_SYNC_BEGIN() /* empty */
+ #define ANNOTATE_IGNORE_SYNC_END() /* empty */
+ #define ANNOTATE_ENABLE_RACE_DETECTION(enable) /* empty */
+ #define ANNOTATE_NO_OP(arg) /* empty */
+ #define ANNOTATE_FLUSH_STATE() /* empty */
+
+#endif /* DYNAMIC_ANNOTATIONS_ENABLED */
+
+/* Use the macros above rather than using these functions directly. */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(COMPILER_GHS)
+#pragma ghs nowarning 76 // Argument to macro is empty.
+#endif
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockCreate)(
+ const char *file, int line,
+ const volatile void *lock) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockDestroy)(
+ const char *file, int line,
+ const volatile void *lock) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockAcquired)(
+ const char *file, int line,
+ const volatile void *lock, long is_w) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockReleased)(
+ const char *file, int line,
+ const volatile void *lock, long is_w) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierInit)(
+ const char *file, int line, const volatile void *barrier, long count,
+ long reinitialization_allowed) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitBefore)(
+ const char *file, int line,
+ const volatile void *barrier) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitAfter)(
+ const char *file, int line,
+ const volatile void *barrier) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierDestroy)(
+ const char *file, int line,
+ const volatile void *barrier) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarWait)(
+ const char *file, int line, const volatile void *cv,
+ const volatile void *lock) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignal)(
+ const char *file, int line,
+ const volatile void *cv) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignalAll)(
+ const char *file, int line,
+ const volatile void *cv) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateHappensBefore)(
+ const char *file, int line,
+ const volatile void *obj) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateHappensAfter)(
+ const char *file, int line,
+ const volatile void *obj) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotatePublishMemoryRange)(
+ const char *file, int line,
+ const volatile void *address, long size) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateUnpublishMemoryRange)(
+ const char *file, int line,
+ const volatile void *address, long size) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQCreate)(
+ const char *file, int line,
+ const volatile void *pcq) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQDestroy)(
+ const char *file, int line,
+ const volatile void *pcq) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQPut)(
+ const char *file, int line,
+ const volatile void *pcq) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQGet)(
+ const char *file, int line,
+ const volatile void *pcq) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateNewMemory)(
+ const char *file, int line,
+ const volatile void *mem, long size) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateExpectRace)(
+ const char *file, int line, const volatile void *mem,
+ const char *description) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushExpectedRaces)(
+ const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRace)(
+ const char *file, int line, const volatile void *mem,
+ const char *description) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRaceSized)(
+ const char *file, int line, const volatile void *mem, long size,
+ const char *description) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsUsedAsCondVar)(
+ const char *file, int line,
+ const volatile void *mu) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsNotPHB)(
+ const char *file, int line,
+ const volatile void *mu) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateTraceMemory)(
+ const char *file, int line,
+ const volatile void *arg) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateThreadName)(
+ const char *file, int line,
+ const char *name) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsBegin)(
+ const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsEnd)(
+ const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesBegin)(
+ const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesEnd)(
+ const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncBegin)(
+ const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncEnd)(
+ const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateEnableRaceDetection)(
+ const char *file, int line, int enable) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateNoOp)(
+ const char *file, int line,
+ const volatile void *arg) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushState)(
+ const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+
+#if defined(COMPILER_GHS)
+#pragma ghs endnowarning
+#endif
+
+#if DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND == 1
+/* Return non-zero value if running under valgrind.
+
+ If "valgrind.h" is included into dynamic_annotations.c,
+ the regular valgrind mechanism will be used.
+ See http://valgrind.org/docs/manual/manual-core-adv.html about
+ RUNNING_ON_VALGRIND and other valgrind "client requests".
+ The file "valgrind.h" may be obtained by doing
+ svn co svn://svn.valgrind.org/valgrind/trunk/include
+
+ If for some reason you can't use "valgrind.h" or want to fake valgrind,
+ there are two ways to make this function return non-zero:
+ - Use environment variable: export RUNNING_ON_VALGRIND=1
+ - Make your tool intercept the function RunningOnValgrind() and
+ change its return value.
+ */
+int RunningOnValgrind(void) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+#endif /* DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND == 1 */
+
+#ifdef __cplusplus
+}
+#endif
+
+#if DYNAMIC_ANNOTATIONS_ENABLED != 0 && defined(__cplusplus)
+
+ /* ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads.
+
+ Instead of doing
+ ANNOTATE_IGNORE_READS_BEGIN();
+ ... = x;
+ ANNOTATE_IGNORE_READS_END();
+ one can use
+ ... = ANNOTATE_UNPROTECTED_READ(x); */
+ template <class T>
+ inline T ANNOTATE_UNPROTECTED_READ(const volatile T &x) {
+ ANNOTATE_IGNORE_READS_BEGIN();
+ T res = x;
+ ANNOTATE_IGNORE_READS_END();
+ return res;
+ }
+ /* Apply ANNOTATE_BENIGN_RACE_SIZED to a static variable. */
+ #define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) \
+ namespace { \
+ class static_var ## _annotator { \
+ public: \
+ static_var ## _annotator() { \
+ ANNOTATE_BENIGN_RACE_SIZED(&static_var, \
+ sizeof(static_var), \
+ # static_var ": " description); \
+ } \
+ }; \
+ static static_var ## _annotator the ## static_var ## _annotator;\
+ }
+#else /* DYNAMIC_ANNOTATIONS_ENABLED == 0 */
+
+ #define ANNOTATE_UNPROTECTED_READ(x) (x)
+ #define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) /* empty */
+
+#endif /* DYNAMIC_ANNOTATIONS_ENABLED */
+
+#endif /* __DYNAMIC_ANNOTATIONS_H__ */
diff --git a/src/base/third_party/icu/LICENSE b/src/base/third_party/icu/LICENSE
new file mode 100644
index 0000000..40282f4
--- /dev/null
+++ b/src/base/third_party/icu/LICENSE
@@ -0,0 +1,32 @@
+ICU License - ICU 1.8.1 and later
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright (c) 1995-2009 International Business Machines Corporation and others
+
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, and/or sell copies of the Software, and to permit persons
+to whom the Software is furnished to do so, provided that the above
+copyright notice(s) and this permission notice appear in all copies of
+the Software and that both the above copyright notice(s) and this
+permission notice appear in supporting documentation.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY
+SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
+RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+of the copyright holder.
diff --git a/src/base/third_party/icu/README.chromium b/src/base/third_party/icu/README.chromium
new file mode 100644
index 0000000..6a9a15a
--- /dev/null
+++ b/src/base/third_party/icu/README.chromium
@@ -0,0 +1,16 @@
+Name: ICU
+URL: http://site.icu-project.org/
+License: MIT
+License File: NOT_SHIPPED
+
+This file has the relevant components from ICU copied to handle basic
+UTF8/16/32 conversions. Components are copied from utf.h utf8.h utf16.h and
+utf_impl.c
+
+The same module appears in third_party/icu, so we don't repeat the license
+file here.
+
+The main change is that U_/U8_/U16_ prefixes have been replaced with
+CBU_/CBU8_/CBU16_ (for "Chrome Base") to avoid confusion with the "real" ICU
+macros should ICU be in use on the system. For the same reason, the functions
+and types have been put in the "base_icu" namespace.
diff --git a/src/base/third_party/icu/icu_utf.cc b/src/base/third_party/icu/icu_utf.cc
new file mode 100644
index 0000000..b47c8ac
--- /dev/null
+++ b/src/base/third_party/icu/icu_utf.cc
@@ -0,0 +1,228 @@
+/*
+******************************************************************************
+*
+* Copyright (C) 1999-2006, International Business Machines
+* Corporation and others. All Rights Reserved.
+*
+******************************************************************************
+* file name: utf_impl.c
+* encoding: US-ASCII
+* tab size: 8 (not used)
+* indentation:4
+*
+* created on: 1999sep13
+* created by: Markus W. Scherer
+*
+* This file provides implementation functions for macros in the utfXX.h
+* that would otherwise be too long as macros.
+*/
+
+#include "base/third_party/icu/icu_utf.h"
+
+namespace base_icu {
+
+/**
+ * UTF8_ERROR_VALUE_1 and UTF8_ERROR_VALUE_2 are special error values for UTF-8,
+ * which need 1 or 2 bytes in UTF-8:
+ * \code
+ * U+0015 = NAK = Negative Acknowledge, C0 control character
+ * U+009f = highest C1 control character
+ * \endcode
+ *
+ * These are used by UTF8_..._SAFE macros so that they can return an error value
+ * that needs the same number of code units (bytes) as were seen by
+ * a macro. They should be tested with UTF_IS_ERROR() or UTF_IS_VALID().
+ *
+ * @deprecated ICU 2.4. Obsolete, see utf_old.h.
+ */
+#define CBUTF8_ERROR_VALUE_1 0x15
+
+/**
+ * See documentation on UTF8_ERROR_VALUE_1 for details.
+ *
+ * @deprecated ICU 2.4. Obsolete, see utf_old.h.
+ */
+#define CBUTF8_ERROR_VALUE_2 0x9f
+
+
+/**
+ * Error value for all UTFs. This code point value will be set by macros with e>
+ * checking if an error is detected.
+ *
+ * @deprecated ICU 2.4. Obsolete, see utf_old.h.
+ */
+#define CBUTF_ERROR_VALUE 0xffff
+
+/*
+ * This table could be replaced on many machines by
+ * a few lines of assembler code using an
+ * "index of first 0-bit from msb" instruction and
+ * one or two more integer instructions.
+ *
+ * For example, on an i386, do something like
+ * - MOV AL, leadByte
+ * - NOT AL (8-bit, leave b15..b8==0..0, reverse only b7..b0)
+ * - MOV AH, 0
+ * - BSR BX, AX (16-bit)
+ * - MOV AX, 6 (result)
+ * - JZ finish (ZF==1 if leadByte==0xff)
+ * - SUB AX, BX (result)
+ * -finish:
+ * (BSR: Bit Scan Reverse, scans for a 1-bit, starting from the MSB)
+ *
+ * In Unicode, all UTF-8 byte sequences with more than 4 bytes are illegal;
+ * lead bytes above 0xf4 are illegal.
+ * We keep them in this table for skipping long ISO 10646-UTF-8 sequences.
+ */
+const uint8
+utf8_countTrailBytes[256]={
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3,
+ 3, 3, 3, /* illegal in Unicode */
+ 4, 4, 4, 4, /* illegal in Unicode */
+ 5, 5, /* illegal in Unicode */
+ 0, 0 /* illegal bytes 0xfe and 0xff */
+};
+
+static const UChar32
+utf8_minLegal[4]={ 0, 0x80, 0x800, 0x10000 };
+
+static const UChar32
+utf8_errorValue[6]={
+ CBUTF8_ERROR_VALUE_1, CBUTF8_ERROR_VALUE_2, CBUTF_ERROR_VALUE, 0x10ffff,
+ 0x3ffffff, 0x7fffffff
+};
+
+/*
+ * Handle the non-inline part of the U8_NEXT() macro and its obsolete sibling
+ * UTF8_NEXT_CHAR_SAFE().
+ *
+ * The "strict" parameter controls the error behavior:
+ * <0 "Safe" behavior of U8_NEXT(): All illegal byte sequences yield a negative
+ * code point result.
+ * 0 Obsolete "safe" behavior of UTF8_NEXT_CHAR_SAFE(..., FALSE):
+ * All illegal byte sequences yield a positive code point such that this
+ * result code point would be encoded with the same number of bytes as
+ * the illegal sequence.
+ * >0 Obsolete "strict" behavior of UTF8_NEXT_CHAR_SAFE(..., TRUE):
+ * Same as the obsolete "safe" behavior, but non-characters are also treated
+ * like illegal sequences.
+ *
+ * The special negative (<0) value -2 is used for lenient treatment of surrogate
+ * code points as legal. Some implementations use this for roundtripping of
+ * Unicode 16-bit strings that are not well-formed UTF-16, that is, they
+ * contain unpaired surrogates.
+ *
+ * Note that a UBool is the same as an int8_t.
+ */
+UChar32
+utf8_nextCharSafeBody(const uint8 *s, int32 *pi, int32 length, UChar32 c, UBool strict) {
+ int32 i=*pi;
+ uint8 count=CBU8_COUNT_TRAIL_BYTES(c);
+ if((i)+count<=(length)) {
+ uint8 trail, illegal=0;
+
+ CBU8_MASK_LEAD_BYTE((c), count);
+ /* count==0 for illegally leading trail bytes and the illegal bytes 0xfe and 0xff */
+ switch(count) {
+ /* each branch falls through to the next one */
+ case 5:
+ case 4:
+ /* count>=4 is always illegal: no more than 3 trail bytes in Unicode's UTF-8 */
+ illegal=1;
+ break;
+ case 3:
+ trail=s[(i)++];
+ (c)=((c)<<6)|(trail&0x3f);
+ if(c<0x110) {
+ illegal|=(trail&0xc0)^0x80;
+ } else {
+ /* code point>0x10ffff, outside Unicode */
+ illegal=1;
+ break;
+ }
+ case 2:
+ trail=s[(i)++];
+ (c)=((c)<<6)|(trail&0x3f);
+ illegal|=(trail&0xc0)^0x80;
+ case 1:
+ trail=s[(i)++];
+ (c)=((c)<<6)|(trail&0x3f);
+ illegal|=(trail&0xc0)^0x80;
+ break;
+ case 0:
+ if(strict>=0) {
+ return CBUTF8_ERROR_VALUE_1;
+ } else {
+ return CBU_SENTINEL;
+ }
+ /* no default branch to optimize switch() - all values are covered */
+ }
+
+ /*
+ * All the error handling should return a value
+ * that needs count bytes so that UTF8_GET_CHAR_SAFE() works right.
+ *
+ * Starting with Unicode 3.0.1, non-shortest forms are illegal.
+ * Starting with Unicode 3.2, surrogate code points must not be
+ * encoded in UTF-8, and there are no irregular sequences any more.
+ *
+ * U8_ macros (new in ICU 2.4) return negative values for error conditions.
+ */
+
+ /* correct sequence - all trail bytes have (b7..b6)==(10)? */
+ /* illegal is also set if count>=4 */
+ if(illegal || (c)<utf8_minLegal[count] || (CBU_IS_SURROGATE(c) && strict!=-2)) {
+ /* error handling */
+ uint8 errorCount=count;
+ /* don't go beyond this sequence */
+ i=*pi;
+ while(count>0 && CBU8_IS_TRAIL(s[i])) {
+ ++(i);
+ --count;
+ }
+ if(strict>=0) {
+ c=utf8_errorValue[errorCount-count];
+ } else {
+ c=CBU_SENTINEL;
+ }
+ } else if((strict)>0 && CBU_IS_UNICODE_NONCHAR(c)) {
+ /* strict: forbid non-characters like U+fffe */
+ c=utf8_errorValue[count];
+ }
+ } else /* too few bytes left */ {
+ /* error handling */
+ int32 i0=i;
+ /* don't just set (i)=(length) in case there is an illegal sequence */
+ while((i)<(length) && CBU8_IS_TRAIL(s[i])) {
+ ++(i);
+ }
+ if(strict>=0) {
+ c=utf8_errorValue[i-i0];
+ } else {
+ c=CBU_SENTINEL;
+ }
+ }
+ *pi=i;
+ return c;
+}
+
+} // namespace base_icu
diff --git a/src/base/third_party/icu/icu_utf.h b/src/base/third_party/icu/icu_utf.h
new file mode 100644
index 0000000..9cb1247
--- /dev/null
+++ b/src/base/third_party/icu/icu_utf.h
@@ -0,0 +1,388 @@
+/*
+*******************************************************************************
+*
+* Copyright (C) 1999-2004, International Business Machines
+* Corporation and others. All Rights Reserved.
+*
+*******************************************************************************
+* file name: utf.h
+* encoding: US-ASCII
+* tab size: 8 (not used)
+* indentation:4
+*
+* created on: 1999sep09
+* created by: Markus W. Scherer
+*/
+
+#ifndef BASE_THIRD_PARTY_ICU_ICU_UTF_H_
+#define BASE_THIRD_PARTY_ICU_ICU_UTF_H_
+
+#include "base/basictypes.h"
+
+namespace base_icu {
+
+typedef uint32 UChar32;
+typedef int8 UBool;
+
+// General ---------------------------------------------------------------------
+// from utf.h
+
+/**
+ * This value is intended for sentinel values for APIs that
+ * (take or) return single code points (UChar32).
+ * It is outside of the Unicode code point range 0..0x10ffff.
+ *
+ * For example, a "done" or "error" value in a new API
+ * could be indicated with CBU_SENTINEL.
+ *
+ * ICU APIs designed before ICU 2.4 usually define service-specific "done"
+ * values, mostly 0xffff.
+ * Those may need to be distinguished from
+ * actual U+ffff text contents by calling functions like
+ * CharacterIterator::hasNext() or UnicodeString::length().
+ *
+ * @return -1
+ * @see UChar32
+ * @stable ICU 2.4
+ */
+#define CBU_SENTINEL (-1)
+
+/**
+ * Is this code point a Unicode noncharacter?
+ * @param c 32-bit code point
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU_IS_UNICODE_NONCHAR(c) \
+ ((c)>=0xfdd0 && \
+ ((uint32)(c)<=0xfdef || ((c)&0xfffe)==0xfffe) && \
+ (uint32)(c)<=0x10ffff)
+
+/**
+ * Is c a Unicode code point value (0..U+10ffff)
+ * that can be assigned a character?
+ *
+ * Code points that are not characters include:
+ * - single surrogate code points (U+d800..U+dfff, 2048 code points)
+ * - the last two code points on each plane (U+__fffe and U+__ffff, 34 code points)
+ * - U+fdd0..U+fdef (new with Unicode 3.1, 32 code points)
+ * - the highest Unicode code point value is U+10ffff
+ *
+ * This means that all code points below U+d800 are character code points,
+ * and that boundary is tested first for performance.
+ *
+ * @param c 32-bit code point
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU_IS_UNICODE_CHAR(c) \
+ ((uint32)(c)<0xd800 || \
+ ((uint32)(c)>0xdfff && \
+ (uint32)(c)<=0x10ffff && \
+ !CBU_IS_UNICODE_NONCHAR(c)))
+
+/**
+ * Is this code point a surrogate (U+d800..U+dfff)?
+ * @param c 32-bit code point
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU_IS_SURROGATE(c) (((c)&0xfffff800)==0xd800)
+
+/**
+ * Assuming c is a surrogate code point (U_IS_SURROGATE(c)),
+ * is it a lead surrogate?
+ * @param c 32-bit code point
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU_IS_SURROGATE_LEAD(c) (((c)&0x400)==0)
+
+
+// UTF-8 macros ----------------------------------------------------------------
+// from utf8.h
+
+extern const uint8 utf8_countTrailBytes[256];
+
+/**
+ * Count the trail bytes for a UTF-8 lead byte.
+ * @internal
+ */
+#define CBU8_COUNT_TRAIL_BYTES(leadByte) (base_icu::utf8_countTrailBytes[(uint8)leadByte])
+
+/**
+ * Mask a UTF-8 lead byte, leave only the lower bits that form part of the code point value.
+ * @internal
+ */
+#define CBU8_MASK_LEAD_BYTE(leadByte, countTrailBytes) ((leadByte)&=(1<<(6-(countTrailBytes)))-1)
+
+/**
+ * Does this code unit (byte) encode a code point by itself (US-ASCII 0..0x7f)?
+ * @param c 8-bit code unit (byte)
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU8_IS_SINGLE(c) (((c)&0x80)==0)
+
+/**
+ * Is this code unit (byte) a UTF-8 lead byte?
+ * @param c 8-bit code unit (byte)
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU8_IS_LEAD(c) ((uint8)((c)-0xc0)<0x3e)
+
+/**
+ * Is this code unit (byte) a UTF-8 trail byte?
+ * @param c 8-bit code unit (byte)
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU8_IS_TRAIL(c) (((c)&0xc0)==0x80)
+
+/**
+ * How many code units (bytes) are used for the UTF-8 encoding
+ * of this Unicode code point?
+ * @param c 32-bit code point
+ * @return 1..4, or 0 if c is a surrogate or not a Unicode code point
+ * @stable ICU 2.4
+ */
+#define CBU8_LENGTH(c) \
+ ((uint32)(c)<=0x7f ? 1 : \
+ ((uint32)(c)<=0x7ff ? 2 : \
+ ((uint32)(c)<=0xd7ff ? 3 : \
+ ((uint32)(c)<=0xdfff || (uint32)(c)>0x10ffff ? 0 : \
+ ((uint32)(c)<=0xffff ? 3 : 4)\
+ ) \
+ ) \
+ ) \
+ )
+
+/**
+ * The maximum number of UTF-8 code units (bytes) per Unicode code point (U+0000..U+10ffff).
+ * @return 4
+ * @stable ICU 2.4
+ */
+#define CBU8_MAX_LENGTH 4
+
+/**
+ * Function for handling "next code point" with error-checking.
+ * @internal
+ */
+UChar32 utf8_nextCharSafeBody(const uint8 *s, int32 *pi, int32 length, UChar32 c, UBool strict);
+
+/**
+ * Get a code point from a string at a code point boundary offset,
+ * and advance the offset to the next code point boundary.
+ * (Post-incrementing forward iteration.)
+ * "Safe" macro, checks for illegal sequences and for string boundaries.
+ *
+ * The offset may point to the lead byte of a multi-byte sequence,
+ * in which case the macro will read the whole sequence.
+ * If the offset points to a trail byte or an illegal UTF-8 sequence, then
+ * c is set to a negative value.
+ *
+ * @param s const uint8 * string
+ * @param i string offset, i<length
+ * @param length string length
+ * @param c output UChar32 variable, set to <0 in case of an error
+ * @see CBU8_NEXT_UNSAFE
+ * @stable ICU 2.4
+ */
+#define CBU8_NEXT(s, i, length, c) { \
+ (c)=(s)[(i)++]; \
+ if(((uint8)(c))>=0x80) { \
+ if(CBU8_IS_LEAD(c)) { \
+ (c)=base_icu::utf8_nextCharSafeBody((const uint8 *)s, &(i), (int32)(length), c, -1); \
+ } else { \
+ (c)=CBU_SENTINEL; \
+ } \
+ } \
+}
+
+/**
+ * Append a code point to a string, overwriting 1 to 4 bytes.
+ * The offset points to the current end of the string contents
+ * and is advanced (post-increment).
+ * "Unsafe" macro, assumes a valid code point and sufficient space in the string.
+ * Otherwise, the result is undefined.
+ *
+ * @param s const uint8 * string buffer
+ * @param i string offset
+ * @param c code point to append
+ * @see CBU8_APPEND
+ * @stable ICU 2.4
+ */
+#define CBU8_APPEND_UNSAFE(s, i, c) { \
+ if((uint32)(c)<=0x7f) { \
+ (s)[(i)++]=(uint8)(c); \
+ } else { \
+ if((uint32)(c)<=0x7ff) { \
+ (s)[(i)++]=(uint8)(((c)>>6)|0xc0); \
+ } else { \
+ if((uint32)(c)<=0xffff) { \
+ (s)[(i)++]=(uint8)(((c)>>12)|0xe0); \
+ } else { \
+ (s)[(i)++]=(uint8)(((c)>>18)|0xf0); \
+ (s)[(i)++]=(uint8)((((c)>>12)&0x3f)|0x80); \
+ } \
+ (s)[(i)++]=(uint8)((((c)>>6)&0x3f)|0x80); \
+ } \
+ (s)[(i)++]=(uint8)(((c)&0x3f)|0x80); \
+ } \
+}
+
+// UTF-16 macros ---------------------------------------------------------------
+// from utf16.h
+
+/**
+ * Does this code unit alone encode a code point (BMP, not a surrogate)?
+ * @param c 16-bit code unit
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU16_IS_SINGLE(c) !CBU_IS_SURROGATE(c)
+
+/**
+ * Is this code unit a lead surrogate (U+d800..U+dbff)?
+ * @param c 16-bit code unit
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU16_IS_LEAD(c) (((c)&0xfffffc00)==0xd800)
+
+/**
+ * Is this code unit a trail surrogate (U+dc00..U+dfff)?
+ * @param c 16-bit code unit
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU16_IS_TRAIL(c) (((c)&0xfffffc00)==0xdc00)
+
+/**
+ * Is this code unit a surrogate (U+d800..U+dfff)?
+ * @param c 16-bit code unit
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU16_IS_SURROGATE(c) CBU_IS_SURROGATE(c)
+
+/**
+ * Assuming c is a surrogate code point (U16_IS_SURROGATE(c)),
+ * is it a lead surrogate?
+ * @param c 16-bit code unit
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU16_IS_SURROGATE_LEAD(c) (((c)&0x400)==0)
+
+/**
+ * Helper constant for CBU16_GET_SUPPLEMENTARY.
+ * @internal
+ */
+#define CBU16_SURROGATE_OFFSET ((0xd800<<10UL)+0xdc00-0x10000)
+
+/**
+ * Get a supplementary code point value (U+10000..U+10ffff)
+ * from its lead and trail surrogates.
+ * The result is undefined if the input values are not
+ * lead and trail surrogates.
+ *
+ * @param lead lead surrogate (U+d800..U+dbff)
+ * @param trail trail surrogate (U+dc00..U+dfff)
+ * @return supplementary code point (U+10000..U+10ffff)
+ * @stable ICU 2.4
+ */
+#define CBU16_GET_SUPPLEMENTARY(lead, trail) \
+ (((base_icu::UChar32)(lead)<<10UL)+(base_icu::UChar32)(trail)-CBU16_SURROGATE_OFFSET)
+
+
+/**
+ * Get the lead surrogate (0xd800..0xdbff) for a
+ * supplementary code point (0x10000..0x10ffff).
+ * @param supplementary 32-bit code point (U+10000..U+10ffff)
+ * @return lead surrogate (U+d800..U+dbff) for supplementary
+ * @stable ICU 2.4
+ */
+#define CBU16_LEAD(supplementary) (UChar)(((supplementary)>>10)+0xd7c0)
+
+/**
+ * Get the trail surrogate (0xdc00..0xdfff) for a
+ * supplementary code point (0x10000..0x10ffff).
+ * @param supplementary 32-bit code point (U+10000..U+10ffff)
+ * @return trail surrogate (U+dc00..U+dfff) for supplementary
+ * @stable ICU 2.4
+ */
+#define CBU16_TRAIL(supplementary) (UChar)(((supplementary)&0x3ff)|0xdc00)
+
+/**
+ * How many 16-bit code units are used to encode this Unicode code point? (1 or 2)
+ * The result is not defined if c is not a Unicode code point (U+0000..U+10ffff).
+ * @param c 32-bit code point
+ * @return 1 or 2
+ * @stable ICU 2.4
+ */
+#define CBU16_LENGTH(c) ((uint32)(c)<=0xffff ? 1 : 2)
+
+/**
+ * The maximum number of 16-bit code units per Unicode code point (U+0000..U+10ffff).
+ * @return 2
+ * @stable ICU 2.4
+ */
+#define CBU16_MAX_LENGTH 2
+
+/**
+ * Get a code point from a string at a code point boundary offset,
+ * and advance the offset to the next code point boundary.
+ * (Post-incrementing forward iteration.)
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * The offset may point to the lead surrogate unit
+ * for a supplementary code point, in which case the macro will read
+ * the following trail surrogate as well.
+ * If the offset points to a trail surrogate or
+ * to a single, unpaired lead surrogate, then that itself
+ * will be returned as the code point.
+ *
+ * @param s const UChar * string
+ * @param i string offset, i<length
+ * @param length string length
+ * @param c output UChar32 variable
+ * @stable ICU 2.4
+ */
+#define CBU16_NEXT(s, i, length, c) { \
+ (c)=(s)[(i)++]; \
+ if(CBU16_IS_LEAD(c)) { \
+ uint16 __c2; \
+ if((i)<(length) && CBU16_IS_TRAIL(__c2=(s)[(i)])) { \
+ ++(i); \
+ (c)=CBU16_GET_SUPPLEMENTARY((c), __c2); \
+ } \
+ } \
+}
+
+/**
+ * Append a code point to a string, overwriting 1 or 2 code units.
+ * The offset points to the current end of the string contents
+ * and is advanced (post-increment).
+ * "Unsafe" macro, assumes a valid code point and sufficient space in the string.
+ * Otherwise, the result is undefined.
+ *
+ * @param s const UChar * string buffer
+ * @param i string offset
+ * @param c code point to append
+ * @see CBU16_APPEND
+ * @stable ICU 2.4
+ */
+#define CBU16_APPEND_UNSAFE(s, i, c) { \
+ if((uint32)(c)<=0xffff) { \
+ (s)[(i)++]=(uint16)(c); \
+ } else { \
+ (s)[(i)++]=(uint16)(((c)>>10)+0xd7c0); \
+ (s)[(i)++]=(uint16)(((c)&0x3ff)|0xdc00); \
+ } \
+}
+
+} // namesapce base_icu
+
+#endif // BASE_THIRD_PARTY_ICU_ICU_UTF_H_
diff --git a/src/base/third_party/nspr/LICENSE b/src/base/third_party/nspr/LICENSE
new file mode 100644
index 0000000..eba7b77
--- /dev/null
+++ b/src/base/third_party/nspr/LICENSE
@@ -0,0 +1,35 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
diff --git a/src/base/third_party/nspr/README.chromium b/src/base/third_party/nspr/README.chromium
new file mode 100644
index 0000000..3659a2c
--- /dev/null
+++ b/src/base/third_party/nspr/README.chromium
@@ -0,0 +1,3 @@
+Name: Netscape Portable Runtime (NSPR)
+URL: http://www.mozilla.org/projects/nspr/
+License: MPL 1.1/GPL 2.0/LGPL 2.1
diff --git a/src/base/third_party/nspr/prcpucfg.h b/src/base/third_party/nspr/prcpucfg.h
new file mode 100644
index 0000000..c362f4b
--- /dev/null
+++ b/src/base/third_party/nspr/prcpucfg.h
@@ -0,0 +1,53 @@
+// Copyright 2008, Google 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:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 THE COPYRIGHT
+// OWNER 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.
+
+#ifndef BASE_THIRD_PARTY_NSPR_PRCPUCFG_H__
+#define BASE_THIRD_PARTY_NSPR_PRCPUCFG_H__
+
+#if defined(OS_STARBOARD)
+#include "base/third_party/nspr/prcpucfg_starboard.h"
+#elif defined(WIN32) || defined (COBALT_WIN)
+#include "base/third_party/nspr/prcpucfg_win.h"
+#elif defined(__APPLE__)
+#include "base/third_party/nspr/prcpucfg_mac.h"
+#elif defined(__native_client__)
+#include "base/third_party/nspr/prcpucfg_nacl.h"
+#elif defined(__linux__) || defined(ANDROID) || defined(__LB_SHELL__)
+#include "base/third_party/nspr/prcpucfg_linux.h"
+#elif defined(__FreeBSD__)
+#include "base/third_party/nspr/prcpucfg_freebsd.h"
+#elif defined(__OpenBSD__)
+#include "base/third_party/nspr/prcpucfg_openbsd.h"
+#elif defined(__sun)
+#include "base/third_party/nspr/prcpucfg_solaris.h"
+#else
+#error Provide a prcpucfg.h appropriate for your platform
+#endif
+
+#endif // BASE_THIRD_PARTY_NSPR_PRCPUCFG_H__
diff --git a/src/base/third_party/nspr/prcpucfg_freebsd.h b/src/base/third_party/nspr/prcpucfg_freebsd.h
new file mode 100644
index 0000000..76d3542
--- /dev/null
+++ b/src/base/third_party/nspr/prcpucfg_freebsd.h
@@ -0,0 +1,337 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef FREEBSD
+#define FREEBSD
+#endif
+
+#define PR_AF_INET6 28 /* same as AF_INET6 */
+
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+
+#if defined(__i386__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#elif defined(__alpha__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#elif defined(__sparc__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#elif defined(__ia64__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#elif defined(__amd64__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#else
+
+#error "Unknown CPU architecture"
+
+#endif
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/src/base/third_party/nspr/prcpucfg_linux.h b/src/base/third_party/nspr/prcpucfg_linux.h
new file mode 100644
index 0000000..776c21e
--- /dev/null
+++ b/src/base/third_party/nspr/prcpucfg_linux.h
@@ -0,0 +1,707 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef LINUX
+#define LINUX
+#endif
+
+#define PR_AF_INET6 10 /* same as AF_INET6 */
+
+#ifdef __powerpc64__
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__powerpc__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__alpha)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__ia64__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__x86_64__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__mc68000__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 2
+#define PR_ALIGN_OF_LONG 2
+#define PR_ALIGN_OF_INT64 2
+#define PR_ALIGN_OF_FLOAT 2
+#define PR_ALIGN_OF_DOUBLE 2
+#define PR_ALIGN_OF_POINTER 2
+#define PR_ALIGN_OF_WORD 2
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__sparc__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__i386__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__mips__)
+
+#ifdef __MIPSEB__
+#define IS_BIG_ENDIAN 1
+#undef IS_LITTLE_ENDIAN
+#elif defined(__MIPSEL__)
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#else
+#error "Unknown MIPS endianness."
+#endif
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__arm__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__hppa__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__s390x__)
+
+#define IS_BIG_ENDIAN 1
+#undef IS_LITTLE_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__s390__)
+
+#define IS_BIG_ENDIAN 1
+#undef IS_LITTLE_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#else
+
+#error "Unknown CPU architecture"
+
+#endif
+
+#define HAVE_LONG_LONG
+#if PR_ALIGN_OF_DOUBLE == 8
+#define HAVE_ALIGNED_DOUBLES
+#endif
+#if PR_ALIGN_OF_INT64 == 8
+#define HAVE_ALIGNED_LONGLONGS
+#endif
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/src/base/third_party/nspr/prcpucfg_mac.h b/src/base/third_party/nspr/prcpucfg_mac.h
new file mode 100644
index 0000000..60bea8e
--- /dev/null
+++ b/src/base/third_party/nspr/prcpucfg_mac.h
@@ -0,0 +1,197 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#define PR_AF_INET6 30 /* same as AF_INET6 */
+
+#ifdef __LITTLE_ENDIAN__
+#undef IS_BIG_ENDIAN
+#define IS_LITTLE_ENDIAN 1
+#else
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#endif
+
+#ifdef __x86_64__
+#define IS_64
+#endif
+
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+#undef HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS 1
+
+#ifdef IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+#define PR_BITS_PER_DWORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+#define PR_BITS_PER_DWORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+#define PR_ALIGN_OF_DWORD 8
+
+#else /* IS_64 */
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+#define PR_BITS_PER_DWORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#endif /* IS_64 */
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
+
diff --git a/src/base/third_party/nspr/prcpucfg_nacl.h b/src/base/third_party/nspr/prcpucfg_nacl.h
new file mode 100644
index 0000000..d8602d3
--- /dev/null
+++ b/src/base/third_party/nspr/prcpucfg_nacl.h
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef LINUX
+#define LINUX
+#endif
+
+#define PR_AF_INET6 10 /* same as AF_INET6 */
+
+#if defined(__x86_64__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__i386__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(__arm__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#else
+
+#error "Unknown CPU architecture"
+
+#endif
+
+#define HAVE_LONG_LONG
+#if PR_ALIGN_OF_DOUBLE == 8
+#define HAVE_ALIGNED_DOUBLES
+#endif
+#if PR_ALIGN_OF_INT64 == 8
+#define HAVE_ALIGNED_LONGLONGS
+#endif
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/src/base/third_party/nspr/prcpucfg_openbsd.h b/src/base/third_party/nspr/prcpucfg_openbsd.h
new file mode 100644
index 0000000..93c5b32
--- /dev/null
+++ b/src/base/third_party/nspr/prcpucfg_openbsd.h
@@ -0,0 +1,337 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef OPENBSD
+#define OPENBSD
+#endif
+
+#define PR_AF_INET6 28 /* same as AF_INET6 */
+
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+
+#if defined(__i386__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#undef HAVE_ALIGNED_DOUBLES
+#undef HAVE_ALIGNED_LONGLONGS
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#elif defined(__alpha__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#elif defined(__sparc__)
+
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#elif defined(__ia64__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#elif defined(__amd64__)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#else
+
+#error "Unknown CPU architecture"
+
+#endif
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/src/base/third_party/nspr/prcpucfg_solaris.h b/src/base/third_party/nspr/prcpucfg_solaris.h
new file mode 100644
index 0000000..81313e5
--- /dev/null
+++ b/src/base/third_party/nspr/prcpucfg_solaris.h
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#ifndef SOLARIS
+#define SOLARIS
+#endif
+
+#define PR_AF_INET6 26 /* same as AF_INET6 */
+
+#if defined(sparc) || defined(__sparc)
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_DOUBLE 8
+#if defined(__sparcv9)
+#define IS_64
+#endif
+#elif defined(__x86_64)
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_DOUBLE 8
+#define IS_64
+#elif defined(i386) || defined(__i386)
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_DOUBLE 4
+#else
+#error unknown processor
+#endif
+
+#ifdef IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_POINTER 8
+
+#else /* IS_64 */
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_POINTER 4
+
+#endif /* IS_64 */
+
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+#define HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* ifndef nspr_cpucfg___ */
diff --git a/src/base/third_party/nspr/prcpucfg_starboard.h b/src/base/third_party/nspr/prcpucfg_starboard.h
new file mode 100644
index 0000000..5bdf40d
--- /dev/null
+++ b/src/base/third_party/nspr/prcpucfg_starboard.h
@@ -0,0 +1,312 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#include "starboard/configuration.h"
+
+// This is only used by third_party/nss/ssl
+#define PR_AF_INET6 23 /* same as AF_INET6 */
+
+#if SB_IS(BIG_ENDIAN)
+# undef IS_LITTLE_ENDIAN
+# define IS_BIG_ENDIAN 1
+#else
+# define IS_LITTLE_ENDIAN 1
+# undef IS_BIG_ENDIAN
+#endif
+
+#if SB_IS(64_BIT)
+# define IS_64
+#endif
+
+#if SB_IS(ARCH_PPC) && SB_IS(32_BIT)
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+#elif SB_IS(ARCH_X86) && SB_IS(64_BIT)
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 8
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 64
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 6
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 8
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+#define PR_ALIGN_OF_WORD 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+#elif SB_IS(ARCH_X86) && SB_IS(32_BIT)
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+#elif SB_IS(ARCH_MIPS) && SB_IS(32_BIT)
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+#elif SB_IS(ARCH_ARM) && SB_IS(32_BIT)
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+#else
+# error "No configuration definition for Starboard ARCH + BITS."
+#endif
+
+// TODO: Base this off of Starboard configuration defines.
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/src/base/third_party/nspr/prcpucfg_win.h b/src/base/third_party/nspr/prcpucfg_win.h
new file mode 100644
index 0000000..d08df97
--- /dev/null
+++ b/src/base/third_party/nspr/prcpucfg_win.h
@@ -0,0 +1,304 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_PC
+#define XP_PC
+#endif
+
+#ifndef WIN32
+#define WIN32
+#endif
+
+#ifndef WIN95
+#define WIN95
+#endif
+
+#define PR_AF_INET6 23 /* same as AF_INET6 */
+
+#if defined(_M_IX86) || defined(_X86_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_DOUBLE 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 32
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 5
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_WORD 4
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 2
+
+#elif defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_DOUBLE 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 64
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 6
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_WORD 8
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(_M_IA64) || defined(_IA64_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_DOUBLE 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 64
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 6
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_WORD 8
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(_M_PPC) || defined(_PPC_)
+
+#define IS_BIG_ENDIAN 1
+#undef IS_LITTLE_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_DOUBLE 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 32
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 5
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_WORD 4
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 2
+
+#else /* defined(_M_IX86) || defined(_X86_) */
+
+#error unknown processor architecture
+
+#endif /* defined(_M_IX86) || defined(_X86_) */
+
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/src/base/third_party/nspr/prtime.cc b/src/base/third_party/nspr/prtime.cc
new file mode 100644
index 0000000..133913f
--- /dev/null
+++ b/src/base/third_party/nspr/prtime.cc
@@ -0,0 +1,1258 @@
+/* Portions are Copyright (C) 2011 Google Inc */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * prtime.cc --
+ * NOTE: The original nspr file name is prtime.c
+ *
+ * NSPR date and time functions
+ *
+ * CVS revision 3.37
+ */
+
+/*
+ * The following functions were copied from the NSPR prtime.c file.
+ * PR_ParseTimeString
+ * We inlined the new PR_ParseTimeStringToExplodedTime function to avoid
+ * copying PR_ExplodeTime and PR_LocalTimeParameters. (The PR_ExplodeTime
+ * and PR_ImplodeTime calls cancel each other out.)
+ * PR_NormalizeTime
+ * PR_GMTParameters
+ * PR_ImplodeTime
+ * This was modified to use the Win32 SYSTEMTIME/FILETIME structures
+ * and the timezone offsets are applied to the FILETIME structure.
+ * All types and macros are defined in the base/third_party/prtime.h file.
+ * These have been copied from the following nspr files. We have only copied
+ * over the types we need.
+ * 1. prtime.h
+ * 2. prtypes.h
+ * 3. prlong.h
+ */
+
+#include "base/logging.h"
+#include "base/third_party/nspr/prtime.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN) || defined(COBALT_WIN)
+#include <windows.h>
+#elif defined(OS_MACOSX)
+#include <CoreFoundation/CoreFoundation.h>
+#elif defined(OS_ANDROID) || defined(__LB_ANDROID__)
+#include <ctype.h>
+#include "base/os_compat_android.h" // For timegm()
+#elif defined(OS_NACL)
+#include "base/os_compat_nacl.h" // For timegm()
+#else
+#define PRTIME_USE_BASE_TIME
+#include "base/time.h"
+#endif
+
+#if !defined(OS_STARBOARD)
+#include <errno.h> /* for EINVAL */
+#include <time.h>
+#endif
+
+/* Implements the Unix localtime_r() function for windows */
+#if defined(OS_WIN)
+static void localtime_r(const time_t* secs, struct tm* time) {
+ (void) localtime_s(time, secs);
+}
+#endif
+
+#if defined(PRTIME_USE_BASE_TIME)
+// Implodes |exploded| using base::Time's implosion methods. |is_local| states
+// whether to ignore the time zone params and just interpret as a local time, as
+// opposed to treating like a UTC exploded time and then adjusting by the TZ
+// params.
+static PRTime BaseImplode(const PRExplodedTime* exploded, bool is_local) {
+ static const PRTime kSecondsToMicroseconds = static_cast<PRTime>(1000000);
+ base::Time::Exploded base_exploded;
+ base_exploded.year = exploded->tm_year;
+ base_exploded.month = exploded->tm_month + 1;
+ base_exploded.day_of_week = 0;
+ base_exploded.day_of_month = exploded->tm_mday;
+ base_exploded.hour = exploded->tm_hour;
+ base_exploded.minute = exploded->tm_min;
+ base_exploded.second = exploded->tm_sec;
+ base_exploded.millisecond = 0;
+ base::Time base_time;
+ if (is_local) {
+ base_time = base::Time::FromLocalExploded(base_exploded);
+ } else {
+ base_time = base::Time::FromUTCExploded(base_exploded);
+ }
+ PRTime result = static_cast<PRTime>(
+ (base_time - base::Time::UnixEpoch()).InMicroseconds());
+ if (!is_local) {
+ result -= (exploded->tm_params.tp_gmt_offset +
+ exploded->tm_params.tp_dst_offset) *
+ kSecondsToMicroseconds;
+ }
+ result += exploded->tm_usec;
+ return result;
+}
+#endif // defined(PRTIME_USE_BASE_TIME)
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * PR_ImplodeTime --
+ *
+ * Cf. time_t mktime(struct tm *tp)
+ * Note that 1 year has < 2^25 seconds. So an PRInt32 is large enough.
+ *
+ *------------------------------------------------------------------------
+ */
+PRTime
+PR_ImplodeTime(const PRExplodedTime *exploded)
+{
+ // This is important, we want to make sure multiplications are
+ // done with the correct precision.
+ static const PRTime kSecondsToMicroseconds = static_cast<PRTime>(1000000);
+#if defined(OS_WIN) || defined(COBALT_WIN)
+ // Create the system struct representing our exploded time.
+ SYSTEMTIME st = {0};
+ FILETIME ft = {0};
+ ULARGE_INTEGER uli = {0};
+
+ st.wYear = exploded->tm_year;
+ st.wMonth = exploded->tm_month + 1;
+ st.wDayOfWeek = exploded->tm_wday;
+ st.wDay = exploded->tm_mday;
+ st.wHour = exploded->tm_hour;
+ st.wMinute = exploded->tm_min;
+ st.wSecond = exploded->tm_sec;
+ st.wMilliseconds = exploded->tm_usec/1000;
+ // Convert to FILETIME.
+ if (!SystemTimeToFileTime(&st, &ft)) {
+ NOTREACHED() << "Unable to convert time";
+ return 0;
+ }
+ // Apply offsets.
+ uli.LowPart = ft.dwLowDateTime;
+ uli.HighPart = ft.dwHighDateTime;
+ // Convert from Windows epoch to NSPR epoch, and 100-nanoseconds units
+ // to microsecond units.
+ PRTime result =
+ static_cast<PRTime>((uli.QuadPart / 10) - 11644473600000000i64);
+ // Adjust for time zone and dst. Convert from seconds to microseconds.
+ result -= (exploded->tm_params.tp_gmt_offset +
+ exploded->tm_params.tp_dst_offset) * kSecondsToMicroseconds;
+ return result;
+#elif defined(OS_MACOSX)
+ // Create the system struct representing our exploded time.
+ CFGregorianDate gregorian_date;
+ gregorian_date.year = exploded->tm_year;
+ gregorian_date.month = exploded->tm_month + 1;
+ gregorian_date.day = exploded->tm_mday;
+ gregorian_date.hour = exploded->tm_hour;
+ gregorian_date.minute = exploded->tm_min;
+ gregorian_date.second = exploded->tm_sec;
+
+ // Compute |absolute_time| in seconds, correct for gmt and dst
+ // (note the combined offset will be negative when we need to add it), then
+ // convert to microseconds which is what PRTime expects.
+ CFAbsoluteTime absolute_time =
+ CFGregorianDateGetAbsoluteTime(gregorian_date, NULL);
+ PRTime result = static_cast<PRTime>(absolute_time);
+ result -= exploded->tm_params.tp_gmt_offset +
+ exploded->tm_params.tp_dst_offset;
+ result += kCFAbsoluteTimeIntervalSince1970; // PRTime epoch is 1970
+ result *= kSecondsToMicroseconds;
+ result += exploded->tm_usec;
+ return result;
+#elif defined(OS_POSIX)
+ struct tm exp_tm = {0};
+ exp_tm.tm_sec = exploded->tm_sec;
+ exp_tm.tm_min = exploded->tm_min;
+ exp_tm.tm_hour = exploded->tm_hour;
+ exp_tm.tm_mday = exploded->tm_mday;
+ exp_tm.tm_mon = exploded->tm_month;
+ exp_tm.tm_year = exploded->tm_year - 1900;
+ time_t absolute_time = timegm(&exp_tm);
+
+ // If timegm returned -1. Since we don't pass it a time zone, the only
+ // valid case of returning -1 is 1 second before Epoch (Dec 31, 1969).
+ if (absolute_time == -1 &&
+ !(exploded->tm_year == 1969 && exploded->tm_month == 11 &&
+ exploded->tm_mday == 31 && exploded->tm_hour == 23 &&
+ exploded->tm_min == 59 && exploded->tm_sec == 59)) {
+ // If we get here, time_t must be 32 bits.
+ // Date was possibly too far in the future and would overflow. Return
+ // the most future date possible (year 2038).
+ if (exploded->tm_year >= 1970)
+ return INT_MAX * kSecondsToMicroseconds;
+ // Date was possibly too far in the past and would underflow. Return
+ // the most past date possible (year 1901).
+ return INT_MIN * kSecondsToMicroseconds;
+ }
+
+ PRTime result = static_cast<PRTime>(absolute_time);
+ result -= exploded->tm_params.tp_gmt_offset +
+ exploded->tm_params.tp_dst_offset;
+ result *= kSecondsToMicroseconds;
+ result += exploded->tm_usec;
+ return result;
+#elif defined(PRTIME_USE_BASE_TIME)
+ return BaseImplode(exploded, false /*is_local*/);
+#else
+#error No PR_ImplodeTime implemented on your platform.
+#endif
+}
+
+/*
+ * The COUNT_LEAPS macro counts the number of leap years passed by
+ * till the start of the given year Y. At the start of the year 4
+ * A.D. the number of leap years passed by is 0, while at the start of
+ * the year 5 A.D. this count is 1. The number of years divisible by
+ * 100 but not divisible by 400 (the non-leap years) is deducted from
+ * the count to get the correct number of leap years.
+ *
+ * The COUNT_DAYS macro counts the number of days since 01/01/01 till the
+ * start of the given year Y. The number of days at the start of the year
+ * 1 is 0 while the number of days at the start of the year 2 is 365
+ * (which is ((2)-1) * 365) and so on. The reference point is 01/01/01
+ * midnight 00:00:00.
+ */
+
+#define COUNT_LEAPS(Y) ( ((Y)-1)/4 - ((Y)-1)/100 + ((Y)-1)/400 )
+#define COUNT_DAYS(Y) ( ((Y)-1)*365 + COUNT_LEAPS(Y) )
+#define DAYS_BETWEEN_YEARS(A, B) (COUNT_DAYS(B) - COUNT_DAYS(A))
+
+/*
+ * Static variables used by functions in this file
+ */
+
+/*
+ * The following array contains the day of year for the last day of
+ * each month, where index 1 is January, and day 0 is January 1.
+ */
+
+static const int lastDayOfMonth[2][13] = {
+ {-1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364},
+ {-1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}
+};
+
+/*
+ * The number of days in a month
+ */
+
+static const PRInt8 nDays[2][12] = {
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+};
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * IsLeapYear --
+ *
+ * Returns 1 if the year is a leap year, 0 otherwise.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static int IsLeapYear(PRInt16 year)
+{
+ if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * 'secOffset' should be less than 86400 (i.e., a day).
+ * 'time' should point to a normalized PRExplodedTime.
+ */
+
+static void
+ApplySecOffset(PRExplodedTime *time, PRInt32 secOffset)
+{
+ time->tm_sec += secOffset;
+
+ /* Note that in this implementation we do not count leap seconds */
+ if (time->tm_sec < 0 || time->tm_sec >= 60) {
+ time->tm_min += time->tm_sec / 60;
+ time->tm_sec %= 60;
+ if (time->tm_sec < 0) {
+ time->tm_sec += 60;
+ time->tm_min--;
+ }
+ }
+
+ if (time->tm_min < 0 || time->tm_min >= 60) {
+ time->tm_hour += time->tm_min / 60;
+ time->tm_min %= 60;
+ if (time->tm_min < 0) {
+ time->tm_min += 60;
+ time->tm_hour--;
+ }
+ }
+
+ if (time->tm_hour < 0) {
+ /* Decrement mday, yday, and wday */
+ time->tm_hour += 24;
+ time->tm_mday--;
+ time->tm_yday--;
+ if (time->tm_mday < 1) {
+ time->tm_month--;
+ if (time->tm_month < 0) {
+ time->tm_month = 11;
+ time->tm_year--;
+ if (IsLeapYear(time->tm_year))
+ time->tm_yday = 365;
+ else
+ time->tm_yday = 364;
+ }
+ time->tm_mday = nDays[IsLeapYear(time->tm_year)][time->tm_month];
+ }
+ time->tm_wday--;
+ if (time->tm_wday < 0)
+ time->tm_wday = 6;
+ } else if (time->tm_hour > 23) {
+ /* Increment mday, yday, and wday */
+ time->tm_hour -= 24;
+ time->tm_mday++;
+ time->tm_yday++;
+ if (time->tm_mday >
+ nDays[IsLeapYear(time->tm_year)][time->tm_month]) {
+ time->tm_mday = 1;
+ time->tm_month++;
+ if (time->tm_month > 11) {
+ time->tm_month = 0;
+ time->tm_year++;
+ time->tm_yday = 0;
+ }
+ }
+ time->tm_wday++;
+ if (time->tm_wday > 6)
+ time->tm_wday = 0;
+ }
+}
+
+void
+PR_NormalizeTime(PRExplodedTime *time, PRTimeParamFn params)
+{
+ int daysInMonth;
+ PRInt32 numDays;
+
+ /* Get back to GMT */
+ time->tm_sec -= time->tm_params.tp_gmt_offset
+ + time->tm_params.tp_dst_offset;
+ time->tm_params.tp_gmt_offset = 0;
+ time->tm_params.tp_dst_offset = 0;
+
+ /* Now normalize GMT */
+
+ if (time->tm_usec < 0 || time->tm_usec >= 1000000) {
+ time->tm_sec += time->tm_usec / 1000000;
+ time->tm_usec %= 1000000;
+ if (time->tm_usec < 0) {
+ time->tm_usec += 1000000;
+ time->tm_sec--;
+ }
+ }
+
+ /* Note that we do not count leap seconds in this implementation */
+ if (time->tm_sec < 0 || time->tm_sec >= 60) {
+ time->tm_min += time->tm_sec / 60;
+ time->tm_sec %= 60;
+ if (time->tm_sec < 0) {
+ time->tm_sec += 60;
+ time->tm_min--;
+ }
+ }
+
+ if (time->tm_min < 0 || time->tm_min >= 60) {
+ time->tm_hour += time->tm_min / 60;
+ time->tm_min %= 60;
+ if (time->tm_min < 0) {
+ time->tm_min += 60;
+ time->tm_hour--;
+ }
+ }
+
+ if (time->tm_hour < 0 || time->tm_hour >= 24) {
+ time->tm_mday += time->tm_hour / 24;
+ time->tm_hour %= 24;
+ if (time->tm_hour < 0) {
+ time->tm_hour += 24;
+ time->tm_mday--;
+ }
+ }
+
+ /* Normalize month and year before mday */
+ if (time->tm_month < 0 || time->tm_month >= 12) {
+ time->tm_year += time->tm_month / 12;
+ time->tm_month %= 12;
+ if (time->tm_month < 0) {
+ time->tm_month += 12;
+ time->tm_year--;
+ }
+ }
+
+ /* Now that month and year are in proper range, normalize mday */
+
+ if (time->tm_mday < 1) {
+ /* mday too small */
+ do {
+ /* the previous month */
+ time->tm_month--;
+ if (time->tm_month < 0) {
+ time->tm_month = 11;
+ time->tm_year--;
+ }
+ time->tm_mday += nDays[IsLeapYear(time->tm_year)][time->tm_month];
+ } while (time->tm_mday < 1);
+ } else {
+ daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month];
+ while (time->tm_mday > daysInMonth) {
+ /* mday too large */
+ time->tm_mday -= daysInMonth;
+ time->tm_month++;
+ if (time->tm_month > 11) {
+ time->tm_month = 0;
+ time->tm_year++;
+ }
+ daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month];
+ }
+ }
+
+ /* Recompute yday and wday */
+ time->tm_yday = time->tm_mday +
+ lastDayOfMonth[IsLeapYear(time->tm_year)][time->tm_month];
+
+ numDays = DAYS_BETWEEN_YEARS(1970, time->tm_year) + time->tm_yday;
+ time->tm_wday = (numDays + 4) % 7;
+ if (time->tm_wday < 0) {
+ time->tm_wday += 7;
+ }
+
+ /* Recompute time parameters */
+
+ time->tm_params = params(time);
+
+ ApplySecOffset(time, time->tm_params.tp_gmt_offset
+ + time->tm_params.tp_dst_offset);
+}
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * PR_GMTParameters --
+ *
+ * Returns the PRTimeParameters for Greenwich Mean Time.
+ * Trivially, both the tp_gmt_offset and tp_dst_offset fields are 0.
+ *
+ *------------------------------------------------------------------------
+ */
+
+PRTimeParameters
+PR_GMTParameters(const PRExplodedTime *gmt)
+{
+#if defined(XP_MAC)
+#pragma unused (gmt)
+#endif
+
+ PRTimeParameters retVal = { 0, 0 };
+ return retVal;
+}
+
+/*
+ * The following code implements PR_ParseTimeString(). It is based on
+ * ns/lib/xp/xp_time.c, revision 1.25, by Jamie Zawinski <jwz@netscape.com>.
+ */
+
+/*
+ * We only recognize the abbreviations of a small subset of time zones
+ * in North America, Europe, and Japan.
+ *
+ * PST/PDT: Pacific Standard/Daylight Time
+ * MST/MDT: Mountain Standard/Daylight Time
+ * CST/CDT: Central Standard/Daylight Time
+ * EST/EDT: Eastern Standard/Daylight Time
+ * AST: Atlantic Standard Time
+ * NST: Newfoundland Standard Time
+ * GMT: Greenwich Mean Time
+ * BST: British Summer Time
+ * MET: Middle Europe Time
+ * EET: Eastern Europe Time
+ * JST: Japan Standard Time
+ */
+
+typedef enum
+{
+ TT_UNKNOWN,
+
+ TT_SUN, TT_MON, TT_TUE, TT_WED, TT_THU, TT_FRI, TT_SAT,
+
+ TT_JAN, TT_FEB, TT_MAR, TT_APR, TT_MAY, TT_JUN,
+ TT_JUL, TT_AUG, TT_SEP, TT_OCT, TT_NOV, TT_DEC,
+
+ TT_PST, TT_PDT, TT_MST, TT_MDT, TT_CST, TT_CDT, TT_EST, TT_EDT,
+ TT_AST, TT_NST, TT_GMT, TT_BST, TT_MET, TT_EET, TT_JST
+} TIME_TOKEN;
+
+/*
+ * This parses a time/date string into a PRTime
+ * (microseconds after "1-Jan-1970 00:00:00 GMT").
+ * It returns PR_SUCCESS on success, and PR_FAILURE
+ * if the time/date string can't be parsed.
+ *
+ * Many formats are handled, including:
+ *
+ * 14 Apr 89 03:20:12
+ * 14 Apr 89 03:20 GMT
+ * Fri, 17 Mar 89 4:01:33
+ * Fri, 17 Mar 89 4:01 GMT
+ * Mon Jan 16 16:12 PDT 1989
+ * Mon Jan 16 16:12 +0130 1989
+ * 6 May 1992 16:41-JST (Wednesday)
+ * 22-AUG-1993 10:59:12.82
+ * 22-AUG-1993 10:59pm
+ * 22-AUG-1993 12:59am
+ * 22-AUG-1993 12:59 PM
+ * Friday, August 04, 1995 3:54 PM
+ * 06/21/95 04:24:34 PM
+ * 20/06/95 21:07
+ * 95-06-08 19:32:48 EDT
+ *
+ * If the input string doesn't contain a description of the timezone,
+ * we consult the `default_to_gmt' to decide whether the string should
+ * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE).
+ * The correct value for this argument depends on what standard specified
+ * the time string which you are parsing.
+ */
+
+PRStatus
+PR_ParseTimeString(
+ const char *string,
+ PRBool default_to_gmt,
+ PRTime *result_imploded)
+{
+ PRExplodedTime tm;
+ PRExplodedTime *result = &tm;
+ TIME_TOKEN dotw = TT_UNKNOWN;
+ TIME_TOKEN month = TT_UNKNOWN;
+ TIME_TOKEN zone = TT_UNKNOWN;
+ int zone_offset = -1;
+ int dst_offset = 0;
+ int date = -1;
+ PRInt32 year = -1;
+ int hour = -1;
+ int min = -1;
+ int sec = -1;
+
+ const char *rest = string;
+
+ int iterations = 0;
+
+ PR_ASSERT(string && result);
+ if (!string || !result) return PR_FAILURE;
+
+ while (*rest)
+ {
+
+ if (iterations++ > 1000)
+ {
+ return PR_FAILURE;
+ }
+
+ switch (*rest)
+ {
+ case 'a': case 'A':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'p' || rest[1] == 'P') &&
+ (rest[2] == 'r' || rest[2] == 'R'))
+ month = TT_APR;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_AST;
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'g' || rest[2] == 'G'))
+ month = TT_AUG;
+ break;
+ case 'b': case 'B':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_BST;
+ break;
+ case 'c': case 'C':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 'd' || rest[1] == 'D') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_CDT;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_CST;
+ break;
+ case 'd': case 'D':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 'c' || rest[2] == 'C'))
+ month = TT_DEC;
+ break;
+ case 'e': case 'E':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 'd' || rest[1] == 'D') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_EDT;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_EET;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_EST;
+ break;
+ case 'f': case 'F':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 'b' || rest[2] == 'B'))
+ month = TT_FEB;
+ else if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'r' || rest[1] == 'R') &&
+ (rest[2] == 'i' || rest[2] == 'I'))
+ dotw = TT_FRI;
+ break;
+ case 'g': case 'G':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 'm' || rest[1] == 'M') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_GMT;
+ break;
+ case 'j': case 'J':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'a' || rest[1] == 'A') &&
+ (rest[2] == 'n' || rest[2] == 'N'))
+ month = TT_JAN;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_JST;
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'l' || rest[2] == 'L'))
+ month = TT_JUL;
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'n' || rest[2] == 'N'))
+ month = TT_JUN;
+ break;
+ case 'm': case 'M':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'a' || rest[1] == 'A') &&
+ (rest[2] == 'r' || rest[2] == 'R'))
+ month = TT_MAR;
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'a' || rest[1] == 'A') &&
+ (rest[2] == 'y' || rest[2] == 'Y'))
+ month = TT_MAY;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 'd' || rest[1] == 'D') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_MDT;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_MET;
+ else if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'o' || rest[1] == 'O') &&
+ (rest[2] == 'n' || rest[2] == 'N'))
+ dotw = TT_MON;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_MST;
+ break;
+ case 'n': case 'N':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'o' || rest[1] == 'O') &&
+ (rest[2] == 'v' || rest[2] == 'V'))
+ month = TT_NOV;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_NST;
+ break;
+ case 'o': case 'O':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'c' || rest[1] == 'C') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ month = TT_OCT;
+ break;
+ case 'p': case 'P':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 'd' || rest[1] == 'D') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_PDT;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_PST;
+ break;
+ case 's': case 'S':
+ if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'a' || rest[1] == 'A') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ dotw = TT_SAT;
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 'p' || rest[2] == 'P'))
+ month = TT_SEP;
+ else if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'n' || rest[2] == 'N'))
+ dotw = TT_SUN;
+ break;
+ case 't': case 'T':
+ if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'h' || rest[1] == 'H') &&
+ (rest[2] == 'u' || rest[2] == 'U'))
+ dotw = TT_THU;
+ else if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'e' || rest[2] == 'E'))
+ dotw = TT_TUE;
+ break;
+ case 'u': case 'U':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 't' || rest[1] == 'T') &&
+ !(rest[2] >= 'A' && rest[2] <= 'Z') &&
+ !(rest[2] >= 'a' && rest[2] <= 'z'))
+ /* UT is the same as GMT but UTx is not. */
+ zone = TT_GMT;
+ break;
+ case 'w': case 'W':
+ if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 'd' || rest[2] == 'D'))
+ dotw = TT_WED;
+ break;
+
+ case '+': case '-':
+ {
+ const char *end;
+ int sign;
+ if (zone_offset != -1)
+ {
+ /* already got one... */
+ rest++;
+ break;
+ }
+ if (zone != TT_UNKNOWN && zone != TT_GMT)
+ {
+ /* GMT+0300 is legal, but PST+0300 is not. */
+ rest++;
+ break;
+ }
+
+ sign = ((*rest == '+') ? 1 : -1);
+ rest++; /* move over sign */
+ end = rest;
+ while (*end >= '0' && *end <= '9')
+ end++;
+ if (rest == end) /* no digits here */
+ break;
+
+ if ((end - rest) == 4)
+ /* offset in HHMM */
+ zone_offset = (((((rest[0]-'0')*10) + (rest[1]-'0')) * 60) +
+ (((rest[2]-'0')*10) + (rest[3]-'0')));
+ else if ((end - rest) == 2)
+ /* offset in hours */
+ zone_offset = (((rest[0]-'0')*10) + (rest[1]-'0')) * 60;
+ else if ((end - rest) == 1)
+ /* offset in hours */
+ zone_offset = (rest[0]-'0') * 60;
+ else
+ /* 3 or >4 */
+ break;
+
+ zone_offset *= sign;
+ zone = TT_GMT;
+ break;
+ }
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ int tmp_hour = -1;
+ int tmp_min = -1;
+ int tmp_sec = -1;
+ const char *end = rest + 1;
+ while (*end >= '0' && *end <= '9')
+ end++;
+
+ /* end is now the first character after a range of digits. */
+
+ if (*end == ':')
+ {
+ if (hour >= 0 && min >= 0) /* already got it */
+ break;
+
+ /* We have seen "[0-9]+:", so this is probably HH:MM[:SS] */
+ if ((end - rest) > 2)
+ /* it is [0-9][0-9][0-9]+: */
+ break;
+ else if ((end - rest) == 2)
+ tmp_hour = ((rest[0]-'0')*10 +
+ (rest[1]-'0'));
+ else
+ tmp_hour = (rest[0]-'0');
+
+ /* move over the colon, and parse minutes */
+
+ rest = ++end;
+ while (*end >= '0' && *end <= '9')
+ end++;
+
+ if (end == rest)
+ /* no digits after first colon? */
+ break;
+ else if ((end - rest) > 2)
+ /* it is [0-9][0-9][0-9]+: */
+ break;
+ else if ((end - rest) == 2)
+ tmp_min = ((rest[0]-'0')*10 +
+ (rest[1]-'0'));
+ else
+ tmp_min = (rest[0]-'0');
+
+ /* now go for seconds */
+ rest = end;
+ if (*rest == ':')
+ rest++;
+ end = rest;
+ while (*end >= '0' && *end <= '9')
+ end++;
+
+ if (end == rest)
+ /* no digits after second colon - that's ok. */
+ ;
+ else if ((end - rest) > 2)
+ /* it is [0-9][0-9][0-9]+: */
+ break;
+ else if ((end - rest) == 2)
+ tmp_sec = ((rest[0]-'0')*10 +
+ (rest[1]-'0'));
+ else
+ tmp_sec = (rest[0]-'0');
+
+ /* If we made it here, we've parsed hour and min,
+ and possibly sec, so it worked as a unit. */
+
+ /* skip over whitespace and see if there's an AM or PM
+ directly following the time.
+ */
+ if (tmp_hour <= 12)
+ {
+ const char *s = end;
+ while (*s && (*s == ' ' || *s == '\t'))
+ s++;
+ if ((s[0] == 'p' || s[0] == 'P') &&
+ (s[1] == 'm' || s[1] == 'M'))
+ /* 10:05pm == 22:05, and 12:05pm == 12:05 */
+ tmp_hour = (tmp_hour == 12 ? 12 : tmp_hour + 12);
+ else if (tmp_hour == 12 &&
+ (s[0] == 'a' || s[0] == 'A') &&
+ (s[1] == 'm' || s[1] == 'M'))
+ /* 12:05am == 00:05 */
+ tmp_hour = 0;
+ }
+
+ hour = tmp_hour;
+ min = tmp_min;
+ sec = tmp_sec;
+ rest = end;
+ break;
+ }
+ else if ((*end == '/' || *end == '-') &&
+ end[1] >= '0' && end[1] <= '9')
+ {
+ /* Perhaps this is 6/16/95, 16/6/95, 6-16-95, or 16-6-95
+ or even 95-06-05...
+ #### But it doesn't handle 1995-06-22.
+ */
+ int n1, n2, n3;
+ const char *s;
+
+ if (month != TT_UNKNOWN)
+ /* if we saw a month name, this can't be. */
+ break;
+
+ s = rest;
+
+ n1 = (*s++ - '0'); /* first 1 or 2 digits */
+ if (*s >= '0' && *s <= '9')
+ n1 = n1*10 + (*s++ - '0');
+
+ if (*s != '/' && *s != '-') /* slash */
+ break;
+ s++;
+
+ if (*s < '0' || *s > '9') /* second 1 or 2 digits */
+ break;
+ n2 = (*s++ - '0');
+ if (*s >= '0' && *s <= '9')
+ n2 = n2*10 + (*s++ - '0');
+
+ if (*s != '/' && *s != '-') /* slash */
+ break;
+ s++;
+
+ if (*s < '0' || *s > '9') /* third 1, 2, 4, or 5 digits */
+ break;
+ n3 = (*s++ - '0');
+ if (*s >= '0' && *s <= '9')
+ n3 = n3*10 + (*s++ - '0');
+
+ if (*s >= '0' && *s <= '9') /* optional digits 3, 4, and 5 */
+ {
+ n3 = n3*10 + (*s++ - '0');
+ if (*s < '0' || *s > '9')
+ break;
+ n3 = n3*10 + (*s++ - '0');
+ if (*s >= '0' && *s <= '9')
+ n3 = n3*10 + (*s++ - '0');
+ }
+
+ if ((*s >= '0' && *s <= '9') || /* followed by non-alphanum */
+ (*s >= 'A' && *s <= 'Z') ||
+ (*s >= 'a' && *s <= 'z'))
+ break;
+
+ /* Ok, we parsed three 1-2 digit numbers, with / or -
+ between them. Now decide what the hell they are
+ (DD/MM/YY or MM/DD/YY or YY/MM/DD.)
+ */
+
+ if (n1 > 31 || n1 == 0) /* must be YY/MM/DD */
+ {
+ if (n2 > 12) break;
+ if (n3 > 31) break;
+ year = n1;
+ if (year < 70)
+ year += 2000;
+ else if (year < 100)
+ year += 1900;
+ month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
+ date = n3;
+ rest = s;
+ break;
+ }
+
+ if (n1 > 12 && n2 > 12) /* illegal */
+ {
+ rest = s;
+ break;
+ }
+
+ if (n3 < 70)
+ n3 += 2000;
+ else if (n3 < 100)
+ n3 += 1900;
+
+ if (n1 > 12) /* must be DD/MM/YY */
+ {
+ date = n1;
+ month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
+ year = n3;
+ }
+ else /* assume MM/DD/YY */
+ {
+ /* #### In the ambiguous case, should we consult the
+ locale to find out the local default? */
+ month = (TIME_TOKEN)(n1 + ((int)TT_JAN) - 1);
+ date = n2;
+ year = n3;
+ }
+ rest = s;
+ }
+ else if ((*end >= 'A' && *end <= 'Z') ||
+ (*end >= 'a' && *end <= 'z'))
+ /* Digits followed by non-punctuation - what's that? */
+ ;
+ else if ((end - rest) == 5) /* five digits is a year */
+ year = (year < 0
+ ? ((rest[0]-'0')*10000L +
+ (rest[1]-'0')*1000L +
+ (rest[2]-'0')*100L +
+ (rest[3]-'0')*10L +
+ (rest[4]-'0'))
+ : year);
+ else if ((end - rest) == 4) /* four digits is a year */
+ year = (year < 0
+ ? ((rest[0]-'0')*1000L +
+ (rest[1]-'0')*100L +
+ (rest[2]-'0')*10L +
+ (rest[3]-'0'))
+ : year);
+ else if ((end - rest) == 2) /* two digits - date or year */
+ {
+ int n = ((rest[0]-'0')*10 +
+ (rest[1]-'0'));
+ /* If we don't have a date (day of the month) and we see a number
+ less than 32, then assume that is the date.
+
+ Otherwise, if we have a date and not a year, assume this is the
+ year. If it is less than 70, then assume it refers to the 21st
+ century. If it is two digits (>= 70), assume it refers to this
+ century. Otherwise, assume it refers to an unambiguous year.
+
+ The world will surely end soon.
+ */
+ if (date < 0 && n < 32)
+ date = n;
+ else if (year < 0)
+ {
+ if (n < 70)
+ year = 2000 + n;
+ else if (n < 100)
+ year = 1900 + n;
+ else
+ year = n;
+ }
+ /* else what the hell is this. */
+ }
+ else if ((end - rest) == 1) /* one digit - date */
+ date = (date < 0 ? (rest[0]-'0') : date);
+ /* else, three or more than five digits - what's that? */
+
+ break;
+ }
+ }
+
+ /* Skip to the end of this token, whether we parsed it or not.
+ Tokens are delimited by whitespace, or ,;-/
+ But explicitly not :+-.
+ */
+ while (*rest &&
+ *rest != ' ' && *rest != '\t' &&
+ *rest != ',' && *rest != ';' &&
+ *rest != '-' && *rest != '+' &&
+ *rest != '/' &&
+ *rest != '(' && *rest != ')' && *rest != '[' && *rest != ']')
+ rest++;
+ /* skip over uninteresting chars. */
+ SKIP_MORE:
+ while (*rest &&
+ (*rest == ' ' || *rest == '\t' ||
+ *rest == ',' || *rest == ';' || *rest == '/' ||
+ *rest == '(' || *rest == ')' || *rest == '[' || *rest == ']'))
+ rest++;
+
+ /* "-" is ignored at the beginning of a token if we have not yet
+ parsed a year (e.g., the second "-" in "30-AUG-1966"), or if
+ the character after the dash is not a digit. */
+ if (*rest == '-' && ((rest > string &&
+ isalpha((unsigned char)rest[-1]) && year < 0) ||
+ rest[1] < '0' || rest[1] > '9'))
+ {
+ rest++;
+ goto SKIP_MORE;
+ }
+
+ }
+
+ if (zone != TT_UNKNOWN && zone_offset == -1)
+ {
+ switch (zone)
+ {
+ case TT_PST: zone_offset = -8 * 60; break;
+ case TT_PDT: zone_offset = -8 * 60; dst_offset = 1 * 60; break;
+ case TT_MST: zone_offset = -7 * 60; break;
+ case TT_MDT: zone_offset = -7 * 60; dst_offset = 1 * 60; break;
+ case TT_CST: zone_offset = -6 * 60; break;
+ case TT_CDT: zone_offset = -6 * 60; dst_offset = 1 * 60; break;
+ case TT_EST: zone_offset = -5 * 60; break;
+ case TT_EDT: zone_offset = -5 * 60; dst_offset = 1 * 60; break;
+ case TT_AST: zone_offset = -4 * 60; break;
+ case TT_NST: zone_offset = -3 * 60 - 30; break;
+ case TT_GMT: zone_offset = 0 * 60; break;
+ case TT_BST: zone_offset = 0 * 60; dst_offset = 1 * 60; break;
+ case TT_MET: zone_offset = 1 * 60; break;
+ case TT_EET: zone_offset = 2 * 60; break;
+ case TT_JST: zone_offset = 9 * 60; break;
+ default:
+ PR_ASSERT (0);
+ break;
+ }
+ }
+
+ /* If we didn't find a year, month, or day-of-the-month, we can't
+ possibly parse this, and in fact, mktime() will do something random
+ (I'm seeing it return "Tue Feb 5 06:28:16 2036", which is no doubt
+ a numerologically significant date... */
+ if (month == TT_UNKNOWN || date == -1 || year == -1 || year > PR_INT16_MAX)
+ return PR_FAILURE;
+
+ memset(result, 0, sizeof(*result));
+ if (sec != -1)
+ result->tm_sec = sec;
+ if (min != -1)
+ result->tm_min = min;
+ if (hour != -1)
+ result->tm_hour = hour;
+ if (date != -1)
+ result->tm_mday = date;
+ if (month != TT_UNKNOWN)
+ result->tm_month = (((int)month) - ((int)TT_JAN));
+ if (year != -1)
+ result->tm_year = year;
+ if (dotw != TT_UNKNOWN)
+ result->tm_wday = (((int)dotw) - ((int)TT_SUN));
+ /*
+ * Mainly to compute wday and yday, but normalized time is also required
+ * by the check below that works around a Visual C++ 2005 mktime problem.
+ */
+ PR_NormalizeTime(result, PR_GMTParameters);
+ /* The remaining work is to set the gmt and dst offsets in tm_params. */
+
+ if (zone == TT_UNKNOWN && default_to_gmt)
+ {
+ /* No zone was specified, so pretend the zone was GMT. */
+ zone = TT_GMT;
+ zone_offset = 0;
+ }
+
+ if (zone_offset == -1)
+ {
+ /* no zone was specified, and we're to assume that everything
+ is local. */
+#if !defined(PRTIME_USE_BASE_TIME)
+ struct tm localTime;
+ time_t secs;
+#endif
+
+ PR_ASSERT(result->tm_month > -1 &&
+ result->tm_mday > 0 &&
+ result->tm_hour > -1 &&
+ result->tm_min > -1 &&
+ result->tm_sec > -1);
+
+#if defined(PRTIME_USE_BASE_TIME)
+ *result_imploded = BaseImplode(result, true /*is_local*/);
+ return PR_SUCCESS;
+#else // defined(PRTIME_USE_BASE_TIME)
+ /*
+ * To obtain time_t from a tm structure representing the local
+ * time, we call mktime(). However, we need to see if we are
+ * on 1-Jan-1970 or before. If we are, we can't call mktime()
+ * because mktime() will crash on win16. In that case, we
+ * calculate zone_offset based on the zone offset at
+ * 00:00:00, 2 Jan 1970 GMT, and subtract zone_offset from the
+ * date we are parsing to transform the date to GMT. We also
+ * do so if mktime() returns (time_t) -1 (time out of range).
+ */
+
+ /* month, day, hours, mins and secs are always non-negative
+ so we dont need to worry about them. */
+ if(result->tm_year >= 1970)
+ {
+ PRInt64 usec_per_sec;
+
+ localTime.tm_sec = result->tm_sec;
+ localTime.tm_min = result->tm_min;
+ localTime.tm_hour = result->tm_hour;
+ localTime.tm_mday = result->tm_mday;
+ localTime.tm_mon = result->tm_month;
+ localTime.tm_year = result->tm_year - 1900;
+ /* Set this to -1 to tell mktime "I don't care". If you set
+ it to 0 or 1, you are making assertions about whether the
+ date you are handing it is in daylight savings mode or not;
+ and if you're wrong, it will "fix" it for you. */
+ localTime.tm_isdst = -1;
+
+#if _MSC_VER == 1400 /* 1400 = Visual C++ 2005 (8.0) */
+ /*
+ * mktime will return (time_t) -1 if the input is a date
+ * after 23:59:59, December 31, 3000, US Pacific Time (not
+ * UTC as documented):
+ * http://msdn.microsoft.com/en-us/library/d1y53h2a(VS.80).aspx
+ * But if the year is 3001, mktime also invokes the invalid
+ * parameter handler, causing the application to crash. This
+ * problem has been reported in
+ * http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=266036.
+ * We avoid this crash by not calling mktime if the date is
+ * out of range. To use a simple test that works in any time
+ * zone, we consider year 3000 out of range as well. (See
+ * bug 480740.)
+ */
+ if (result->tm_year >= 3000) {
+ /* Emulate what mktime would have done. */
+ errno = EINVAL;
+ secs = (time_t) -1;
+ } else {
+ secs = mktime(&localTime);
+ }
+#else
+ secs = mktime(&localTime);
+#endif
+ if (secs != (time_t) -1)
+ {
+ PRTime usecs64;
+ LL_I2L(usecs64, secs);
+ LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
+ LL_MUL(usecs64, usecs64, usec_per_sec);
+ *result_imploded = usecs64;
+ return PR_SUCCESS;
+ }
+ }
+
+ /* So mktime() can't handle this case. We assume the
+ zone_offset for the date we are parsing is the same as
+ the zone offset on 00:00:00 2 Jan 1970 GMT. */
+ secs = 86400;
+ localtime_r(&secs, &localTime);
+ zone_offset = localTime.tm_min
+ + 60 * localTime.tm_hour
+ + 1440 * (localTime.tm_mday - 2);
+#endif // defined(PRTIME_USE_BASE_TIME)
+ }
+
+ result->tm_params.tp_gmt_offset = zone_offset * 60;
+ result->tm_params.tp_dst_offset = dst_offset * 60;
+
+ *result_imploded = PR_ImplodeTime(result);
+ return PR_SUCCESS;
+}
diff --git a/src/base/third_party/nspr/prtime.h b/src/base/third_party/nspr/prtime.h
new file mode 100644
index 0000000..ffbedec
--- /dev/null
+++ b/src/base/third_party/nspr/prtime.h
@@ -0,0 +1,238 @@
+/* Portions are Copyright (C) 2011 Google Inc */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * prtime.h --
+ *
+ * NSPR date and time functions
+ * CVS revision 3.10
+ * This file contains definitions of NSPR's basic types required by
+ * prtime.cc. These types have been copied over from the following NSPR
+ * files prtime.h, prtypes.h(CVS revision 3.35), prlong.h(CVS revision 3.13)
+ *
+ *---------------------------------------------------------------------------
+ */
+
+#ifndef BASE_PRTIME_H__
+#define BASE_PRTIME_H__
+
+#include "base/base_export.h"
+#include "base/third_party/nspr/prtypes.h"
+
+#define PR_ASSERT DCHECK
+
+#define LL_I2L(l, i) ((l) = (PRInt64)(i))
+#define LL_MUL(r, a, b) ((r) = (a) * (b))
+
+/**********************************************************************/
+/************************* TYPES AND CONSTANTS ************************/
+/**********************************************************************/
+
+#define PR_MSEC_PER_SEC 1000UL
+#define PR_USEC_PER_SEC 1000000UL
+#define PR_NSEC_PER_SEC 1000000000UL
+#define PR_USEC_PER_MSEC 1000UL
+#define PR_NSEC_PER_MSEC 1000000UL
+
+/*
+ * PRTime --
+ *
+ * NSPR represents basic time as 64-bit signed integers relative
+ * to midnight (00:00:00), January 1, 1970 Greenwich Mean Time (GMT).
+ * (GMT is also known as Coordinated Universal Time, UTC.)
+ * The units of time are in microseconds. Negative times are allowed
+ * to represent times prior to the January 1970 epoch. Such values are
+ * intended to be exported to other systems or converted to human
+ * readable form.
+ *
+ * Notes on porting: PRTime corresponds to time_t in ANSI C. NSPR 1.0
+ * simply uses PRInt64.
+ */
+
+typedef PRInt64 PRTime;
+
+/*
+ * Time zone and daylight saving time corrections applied to GMT to
+ * obtain the local time of some geographic location
+ */
+
+typedef struct PRTimeParameters {
+ PRInt32 tp_gmt_offset; /* the offset from GMT in seconds */
+ PRInt32 tp_dst_offset; /* contribution of DST in seconds */
+} PRTimeParameters;
+
+/*
+ * PRExplodedTime --
+ *
+ * Time broken down into human-readable components such as year, month,
+ * day, hour, minute, second, and microsecond. Time zone and daylight
+ * saving time corrections may be applied. If they are applied, the
+ * offsets from the GMT must be saved in the 'tm_params' field so that
+ * all the information is available to reconstruct GMT.
+ *
+ * Notes on porting: PRExplodedTime corrresponds to struct tm in
+ * ANSI C, with the following differences:
+ * - an additional field tm_usec;
+ * - replacing tm_isdst by tm_params;
+ * - the month field is spelled tm_month, not tm_mon;
+ * - we use absolute year, AD, not the year since 1900.
+ * The corresponding type in NSPR 1.0 is called PRTime. Below is
+ * a table of date/time type correspondence in the three APIs:
+ * API time since epoch time in components
+ * ANSI C time_t struct tm
+ * NSPR 1.0 PRInt64 PRTime
+ * NSPR 2.0 PRTime PRExplodedTime
+ */
+
+typedef struct PRExplodedTime {
+ PRInt32 tm_usec; /* microseconds past tm_sec (0-99999) */
+ PRInt32 tm_sec; /* seconds past tm_min (0-61, accomodating
+ up to two leap seconds) */
+ PRInt32 tm_min; /* minutes past tm_hour (0-59) */
+ PRInt32 tm_hour; /* hours past tm_day (0-23) */
+ PRInt32 tm_mday; /* days past tm_mon (1-31, note that it
+ starts from 1) */
+ PRInt32 tm_month; /* months past tm_year (0-11, Jan = 0) */
+ PRInt16 tm_year; /* absolute year, AD (note that we do not
+ count from 1900) */
+
+ PRInt8 tm_wday; /* calculated day of the week
+ (0-6, Sun = 0) */
+ PRInt16 tm_yday; /* calculated day of the year
+ (0-365, Jan 1 = 0) */
+
+ PRTimeParameters tm_params; /* time parameters used by conversion */
+} PRExplodedTime;
+
+/*
+ * PRTimeParamFn --
+ *
+ * A function of PRTimeParamFn type returns the time zone and
+ * daylight saving time corrections for some geographic location,
+ * given the current time in GMT. The input argument gmt should
+ * point to a PRExplodedTime that is in GMT, i.e., whose
+ * tm_params contains all 0's.
+ *
+ * For any time zone other than GMT, the computation is intended to
+ * consist of two steps:
+ * - Figure out the time zone correction, tp_gmt_offset. This number
+ * usually depends on the geographic location only. But it may
+ * also depend on the current time. For example, all of China
+ * is one time zone right now. But this situation may change
+ * in the future.
+ * - Figure out the daylight saving time correction, tp_dst_offset.
+ * This number depends on both the geographic location and the
+ * current time. Most of the DST rules are expressed in local
+ * current time. If so, one should apply the time zone correction
+ * to GMT before applying the DST rules.
+ */
+
+typedef PRTimeParameters (PR_CALLBACK *PRTimeParamFn)(const PRExplodedTime *gmt);
+
+/**********************************************************************/
+/****************************** FUNCTIONS *****************************/
+/**********************************************************************/
+
+NSPR_API(PRTime)
+PR_ImplodeTime(const PRExplodedTime *exploded);
+
+/*
+ * Adjust exploded time to normalize field overflows after manipulation.
+ * Note that the following fields of PRExplodedTime should not be
+ * manipulated:
+ * - tm_month and tm_year: because the number of days in a month and
+ * number of days in a year are not constant, it is ambiguous to
+ * manipulate the month and year fields, although one may be tempted
+ * to. For example, what does "a month from January 31st" mean?
+ * - tm_wday and tm_yday: these fields are calculated by NSPR. Users
+ * should treat them as "read-only".
+ */
+
+NSPR_API(void) PR_NormalizeTime(
+ PRExplodedTime *exploded, PRTimeParamFn params);
+
+/**********************************************************************/
+/*********************** TIME PARAMETER FUNCTIONS *********************/
+/**********************************************************************/
+
+/* Time parameters that represent Greenwich Mean Time */
+NSPR_API(PRTimeParameters) PR_GMTParameters(const PRExplodedTime *gmt);
+
+/*
+ * This parses a time/date string into a PRTime
+ * (microseconds after "1-Jan-1970 00:00:00 GMT").
+ * It returns PR_SUCCESS on success, and PR_FAILURE
+ * if the time/date string can't be parsed.
+ *
+ * Many formats are handled, including:
+ *
+ * 14 Apr 89 03:20:12
+ * 14 Apr 89 03:20 GMT
+ * Fri, 17 Mar 89 4:01:33
+ * Fri, 17 Mar 89 4:01 GMT
+ * Mon Jan 16 16:12 PDT 1989
+ * Mon Jan 16 16:12 +0130 1989
+ * 6 May 1992 16:41-JST (Wednesday)
+ * 22-AUG-1993 10:59:12.82
+ * 22-AUG-1993 10:59pm
+ * 22-AUG-1993 12:59am
+ * 22-AUG-1993 12:59 PM
+ * Friday, August 04, 1995 3:54 PM
+ * 06/21/95 04:24:34 PM
+ * 20/06/95 21:07
+ * 95-06-08 19:32:48 EDT
+ *
+ * If the input string doesn't contain a description of the timezone,
+ * we consult the `default_to_gmt' to decide whether the string should
+ * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE).
+ * The correct value for this argument depends on what standard specified
+ * the time string which you are parsing.
+ */
+
+/*
+ * This is the only funtion that should be called from outside base, and only
+ * from the unit test.
+ */
+
+BASE_EXPORT PRStatus PR_ParseTimeString (
+ const char *string,
+ PRBool default_to_gmt,
+ PRTime *result);
+
+#endif // BASE_PRTIME_H__
diff --git a/src/base/third_party/nspr/prtypes.h b/src/base/third_party/nspr/prtypes.h
new file mode 100644
index 0000000..630df81
--- /dev/null
+++ b/src/base/third_party/nspr/prtypes.h
@@ -0,0 +1,558 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+** File: prtypes.h
+** Description: Definitions of NSPR's basic types
+**
+** Prototypes and macros used to make up for deficiencies that we have found
+** in ANSI environments.
+**
+** Since we do not wrap <stdlib.h> and all the other standard headers, authors
+** of portable code will not know in general that they need these definitions.
+** Instead of requiring these authors to find the dependent uses in their code
+** and take the following steps only in those C files, we take steps once here
+** for all C files.
+**/
+
+#ifndef prtypes_h___
+#define prtypes_h___
+
+#ifdef MDCPUCFG
+#include MDCPUCFG
+#else
+#include "base/third_party/nspr/prcpucfg.h"
+#endif
+
+#include <stddef.h>
+
+/***********************************************************************
+** MACROS: PR_EXTERN
+** PR_IMPLEMENT
+** DESCRIPTION:
+** These are only for externally visible routines and globals. For
+** internal routines, just use "extern" for type checking and that
+** will not export internal cross-file or forward-declared symbols.
+** Define a macro for declaring procedures return types. We use this to
+** deal with windoze specific type hackery for DLL definitions. Use
+** PR_EXTERN when the prototype for the method is declared. Use
+** PR_IMPLEMENT for the implementation of the method.
+**
+** Example:
+** in dowhim.h
+** PR_EXTERN( void ) DoWhatIMean( void );
+** in dowhim.c
+** PR_IMPLEMENT( void ) DoWhatIMean( void ) { return; }
+**
+**
+***********************************************************************/
+#if 1
+
+/*
+** Local change: the portions of NSPR used by the base module are
+** implementation details. NSPR symbols do not need to be exported beyond
+** the base module. For all platforms, avoid decorating functions with
+** specific visibility and access keywords.
+*/
+
+#define PR_EXPORT(__type) extern __type
+#define PR_EXPORT_DATA(__type) extern __type
+#define PR_IMPORT(__type) extern __type
+#define PR_IMPORT_DATA(__type) extern __type
+
+#define PR_EXTERN(__type) extern __type
+#define PR_IMPLEMENT(__type) __type
+#define PR_EXTERN_DATA(__type) extern __type
+#define PR_IMPLEMENT_DATA(__type) __type
+
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#elif defined(WIN32)
+
+#define PR_EXPORT(__type) extern __declspec(dllexport) __type
+#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPORT(__type) __declspec(dllimport) __type
+#define PR_IMPORT_DATA(__type) __declspec(dllimport) __type
+
+#define PR_EXTERN(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT(__type) __declspec(dllexport) __type
+#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type
+
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#elif defined(XP_BEOS)
+
+#define PR_EXPORT(__type) extern __declspec(dllexport) __type
+#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPORT(__type) extern __declspec(dllexport) __type
+#define PR_IMPORT_DATA(__type) extern __declspec(dllexport) __type
+
+#define PR_EXTERN(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT(__type) __declspec(dllexport) __type
+#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type
+
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#elif defined(XP_OS2) && defined(__declspec)
+
+#define PR_EXPORT(__type) extern __declspec(dllexport) __type
+#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPORT(__type) extern __declspec(dllimport) __type
+#define PR_IMPORT_DATA(__type) extern __declspec(dllimport) __type
+
+#define PR_EXTERN(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT(__type) __declspec(dllexport) __type
+#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type
+
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#elif defined(SYMBIAN)
+
+#define PR_EXPORT(__type) extern __declspec(dllexport) __type
+#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type
+#ifdef __WINS__
+#define PR_IMPORT(__type) extern __declspec(dllexport) __type
+#define PR_IMPORT_DATA(__type) extern __declspec(dllexport) __type
+#else
+#define PR_IMPORT(__type) extern __declspec(dllimport) __type
+#define PR_IMPORT_DATA(__type) extern __declspec(dllimport) __type
+#endif
+
+#define PR_EXTERN(__type) extern __type
+#define PR_IMPLEMENT(__type) __type
+#define PR_EXTERN_DATA(__type) extern __type
+#define PR_IMPLEMENT_DATA(__type) __type
+
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#else /* Unix */
+
+/* GCC 3.3 and later support the visibility attribute. */
+#if (__GNUC__ >= 4) || \
+ (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
+#define PR_VISIBILITY_DEFAULT __attribute__((visibility("default")))
+#else
+#define PR_VISIBILITY_DEFAULT
+#endif
+
+#define PR_EXPORT(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_EXPORT_DATA(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_IMPORT(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_IMPORT_DATA(__type) extern PR_VISIBILITY_DEFAULT __type
+
+#define PR_EXTERN(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_IMPLEMENT(__type) PR_VISIBILITY_DEFAULT __type
+#define PR_EXTERN_DATA(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_IMPLEMENT_DATA(__type) PR_VISIBILITY_DEFAULT __type
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#endif
+
+#if defined(_NSPR_BUILD_)
+#define NSPR_API(__type) PR_EXPORT(__type)
+#define NSPR_DATA_API(__type) PR_EXPORT_DATA(__type)
+#else
+#define NSPR_API(__type) PR_IMPORT(__type)
+#define NSPR_DATA_API(__type) PR_IMPORT_DATA(__type)
+#endif
+
+/***********************************************************************
+** MACROS: PR_BEGIN_MACRO
+** PR_END_MACRO
+** DESCRIPTION:
+** Macro body brackets so that macros with compound statement definitions
+** behave syntactically more like functions when called.
+***********************************************************************/
+#define PR_BEGIN_MACRO do {
+#define PR_END_MACRO } while (0)
+
+/***********************************************************************
+** MACROS: PR_BEGIN_EXTERN_C
+** PR_END_EXTERN_C
+** DESCRIPTION:
+** Macro shorthands for conditional C++ extern block delimiters.
+***********************************************************************/
+#ifdef __cplusplus
+#define PR_BEGIN_EXTERN_C extern "C" {
+#define PR_END_EXTERN_C }
+#else
+#define PR_BEGIN_EXTERN_C
+#define PR_END_EXTERN_C
+#endif
+
+/***********************************************************************
+** MACROS: PR_BIT
+** PR_BITMASK
+** DESCRIPTION:
+** Bit masking macros. XXX n must be <= 31 to be portable
+***********************************************************************/
+#define PR_BIT(n) ((PRUint32)1 << (n))
+#define PR_BITMASK(n) (PR_BIT(n) - 1)
+
+/***********************************************************************
+** MACROS: PR_ROUNDUP
+** PR_MIN
+** PR_MAX
+** PR_ABS
+** DESCRIPTION:
+** Commonly used macros for operations on compatible types.
+***********************************************************************/
+#define PR_ROUNDUP(x,y) ((((x)+((y)-1))/(y))*(y))
+#define PR_MIN(x,y) ((x)<(y)?(x):(y))
+#define PR_MAX(x,y) ((x)>(y)?(x):(y))
+#define PR_ABS(x) ((x)<0?-(x):(x))
+
+PR_BEGIN_EXTERN_C
+
+/************************************************************************
+** TYPES: PRUint8
+** PRInt8
+** DESCRIPTION:
+** The int8 types are known to be 8 bits each. There is no type that
+** is equivalent to a plain "char".
+************************************************************************/
+#if PR_BYTES_PER_BYTE == 1
+typedef unsigned char PRUint8;
+/*
+** Some cfront-based C++ compilers do not like 'signed char' and
+** issue the warning message:
+** warning: "signed" not implemented (ignored)
+** For these compilers, we have to define PRInt8 as plain 'char'.
+** Make sure that plain 'char' is indeed signed under these compilers.
+*/
+#if (defined(HPUX) && defined(__cplusplus) \
+ && !defined(__GNUC__) && __cplusplus < 199707L) \
+ || (defined(SCO) && defined(__cplusplus) \
+ && !defined(__GNUC__) && __cplusplus == 1L)
+typedef char PRInt8;
+#else
+typedef signed char PRInt8;
+#endif
+#else
+#error No suitable type for PRInt8/PRUint8
+#endif
+
+/************************************************************************
+ * MACROS: PR_INT8_MAX
+ * PR_INT8_MIN
+ * PR_UINT8_MAX
+ * DESCRIPTION:
+ * The maximum and minimum values of a PRInt8 or PRUint8.
+************************************************************************/
+
+#define PR_INT8_MAX 127
+#define PR_INT8_MIN (-128)
+#define PR_UINT8_MAX 255U
+
+/************************************************************************
+** TYPES: PRUint16
+** PRInt16
+** DESCRIPTION:
+** The int16 types are known to be 16 bits each.
+************************************************************************/
+#if PR_BYTES_PER_SHORT == 2
+typedef unsigned short PRUint16;
+typedef short PRInt16;
+#else
+#error No suitable type for PRInt16/PRUint16
+#endif
+
+/************************************************************************
+ * MACROS: PR_INT16_MAX
+ * PR_INT16_MIN
+ * PR_UINT16_MAX
+ * DESCRIPTION:
+ * The maximum and minimum values of a PRInt16 or PRUint16.
+************************************************************************/
+
+#define PR_INT16_MAX 32767
+#define PR_INT16_MIN (-32768)
+#define PR_UINT16_MAX 65535U
+
+/************************************************************************
+** TYPES: PRUint32
+** PRInt32
+** DESCRIPTION:
+** The int32 types are known to be 32 bits each.
+************************************************************************/
+#if PR_BYTES_PER_INT == 4
+typedef unsigned int PRUint32;
+typedef int PRInt32;
+#define PR_INT32(x) x
+#define PR_UINT32(x) x ## U
+#elif PR_BYTES_PER_LONG == 4
+typedef unsigned long PRUint32;
+typedef long PRInt32;
+#define PR_INT32(x) x ## L
+#define PR_UINT32(x) x ## UL
+#else
+#error No suitable type for PRInt32/PRUint32
+#endif
+
+/************************************************************************
+ * MACROS: PR_INT32_MAX
+ * PR_INT32_MIN
+ * PR_UINT32_MAX
+ * DESCRIPTION:
+ * The maximum and minimum values of a PRInt32 or PRUint32.
+************************************************************************/
+
+#define PR_INT32_MAX PR_INT32(2147483647)
+#define PR_INT32_MIN (-PR_INT32_MAX - 1)
+#define PR_UINT32_MAX PR_UINT32(4294967295)
+
+/************************************************************************
+** TYPES: PRUint64
+** PRInt64
+** DESCRIPTION:
+** The int64 types are known to be 64 bits each. Care must be used when
+** declaring variables of type PRUint64 or PRInt64. Different hardware
+** architectures and even different compilers have varying support for
+** 64 bit values. The only guaranteed portability requires the use of
+** the LL_ macros (see prlong.h).
+************************************************************************/
+#ifdef HAVE_LONG_LONG
+/* Keep this in sync with prlong.h. */
+/*
+ * On 64-bit Mac OS X, uint64 needs to be defined as unsigned long long to
+ * match uint64_t, otherwise our uint64 typedef conflicts with the uint64
+ * typedef in cssmconfig.h, which CoreServices.h includes indirectly.
+ */
+#if PR_BYTES_PER_LONG == 8 && !defined(__APPLE__)
+typedef long PRInt64;
+typedef unsigned long PRUint64;
+#elif defined(WIN32) && !defined(__GNUC__)
+typedef __int64 PRInt64;
+typedef unsigned __int64 PRUint64;
+#else
+typedef long long PRInt64;
+typedef unsigned long long PRUint64;
+#endif /* PR_BYTES_PER_LONG == 8 */
+#else /* !HAVE_LONG_LONG */
+typedef struct {
+#ifdef IS_LITTLE_ENDIAN
+ PRUint32 lo, hi;
+#else
+ PRUint32 hi, lo;
+#endif
+} PRInt64;
+typedef PRInt64 PRUint64;
+#endif /* !HAVE_LONG_LONG */
+
+/************************************************************************
+** TYPES: PRUintn
+** PRIntn
+** DESCRIPTION:
+** The PRIntn types are most appropriate for automatic variables. They are
+** guaranteed to be at least 16 bits, though various architectures may
+** define them to be wider (e.g., 32 or even 64 bits). These types are
+** never valid for fields of a structure.
+************************************************************************/
+#if PR_BYTES_PER_INT >= 2
+typedef int PRIntn;
+typedef unsigned int PRUintn;
+#else
+#error 'sizeof(int)' not sufficient for platform use
+#endif
+
+/************************************************************************
+** TYPES: PRFloat64
+** DESCRIPTION:
+** NSPR's floating point type is always 64 bits.
+************************************************************************/
+typedef double PRFloat64;
+
+/************************************************************************
+** TYPES: PRSize
+** DESCRIPTION:
+** A type for representing the size of objects.
+************************************************************************/
+typedef size_t PRSize;
+
+
+/************************************************************************
+** TYPES: PROffset32, PROffset64
+** DESCRIPTION:
+** A type for representing byte offsets from some location.
+************************************************************************/
+typedef PRInt32 PROffset32;
+typedef PRInt64 PROffset64;
+
+/************************************************************************
+** TYPES: PRPtrDiff
+** DESCRIPTION:
+** A type for pointer difference. Variables of this type are suitable
+** for storing a pointer or pointer subtraction.
+************************************************************************/
+typedef ptrdiff_t PRPtrdiff;
+
+/************************************************************************
+** TYPES: PRUptrdiff
+** DESCRIPTION:
+** A type for pointer difference. Variables of this type are suitable
+** for storing a pointer or pointer sutraction.
+************************************************************************/
+#ifdef _WIN64
+typedef PRUint64 PRUptrdiff;
+#else
+typedef unsigned long PRUptrdiff;
+#endif
+
+/************************************************************************
+** TYPES: PRBool
+** DESCRIPTION:
+** Use PRBool for variables and parameter types. Use PR_FALSE and PR_TRUE
+** for clarity of target type in assignments and actual arguments. Use
+** 'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans
+** just as you would C int-valued conditions.
+************************************************************************/
+typedef PRIntn PRBool;
+#define PR_TRUE 1
+#define PR_FALSE 0
+
+/************************************************************************
+** TYPES: PRPackedBool
+** DESCRIPTION:
+** Use PRPackedBool within structs where bitfields are not desirable
+** but minimum and consistant overhead matters.
+************************************************************************/
+typedef PRUint8 PRPackedBool;
+
+/*
+** Status code used by some routines that have a single point of failure or
+** special status return.
+*/
+typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus;
+
+#ifndef __PRUNICHAR__
+#define __PRUNICHAR__
+#ifdef WIN32
+typedef wchar_t PRUnichar;
+#else
+typedef PRUint16 PRUnichar;
+#endif
+#endif
+
+/*
+** WARNING: The undocumented data types PRWord and PRUword are
+** only used in the garbage collection and arena code. Do not
+** use PRWord and PRUword in new code.
+**
+** A PRWord is an integer that is the same size as a void*.
+** It implements the notion of a "word" in the Java Virtual
+** Machine. (See Sec. 3.4 "Words", The Java Virtual Machine
+** Specification, Addison-Wesley, September 1996.
+** http://java.sun.com/docs/books/vmspec/index.html.)
+*/
+#ifdef _WIN64
+typedef PRInt64 PRWord;
+typedef PRUint64 PRUword;
+#else
+typedef long PRWord;
+typedef unsigned long PRUword;
+#endif
+
+#if defined(NO_NSPR_10_SUPPORT)
+#else
+/********* ???????????????? FIX ME ??????????????????????????? *****/
+/********************** Some old definitions until pr=>ds transition is done ***/
+/********************** Also, we are still using NSPR 1.0. GC ******************/
+/*
+** Fundamental NSPR macros, used nearly everywhere.
+*/
+
+#define PR_PUBLIC_API PR_IMPLEMENT
+
+/*
+** Macro body brackets so that macros with compound statement definitions
+** behave syntactically more like functions when called.
+*/
+#define NSPR_BEGIN_MACRO do {
+#define NSPR_END_MACRO } while (0)
+
+/*
+** Macro shorthands for conditional C++ extern block delimiters.
+*/
+#ifdef NSPR_BEGIN_EXTERN_C
+#undef NSPR_BEGIN_EXTERN_C
+#endif
+#ifdef NSPR_END_EXTERN_C
+#undef NSPR_END_EXTERN_C
+#endif
+
+#ifdef __cplusplus
+#define NSPR_BEGIN_EXTERN_C extern "C" {
+#define NSPR_END_EXTERN_C }
+#else
+#define NSPR_BEGIN_EXTERN_C
+#define NSPR_END_EXTERN_C
+#endif
+
+/********* ????????????? End Fix me ?????????????????????????????? *****/
+#endif /* NO_NSPR_10_SUPPORT */
+
+/*
+** Compile-time assert. "condition" must be a constant expression.
+** The macro can be used only in places where an "extern" declaration is
+** allowed.
+*/
+#define PR_STATIC_ASSERT(condition) \
+ extern void pr_static_assert(int arg[(condition) ? 1 : -1])
+
+PR_END_EXTERN_C
+
+#if !defined(NO_NSPR_10_SUPPORT)
+#include "base/basictypes.h"
+#endif
+
+#endif /* prtypes_h___ */
+
diff --git a/src/base/third_party/symbolize/LICENSE b/src/base/third_party/symbolize/LICENSE
new file mode 100644
index 0000000..433a3d1
--- /dev/null
+++ b/src/base/third_party/symbolize/LICENSE
@@ -0,0 +1,28 @@
+// Copyright (c) 2006, Google 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:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 THE COPYRIGHT
+// OWNER 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.
diff --git a/src/base/third_party/symbolize/README.chromium b/src/base/third_party/symbolize/README.chromium
new file mode 100644
index 0000000..b86b4e0
--- /dev/null
+++ b/src/base/third_party/symbolize/README.chromium
@@ -0,0 +1,18 @@
+Name: google-glog's symbolization library
+URL: http://code.google.com/p/google-glog/
+License: BSD
+
+The following files are copied AS-IS from:
+http://code.google.com/p/google-glog/source/browse/#svn/trunk/src (r76)
+
+- demangle.cc
+- demangle.h
+- symbolize.cc
+- symbolize.h
+
+The following files are minimal stubs created for use in Chromium:
+
+- config.h
+- glog/logging.h
+- glog/raw_logging.h
+- utilities.h
diff --git a/src/base/third_party/symbolize/config.h b/src/base/third_party/symbolize/config.h
new file mode 100644
index 0000000..945f5a6
--- /dev/null
+++ b/src/base/third_party/symbolize/config.h
@@ -0,0 +1,7 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#define GOOGLE_NAMESPACE google
+#define _END_GOOGLE_NAMESPACE_ }
+#define _START_GOOGLE_NAMESPACE_ namespace google {
diff --git a/src/base/third_party/symbolize/demangle.cc b/src/base/third_party/symbolize/demangle.cc
new file mode 100644
index 0000000..46556bf
--- /dev/null
+++ b/src/base/third_party/symbolize/demangle.cc
@@ -0,0 +1,1231 @@
+// Copyright (c) 2006, Google 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:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 THE COPYRIGHT
+// OWNER 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.
+//
+// Author: Satoru Takabayashi
+
+#include <stdio.h> // for NULL
+#include "demangle.h"
+
+_START_GOOGLE_NAMESPACE_
+
+typedef struct {
+ const char *abbrev;
+ const char *real_name;
+} AbbrevPair;
+
+// List of operators from Itanium C++ ABI.
+static const AbbrevPair kOperatorList[] = {
+ { "nw", "new" },
+ { "na", "new[]" },
+ { "dl", "delete" },
+ { "da", "delete[]" },
+ { "ps", "+" },
+ { "ng", "-" },
+ { "ad", "&" },
+ { "de", "*" },
+ { "co", "~" },
+ { "pl", "+" },
+ { "mi", "-" },
+ { "ml", "*" },
+ { "dv", "/" },
+ { "rm", "%" },
+ { "an", "&" },
+ { "or", "|" },
+ { "eo", "^" },
+ { "aS", "=" },
+ { "pL", "+=" },
+ { "mI", "-=" },
+ { "mL", "*=" },
+ { "dV", "/=" },
+ { "rM", "%=" },
+ { "aN", "&=" },
+ { "oR", "|=" },
+ { "eO", "^=" },
+ { "ls", "<<" },
+ { "rs", ">>" },
+ { "lS", "<<=" },
+ { "rS", ">>=" },
+ { "eq", "==" },
+ { "ne", "!=" },
+ { "lt", "<" },
+ { "gt", ">" },
+ { "le", "<=" },
+ { "ge", ">=" },
+ { "nt", "!" },
+ { "aa", "&&" },
+ { "oo", "||" },
+ { "pp", "++" },
+ { "mm", "--" },
+ { "cm", "," },
+ { "pm", "->*" },
+ { "pt", "->" },
+ { "cl", "()" },
+ { "ix", "[]" },
+ { "qu", "?" },
+ { "st", "sizeof" },
+ { "sz", "sizeof" },
+ { NULL, NULL },
+};
+
+// List of builtin types from Itanium C++ ABI.
+static const AbbrevPair kBuiltinTypeList[] = {
+ { "v", "void" },
+ { "w", "wchar_t" },
+ { "b", "bool" },
+ { "c", "char" },
+ { "a", "signed char" },
+ { "h", "unsigned char" },
+ { "s", "short" },
+ { "t", "unsigned short" },
+ { "i", "int" },
+ { "j", "unsigned int" },
+ { "l", "long" },
+ { "m", "unsigned long" },
+ { "x", "long long" },
+ { "y", "unsigned long long" },
+ { "n", "__int128" },
+ { "o", "unsigned __int128" },
+ { "f", "float" },
+ { "d", "double" },
+ { "e", "long double" },
+ { "g", "__float128" },
+ { "z", "ellipsis" },
+ { NULL, NULL }
+};
+
+// List of substitutions Itanium C++ ABI.
+static const AbbrevPair kSubstitutionList[] = {
+ { "St", "" },
+ { "Sa", "allocator" },
+ { "Sb", "basic_string" },
+ // std::basic_string<char, std::char_traits<char>,std::allocator<char> >
+ { "Ss", "string"},
+ // std::basic_istream<char, std::char_traits<char> >
+ { "Si", "istream" },
+ // std::basic_ostream<char, std::char_traits<char> >
+ { "So", "ostream" },
+ // std::basic_iostream<char, std::char_traits<char> >
+ { "Sd", "iostream" },
+ { NULL, NULL }
+};
+
+// State needed for demangling.
+typedef struct {
+ const char *mangled_cur; // Cursor of mangled name.
+ const char *mangled_end; // End of mangled name.
+ char *out_cur; // Cursor of output string.
+ const char *out_begin; // Beginning of output string.
+ const char *out_end; // End of output string.
+ const char *prev_name; // For constructors/destructors.
+ int prev_name_length; // For constructors/destructors.
+ int nest_level; // For nested names.
+ int number; // Remember the previous number.
+ bool append; // Append flag.
+ bool overflowed; // True if output gets overflowed.
+} State;
+
+// We don't use strlen() in libc since it's not guaranteed to be async
+// signal safe.
+static size_t StrLen(const char *str) {
+ size_t len = 0;
+ while (*str != '\0') {
+ ++str;
+ ++len;
+ }
+ return len;
+}
+
+// Returns true if "str" has "prefix" as a prefix.
+static bool StrPrefix(const char *str, const char *prefix) {
+ size_t i = 0;
+ while (str[i] != '\0' && prefix[i] != '\0' &&
+ str[i] == prefix[i]) {
+ ++i;
+ }
+ return prefix[i] == '\0'; // Consumed everything in "prefix".
+}
+
+static void InitState(State *state, const char *mangled,
+ char *out, int out_size) {
+ state->mangled_cur = mangled;
+ state->mangled_end = mangled + StrLen(mangled);
+ state->out_cur = out;
+ state->out_begin = out;
+ state->out_end = out + out_size;
+ state->prev_name = NULL;
+ state->prev_name_length = -1;
+ state->nest_level = -1;
+ state->number = -1;
+ state->append = true;
+ state->overflowed = false;
+}
+
+// Calculates the remaining length of the mangled name.
+static int RemainingLength(State *state) {
+ return state->mangled_end - state->mangled_cur;
+}
+
+// Returns true and advances "mangled_cur" if we find "c" at
+// "mangled_cur" position.
+static bool ParseChar(State *state, const char c) {
+ if (RemainingLength(state) >= 1 && *state->mangled_cur == c) {
+ ++state->mangled_cur;
+ return true;
+ }
+ return false;
+}
+
+// Returns true and advances "mangled_cur" if we find "two_chars" at
+// "mangled_cur" position.
+static bool ParseTwoChar(State *state, const char *two_chars) {
+ if (RemainingLength(state) >= 2 &&
+ state->mangled_cur[0] == two_chars[0] &&
+ state->mangled_cur[1] == two_chars[1]) {
+ state->mangled_cur += 2;
+ return true;
+ }
+ return false;
+}
+
+// Returns true and advances "mangled_cur" if we find any character in
+// "char_class" at "mangled_cur" position.
+static bool ParseCharClass(State *state, const char *char_class) {
+ if (state->mangled_cur == state->mangled_end) {
+ return false;
+ }
+ const char *p = char_class;
+ for (; *p != '\0'; ++p) {
+ if (*state->mangled_cur == *p) {
+ state->mangled_cur += 1;
+ return true;
+ }
+ }
+ return false;
+}
+
+// This function is used for handling an optional non-terminal.
+static bool Optional(bool status) {
+ return true;
+}
+
+// This function is used for handling <non-terminal>+ syntax.
+typedef bool (*ParseFunc)(State *);
+static bool OneOrMore(ParseFunc parse_func, State *state) {
+ if (parse_func(state)) {
+ while (parse_func(state)) {
+ }
+ return true;
+ }
+ return false;
+}
+
+// Append "str" at "out_cur". If there is an overflow, "overflowed"
+// is set to true for later use. The output string is ensured to
+// always terminate with '\0' as long as there is no overflow.
+static void Append(State *state, const char * const str, const int length) {
+ int i;
+ for (i = 0; i < length; ++i) {
+ if (state->out_cur + 1 < state->out_end) { // +1 for '\0'
+ *state->out_cur = str[i];
+ ++state->out_cur;
+ } else {
+ state->overflowed = true;
+ break;
+ }
+ }
+ if (!state->overflowed) {
+ *state->out_cur = '\0'; // Terminate it with '\0'
+ }
+}
+
+// We don't use equivalents in libc to avoid locale issues.
+static bool IsLower(char c) {
+ return c >= 'a' && c <= 'z';
+}
+
+static bool IsAlpha(char c) {
+ return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
+}
+
+// Append "str" with some tweaks, iff "append" state is true.
+// Returns true so that it can be placed in "if" conditions.
+static void MaybeAppendWithLength(State *state, const char * const str,
+ const int length) {
+ if (state->append && length > 0) {
+ // Append a space if the output buffer ends with '<' and "str"
+ // starts with '<' to avoid <<<.
+ if (str[0] == '<' && state->out_begin < state->out_cur &&
+ state->out_cur[-1] == '<') {
+ Append(state, " ", 1);
+ }
+ // Remember the last identifier name for ctors/dtors.
+ if (IsAlpha(str[0]) || str[0] == '_') {
+ state->prev_name = state->out_cur;
+ state->prev_name_length = length;
+ }
+ Append(state, str, length);
+ }
+}
+
+// A convenient wrapper arount MaybeAppendWithLength().
+static bool MaybeAppend(State *state, const char * const str) {
+ if (state->append) {
+ int length = StrLen(str);
+ MaybeAppendWithLength(state, str, length);
+ }
+ return true;
+}
+
+// This function is used for handling nested names.
+static bool EnterNestedName(State *state) {
+ state->nest_level = 0;
+ return true;
+}
+
+// This function is used for handling nested names.
+static bool LeaveNestedName(State *state, int prev_value) {
+ state->nest_level = prev_value;
+ return true;
+}
+
+// Disable the append mode not to print function parameters, etc.
+static bool DisableAppend(State *state) {
+ state->append = false;
+ return true;
+}
+
+// Restore the append mode to the previous state.
+static bool RestoreAppend(State *state, bool prev_value) {
+ state->append = prev_value;
+ return true;
+}
+
+// Increase the nest level for nested names.
+static void MaybeIncreaseNestLevel(State *state) {
+ if (state->nest_level > -1) {
+ ++state->nest_level;
+ }
+}
+
+// Appends :: for nested names if necessary.
+static void MaybeAppendSeparator(State *state) {
+ if (state->nest_level >= 1) {
+ MaybeAppend(state, "::");
+ }
+}
+
+// Cancel the last separator if necessary.
+static void MaybeCancelLastSeparator(State *state) {
+ if (state->nest_level >= 1 && state->append &&
+ state->out_begin <= state->out_cur - 2) {
+ state->out_cur -= 2;
+ *state->out_cur = '\0';
+ }
+}
+
+// Returns true if identifier pointed by "mangled_cur" is anonymous
+// namespace.
+static bool IdentifierIsAnonymousNamespace(State *state) {
+ const char anon_prefix[] = "_GLOBAL__N_";
+ return (state->number > sizeof(anon_prefix) - 1 && // Should be longer.
+ StrPrefix(state->mangled_cur, anon_prefix));
+}
+
+// Forward declarations of our parsing functions.
+static bool ParseMangledName(State *state);
+static bool ParseEncoding(State *state);
+static bool ParseName(State *state);
+static bool ParseUnscopedName(State *state);
+static bool ParseUnscopedTemplateName(State *state);
+static bool ParseNestedName(State *state);
+static bool ParsePrefix(State *state);
+static bool ParseUnqualifiedName(State *state);
+static bool ParseSourceName(State *state);
+static bool ParseLocalSourceName(State *state);
+static bool ParseNumber(State *state);
+static bool ParseFloatNumber(State *state);
+static bool ParseSeqId(State *state);
+static bool ParseIdentifier(State *state);
+static bool ParseOperatorName(State *state);
+static bool ParseSpecialName(State *state);
+static bool ParseCallOffset(State *state);
+static bool ParseNVOffset(State *state);
+static bool ParseVOffset(State *state);
+static bool ParseCtorDtorName(State *state);
+static bool ParseType(State *state);
+static bool ParseCVQualifiers(State *state);
+static bool ParseBuiltinType(State *state);
+static bool ParseFunctionType(State *state);
+static bool ParseBareFunctionType(State *state);
+static bool ParseClassEnumType(State *state);
+static bool ParseArrayType(State *state);
+static bool ParsePointerToMemberType(State *state);
+static bool ParseTemplateParam(State *state);
+static bool ParseTemplateTemplateParam(State *state);
+static bool ParseTemplateArgs(State *state);
+static bool ParseTemplateArg(State *state);
+static bool ParseExpression(State *state);
+static bool ParseExprPrimary(State *state);
+static bool ParseLocalName(State *state);
+static bool ParseDiscriminator(State *state);
+static bool ParseSubstitution(State *state);
+
+// Implementation note: the following code is a straightforward
+// translation of the Itanium C++ ABI defined in BNF with a couple of
+// exceptions.
+//
+// - Support GNU extensions not defined in the Itanium C++ ABI
+// - <prefix> and <template-prefix> are combined to avoid infinite loop
+// - Reorder patterns to shorten the code
+// - Reorder patterns to give greedier functions precedence
+// We'll mark "Less greedy than" for these cases in the code
+//
+// Each parsing function changes the state and returns true on
+// success. Otherwise, don't change the state and returns false. To
+// ensure that the state isn't changed in the latter case, we save the
+// original state before we call more than one parsing functions
+// consecutively with &&, and restore the state if unsuccessful. See
+// ParseEncoding() as an example of this convention. We follow the
+// convention throughout the code.
+//
+// Originally we tried to do demangling without following the full ABI
+// syntax but it turned out we needed to follow the full syntax to
+// parse complicated cases like nested template arguments. Note that
+// implementing a full-fledged demangler isn't trivial (libiberty's
+// cp-demangle.c has +4300 lines).
+//
+// Note that (foo) in <(foo) ...> is a modifier to be ignored.
+//
+// Reference:
+// - Itanium C++ ABI
+// <http://www.codesourcery.com/cxx-abi/abi.html#mangling>
+
+// <mangled-name> ::= _Z <encoding>
+static bool ParseMangledName(State *state) {
+ if (ParseTwoChar(state, "_Z") && ParseEncoding(state)) {
+ // Append trailing version suffix if any.
+ // ex. _Z3foo@@GLIBCXX_3.4
+ if (state->mangled_cur < state->mangled_end &&
+ state->mangled_cur[0] == '@') {
+ MaybeAppend(state, state->mangled_cur);
+ state->mangled_cur = state->mangled_end;
+ }
+ return true;
+ }
+ return false;
+}
+
+// <encoding> ::= <(function) name> <bare-function-type>
+// ::= <(data) name>
+// ::= <special-name>
+static bool ParseEncoding(State *state) {
+ State copy = *state;
+ if (ParseName(state) && ParseBareFunctionType(state)) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseName(state) || ParseSpecialName(state)) {
+ return true;
+ }
+ return false;
+}
+
+// <name> ::= <nested-name>
+// ::= <unscoped-template-name> <template-args>
+// ::= <unscoped-name>
+// ::= <local-name>
+static bool ParseName(State *state) {
+ if (ParseNestedName(state) || ParseLocalName(state)) {
+ return true;
+ }
+
+ State copy = *state;
+ if (ParseUnscopedTemplateName(state) &&
+ ParseTemplateArgs(state)) {
+ return true;
+ }
+ *state = copy;
+
+ // Less greedy than <unscoped-template-name> <template-args>.
+ if (ParseUnscopedName(state)) {
+ return true;
+ }
+ return false;
+}
+
+// <unscoped-name> ::= <unqualified-name>
+// ::= St <unqualified-name>
+static bool ParseUnscopedName(State *state) {
+ if (ParseUnqualifiedName(state)) {
+ return true;
+ }
+
+ State copy = *state;
+ if (ParseTwoChar(state, "St") &&
+ MaybeAppend(state, "std::") &&
+ ParseUnqualifiedName(state)) {
+ return true;
+ }
+ *state = copy;
+ return false;
+}
+
+// <unscoped-template-name> ::= <unscoped-name>
+// ::= <substitution>
+static bool ParseUnscopedTemplateName(State *state) {
+ return ParseUnscopedName(state) || ParseSubstitution(state);
+}
+
+// <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E
+// ::= N [<CV-qualifiers>] <template-prefix> <template-args> E
+static bool ParseNestedName(State *state) {
+ State copy = *state;
+ if (ParseChar(state, 'N') &&
+ EnterNestedName(state) &&
+ Optional(ParseCVQualifiers(state)) &&
+ ParsePrefix(state) &&
+ LeaveNestedName(state, copy.nest_level) &&
+ ParseChar(state, 'E')) {
+ return true;
+ }
+ *state = copy;
+ return false;
+}
+
+// This part is tricky. If we literally translate them to code, we'll
+// end up infinite loop. Hence we merge them to avoid the case.
+//
+// <prefix> ::= <prefix> <unqualified-name>
+// ::= <template-prefix> <template-args>
+// ::= <template-param>
+// ::= <substitution>
+// ::= # empty
+// <template-prefix> ::= <prefix> <(template) unqualified-name>
+// ::= <template-param>
+// ::= <substitution>
+static bool ParsePrefix(State *state) {
+ bool has_something = false;
+ while (true) {
+ MaybeAppendSeparator(state);
+ if (ParseTemplateParam(state) ||
+ ParseSubstitution(state) ||
+ ParseUnscopedName(state)) {
+ has_something = true;
+ MaybeIncreaseNestLevel(state);
+ continue;
+ }
+ MaybeCancelLastSeparator(state);
+ if (has_something && ParseTemplateArgs(state)) {
+ return ParsePrefix(state);
+ } else {
+ break;
+ }
+ }
+ return true;
+}
+
+// <unqualified-name> ::= <operator-name>
+// ::= <ctor-dtor-name>
+// ::= <source-name>
+// ::= <local-source-name>
+static bool ParseUnqualifiedName(State *state) {
+ return (ParseOperatorName(state) ||
+ ParseCtorDtorName(state) ||
+ ParseSourceName(state) ||
+ ParseLocalSourceName(state));
+}
+
+// <source-name> ::= <positive length number> <identifier>
+static bool ParseSourceName(State *state) {
+ State copy = *state;
+ if (ParseNumber(state) && ParseIdentifier(state)) {
+ return true;
+ }
+ *state = copy;
+ return false;
+}
+
+// <local-source-name> ::= L <source-name> [<discriminator>]
+//
+// References:
+// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775
+// http://gcc.gnu.org/viewcvs?view=rev&revision=124467
+static bool ParseLocalSourceName(State *state) {
+ State copy = *state;
+ if (ParseChar(state, 'L') && ParseSourceName(state) &&
+ Optional(ParseDiscriminator(state))) {
+ return true;
+ }
+ *state = copy;
+ return false;
+}
+
+// <number> ::= [n] <non-negative decimal integer>
+static bool ParseNumber(State *state) {
+ int sign = 1;
+ if (ParseChar(state, 'n')) {
+ sign = -1;
+ }
+ const char *p = state->mangled_cur;
+ int number = 0;
+ for (;p < state->mangled_end; ++p) {
+ if ((*p >= '0' && *p <= '9')) {
+ number = number * 10 + (*p - '0');
+ } else {
+ break;
+ }
+ }
+ if (p != state->mangled_cur) { // Conversion succeeded.
+ state->mangled_cur = p;
+ state->number = number * sign;
+ return true;
+ }
+ return false;
+}
+
+// Floating-point literals are encoded using a fixed-length lowercase
+// hexadecimal string.
+static bool ParseFloatNumber(State *state) {
+ const char *p = state->mangled_cur;
+ int number = 0;
+ for (;p < state->mangled_end; ++p) {
+ if ((*p >= '0' && *p <= '9')) {
+ number = number * 16 + (*p - '0');
+ } else if (*p >= 'a' && *p <= 'f') {
+ number = number * 16 + (*p - 'a' + 10);
+ } else {
+ break;
+ }
+ }
+ if (p != state->mangled_cur) { // Conversion succeeded.
+ state->mangled_cur = p;
+ state->number = number;
+ return true;
+ }
+ return false;
+}
+
+// The <seq-id> is a sequence number in base 36,
+// using digits and upper case letters
+static bool ParseSeqId(State *state) {
+ const char *p = state->mangled_cur;
+ int number = 0;
+ for (;p < state->mangled_end; ++p) {
+ if ((*p >= '0' && *p <= '9')) {
+ number = number * 36 + (*p - '0');
+ } else if (*p >= 'A' && *p <= 'Z') {
+ number = number * 36 + (*p - 'A' + 10);
+ } else {
+ break;
+ }
+ }
+ if (p != state->mangled_cur) { // Conversion succeeded.
+ state->mangled_cur = p;
+ state->number = number;
+ return true;
+ }
+ return false;
+}
+
+// <identifier> ::= <unqualified source code identifier>
+static bool ParseIdentifier(State *state) {
+ if (state->number == -1 ||
+ RemainingLength(state) < state->number) {
+ return false;
+ }
+ if (IdentifierIsAnonymousNamespace(state)) {
+ MaybeAppend(state, "(anonymous namespace)");
+ } else {
+ MaybeAppendWithLength(state, state->mangled_cur, state->number);
+ }
+ state->mangled_cur += state->number;
+ state->number = -1; // Reset the number.
+ return true;
+}
+
+// <operator-name> ::= nw, and other two letters cases
+// ::= cv <type> # (cast)
+// ::= v <digit> <source-name> # vendor extended operator
+static bool ParseOperatorName(State *state) {
+ if (RemainingLength(state) < 2) {
+ return false;
+ }
+ // First check with "cv" (cast) case.
+ State copy = *state;
+ if (ParseTwoChar(state, "cv") &&
+ MaybeAppend(state, "operator ") &&
+ EnterNestedName(state) &&
+ ParseType(state) &&
+ LeaveNestedName(state, copy.nest_level)) {
+ return true;
+ }
+ *state = copy;
+
+ // Then vendor extended operators.
+ if (ParseChar(state, 'v') && ParseCharClass(state, "0123456789") &&
+ ParseSourceName(state)) {
+ return true;
+ }
+ *state = copy;
+
+ // Other operator names should start with a lower alphabet followed
+ // by a lower/upper alphabet.
+ if (!(IsLower(state->mangled_cur[0]) &&
+ IsAlpha(state->mangled_cur[1]))) {
+ return false;
+ }
+ // We may want to perform a binary search if we really need speed.
+ const AbbrevPair *p;
+ for (p = kOperatorList; p->abbrev != NULL; ++p) {
+ if (state->mangled_cur[0] == p->abbrev[0] &&
+ state->mangled_cur[1] == p->abbrev[1]) {
+ MaybeAppend(state, "operator");
+ if (IsLower(*p->real_name)) { // new, delete, etc.
+ MaybeAppend(state, " ");
+ }
+ MaybeAppend(state, p->real_name);
+ state->mangled_cur += 2;
+ return true;
+ }
+ }
+ return false;
+}
+
+// <special-name> ::= TV <type>
+// ::= TT <type>
+// ::= TI <type>
+// ::= TS <type>
+// ::= Tc <call-offset> <call-offset> <(base) encoding>
+// ::= GV <(object) name>
+// ::= T <call-offset> <(base) encoding>
+// G++ extensions:
+// ::= TC <type> <(offset) number> _ <(base) type>
+// ::= TF <type>
+// ::= TJ <type>
+// ::= GR <name>
+// ::= GA <encoding>
+// ::= Th <call-offset> <(base) encoding>
+// ::= Tv <call-offset> <(base) encoding>
+//
+// Note: we don't care much about them since they don't appear in
+// stack traces. The are special data.
+static bool ParseSpecialName(State *state) {
+ State copy = *state;
+ if (ParseChar(state, 'T') &&
+ ParseCharClass(state, "VTIS") &&
+ ParseType(state)) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseTwoChar(state, "Tc") && ParseCallOffset(state) &&
+ ParseCallOffset(state) && ParseEncoding(state)) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseTwoChar(state, "GV") &&
+ ParseName(state)) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseChar(state, 'T') && ParseCallOffset(state) &&
+ ParseEncoding(state)) {
+ return true;
+ }
+ *state = copy;
+
+ // G++ extensions
+ if (ParseTwoChar(state, "TC") && ParseType(state) &&
+ ParseNumber(state) && ParseChar(state, '_') &&
+ DisableAppend(state) &&
+ ParseType(state)) {
+ RestoreAppend(state, copy.append);
+ return true;
+ }
+ *state = copy;
+
+ if (ParseChar(state, 'T') && ParseCharClass(state, "FJ") &&
+ ParseType(state)) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseTwoChar(state, "GR") && ParseName(state)) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseTwoChar(state, "GA") && ParseEncoding(state)) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseChar(state, 'T') && ParseCharClass(state, "hv") &&
+ ParseCallOffset(state) && ParseEncoding(state)) {
+ return true;
+ }
+ *state = copy;
+ return false;
+}
+
+// <call-offset> ::= h <nv-offset> _
+// ::= v <v-offset> _
+static bool ParseCallOffset(State *state) {
+ State copy = *state;
+ if (ParseChar(state, 'h') &&
+ ParseNVOffset(state) && ParseChar(state, '_')) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseChar(state, 'v') &&
+ ParseVOffset(state) && ParseChar(state, '_')) {
+ return true;
+ }
+ *state = copy;
+
+ return false;
+}
+
+// <nv-offset> ::= <(offset) number>
+static bool ParseNVOffset(State *state) {
+ return ParseNumber(state);
+}
+
+// <v-offset> ::= <(offset) number> _ <(virtual offset) number>
+static bool ParseVOffset(State *state) {
+ State copy = *state;
+ if (ParseNumber(state) && ParseChar(state, '_') &&
+ ParseNumber(state)) {
+ return true;
+ }
+ *state = copy;
+ return false;
+}
+
+// <ctor-dtor-name> ::= C1 | C2 | C3
+// ::= D0 | D1 | D2
+static bool ParseCtorDtorName(State *state) {
+ State copy = *state;
+ if (ParseChar(state, 'C') &&
+ ParseCharClass(state, "123")) {
+ const char * const prev_name = state->prev_name;
+ const int prev_name_length = state->prev_name_length;
+ MaybeAppendWithLength(state, prev_name, prev_name_length);
+ return true;
+ }
+ *state = copy;
+
+ if (ParseChar(state, 'D') &&
+ ParseCharClass(state, "012")) {
+ const char * const prev_name = state->prev_name;
+ const int prev_name_length = state->prev_name_length;
+ MaybeAppend(state, "~");
+ MaybeAppendWithLength(state, prev_name, prev_name_length);
+ return true;
+ }
+ *state = copy;
+ return false;
+}
+
+// <type> ::= <CV-qualifiers> <type>
+// ::= P <type>
+// ::= R <type>
+// ::= C <type>
+// ::= G <type>
+// ::= U <source-name> <type>
+// ::= <builtin-type>
+// ::= <function-type>
+// ::= <class-enum-type>
+// ::= <array-type>
+// ::= <pointer-to-member-type>
+// ::= <template-template-param> <template-args>
+// ::= <template-param>
+// ::= <substitution>
+static bool ParseType(State *state) {
+ // We should check CV-qualifers, and PRGC things first.
+ State copy = *state;
+ if (ParseCVQualifiers(state) && ParseType(state)) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseCharClass(state, "PRCG") && ParseType(state)) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseChar(state, 'U') && ParseSourceName(state) &&
+ ParseType(state)) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseBuiltinType(state) ||
+ ParseFunctionType(state) ||
+ ParseClassEnumType(state) ||
+ ParseArrayType(state) ||
+ ParsePointerToMemberType(state) ||
+ ParseSubstitution(state)) {
+ return true;
+ }
+
+ if (ParseTemplateTemplateParam(state) &&
+ ParseTemplateArgs(state)) {
+ return true;
+ }
+ *state = copy;
+
+ // Less greedy than <template-template-param> <template-args>.
+ if (ParseTemplateParam(state)) {
+ return true;
+ }
+
+ return false;
+}
+
+// <CV-qualifiers> ::= [r] [V] [K]
+// We don't allow empty <CV-qualifiers> to avoid infinite loop in
+// ParseType().
+static bool ParseCVQualifiers(State *state) {
+ int num_cv_qualifiers = 0;
+ num_cv_qualifiers += ParseChar(state, 'r');
+ num_cv_qualifiers += ParseChar(state, 'V');
+ num_cv_qualifiers += ParseChar(state, 'K');
+ return num_cv_qualifiers > 0;
+}
+
+// <builtin-type> ::= v, etc.
+// ::= u <source-name>
+static bool ParseBuiltinType(State *state) {
+ const AbbrevPair *p;
+ for (p = kBuiltinTypeList; p->abbrev != NULL; ++p) {
+ if (state->mangled_cur[0] == p->abbrev[0]) {
+ MaybeAppend(state, p->real_name);
+ ++state->mangled_cur;
+ return true;
+ }
+ }
+
+ State copy = *state;
+ if (ParseChar(state, 'u') && ParseSourceName(state)) {
+ return true;
+ }
+ *state = copy;
+ return false;
+}
+
+// <function-type> ::= F [Y] <bare-function-type> E
+static bool ParseFunctionType(State *state) {
+ State copy = *state;
+ if (ParseChar(state, 'F') && Optional(ParseChar(state, 'Y')) &&
+ ParseBareFunctionType(state) && ParseChar(state, 'E')) {
+ return true;
+ }
+ *state = copy;
+ return false;
+}
+
+// <bare-function-type> ::= <(signature) type>+
+static bool ParseBareFunctionType(State *state) {
+ State copy = *state;
+ DisableAppend(state);
+ if (OneOrMore(ParseType, state)) {
+ RestoreAppend(state, copy.append);
+ MaybeAppend(state, "()");
+ return true;
+ }
+ *state = copy;
+ return false;
+}
+
+// <class-enum-type> ::= <name>
+static bool ParseClassEnumType(State *state) {
+ return ParseName(state);
+}
+
+// <array-type> ::= A <(positive dimension) number> _ <(element) type>
+// ::= A [<(dimension) expression>] _ <(element) type>
+static bool ParseArrayType(State *state) {
+ State copy = *state;
+ if (ParseChar(state, 'A') && ParseNumber(state) &&
+ ParseChar(state, '_') && ParseType(state)) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseChar(state, 'A') && Optional(ParseExpression(state)) &&
+ ParseChar(state, '_') && ParseType(state)) {
+ return true;
+ }
+ *state = copy;
+ return false;
+}
+
+// <pointer-to-member-type> ::= M <(class) type> <(member) type>
+static bool ParsePointerToMemberType(State *state) {
+ State copy = *state;
+ if (ParseChar(state, 'M') && ParseType(state) &&
+ ParseType(state)) {
+ return true;
+ }
+ *state = copy;
+ return false;
+}
+
+// <template-param> ::= T_
+// ::= T <parameter-2 non-negative number> _
+static bool ParseTemplateParam(State *state) {
+ if (ParseTwoChar(state, "T_")) {
+ MaybeAppend(state, "?"); // We don't support template substitutions.
+ return true;
+ }
+
+ State copy = *state;
+ if (ParseChar(state, 'T') && ParseNumber(state) &&
+ ParseChar(state, '_')) {
+ MaybeAppend(state, "?"); // We don't support template substitutions.
+ return true;
+ }
+ *state = copy;
+ return false;
+}
+
+
+// <template-template-param> ::= <template-param>
+// ::= <substitution>
+static bool ParseTemplateTemplateParam(State *state) {
+ return (ParseTemplateParam(state) ||
+ ParseSubstitution(state));
+}
+
+// <template-args> ::= I <template-arg>+ E
+static bool ParseTemplateArgs(State *state) {
+ State copy = *state;
+ DisableAppend(state);
+ if (ParseChar(state, 'I') &&
+ OneOrMore(ParseTemplateArg, state) &&
+ ParseChar(state, 'E')) {
+ RestoreAppend(state, copy.append);
+ MaybeAppend(state, "<>");
+ return true;
+ }
+ *state = copy;
+ return false;
+}
+
+// <template-arg> ::= <type>
+// ::= <expr-primary>
+// ::= X <expression> E
+static bool ParseTemplateArg(State *state) {
+ if (ParseType(state) ||
+ ParseExprPrimary(state)) {
+ return true;
+ }
+
+ State copy = *state;
+ if (ParseChar(state, 'X') && ParseExpression(state) &&
+ ParseChar(state, 'E')) {
+ return true;
+ }
+ *state = copy;
+ return false;
+}
+
+// <expression> ::= <template-param>
+// ::= <expr-primary>
+// ::= <unary operator-name> <expression>
+// ::= <binary operator-name> <expression> <expression>
+// ::= <trinary operator-name> <expression> <expression>
+// <expression>
+// ::= st <type>
+// ::= sr <type> <unqualified-name> <template-args>
+// ::= sr <type> <unqualified-name>
+static bool ParseExpression(State *state) {
+ if (ParseTemplateParam(state) || ParseExprPrimary(state)) {
+ return true;
+ }
+
+ State copy = *state;
+ if (ParseOperatorName(state) &&
+ ParseExpression(state) &&
+ ParseExpression(state) &&
+ ParseExpression(state)) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseOperatorName(state) &&
+ ParseExpression(state) &&
+ ParseExpression(state)) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseOperatorName(state) &&
+ ParseExpression(state)) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseTwoChar(state, "st") && ParseType(state)) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseTwoChar(state, "sr") && ParseType(state) &&
+ ParseUnqualifiedName(state) &&
+ ParseTemplateArgs(state)) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseTwoChar(state, "sr") && ParseType(state) &&
+ ParseUnqualifiedName(state)) {
+ return true;
+ }
+ *state = copy;
+ return false;
+}
+
+// <expr-primary> ::= L <type> <(value) number> E
+// ::= L <type> <(value) float> E
+// ::= L <mangled-name> E
+// // A bug in g++'s C++ ABI version 2 (-fabi-version=2).
+// ::= LZ <encoding> E
+static bool ParseExprPrimary(State *state) {
+ State copy = *state;
+ if (ParseChar(state, 'L') && ParseType(state) &&
+ ParseNumber(state) &&
+ ParseChar(state, 'E')) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseChar(state, 'L') && ParseType(state) &&
+ ParseFloatNumber(state) &&
+ ParseChar(state, 'E')) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseChar(state, 'L') && ParseMangledName(state) &&
+ ParseChar(state, 'E')) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseTwoChar(state, "LZ") && ParseEncoding(state) &&
+ ParseChar(state, 'E')) {
+ return true;
+ }
+ *state = copy;
+
+ return false;
+}
+
+// <local-name> := Z <(function) encoding> E <(entity) name>
+// [<discriminator>]
+// := Z <(function) encoding> E s [<discriminator>]
+static bool ParseLocalName(State *state) {
+ State copy = *state;
+ if (ParseChar(state, 'Z') && ParseEncoding(state) &&
+ ParseChar(state, 'E') && MaybeAppend(state, "::") &&
+ ParseName(state) && Optional(ParseDiscriminator(state))) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseChar(state, 'Z') && ParseEncoding(state) &&
+ ParseTwoChar(state, "Es") && Optional(ParseDiscriminator(state))) {
+ return true;
+ }
+ *state = copy;
+ return false;
+}
+
+// <discriminator> := _ <(non-negative) number>
+static bool ParseDiscriminator(State *state) {
+ State copy = *state;
+ if (ParseChar(state, '_') && ParseNumber(state)) {
+ return true;
+ }
+ *state = copy;
+ return false;
+}
+
+// <substitution> ::= S_
+// ::= S <seq-id> _
+// ::= St, etc.
+static bool ParseSubstitution(State *state) {
+ if (ParseTwoChar(state, "S_")) {
+ MaybeAppend(state, "?"); // We don't support substitutions.
+ return true;
+ }
+
+ State copy = *state;
+ if (ParseChar(state, 'S') && ParseSeqId(state) &&
+ ParseChar(state, '_')) {
+ MaybeAppend(state, "?"); // We don't support substitutions.
+ return true;
+ }
+ *state = copy;
+
+ // Expand abbreviations like "St" => "std".
+ if (ParseChar(state, 'S')) {
+ const AbbrevPair *p;
+ for (p = kSubstitutionList; p->abbrev != NULL; ++p) {
+ if (state->mangled_cur[0] == p->abbrev[1]) {
+ MaybeAppend(state, "std");
+ if (p->real_name[0] != '\0') {
+ MaybeAppend(state, "::");
+ MaybeAppend(state, p->real_name);
+ }
+ state->mangled_cur += 1;
+ return true;
+ }
+ }
+ }
+ *state = copy;
+ return false;
+}
+
+// The demangler entry point.
+bool Demangle(const char *mangled, char *out, int out_size) {
+ State state;
+ InitState(&state, mangled, out, out_size);
+ return (ParseMangledName(&state) &&
+ state.overflowed == false &&
+ RemainingLength(&state) == 0);
+}
+
+_END_GOOGLE_NAMESPACE_
diff --git a/src/base/third_party/symbolize/demangle.h b/src/base/third_party/symbolize/demangle.h
new file mode 100644
index 0000000..9c75915
--- /dev/null
+++ b/src/base/third_party/symbolize/demangle.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2006, Google 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:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 THE COPYRIGHT
+// OWNER 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.
+//
+// Author: Satoru Takabayashi
+//
+// An async-signal-safe and thread-safe demangler for Itanium C++ ABI
+// (aka G++ V3 ABI).
+
+// The demangler is implemented to be used in async signal handlers to
+// symbolize stack traces. We cannot use libstdc++'s
+// abi::__cxa_demangle() in such signal handlers since it's not async
+// signal safe (it uses malloc() internally).
+//
+// Note that this demangler doesn't support full demangling. More
+// specifically, it doesn't print types of function parameters and
+// types of template arguments. It just skips them. However, it's
+// still very useful to extract basic information such as class,
+// function, constructor, destructor, and operator names.
+//
+// See the implementation note in demangle.cc if you are interested.
+//
+// Example:
+//
+// | Mangled Name | The Demangler | abi::__cxa_demangle()
+// |---------------|---------------|-----------------------
+// | _Z1fv | f() | f()
+// | _Z1fi | f() | f(int)
+// | _Z3foo3bar | foo() | foo(bar)
+// | _Z1fIiEvi | f<>() | void f<int>(int)
+// | _ZN1N1fE | N::f | N::f
+// | _ZN3Foo3BarEv | Foo::Bar() | Foo::Bar()
+// | _Zrm1XS_" | operator%() | operator%(X, X)
+// | _ZN3FooC1Ev | Foo::Foo() | Foo::Foo()
+// | _Z1fSs | f() | f(std::basic_string<char,
+// | | | std::char_traits<char>,
+// | | | std::allocator<char> >)
+//
+// See the unit test for more examples.
+//
+// Note: we might want to write demanglers for ABIs other than Itanium
+// C++ ABI in the future.
+//
+
+#ifndef BASE_DEMANGLE_H_
+#define BASE_DEMANGLE_H_
+
+#include "config.h"
+
+_START_GOOGLE_NAMESPACE_
+
+// Demangle "mangled". On success, return true and write the
+// demangled symbol name to "out". Otherwise, return false.
+// "out" is modified even if demangling is unsuccessful.
+bool Demangle(const char *mangled, char *out, int out_size);
+
+_END_GOOGLE_NAMESPACE_
+
+#endif // BASE_DEMANGLE_H_
diff --git a/src/base/third_party/symbolize/glog/logging.h b/src/base/third_party/symbolize/glog/logging.h
new file mode 100644
index 0000000..a42c306
--- /dev/null
+++ b/src/base/third_party/symbolize/glog/logging.h
@@ -0,0 +1,5 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Empty.
diff --git a/src/base/third_party/symbolize/glog/raw_logging.h b/src/base/third_party/symbolize/glog/raw_logging.h
new file mode 100644
index 0000000..f5515c4
--- /dev/null
+++ b/src/base/third_party/symbolize/glog/raw_logging.h
@@ -0,0 +1,6 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#define WARNING 1;
+#define RAW_LOG(severity, ...); // Do nothing.
diff --git a/src/base/third_party/symbolize/symbolize.cc b/src/base/third_party/symbolize/symbolize.cc
new file mode 100644
index 0000000..f765fae
--- /dev/null
+++ b/src/base/third_party/symbolize/symbolize.cc
@@ -0,0 +1,685 @@
+// Copyright (c) 2006, Google 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:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 THE COPYRIGHT
+// OWNER 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.
+//
+// Author: Satoru Takabayashi
+// Stack-footprint reduction work done by Raksit Ashok
+//
+// Implementation note:
+//
+// We don't use heaps but only use stacks. We want to reduce the
+// stack consumption so that the symbolizer can run on small stacks.
+//
+// Here are some numbers collected with GCC 4.1.0 on x86:
+// - sizeof(Elf32_Sym) = 16
+// - sizeof(Elf32_Shdr) = 40
+// - sizeof(Elf64_Sym) = 24
+// - sizeof(Elf64_Shdr) = 64
+//
+// This implementation is intended to be async-signal-safe but uses
+// some functions which are not guaranteed to be so, such as memchr()
+// and memmove(). We assume they are async-signal-safe.
+//
+
+#include "build/build_config.h"
+#include "utilities.h"
+
+#if defined(HAVE_SYMBOLIZE)
+
+#include <limits>
+
+#include "symbolize.h"
+#include "demangle.h"
+
+_START_GOOGLE_NAMESPACE_
+
+// We don't use assert() since it's not guaranteed to be
+// async-signal-safe. Instead we define a minimal assertion
+// macro. So far, we don't need pretty printing for __FILE__, etc.
+
+// A wrapper for abort() to make it callable in ? :.
+static int AssertFail() {
+ abort();
+ return 0; // Should not reach.
+}
+
+#define SAFE_ASSERT(expr) ((expr) ? 0 : AssertFail())
+
+static SymbolizeCallback g_symbolize_callback = NULL;
+void InstallSymbolizeCallback(SymbolizeCallback callback) {
+ g_symbolize_callback = callback;
+}
+
+// This function wraps the Demangle function to provide an interface
+// where the input symbol is demangled in-place.
+// To keep stack consumption low, we would like this function to not
+// get inlined.
+static ATTRIBUTE_NOINLINE void DemangleInplace(char *out, int out_size) {
+ char demangled[256]; // Big enough for sane demangled symbols.
+ if (Demangle(out, demangled, sizeof(demangled))) {
+ // Demangling succeeded. Copy to out if the space allows.
+ int len = strlen(demangled);
+ if (len + 1 <= out_size) { // +1 for '\0'.
+ SAFE_ASSERT(len < sizeof(demangled));
+ memmove(out, demangled, len + 1);
+ }
+ }
+}
+
+_END_GOOGLE_NAMESPACE_
+
+#if defined(__ELF__)
+
+#include <dlfcn.h>
+#if defined(OS_OPENBSD)
+#include <sys/exec_elf.h>
+#else
+#include <elf.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "symbolize.h"
+#include "config.h"
+#include "glog/raw_logging.h"
+
+// Re-runs fn until it doesn't cause EINTR.
+#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR)
+
+_START_GOOGLE_NAMESPACE_
+
+// Read up to "count" bytes from file descriptor "fd" into the buffer
+// starting at "buf" while handling short reads and EINTR. On
+// success, return the number of bytes read. Otherwise, return -1.
+static ssize_t ReadPersistent(const int fd, void *buf, const size_t count) {
+ SAFE_ASSERT(fd >= 0);
+ SAFE_ASSERT(count <= std::numeric_limits<ssize_t>::max());
+ char *buf0 = reinterpret_cast<char *>(buf);
+ ssize_t num_bytes = 0;
+ while (num_bytes < count) {
+ ssize_t len;
+ NO_INTR(len = read(fd, buf0 + num_bytes, count - num_bytes));
+ if (len < 0) { // There was an error other than EINTR.
+ return -1;
+ }
+ if (len == 0) { // Reached EOF.
+ break;
+ }
+ num_bytes += len;
+ }
+ SAFE_ASSERT(num_bytes <= count);
+ return num_bytes;
+}
+
+// Read up to "count" bytes from "offset" in the file pointed by file
+// descriptor "fd" into the buffer starting at "buf". On success,
+// return the number of bytes read. Otherwise, return -1.
+static ssize_t ReadFromOffset(const int fd, void *buf,
+ const size_t count, const off_t offset) {
+ off_t off = lseek(fd, offset, SEEK_SET);
+ if (off == (off_t)-1) {
+ return -1;
+ }
+ return ReadPersistent(fd, buf, count);
+}
+
+// Try reading exactly "count" bytes from "offset" bytes in a file
+// pointed by "fd" into the buffer starting at "buf" while handling
+// short reads and EINTR. On success, return true. Otherwise, return
+// false.
+static bool ReadFromOffsetExact(const int fd, void *buf,
+ const size_t count, const off_t offset) {
+ ssize_t len = ReadFromOffset(fd, buf, count, offset);
+ return len == count;
+}
+
+// Returns elf_header.e_type if the file pointed by fd is an ELF binary.
+static int FileGetElfType(const int fd) {
+ ElfW(Ehdr) elf_header;
+ if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
+ return -1;
+ }
+ if (memcmp(elf_header.e_ident, ELFMAG, SELFMAG) != 0) {
+ return -1;
+ }
+ return elf_header.e_type;
+}
+
+// Read the section headers in the given ELF binary, and if a section
+// of the specified type is found, set the output to this section header
+// and return true. Otherwise, return false.
+// To keep stack consumption low, we would like this function to not get
+// inlined.
+static ATTRIBUTE_NOINLINE bool
+GetSectionHeaderByType(const int fd, ElfW(Half) sh_num, const off_t sh_offset,
+ ElfW(Word) type, ElfW(Shdr) *out) {
+ // Read at most 16 section headers at a time to save read calls.
+ ElfW(Shdr) buf[16];
+ for (int i = 0; i < sh_num;) {
+ const ssize_t num_bytes_left = (sh_num - i) * sizeof(buf[0]);
+ const ssize_t num_bytes_to_read =
+ (sizeof(buf) > num_bytes_left) ? num_bytes_left : sizeof(buf);
+ const ssize_t len = ReadFromOffset(fd, buf, num_bytes_to_read,
+ sh_offset + i * sizeof(buf[0]));
+ SAFE_ASSERT(len % sizeof(buf[0]) == 0);
+ const ssize_t num_headers_in_buf = len / sizeof(buf[0]);
+ SAFE_ASSERT(num_headers_in_buf <= sizeof(buf) / sizeof(buf[0]));
+ for (int j = 0; j < num_headers_in_buf; ++j) {
+ if (buf[j].sh_type == type) {
+ *out = buf[j];
+ return true;
+ }
+ }
+ i += num_headers_in_buf;
+ }
+ return false;
+}
+
+// There is no particular reason to limit section name to 63 characters,
+// but there has (as yet) been no need for anything longer either.
+const int kMaxSectionNameLen = 64;
+
+// name_len should include terminating '\0'.
+bool GetSectionHeaderByName(int fd, const char *name, size_t name_len,
+ ElfW(Shdr) *out) {
+ ElfW(Ehdr) elf_header;
+ if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
+ return false;
+ }
+
+ ElfW(Shdr) shstrtab;
+ off_t shstrtab_offset = (elf_header.e_shoff +
+ elf_header.e_shentsize * elf_header.e_shstrndx);
+ if (!ReadFromOffsetExact(fd, &shstrtab, sizeof(shstrtab), shstrtab_offset)) {
+ return false;
+ }
+
+ for (int i = 0; i < elf_header.e_shnum; ++i) {
+ off_t section_header_offset = (elf_header.e_shoff +
+ elf_header.e_shentsize * i);
+ if (!ReadFromOffsetExact(fd, out, sizeof(*out), section_header_offset)) {
+ return false;
+ }
+ char header_name[kMaxSectionNameLen];
+ if (sizeof(header_name) < name_len) {
+ RAW_LOG(WARNING, "Section name '%s' is too long (%"PRIuS"); "
+ "section will not be found (even if present).", name, name_len);
+ // No point in even trying.
+ return false;
+ }
+ off_t name_offset = shstrtab.sh_offset + out->sh_name;
+ ssize_t n_read = ReadFromOffset(fd, &header_name, name_len, name_offset);
+ if (n_read == -1) {
+ return false;
+ } else if (n_read != name_len) {
+ // Short read -- name could be at end of file.
+ continue;
+ }
+ if (memcmp(header_name, name, name_len) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Read a symbol table and look for the symbol containing the
+// pc. Iterate over symbols in a symbol table and look for the symbol
+// containing "pc". On success, return true and write the symbol name
+// to out. Otherwise, return false.
+// To keep stack consumption low, we would like this function to not get
+// inlined.
+static ATTRIBUTE_NOINLINE bool
+FindSymbol(uint64_t pc, const int fd, char *out, int out_size,
+ uint64_t symbol_offset, const ElfW(Shdr) *strtab,
+ const ElfW(Shdr) *symtab) {
+ if (symtab == NULL) {
+ return false;
+ }
+ const int num_symbols = symtab->sh_size / symtab->sh_entsize;
+ for (int i = 0; i < num_symbols;) {
+ off_t offset = symtab->sh_offset + i * symtab->sh_entsize;
+
+ // If we are reading Elf64_Sym's, we want to limit this array to
+ // 32 elements (to keep stack consumption low), otherwise we can
+ // have a 64 element Elf32_Sym array.
+#if __WORDSIZE == 64
+#define NUM_SYMBOLS 32
+#else
+#define NUM_SYMBOLS 64
+#endif
+
+ // Read at most NUM_SYMBOLS symbols at once to save read() calls.
+ ElfW(Sym) buf[NUM_SYMBOLS];
+ const ssize_t len = ReadFromOffset(fd, &buf, sizeof(buf), offset);
+ SAFE_ASSERT(len % sizeof(buf[0]) == 0);
+ const ssize_t num_symbols_in_buf = len / sizeof(buf[0]);
+ SAFE_ASSERT(num_symbols_in_buf <= sizeof(buf)/sizeof(buf[0]));
+ for (int j = 0; j < num_symbols_in_buf; ++j) {
+ const ElfW(Sym)& symbol = buf[j];
+ uint64_t start_address = symbol.st_value;
+ start_address += symbol_offset;
+ uint64_t end_address = start_address + symbol.st_size;
+ if (symbol.st_value != 0 && // Skip null value symbols.
+ symbol.st_shndx != 0 && // Skip undefined symbols.
+ start_address <= pc && pc < end_address) {
+ ssize_t len1 = ReadFromOffset(fd, out, out_size,
+ strtab->sh_offset + symbol.st_name);
+ if (len1 <= 0 || memchr(out, '\0', out_size) == NULL) {
+ return false;
+ }
+ return true; // Obtained the symbol name.
+ }
+ }
+ i += num_symbols_in_buf;
+ }
+ return false;
+}
+
+// Get the symbol name of "pc" from the file pointed by "fd". Process
+// both regular and dynamic symbol tables if necessary. On success,
+// write the symbol name to "out" and return true. Otherwise, return
+// false.
+static bool GetSymbolFromObjectFile(const int fd, uint64_t pc,
+ char *out, int out_size,
+ uint64_t map_start_address) {
+ // Read the ELF header.
+ ElfW(Ehdr) elf_header;
+ if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) {
+ return false;
+ }
+
+ uint64_t symbol_offset = 0;
+ if (elf_header.e_type == ET_DYN) { // DSO needs offset adjustment.
+ symbol_offset = map_start_address;
+ }
+
+ ElfW(Shdr) symtab, strtab;
+
+ // Consult a regular symbol table first.
+ if (!GetSectionHeaderByType(fd, elf_header.e_shnum, elf_header.e_shoff,
+ SHT_SYMTAB, &symtab)) {
+ return false;
+ }
+ if (!ReadFromOffsetExact(fd, &strtab, sizeof(strtab), elf_header.e_shoff +
+ symtab.sh_link * sizeof(symtab))) {
+ return false;
+ }
+ if (FindSymbol(pc, fd, out, out_size, symbol_offset,
+ &strtab, &symtab)) {
+ return true; // Found the symbol in a regular symbol table.
+ }
+
+ // If the symbol is not found, then consult a dynamic symbol table.
+ if (!GetSectionHeaderByType(fd, elf_header.e_shnum, elf_header.e_shoff,
+ SHT_DYNSYM, &symtab)) {
+ return false;
+ }
+ if (!ReadFromOffsetExact(fd, &strtab, sizeof(strtab), elf_header.e_shoff +
+ symtab.sh_link * sizeof(symtab))) {
+ return false;
+ }
+ if (FindSymbol(pc, fd, out, out_size, symbol_offset,
+ &strtab, &symtab)) {
+ return true; // Found the symbol in a dynamic symbol table.
+ }
+
+ return false;
+}
+
+namespace {
+// Thin wrapper around a file descriptor so that the file descriptor
+// gets closed for sure.
+struct FileDescriptor {
+ const int fd_;
+ explicit FileDescriptor(int fd) : fd_(fd) {}
+ ~FileDescriptor() {
+ if (fd_ >= 0) {
+ NO_INTR(close(fd_));
+ }
+ }
+ int get() { return fd_; }
+
+ private:
+ explicit FileDescriptor(const FileDescriptor&);
+ void operator=(const FileDescriptor&);
+};
+
+// Helper class for reading lines from file.
+//
+// Note: we don't use ProcMapsIterator since the object is big (it has
+// a 5k array member) and uses async-unsafe functions such as sscanf()
+// and snprintf().
+class LineReader {
+ public:
+ explicit LineReader(int fd, char *buf, int buf_len) : fd_(fd),
+ buf_(buf), buf_len_(buf_len), bol_(buf), eol_(buf), eod_(buf) {
+ }
+
+ // Read '\n'-terminated line from file. On success, modify "bol"
+ // and "eol", then return true. Otherwise, return false.
+ //
+ // Note: if the last line doesn't end with '\n', the line will be
+ // dropped. It's an intentional behavior to make the code simple.
+ bool ReadLine(const char **bol, const char **eol) {
+ if (BufferIsEmpty()) { // First time.
+ const ssize_t num_bytes = ReadPersistent(fd_, buf_, buf_len_);
+ if (num_bytes <= 0) { // EOF or error.
+ return false;
+ }
+ eod_ = buf_ + num_bytes;
+ bol_ = buf_;
+ } else {
+ bol_ = eol_ + 1; // Advance to the next line in the buffer.
+ SAFE_ASSERT(bol_ <= eod_); // "bol_" can point to "eod_".
+ if (!HasCompleteLine()) {
+ const int incomplete_line_length = eod_ - bol_;
+ // Move the trailing incomplete line to the beginning.
+ memmove(buf_, bol_, incomplete_line_length);
+ // Read text from file and append it.
+ char * const append_pos = buf_ + incomplete_line_length;
+ const int capacity_left = buf_len_ - incomplete_line_length;
+ const ssize_t num_bytes = ReadPersistent(fd_, append_pos,
+ capacity_left);
+ if (num_bytes <= 0) { // EOF or error.
+ return false;
+ }
+ eod_ = append_pos + num_bytes;
+ bol_ = buf_;
+ }
+ }
+ eol_ = FindLineFeed();
+ if (eol_ == NULL) { // '\n' not found. Malformed line.
+ return false;
+ }
+ *eol_ = '\0'; // Replace '\n' with '\0'.
+
+ *bol = bol_;
+ *eol = eol_;
+ return true;
+ }
+
+ // Beginning of line.
+ const char *bol() {
+ return bol_;
+ }
+
+ // End of line.
+ const char *eol() {
+ return eol_;
+ }
+
+ private:
+ explicit LineReader(const LineReader&);
+ void operator=(const LineReader&);
+
+ char *FindLineFeed() {
+ return reinterpret_cast<char *>(memchr(bol_, '\n', eod_ - bol_));
+ }
+
+ bool BufferIsEmpty() {
+ return buf_ == eod_;
+ }
+
+ bool HasCompleteLine() {
+ return !BufferIsEmpty() && FindLineFeed() != NULL;
+ }
+
+ const int fd_;
+ char * const buf_;
+ const int buf_len_;
+ char *bol_;
+ char *eol_;
+ const char *eod_; // End of data in "buf_".
+};
+} // namespace
+
+// Place the hex number read from "start" into "*hex". The pointer to
+// the first non-hex character or "end" is returned.
+static char *GetHex(const char *start, const char *end, uint64_t *hex) {
+ *hex = 0;
+ const char *p;
+ for (p = start; p < end; ++p) {
+ int ch = *p;
+ if ((ch >= '0' && ch <= '9') ||
+ (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')) {
+ *hex = (*hex << 4) | (ch < 'A' ? ch - '0' : (ch & 0xF) + 9);
+ } else { // Encountered the first non-hex character.
+ break;
+ }
+ }
+ SAFE_ASSERT(p <= end);
+ return const_cast<char *>(p);
+}
+
+// Search for the object file (from /proc/self/maps) that contains
+// the specified pc. If found, open this file and return the file handle,
+// and also set start_address to the start address of where this object
+// file is mapped to in memory. Otherwise, return -1.
+static ATTRIBUTE_NOINLINE int
+OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
+ uint64_t &start_address) {
+ int object_fd;
+
+ // Open /proc/self/maps.
+ int maps_fd;
+ NO_INTR(maps_fd = open("/proc/self/maps", O_RDONLY));
+ FileDescriptor wrapped_maps_fd(maps_fd);
+ if (wrapped_maps_fd.get() < 0) {
+ return -1;
+ }
+
+ // Iterate over maps and look for the map containing the pc. Then
+ // look into the symbol tables inside.
+ char buf[1024]; // Big enough for line of sane /proc/self/maps
+ LineReader reader(wrapped_maps_fd.get(), buf, sizeof(buf));
+ while (true) {
+ const char *cursor;
+ const char *eol;
+ if (!reader.ReadLine(&cursor, &eol)) { // EOF or malformed line.
+ return -1;
+ }
+
+ // Start parsing line in /proc/self/maps. Here is an example:
+ //
+ // 08048000-0804c000 r-xp 00000000 08:01 2142121 /bin/cat
+ //
+ // We want start address (08048000), end address (0804c000), flags
+ // (r-xp) and file name (/bin/cat).
+
+ // Read start address.
+ cursor = GetHex(cursor, eol, &start_address);
+ if (cursor == eol || *cursor != '-') {
+ return -1; // Malformed line.
+ }
+ ++cursor; // Skip '-'.
+
+ // Read end address.
+ uint64_t end_address;
+ cursor = GetHex(cursor, eol, &end_address);
+ if (cursor == eol || *cursor != ' ') {
+ return -1; // Malformed line.
+ }
+ ++cursor; // Skip ' '.
+
+ // Check start and end addresses.
+ if (!(start_address <= pc && pc < end_address)) {
+ continue; // We skip this map. PC isn't in this map.
+ }
+
+ // Read flags. Skip flags until we encounter a space or eol.
+ const char * const flags_start = cursor;
+ while (cursor < eol && *cursor != ' ') {
+ ++cursor;
+ }
+ // We expect at least four letters for flags (ex. "r-xp").
+ if (cursor == eol || cursor < flags_start + 4) {
+ return -1; // Malformed line.
+ }
+
+ // Check flags. We are only interested in "r-x" maps.
+ if (memcmp(flags_start, "r-x", 3) != 0) { // Not a "r-x" map.
+ continue; // We skip this map.
+ }
+ ++cursor; // Skip ' '.
+
+ // Skip to file name. "cursor" now points to file offset. We need to
+ // skip at least three spaces for file offset, dev, and inode.
+ int num_spaces = 0;
+ while (cursor < eol) {
+ if (*cursor == ' ') {
+ ++num_spaces;
+ } else if (num_spaces >= 3) {
+ // The first non-space character after skipping three spaces
+ // is the beginning of the file name.
+ break;
+ }
+ ++cursor;
+ }
+ if (cursor == eol) {
+ return -1; // Malformed line.
+ }
+
+ // Finally, "cursor" now points to file name of our interest.
+ NO_INTR(object_fd = open(cursor, O_RDONLY));
+ if (object_fd < 0) {
+ return -1;
+ }
+ return object_fd;
+ }
+}
+
+// The implementation of our symbolization routine. If it
+// successfully finds the symbol containing "pc" and obtains the
+// symbol name, returns true and write the symbol name to "out".
+// Otherwise, returns false. If Callback function is installed via
+// InstallSymbolizeCallback(), the function is also called in this function,
+// and "out" is used as its output.
+// To keep stack consumption low, we would like this function to not
+// get inlined.
+static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out,
+ int out_size) {
+ uint64_t pc0 = reinterpret_cast<uintptr_t>(pc);
+ uint64_t start_address = 0;
+
+ int object_fd = OpenObjectFileContainingPcAndGetStartAddress(pc0,
+ start_address);
+ if (object_fd == -1) {
+ return false;
+ }
+ FileDescriptor wrapped_object_fd(object_fd);
+ int elf_type = FileGetElfType(wrapped_object_fd.get());
+ if (elf_type == -1) {
+ return false;
+ }
+ if (g_symbolize_callback) {
+ // Run the call back if it's installed.
+ // Note: relocation (and much of the rest of this code) will be
+ // wrong for prelinked shared libraries and PIE executables.
+ uint64 relocation = (elf_type == ET_DYN) ? start_address : 0;
+ int num_bytes_written = g_symbolize_callback(wrapped_object_fd.get(),
+ pc, out, out_size,
+ relocation);
+ if (num_bytes_written > 0) {
+ out += num_bytes_written;
+ out_size -= num_bytes_written;
+ }
+ }
+ if (!GetSymbolFromObjectFile(wrapped_object_fd.get(), pc0,
+ out, out_size, start_address)) {
+ return false;
+ }
+
+ // Symbolization succeeded. Now we try to demangle the symbol.
+ DemangleInplace(out, out_size);
+ return true;
+}
+
+_END_GOOGLE_NAMESPACE_
+
+#elif defined(OS_MACOSX) && defined(HAVE_DLADDR)
+
+#include <dlfcn.h>
+#include <string.h>
+
+_START_GOOGLE_NAMESPACE_
+
+static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out,
+ int out_size) {
+ Dl_info info;
+ if (dladdr(pc, &info)) {
+ if (strlen(info.dli_sname) < out_size) {
+ strcpy(out, info.dli_sname);
+ // Symbolization succeeded. Now we try to demangle the symbol.
+ DemangleInplace(out, out_size);
+ return true;
+ }
+ }
+ return false;
+}
+
+_END_GOOGLE_NAMESPACE_
+
+#else
+# error BUG: HAVE_SYMBOLIZE was wrongly set
+#endif
+
+_START_GOOGLE_NAMESPACE_
+
+bool Symbolize(void *pc, char *out, int out_size) {
+ SAFE_ASSERT(out_size >= 0);
+ return SymbolizeAndDemangle(pc, out, out_size);
+}
+
+_END_GOOGLE_NAMESPACE_
+
+#else /* HAVE_SYMBOLIZE */
+
+#include <assert.h>
+
+#include "config.h"
+
+_START_GOOGLE_NAMESPACE_
+
+// TODO: Support other environments.
+bool Symbolize(void *pc, char *out, int out_size) {
+ assert(0);
+ return false;
+}
+
+_END_GOOGLE_NAMESPACE_
+
+#endif
diff --git a/src/base/third_party/symbolize/symbolize.h b/src/base/third_party/symbolize/symbolize.h
new file mode 100644
index 0000000..441e543
--- /dev/null
+++ b/src/base/third_party/symbolize/symbolize.h
@@ -0,0 +1,133 @@
+// Copyright (c) 2006, Google 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:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 THE COPYRIGHT
+// OWNER 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.
+//
+// Author: Satoru Takabayashi
+//
+// This library provides Symbolize() function that symbolizes program
+// counters to their corresponding symbol names on linux platforms.
+// This library has a minimal implementation of an ELF symbol table
+// reader (i.e. it doesn't depend on libelf, etc.).
+//
+// The algorithm used in Symbolize() is as follows.
+//
+// 1. Go through a list of maps in /proc/self/maps and find the map
+// containing the program counter.
+//
+// 2. Open the mapped file and find a regular symbol table inside.
+// Iterate over symbols in the symbol table and look for the symbol
+// containing the program counter. If such a symbol is found,
+// obtain the symbol name, and demangle the symbol if possible.
+// If the symbol isn't found in the regular symbol table (binary is
+// stripped), try the same thing with a dynamic symbol table.
+//
+// Note that Symbolize() is originally implemented to be used in
+// FailureSignalHandler() in base/google.cc. Hence it doesn't use
+// malloc() and other unsafe operations. It should be both
+// thread-safe and async-signal-safe.
+
+#ifndef BASE_SYMBOLIZE_H_
+#define BASE_SYMBOLIZE_H_
+
+#include "utilities.h"
+#include "config.h"
+#include "glog/logging.h"
+
+#ifdef HAVE_SYMBOLIZE
+
+#if defined(__ELF__) // defined by gcc
+#if defined(__OpenBSD__)
+#include <sys/exec_elf.h>
+#else
+#include <elf.h>
+#endif
+
+#if !defined(ANDROID)
+#include <link.h> // For ElfW() macro.
+#endif
+
+// For systems where SIZEOF_VOID_P is not defined, determine it
+// based on __LP64__ (defined by gcc on 64-bit systems)
+#if !defined(SIZEOF_VOID_P)
+# if defined(__LP64__)
+# define SIZEOF_VOID_P 8
+# else
+# define SIZEOF_VOID_P 4
+# endif
+#endif
+
+// If there is no ElfW macro, let's define it by ourself.
+#ifndef ElfW
+# if SIZEOF_VOID_P == 4
+# define ElfW(type) Elf32_##type
+# elif SIZEOF_VOID_P == 8
+# define ElfW(type) Elf64_##type
+# else
+# error "Unknown sizeof(void *)"
+# endif
+#endif
+
+_START_GOOGLE_NAMESPACE_
+
+// Gets the section header for the given name, if it exists. Returns true on
+// success. Otherwise, returns false.
+bool GetSectionHeaderByName(int fd, const char *name, size_t name_len,
+ ElfW(Shdr) *out);
+
+_END_GOOGLE_NAMESPACE_
+
+#endif /* __ELF__ */
+
+_START_GOOGLE_NAMESPACE_
+
+// Installs a callback function, which will be called right before a symbol name
+// is printed. The callback is intended to be used for showing a file name and a
+// line number preceding a symbol name.
+// "fd" is a file descriptor of the object file containing the program
+// counter "pc". The callback function should write output to "out"
+// and return the size of the output written. On error, the callback
+// function should return -1.
+typedef int (*SymbolizeCallback)(int fd, void *pc, char *out, size_t out_size,
+ uint64 relocation);
+void InstallSymbolizeCallback(SymbolizeCallback callback);
+
+_END_GOOGLE_NAMESPACE_
+
+#endif
+
+_START_GOOGLE_NAMESPACE_
+
+// Symbolizes a program counter. On success, returns true and write the
+// symbol name to "out". The symbol name is demangled if possible
+// (supports symbols generated by GCC 3.x or newer). Otherwise,
+// returns false.
+bool Symbolize(void *pc, char *out, int out_size);
+
+_END_GOOGLE_NAMESPACE_
+
+#endif // BASE_SYMBOLIZE_H_
diff --git a/src/base/third_party/symbolize/utilities.h b/src/base/third_party/symbolize/utilities.h
new file mode 100644
index 0000000..0bed526
--- /dev/null
+++ b/src/base/third_party/symbolize/utilities.h
@@ -0,0 +1,11 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+typedef uint64_t uint64;
+#define HAVE_SYMBOLIZE 1
+#define ATTRIBUTE_NOINLINE __attribute__ ((noinline))
diff --git a/src/base/third_party/valgrind/LICENSE b/src/base/third_party/valgrind/LICENSE
new file mode 100644
index 0000000..41f677b
--- /dev/null
+++ b/src/base/third_party/valgrind/LICENSE
@@ -0,0 +1,39 @@
+ Notice that the following BSD-style license applies to the Valgrind header
+ files used by Chromium (valgrind.h and memcheck.h). However, the rest of
+ Valgrind is licensed under the terms of the GNU General Public License,
+ version 2, unless otherwise indicated.
+
+ ----------------------------------------------------------------
+
+ Copyright (C) 2000-2008 Julian Seward. 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. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+ 3. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+
+ 4. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
diff --git a/src/base/third_party/valgrind/README.chromium b/src/base/third_party/valgrind/README.chromium
new file mode 100644
index 0000000..56a1cbb
--- /dev/null
+++ b/src/base/third_party/valgrind/README.chromium
@@ -0,0 +1,11 @@
+Name: valgrind
+URL: http://valgrind.org
+License: BSD
+
+Header files in this directory define runtime macros that determine whether the
+current process is running under Valgrind and tell Memcheck tool about custom
+memory allocators.
+
+These header files were taken from Valgrind source code
+(svn://svn.valgrind.org/valgrind/trunk@11504, dated 21 Jan 2011). The files are
+covered under BSD license as described within.
diff --git a/src/base/third_party/valgrind/memcheck.h b/src/base/third_party/valgrind/memcheck.h
new file mode 100644
index 0000000..f59c212
--- /dev/null
+++ b/src/base/third_party/valgrind/memcheck.h
@@ -0,0 +1,279 @@
+
+/*
+ ----------------------------------------------------------------
+
+ Notice that the following BSD-style license applies to this one
+ file (memcheck.h) only. The rest of Valgrind is licensed under the
+ terms of the GNU General Public License, version 2, unless
+ otherwise indicated. See the COPYING file in the source
+ distribution for details.
+
+ ----------------------------------------------------------------
+
+ This file is part of MemCheck, a heavyweight Valgrind tool for
+ detecting memory errors.
+
+ Copyright (C) 2000-2010 Julian Seward. 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. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+ 3. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+
+ 4. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+
+ ----------------------------------------------------------------
+
+ Notice that the above BSD-style license applies to this one file
+ (memcheck.h) only. The entire rest of Valgrind is licensed under
+ the terms of the GNU General Public License, version 2. See the
+ COPYING file in the source distribution for details.
+
+ ----------------------------------------------------------------
+*/
+
+
+#ifndef __MEMCHECK_H
+#define __MEMCHECK_H
+
+
+/* This file is for inclusion into client (your!) code.
+
+ You can use these macros to manipulate and query memory permissions
+ inside your own programs.
+
+ See comment near the top of valgrind.h on how to use them.
+*/
+
+#include "valgrind.h"
+
+/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !!
+ This enum comprises an ABI exported by Valgrind to programs
+ which use client requests. DO NOT CHANGE THE ORDER OF THESE
+ ENTRIES, NOR DELETE ANY -- add new ones at the end. */
+typedef
+ enum {
+ VG_USERREQ__MAKE_MEM_NOACCESS = VG_USERREQ_TOOL_BASE('M','C'),
+ VG_USERREQ__MAKE_MEM_UNDEFINED,
+ VG_USERREQ__MAKE_MEM_DEFINED,
+ VG_USERREQ__DISCARD,
+ VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE,
+ VG_USERREQ__CHECK_MEM_IS_DEFINED,
+ VG_USERREQ__DO_LEAK_CHECK,
+ VG_USERREQ__COUNT_LEAKS,
+
+ VG_USERREQ__GET_VBITS,
+ VG_USERREQ__SET_VBITS,
+
+ VG_USERREQ__CREATE_BLOCK,
+
+ VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE,
+
+ /* Not next to VG_USERREQ__COUNT_LEAKS because it was added later. */
+ VG_USERREQ__COUNT_LEAK_BLOCKS,
+
+ /* This is just for memcheck's internal use - don't use it */
+ _VG_USERREQ__MEMCHECK_RECORD_OVERLAP_ERROR
+ = VG_USERREQ_TOOL_BASE('M','C') + 256
+ } Vg_MemCheckClientRequest;
+
+
+
+/* Client-code macros to manipulate the state of memory. */
+
+/* Mark memory at _qzz_addr as unaddressable for _qzz_len bytes. */
+#define VALGRIND_MAKE_MEM_NOACCESS(_qzz_addr,_qzz_len) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__MAKE_MEM_NOACCESS, \
+ (_qzz_addr), (_qzz_len), 0, 0, 0)
+
+/* Similarly, mark memory at _qzz_addr as addressable but undefined
+ for _qzz_len bytes. */
+#define VALGRIND_MAKE_MEM_UNDEFINED(_qzz_addr,_qzz_len) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__MAKE_MEM_UNDEFINED, \
+ (_qzz_addr), (_qzz_len), 0, 0, 0)
+
+/* Similarly, mark memory at _qzz_addr as addressable and defined
+ for _qzz_len bytes. */
+#define VALGRIND_MAKE_MEM_DEFINED(_qzz_addr,_qzz_len) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__MAKE_MEM_DEFINED, \
+ (_qzz_addr), (_qzz_len), 0, 0, 0)
+
+/* Similar to VALGRIND_MAKE_MEM_DEFINED except that addressability is
+ not altered: bytes which are addressable are marked as defined,
+ but those which are not addressable are left unchanged. */
+#define VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(_qzz_addr,_qzz_len) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE, \
+ (_qzz_addr), (_qzz_len), 0, 0, 0)
+
+/* Create a block-description handle. The description is an ascii
+ string which is included in any messages pertaining to addresses
+ within the specified memory range. Has no other effect on the
+ properties of the memory range. */
+#define VALGRIND_CREATE_BLOCK(_qzz_addr,_qzz_len, _qzz_desc) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__CREATE_BLOCK, \
+ (_qzz_addr), (_qzz_len), (_qzz_desc), \
+ 0, 0)
+
+/* Discard a block-description-handle. Returns 1 for an
+ invalid handle, 0 for a valid handle. */
+#define VALGRIND_DISCARD(_qzz_blkindex) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__DISCARD, \
+ 0, (_qzz_blkindex), 0, 0, 0)
+
+
+/* Client-code macros to check the state of memory. */
+
+/* Check that memory at _qzz_addr is addressable for _qzz_len bytes.
+ If suitable addressibility is not established, Valgrind prints an
+ error message and returns the address of the first offending byte.
+ Otherwise it returns zero. */
+#define VALGRIND_CHECK_MEM_IS_ADDRESSABLE(_qzz_addr,_qzz_len) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE, \
+ (_qzz_addr), (_qzz_len), 0, 0, 0)
+
+/* Check that memory at _qzz_addr is addressable and defined for
+ _qzz_len bytes. If suitable addressibility and definedness are not
+ established, Valgrind prints an error message and returns the
+ address of the first offending byte. Otherwise it returns zero. */
+#define VALGRIND_CHECK_MEM_IS_DEFINED(_qzz_addr,_qzz_len) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__CHECK_MEM_IS_DEFINED, \
+ (_qzz_addr), (_qzz_len), 0, 0, 0)
+
+/* Use this macro to force the definedness and addressibility of an
+ lvalue to be checked. If suitable addressibility and definedness
+ are not established, Valgrind prints an error message and returns
+ the address of the first offending byte. Otherwise it returns
+ zero. */
+#define VALGRIND_CHECK_VALUE_IS_DEFINED(__lvalue) \
+ VALGRIND_CHECK_MEM_IS_DEFINED( \
+ (volatile unsigned char *)&(__lvalue), \
+ (unsigned long)(sizeof (__lvalue)))
+
+
+/* Do a full memory leak check (like --leak-check=full) mid-execution. */
+#define VALGRIND_DO_LEAK_CHECK \
+ {unsigned long _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__DO_LEAK_CHECK, \
+ 0, 0, 0, 0, 0); \
+ }
+
+/* Do a summary memory leak check (like --leak-check=summary) mid-execution. */
+#define VALGRIND_DO_QUICK_LEAK_CHECK \
+ {unsigned long _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__DO_LEAK_CHECK, \
+ 1, 0, 0, 0, 0); \
+ }
+
+/* Return number of leaked, dubious, reachable and suppressed bytes found by
+ all previous leak checks. They must be lvalues. */
+#define VALGRIND_COUNT_LEAKS(leaked, dubious, reachable, suppressed) \
+ /* For safety on 64-bit platforms we assign the results to private
+ unsigned long variables, then assign these to the lvalues the user
+ specified, which works no matter what type 'leaked', 'dubious', etc
+ are. We also initialise '_qzz_leaked', etc because
+ VG_USERREQ__COUNT_LEAKS doesn't mark the values returned as
+ defined. */ \
+ {unsigned long _qzz_res; \
+ unsigned long _qzz_leaked = 0, _qzz_dubious = 0; \
+ unsigned long _qzz_reachable = 0, _qzz_suppressed = 0; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__COUNT_LEAKS, \
+ &_qzz_leaked, &_qzz_dubious, \
+ &_qzz_reachable, &_qzz_suppressed, 0); \
+ leaked = _qzz_leaked; \
+ dubious = _qzz_dubious; \
+ reachable = _qzz_reachable; \
+ suppressed = _qzz_suppressed; \
+ }
+
+/* Return number of leaked, dubious, reachable and suppressed bytes found by
+ all previous leak checks. They must be lvalues. */
+#define VALGRIND_COUNT_LEAK_BLOCKS(leaked, dubious, reachable, suppressed) \
+ /* For safety on 64-bit platforms we assign the results to private
+ unsigned long variables, then assign these to the lvalues the user
+ specified, which works no matter what type 'leaked', 'dubious', etc
+ are. We also initialise '_qzz_leaked', etc because
+ VG_USERREQ__COUNT_LEAKS doesn't mark the values returned as
+ defined. */ \
+ {unsigned long _qzz_res; \
+ unsigned long _qzz_leaked = 0, _qzz_dubious = 0; \
+ unsigned long _qzz_reachable = 0, _qzz_suppressed = 0; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__COUNT_LEAK_BLOCKS, \
+ &_qzz_leaked, &_qzz_dubious, \
+ &_qzz_reachable, &_qzz_suppressed, 0); \
+ leaked = _qzz_leaked; \
+ dubious = _qzz_dubious; \
+ reachable = _qzz_reachable; \
+ suppressed = _qzz_suppressed; \
+ }
+
+
+/* Get the validity data for addresses [zza..zza+zznbytes-1] and copy it
+ into the provided zzvbits array. Return values:
+ 0 if not running on valgrind
+ 1 success
+ 2 [previously indicated unaligned arrays; these are now allowed]
+ 3 if any parts of zzsrc/zzvbits are not addressable.
+ The metadata is not copied in cases 0, 2 or 3 so it should be
+ impossible to segfault your system by using this call.
+*/
+#define VALGRIND_GET_VBITS(zza,zzvbits,zznbytes) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__GET_VBITS, \
+ (const char*)(zza), \
+ (char*)(zzvbits), \
+ (zznbytes), 0, 0)
+
+/* Set the validity data for addresses [zza..zza+zznbytes-1], copying it
+ from the provided zzvbits array. Return values:
+ 0 if not running on valgrind
+ 1 success
+ 2 [previously indicated unaligned arrays; these are now allowed]
+ 3 if any parts of zza/zzvbits are not addressable.
+ The metadata is not copied in cases 0, 2 or 3 so it should be
+ impossible to segfault your system by using this call.
+*/
+#define VALGRIND_SET_VBITS(zza,zzvbits,zznbytes) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__SET_VBITS, \
+ (const char*)(zza), \
+ (const char*)(zzvbits), \
+ (zznbytes), 0, 0 )
+
+#endif
+
diff --git a/src/base/third_party/valgrind/valgrind.h b/src/base/third_party/valgrind/valgrind.h
new file mode 100644
index 0000000..0bae0aa
--- /dev/null
+++ b/src/base/third_party/valgrind/valgrind.h
@@ -0,0 +1,4792 @@
+/* -*- c -*-
+ ----------------------------------------------------------------
+
+ Notice that the following BSD-style license applies to this one
+ file (valgrind.h) only. The rest of Valgrind is licensed under the
+ terms of the GNU General Public License, version 2, unless
+ otherwise indicated. See the COPYING file in the source
+ distribution for details.
+
+ ----------------------------------------------------------------
+
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2000-2010 Julian Seward. 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. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+ 3. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+
+ 4. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+
+ ----------------------------------------------------------------
+
+ Notice that the above BSD-style license applies to this one file
+ (valgrind.h) only. The entire rest of Valgrind is licensed under
+ the terms of the GNU General Public License, version 2. See the
+ COPYING file in the source distribution for details.
+
+ ----------------------------------------------------------------
+*/
+
+
+/* This file is for inclusion into client (your!) code.
+
+ You can use these macros to manipulate and query Valgrind's
+ execution inside your own programs.
+
+ The resulting executables will still run without Valgrind, just a
+ little bit more slowly than they otherwise would, but otherwise
+ unchanged. When not running on valgrind, each client request
+ consumes very few (eg. 7) instructions, so the resulting performance
+ loss is negligible unless you plan to execute client requests
+ millions of times per second. Nevertheless, if that is still a
+ problem, you can compile with the NVALGRIND symbol defined (gcc
+ -DNVALGRIND) so that client requests are not even compiled in. */
+
+#ifndef __VALGRIND_H
+#define __VALGRIND_H
+
+
+/* ------------------------------------------------------------------ */
+/* VERSION NUMBER OF VALGRIND */
+/* ------------------------------------------------------------------ */
+
+/* Specify Valgrind's version number, so that user code can
+ conditionally compile based on our version number. Note that these
+ were introduced at version 3.6 and so do not exist in version 3.5
+ or earlier. The recommended way to use them to check for "version
+ X.Y or later" is (eg)
+
+#if defined(__VALGRIND_MAJOR__) && defined(__VALGRIND_MINOR__) \
+ && (__VALGRIND_MAJOR__ > 3 \
+ || (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6))
+*/
+#define __VALGRIND_MAJOR__ 3
+#define __VALGRIND_MINOR__ 6
+
+
+#include <stdarg.h>
+
+/* Nb: this file might be included in a file compiled with -ansi. So
+ we can't use C++ style "//" comments nor the "asm" keyword (instead
+ use "__asm__"). */
+
+/* Derive some tags indicating what the target platform is. Note
+ that in this file we're using the compiler's CPP symbols for
+ identifying architectures, which are different to the ones we use
+ within the rest of Valgrind. Note, __powerpc__ is active for both
+ 32 and 64-bit PPC, whereas __powerpc64__ is only active for the
+ latter (on Linux, that is).
+
+ Misc note: how to find out what's predefined in gcc by default:
+ gcc -Wp,-dM somefile.c
+*/
+#undef PLAT_ppc64_aix5
+#undef PLAT_ppc32_aix5
+#undef PLAT_x86_darwin
+#undef PLAT_amd64_darwin
+#undef PLAT_x86_win32
+#undef PLAT_x86_linux
+#undef PLAT_amd64_linux
+#undef PLAT_ppc32_linux
+#undef PLAT_ppc64_linux
+#undef PLAT_arm_linux
+
+#if defined(_AIX) && defined(__64BIT__)
+# define PLAT_ppc64_aix5 1
+#elif defined(_AIX) && !defined(__64BIT__)
+# define PLAT_ppc32_aix5 1
+#elif defined(__APPLE__) && defined(__i386__)
+# define PLAT_x86_darwin 1
+#elif defined(__APPLE__) && defined(__x86_64__)
+# define PLAT_amd64_darwin 1
+#elif defined(__MINGW32__) || defined(__CYGWIN32__) || defined(_WIN32) && defined(_M_IX86)
+# define PLAT_x86_win32 1
+#elif defined(__linux__) && defined(__i386__)
+# define PLAT_x86_linux 1
+#elif defined(__linux__) && defined(__x86_64__)
+# define PLAT_amd64_linux 1
+#elif defined(__linux__) && defined(__powerpc__) && !defined(__powerpc64__)
+# define PLAT_ppc32_linux 1
+#elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__)
+# define PLAT_ppc64_linux 1
+#elif defined(__linux__) && defined(__arm__)
+# define PLAT_arm_linux 1
+#else
+/* If we're not compiling for our target platform, don't generate
+ any inline asms. */
+# if !defined(NVALGRIND)
+# define NVALGRIND 1
+# endif
+#endif
+
+
+/* ------------------------------------------------------------------ */
+/* ARCHITECTURE SPECIFICS for SPECIAL INSTRUCTIONS. There is nothing */
+/* in here of use to end-users -- skip to the next section. */
+/* ------------------------------------------------------------------ */
+
+#if defined(NVALGRIND)
+
+/* Define NVALGRIND to completely remove the Valgrind magic sequence
+ from the compiled code (analogous to NDEBUG's effects on
+ assert()) */
+#define VALGRIND_DO_CLIENT_REQUEST( \
+ _zzq_rlval, _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ { \
+ (_zzq_rlval) = (_zzq_default); \
+ }
+
+#else /* ! NVALGRIND */
+
+/* The following defines the magic code sequences which the JITter
+ spots and handles magically. Don't look too closely at them as
+ they will rot your brain.
+
+ The assembly code sequences for all architectures is in this one
+ file. This is because this file must be stand-alone, and we don't
+ want to have multiple files.
+
+ For VALGRIND_DO_CLIENT_REQUEST, we must ensure that the default
+ value gets put in the return slot, so that everything works when
+ this is executed not under Valgrind. Args are passed in a memory
+ block, and so there's no intrinsic limit to the number that could
+ be passed, but it's currently five.
+
+ The macro args are:
+ _zzq_rlval result lvalue
+ _zzq_default default value (result returned when running on real CPU)
+ _zzq_request request code
+ _zzq_arg1..5 request params
+
+ The other two macros are used to support function wrapping, and are
+ a lot simpler. VALGRIND_GET_NR_CONTEXT returns the value of the
+ guest's NRADDR pseudo-register and whatever other information is
+ needed to safely run the call original from the wrapper: on
+ ppc64-linux, the R2 value at the divert point is also needed. This
+ information is abstracted into a user-visible type, OrigFn.
+
+ VALGRIND_CALL_NOREDIR_* behaves the same as the following on the
+ guest, but guarantees that the branch instruction will not be
+ redirected: x86: call *%eax, amd64: call *%rax, ppc32/ppc64:
+ branch-and-link-to-r11. VALGRIND_CALL_NOREDIR is just text, not a
+ complete inline asm, since it needs to be combined with more magic
+ inline asm stuff to be useful.
+*/
+
+/* ------------------------- x86-{linux,darwin} ---------------- */
+
+#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \
+ || (defined(PLAT_x86_win32) && defined(__GNUC__))
+
+typedef
+ struct {
+ unsigned int nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "roll $3, %%edi ; roll $13, %%edi\n\t" \
+ "roll $29, %%edi ; roll $19, %%edi\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST( \
+ _zzq_rlval, _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ { volatile unsigned int _zzq_args[6]; \
+ volatile unsigned int _zzq_result; \
+ _zzq_args[0] = (unsigned int)(_zzq_request); \
+ _zzq_args[1] = (unsigned int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned int)(_zzq_arg5); \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %EDX = client_request ( %EAX ) */ \
+ "xchgl %%ebx,%%ebx" \
+ : "=d" (_zzq_result) \
+ : "a" (&_zzq_args[0]), "0" (_zzq_default) \
+ : "cc", "memory" \
+ ); \
+ _zzq_rlval = _zzq_result; \
+ }
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ volatile unsigned int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %EAX = guest_NRADDR */ \
+ "xchgl %%ecx,%%ecx" \
+ : "=a" (__addr) \
+ : \
+ : "cc", "memory" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_CALL_NOREDIR_EAX \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* call-noredir *%EAX */ \
+ "xchgl %%edx,%%edx\n\t"
+#endif /* PLAT_x86_linux || PLAT_x86_darwin || (PLAT_x86_win32 && __GNUC__) */
+
+/* ------------------------- x86-Win32 ------------------------- */
+
+#if defined(PLAT_x86_win32) && !defined(__GNUC__)
+
+typedef
+ struct {
+ unsigned int nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+#if defined(_MSC_VER)
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ __asm rol edi, 3 __asm rol edi, 13 \
+ __asm rol edi, 29 __asm rol edi, 19
+
+#define VALGRIND_DO_CLIENT_REQUEST( \
+ _zzq_rlval, _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ { volatile uintptr_t _zzq_args[6]; \
+ volatile unsigned int _zzq_result; \
+ _zzq_args[0] = (uintptr_t)(_zzq_request); \
+ _zzq_args[1] = (uintptr_t)(_zzq_arg1); \
+ _zzq_args[2] = (uintptr_t)(_zzq_arg2); \
+ _zzq_args[3] = (uintptr_t)(_zzq_arg3); \
+ _zzq_args[4] = (uintptr_t)(_zzq_arg4); \
+ _zzq_args[5] = (uintptr_t)(_zzq_arg5); \
+ __asm { __asm lea eax, _zzq_args __asm mov edx, _zzq_default \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %EDX = client_request ( %EAX ) */ \
+ __asm xchg ebx,ebx \
+ __asm mov _zzq_result, edx \
+ } \
+ _zzq_rlval = _zzq_result; \
+ }
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ volatile unsigned int __addr; \
+ __asm { __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %EAX = guest_NRADDR */ \
+ __asm xchg ecx,ecx \
+ __asm mov __addr, eax \
+ } \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_CALL_NOREDIR_EAX ERROR
+
+#else
+#error Unsupported compiler.
+#endif
+
+#endif /* PLAT_x86_win32 */
+
+/* ------------------------ amd64-{linux,darwin} --------------- */
+
+#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin)
+
+typedef
+ struct {
+ unsigned long long int nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "rolq $3, %%rdi ; rolq $13, %%rdi\n\t" \
+ "rolq $61, %%rdi ; rolq $51, %%rdi\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST( \
+ _zzq_rlval, _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ { volatile unsigned long long int _zzq_args[6]; \
+ volatile unsigned long long int _zzq_result; \
+ _zzq_args[0] = (unsigned long long int)(_zzq_request); \
+ _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %RDX = client_request ( %RAX ) */ \
+ "xchgq %%rbx,%%rbx" \
+ : "=d" (_zzq_result) \
+ : "a" (&_zzq_args[0]), "0" (_zzq_default) \
+ : "cc", "memory" \
+ ); \
+ _zzq_rlval = _zzq_result; \
+ }
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ volatile unsigned long long int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %RAX = guest_NRADDR */ \
+ "xchgq %%rcx,%%rcx" \
+ : "=a" (__addr) \
+ : \
+ : "cc", "memory" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_CALL_NOREDIR_RAX \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* call-noredir *%RAX */ \
+ "xchgq %%rdx,%%rdx\n\t"
+#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */
+
+/* ------------------------ ppc32-linux ------------------------ */
+
+#if defined(PLAT_ppc32_linux)
+
+typedef
+ struct {
+ unsigned int nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "rlwinm 0,0,3,0,0 ; rlwinm 0,0,13,0,0\n\t" \
+ "rlwinm 0,0,29,0,0 ; rlwinm 0,0,19,0,0\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST( \
+ _zzq_rlval, _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ \
+ { unsigned int _zzq_args[6]; \
+ unsigned int _zzq_result; \
+ unsigned int* _zzq_ptr; \
+ _zzq_args[0] = (unsigned int)(_zzq_request); \
+ _zzq_args[1] = (unsigned int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned int)(_zzq_arg5); \
+ _zzq_ptr = _zzq_args; \
+ __asm__ volatile("mr 3,%1\n\t" /*default*/ \
+ "mr 4,%2\n\t" /*ptr*/ \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = client_request ( %R4 ) */ \
+ "or 1,1,1\n\t" \
+ "mr %0,3" /*result*/ \
+ : "=b" (_zzq_result) \
+ : "b" (_zzq_default), "b" (_zzq_ptr) \
+ : "cc", "memory", "r3", "r4"); \
+ _zzq_rlval = _zzq_result; \
+ }
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ unsigned int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR */ \
+ "or 2,2,2\n\t" \
+ "mr %0,3" \
+ : "=b" (__addr) \
+ : \
+ : "cc", "memory", "r3" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* branch-and-link-to-noredir *%R11 */ \
+ "or 3,3,3\n\t"
+#endif /* PLAT_ppc32_linux */
+
+/* ------------------------ ppc64-linux ------------------------ */
+
+#if defined(PLAT_ppc64_linux)
+
+typedef
+ struct {
+ unsigned long long int nraddr; /* where's the code? */
+ unsigned long long int r2; /* what tocptr do we need? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \
+ "rotldi 0,0,61 ; rotldi 0,0,51\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST( \
+ _zzq_rlval, _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ \
+ { unsigned long long int _zzq_args[6]; \
+ register unsigned long long int _zzq_result __asm__("r3"); \
+ register unsigned long long int* _zzq_ptr __asm__("r4"); \
+ _zzq_args[0] = (unsigned long long int)(_zzq_request); \
+ _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \
+ _zzq_ptr = _zzq_args; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = client_request ( %R4 ) */ \
+ "or 1,1,1" \
+ : "=r" (_zzq_result) \
+ : "0" (_zzq_default), "r" (_zzq_ptr) \
+ : "cc", "memory"); \
+ _zzq_rlval = _zzq_result; \
+ }
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ register unsigned long long int __addr __asm__("r3"); \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR */ \
+ "or 2,2,2" \
+ : "=r" (__addr) \
+ : \
+ : "cc", "memory" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR_GPR2 */ \
+ "or 4,4,4" \
+ : "=r" (__addr) \
+ : \
+ : "cc", "memory" \
+ ); \
+ _zzq_orig->r2 = __addr; \
+ }
+
+#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* branch-and-link-to-noredir *%R11 */ \
+ "or 3,3,3\n\t"
+
+#endif /* PLAT_ppc64_linux */
+
+/* ------------------------- arm-linux ------------------------- */
+
+#if defined(PLAT_arm_linux)
+
+typedef
+ struct {
+ unsigned int nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "mov r12, r12, ror #3 ; mov r12, r12, ror #13 \n\t" \
+ "mov r12, r12, ror #29 ; mov r12, r12, ror #19 \n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST( \
+ _zzq_rlval, _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ \
+ { volatile unsigned int _zzq_args[6]; \
+ volatile unsigned int _zzq_result; \
+ _zzq_args[0] = (unsigned int)(_zzq_request); \
+ _zzq_args[1] = (unsigned int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned int)(_zzq_arg5); \
+ __asm__ volatile("mov r3, %1\n\t" /*default*/ \
+ "mov r4, %2\n\t" /*ptr*/ \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* R3 = client_request ( R4 ) */ \
+ "orr r10, r10, r10\n\t" \
+ "mov %0, r3" /*result*/ \
+ : "=r" (_zzq_result) \
+ : "r" (_zzq_default), "r" (&_zzq_args[0]) \
+ : "cc","memory", "r3", "r4"); \
+ _zzq_rlval = _zzq_result; \
+ }
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ unsigned int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* R3 = guest_NRADDR */ \
+ "orr r11, r11, r11\n\t" \
+ "mov %0, r3" \
+ : "=r" (__addr) \
+ : \
+ : "cc", "memory", "r3" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* branch-and-link-to-noredir *%R4 */ \
+ "orr r12, r12, r12\n\t"
+
+#endif /* PLAT_arm_linux */
+
+/* ------------------------ ppc32-aix5 ------------------------- */
+
+#if defined(PLAT_ppc32_aix5)
+
+typedef
+ struct {
+ unsigned int nraddr; /* where's the code? */
+ unsigned int r2; /* what tocptr do we need? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "rlwinm 0,0,3,0,0 ; rlwinm 0,0,13,0,0\n\t" \
+ "rlwinm 0,0,29,0,0 ; rlwinm 0,0,19,0,0\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST( \
+ _zzq_rlval, _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ \
+ { unsigned int _zzq_args[7]; \
+ register unsigned int _zzq_result; \
+ register unsigned int* _zzq_ptr; \
+ _zzq_args[0] = (unsigned int)(_zzq_request); \
+ _zzq_args[1] = (unsigned int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned int)(_zzq_arg5); \
+ _zzq_args[6] = (unsigned int)(_zzq_default); \
+ _zzq_ptr = _zzq_args; \
+ __asm__ volatile("mr 4,%1\n\t" \
+ "lwz 3, 24(4)\n\t" \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = client_request ( %R4 ) */ \
+ "or 1,1,1\n\t" \
+ "mr %0,3" \
+ : "=b" (_zzq_result) \
+ : "b" (_zzq_ptr) \
+ : "r3", "r4", "cc", "memory"); \
+ _zzq_rlval = _zzq_result; \
+ }
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ register unsigned int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR */ \
+ "or 2,2,2\n\t" \
+ "mr %0,3" \
+ : "=b" (__addr) \
+ : \
+ : "r3", "cc", "memory" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR_GPR2 */ \
+ "or 4,4,4\n\t" \
+ "mr %0,3" \
+ : "=b" (__addr) \
+ : \
+ : "r3", "cc", "memory" \
+ ); \
+ _zzq_orig->r2 = __addr; \
+ }
+
+#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* branch-and-link-to-noredir *%R11 */ \
+ "or 3,3,3\n\t"
+
+#endif /* PLAT_ppc32_aix5 */
+
+/* ------------------------ ppc64-aix5 ------------------------- */
+
+#if defined(PLAT_ppc64_aix5)
+
+typedef
+ struct {
+ unsigned long long int nraddr; /* where's the code? */
+ unsigned long long int r2; /* what tocptr do we need? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \
+ "rotldi 0,0,61 ; rotldi 0,0,51\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST( \
+ _zzq_rlval, _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ \
+ { unsigned long long int _zzq_args[7]; \
+ register unsigned long long int _zzq_result; \
+ register unsigned long long int* _zzq_ptr; \
+ _zzq_args[0] = (unsigned int long long)(_zzq_request); \
+ _zzq_args[1] = (unsigned int long long)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned int long long)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned int long long)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned int long long)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned int long long)(_zzq_arg5); \
+ _zzq_args[6] = (unsigned int long long)(_zzq_default); \
+ _zzq_ptr = _zzq_args; \
+ __asm__ volatile("mr 4,%1\n\t" \
+ "ld 3, 48(4)\n\t" \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = client_request ( %R4 ) */ \
+ "or 1,1,1\n\t" \
+ "mr %0,3" \
+ : "=b" (_zzq_result) \
+ : "b" (_zzq_ptr) \
+ : "r3", "r4", "cc", "memory"); \
+ _zzq_rlval = _zzq_result; \
+ }
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ register unsigned long long int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR */ \
+ "or 2,2,2\n\t" \
+ "mr %0,3" \
+ : "=b" (__addr) \
+ : \
+ : "r3", "cc", "memory" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR_GPR2 */ \
+ "or 4,4,4\n\t" \
+ "mr %0,3" \
+ : "=b" (__addr) \
+ : \
+ : "r3", "cc", "memory" \
+ ); \
+ _zzq_orig->r2 = __addr; \
+ }
+
+#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* branch-and-link-to-noredir *%R11 */ \
+ "or 3,3,3\n\t"
+
+#endif /* PLAT_ppc64_aix5 */
+
+/* Insert assembly code for other platforms here... */
+
+#endif /* NVALGRIND */
+
+
+/* ------------------------------------------------------------------ */
+/* PLATFORM SPECIFICS for FUNCTION WRAPPING. This is all very */
+/* ugly. It's the least-worst tradeoff I can think of. */
+/* ------------------------------------------------------------------ */
+
+/* This section defines magic (a.k.a appalling-hack) macros for doing
+ guaranteed-no-redirection macros, so as to get from function
+ wrappers to the functions they are wrapping. The whole point is to
+ construct standard call sequences, but to do the call itself with a
+ special no-redirect call pseudo-instruction that the JIT
+ understands and handles specially. This section is long and
+ repetitious, and I can't see a way to make it shorter.
+
+ The naming scheme is as follows:
+
+ CALL_FN_{W,v}_{v,W,WW,WWW,WWWW,5W,6W,7W,etc}
+
+ 'W' stands for "word" and 'v' for "void". Hence there are
+ different macros for calling arity 0, 1, 2, 3, 4, etc, functions,
+ and for each, the possibility of returning a word-typed result, or
+ no result.
+*/
+
+/* Use these to write the name of your wrapper. NOTE: duplicates
+ VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h. */
+
+/* Use an extra level of macroisation so as to ensure the soname/fnname
+ args are fully macro-expanded before pasting them together. */
+#define VG_CONCAT4(_aa,_bb,_cc,_dd) _aa##_bb##_cc##_dd
+
+#define I_WRAP_SONAME_FNNAME_ZU(soname,fnname) \
+ VG_CONCAT4(_vgwZU_,soname,_,fnname)
+
+#define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname) \
+ VG_CONCAT4(_vgwZZ_,soname,_,fnname)
+
+/* Use this macro from within a wrapper function to collect the
+ context (address and possibly other info) of the original function.
+ Once you have that you can then use it in one of the CALL_FN_
+ macros. The type of the argument _lval is OrigFn. */
+#define VALGRIND_GET_ORIG_FN(_lval) VALGRIND_GET_NR_CONTEXT(_lval)
+
+/* Derivatives of the main macros below, for calling functions
+ returning void. */
+
+#define CALL_FN_v_v(fnptr) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_v(_junk,fnptr); } while (0)
+
+#define CALL_FN_v_W(fnptr, arg1) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_W(_junk,fnptr,arg1); } while (0)
+
+#define CALL_FN_v_WW(fnptr, arg1,arg2) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_WW(_junk,fnptr,arg1,arg2); } while (0)
+
+#define CALL_FN_v_WWW(fnptr, arg1,arg2,arg3) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_WWW(_junk,fnptr,arg1,arg2,arg3); } while (0)
+
+#define CALL_FN_v_WWWW(fnptr, arg1,arg2,arg3,arg4) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_WWWW(_junk,fnptr,arg1,arg2,arg3,arg4); } while (0)
+
+#define CALL_FN_v_5W(fnptr, arg1,arg2,arg3,arg4,arg5) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_5W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5); } while (0)
+
+#define CALL_FN_v_6W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_6W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6); } while (0)
+
+#define CALL_FN_v_7W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6,arg7) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_7W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6,arg7); } while (0)
+
+/* ------------------------- x86-{linux,darwin} ---------------- */
+
+#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin)
+
+/* These regs are trashed by the hidden call. No need to mention eax
+ as gcc can already see that, plus causes gcc to bomb. */
+#define __CALLER_SAVED_REGS /*"eax"*/ "ecx", "edx"
+
+/* These CALL_FN_ macros assume that on x86-linux, sizeof(unsigned
+ long) == 4. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ __asm__ volatile( \
+ "subl $12, %%esp\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $16, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ __asm__ volatile( \
+ "subl $8, %%esp\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $16, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ __asm__ volatile( \
+ "subl $4, %%esp\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $16, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ __asm__ volatile( \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $16, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ __asm__ volatile( \
+ "subl $12, %%esp\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $32, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ __asm__ volatile( \
+ "subl $8, %%esp\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $32, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ __asm__ volatile( \
+ "subl $4, %%esp\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $32, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ __asm__ volatile( \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $32, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ __asm__ volatile( \
+ "subl $12, %%esp\n\t" \
+ "pushl 36(%%eax)\n\t" \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $48, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ __asm__ volatile( \
+ "subl $8, %%esp\n\t" \
+ "pushl 40(%%eax)\n\t" \
+ "pushl 36(%%eax)\n\t" \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $48, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ __asm__ volatile( \
+ "subl $4, %%esp\n\t" \
+ "pushl 44(%%eax)\n\t" \
+ "pushl 40(%%eax)\n\t" \
+ "pushl 36(%%eax)\n\t" \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $48, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ _argvec[12] = (unsigned long)(arg12); \
+ __asm__ volatile( \
+ "pushl 48(%%eax)\n\t" \
+ "pushl 44(%%eax)\n\t" \
+ "pushl 40(%%eax)\n\t" \
+ "pushl 36(%%eax)\n\t" \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $48, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_x86_linux || PLAT_x86_darwin */
+
+/* ------------------------ amd64-{linux,darwin} --------------- */
+
+#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin)
+
+/* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS /*"rax",*/ "rcx", "rdx", "rsi", \
+ "rdi", "r8", "r9", "r10", "r11"
+
+/* This is all pretty complex. It's so as to make stack unwinding
+ work reliably. See bug 243270. The basic problem is the sub and
+ add of 128 of %rsp in all of the following macros. If gcc believes
+ the CFA is in %rsp, then unwinding may fail, because what's at the
+ CFA is not what gcc "expected" when it constructs the CFIs for the
+ places where the macros are instantiated.
+
+ But we can't just add a CFI annotation to increase the CFA offset
+ by 128, to match the sub of 128 from %rsp, because we don't know
+ whether gcc has chosen %rsp as the CFA at that point, or whether it
+ has chosen some other register (eg, %rbp). In the latter case,
+ adding a CFI annotation to change the CFA offset is simply wrong.
+
+ So the solution is to get hold of the CFA using
+ __builtin_dwarf_cfa(), put it in a known register, and add a
+ CFI annotation to say what the register is. We choose %rbp for
+ this (perhaps perversely), because:
+
+ (1) %rbp is already subject to unwinding. If a new register was
+ chosen then the unwinder would have to unwind it in all stack
+ traces, which is expensive, and
+
+ (2) %rbp is already subject to precise exception updates in the
+ JIT. If a new register was chosen, we'd have to have precise
+ exceptions for it too, which reduces performance of the
+ generated code.
+
+ However .. one extra complication. We can't just whack the result
+ of __builtin_dwarf_cfa() into %rbp and then add %rbp to the
+ list of trashed registers at the end of the inline assembly
+ fragments; gcc won't allow %rbp to appear in that list. Hence
+ instead we need to stash %rbp in %r15 for the duration of the asm,
+ and say that %r15 is trashed instead. gcc seems happy to go with
+ that.
+
+ Oh .. and this all needs to be conditionalised so that it is
+ unchanged from before this commit, when compiled with older gccs
+ that don't support __builtin_dwarf_cfa. Furthermore, since
+ this header file is freestanding, it has to be independent of
+ config.h, and so the following conditionalisation cannot depend on
+ configure time checks.
+
+ Although it's not clear from
+ 'defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM)',
+ this expression excludes Darwin.
+ .cfi directives in Darwin assembly appear to be completely
+ different and I haven't investigated how they work.
+
+ For even more entertainment value, note we have to use the
+ completely undocumented __builtin_dwarf_cfa(), which appears to
+ really compute the CFA, whereas __builtin_frame_address(0) claims
+ to but actually doesn't. See
+ https://bugs.kde.org/show_bug.cgi?id=243270#c47
+*/
+#if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM)
+# define __FRAME_POINTER \
+ ,"r"(__builtin_dwarf_cfa())
+# define VALGRIND_CFI_PROLOGUE \
+ "movq %%rbp, %%r15\n\t" \
+ "movq %2, %%rbp\n\t" \
+ ".cfi_remember_state\n\t" \
+ ".cfi_def_cfa rbp, 0\n\t"
+# define VALGRIND_CFI_EPILOGUE \
+ "movq %%r15, %%rbp\n\t" \
+ ".cfi_restore_state\n\t"
+#else
+# define __FRAME_POINTER
+# define VALGRIND_CFI_PROLOGUE
+# define VALGRIND_CFI_EPILOGUE
+#endif
+
+
+/* These CALL_FN_ macros assume that on amd64-linux, sizeof(unsigned
+ long) == 8. */
+
+/* NB 9 Sept 07. There is a nasty kludge here in all these CALL_FN_
+ macros. In order not to trash the stack redzone, we need to drop
+ %rsp by 128 before the hidden call, and restore afterwards. The
+ nastyness is that it is only by luck that the stack still appears
+ to be unwindable during the hidden call - since then the behaviour
+ of any routine using this macro does not match what the CFI data
+ says. Sigh.
+
+ Why is this important? Imagine that a wrapper has a stack
+ allocated local, and passes to the hidden call, a pointer to it.
+ Because gcc does not know about the hidden call, it may allocate
+ that local in the redzone. Unfortunately the hidden call may then
+ trash it before it comes to use it. So we must step clear of the
+ redzone, for the duration of the hidden call, to make it safe.
+
+ Probably the same problem afflicts the other redzone-style ABIs too
+ (ppc64-linux, ppc32-aix5, ppc64-aix5); but for those, the stack is
+ self describing (none of this CFI nonsense) so at least messing
+ with the stack pointer doesn't give a danger of non-unwindable
+ stack. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $136,%%rsp\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $8, %%rsp\n" \
+ "addq $136,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $16, %%rsp\n" \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $136,%%rsp\n\t" \
+ "pushq 72(%%rax)\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $24, %%rsp\n" \
+ "addq $136,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "pushq 80(%%rax)\n\t" \
+ "pushq 72(%%rax)\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $32, %%rsp\n" \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $136,%%rsp\n\t" \
+ "pushq 88(%%rax)\n\t" \
+ "pushq 80(%%rax)\n\t" \
+ "pushq 72(%%rax)\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $40, %%rsp\n" \
+ "addq $136,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ _argvec[12] = (unsigned long)(arg12); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "pushq 96(%%rax)\n\t" \
+ "pushq 88(%%rax)\n\t" \
+ "pushq 80(%%rax)\n\t" \
+ "pushq 72(%%rax)\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $48, %%rsp\n" \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */
+
+/* ------------------------ ppc32-linux ------------------------ */
+
+#if defined(PLAT_ppc32_linux)
+
+/* This is useful for finding out about the on-stack stuff:
+
+ extern int f9 ( int,int,int,int,int,int,int,int,int );
+ extern int f10 ( int,int,int,int,int,int,int,int,int,int );
+ extern int f11 ( int,int,int,int,int,int,int,int,int,int,int );
+ extern int f12 ( int,int,int,int,int,int,int,int,int,int,int,int );
+
+ int g9 ( void ) {
+ return f9(11,22,33,44,55,66,77,88,99);
+ }
+ int g10 ( void ) {
+ return f10(11,22,33,44,55,66,77,88,99,110);
+ }
+ int g11 ( void ) {
+ return f11(11,22,33,44,55,66,77,88,99,110,121);
+ }
+ int g12 ( void ) {
+ return f12(11,22,33,44,55,66,77,88,99,110,121,132);
+ }
+*/
+
+/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS \
+ "lr", "ctr", "xer", \
+ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \
+ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \
+ "r11", "r12", "r13"
+
+/* These CALL_FN_ macros assume that on ppc32-linux,
+ sizeof(unsigned long) == 4. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "addi 1,1,-16\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,8(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "addi 1,1,16\n\t" \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ _argvec[10] = (unsigned long)arg10; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "addi 1,1,-16\n\t" \
+ /* arg10 */ \
+ "lwz 3,40(11)\n\t" \
+ "stw 3,12(1)\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,8(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "addi 1,1,16\n\t" \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ _argvec[10] = (unsigned long)arg10; \
+ _argvec[11] = (unsigned long)arg11; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "addi 1,1,-32\n\t" \
+ /* arg11 */ \
+ "lwz 3,44(11)\n\t" \
+ "stw 3,16(1)\n\t" \
+ /* arg10 */ \
+ "lwz 3,40(11)\n\t" \
+ "stw 3,12(1)\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,8(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "addi 1,1,32\n\t" \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ _argvec[10] = (unsigned long)arg10; \
+ _argvec[11] = (unsigned long)arg11; \
+ _argvec[12] = (unsigned long)arg12; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "addi 1,1,-32\n\t" \
+ /* arg12 */ \
+ "lwz 3,48(11)\n\t" \
+ "stw 3,20(1)\n\t" \
+ /* arg11 */ \
+ "lwz 3,44(11)\n\t" \
+ "stw 3,16(1)\n\t" \
+ /* arg10 */ \
+ "lwz 3,40(11)\n\t" \
+ "stw 3,12(1)\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,8(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "addi 1,1,32\n\t" \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_ppc32_linux */
+
+/* ------------------------ ppc64-linux ------------------------ */
+
+#if defined(PLAT_ppc64_linux)
+
+/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS \
+ "lr", "ctr", "xer", \
+ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \
+ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \
+ "r11", "r12", "r13"
+
+/* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned
+ long) == 8. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+0]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+1]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+2]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+3]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+4]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+5]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+6]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+7]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+8]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+9]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "addi 1,1,-128\n\t" /* expand stack frame */ \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ "addi 1,1,128" /* restore frame */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+10]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "addi 1,1,-128\n\t" /* expand stack frame */ \
+ /* arg10 */ \
+ "ld 3,80(11)\n\t" \
+ "std 3,120(1)\n\t" \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ "addi 1,1,128" /* restore frame */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+11]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ _argvec[2+11] = (unsigned long)arg11; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "addi 1,1,-144\n\t" /* expand stack frame */ \
+ /* arg11 */ \
+ "ld 3,88(11)\n\t" \
+ "std 3,128(1)\n\t" \
+ /* arg10 */ \
+ "ld 3,80(11)\n\t" \
+ "std 3,120(1)\n\t" \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ "addi 1,1,144" /* restore frame */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+12]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ _argvec[2+11] = (unsigned long)arg11; \
+ _argvec[2+12] = (unsigned long)arg12; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "addi 1,1,-144\n\t" /* expand stack frame */ \
+ /* arg12 */ \
+ "ld 3,96(11)\n\t" \
+ "std 3,136(1)\n\t" \
+ /* arg11 */ \
+ "ld 3,88(11)\n\t" \
+ "std 3,128(1)\n\t" \
+ /* arg10 */ \
+ "ld 3,80(11)\n\t" \
+ "std 3,120(1)\n\t" \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ "addi 1,1,144" /* restore frame */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_ppc64_linux */
+
+/* ------------------------- arm-linux ------------------------- */
+
+#if defined(PLAT_arm_linux)
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS "r0", "r1", "r2", "r3","r4","r14"
+
+/* These CALL_FN_ macros assume that on arm-linux, sizeof(unsigned
+ long) == 4. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "mov %0, r0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "mov %0, r0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "mov %0, r0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "mov %0, r0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #20] \n\t" \
+ "push {r0} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "add sp, sp, #4 \n\t" \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "push {r0, r1} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "add sp, sp, #8 \n\t" \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "ldr r2, [%1, #28] \n\t" \
+ "push {r0, r1, r2} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "add sp, sp, #12 \n\t" \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "ldr r2, [%1, #28] \n\t" \
+ "ldr r3, [%1, #32] \n\t" \
+ "push {r0, r1, r2, r3} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "add sp, sp, #16 \n\t" \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "ldr r2, [%1, #28] \n\t" \
+ "ldr r3, [%1, #32] \n\t" \
+ "ldr r4, [%1, #36] \n\t" \
+ "push {r0, r1, r2, r3, r4} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "add sp, sp, #20 \n\t" \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #40] \n\t" \
+ "push {r0} \n\t" \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "ldr r2, [%1, #28] \n\t" \
+ "ldr r3, [%1, #32] \n\t" \
+ "ldr r4, [%1, #36] \n\t" \
+ "push {r0, r1, r2, r3, r4} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "add sp, sp, #24 \n\t" \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #40] \n\t" \
+ "ldr r1, [%1, #44] \n\t" \
+ "push {r0, r1} \n\t" \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "ldr r2, [%1, #28] \n\t" \
+ "ldr r3, [%1, #32] \n\t" \
+ "ldr r4, [%1, #36] \n\t" \
+ "push {r0, r1, r2, r3, r4} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "add sp, sp, #28 \n\t" \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory",__CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ _argvec[12] = (unsigned long)(arg12); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #40] \n\t" \
+ "ldr r1, [%1, #44] \n\t" \
+ "ldr r2, [%1, #48] \n\t" \
+ "push {r0, r1, r2} \n\t" \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "ldr r2, [%1, #28] \n\t" \
+ "ldr r3, [%1, #32] \n\t" \
+ "ldr r4, [%1, #36] \n\t" \
+ "push {r0, r1, r2, r3, r4} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "add sp, sp, #32 \n\t" \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_arm_linux */
+
+/* ------------------------ ppc32-aix5 ------------------------- */
+
+#if defined(PLAT_ppc32_aix5)
+
+/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS \
+ "lr", "ctr", "xer", \
+ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \
+ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \
+ "r11", "r12", "r13"
+
+/* Expand the stack frame, copying enough info that unwinding
+ still works. Trashes r3. */
+
+#define VG_EXPAND_FRAME_BY_trashes_r3(_n_fr) \
+ "addi 1,1,-" #_n_fr "\n\t" \
+ "lwz 3," #_n_fr "(1)\n\t" \
+ "stw 3,0(1)\n\t"
+
+#define VG_CONTRACT_FRAME_BY(_n_fr) \
+ "addi 1,1," #_n_fr "\n\t"
+
+/* These CALL_FN_ macros assume that on ppc32-aix5, sizeof(unsigned
+ long) == 4. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+0]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+1]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+2]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+3]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+4]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+5]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+6]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
+ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+7]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
+ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
+ "lwz 9, 28(11)\n\t" /* arg7->r9 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+8]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
+ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
+ "lwz 9, 28(11)\n\t" /* arg7->r9 */ \
+ "lwz 10, 32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+9]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ VG_EXPAND_FRAME_BY_trashes_r3(64) \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,56(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
+ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
+ "lwz 9, 28(11)\n\t" /* arg7->r9 */ \
+ "lwz 10, 32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(64) \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+10]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ VG_EXPAND_FRAME_BY_trashes_r3(64) \
+ /* arg10 */ \
+ "lwz 3,40(11)\n\t" \
+ "stw 3,60(1)\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,56(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
+ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
+ "lwz 9, 28(11)\n\t" /* arg7->r9 */ \
+ "lwz 10, 32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(64) \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+11]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ _argvec[2+11] = (unsigned long)arg11; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ VG_EXPAND_FRAME_BY_trashes_r3(72) \
+ /* arg11 */ \
+ "lwz 3,44(11)\n\t" \
+ "stw 3,64(1)\n\t" \
+ /* arg10 */ \
+ "lwz 3,40(11)\n\t" \
+ "stw 3,60(1)\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,56(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
+ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
+ "lwz 9, 28(11)\n\t" /* arg7->r9 */ \
+ "lwz 10, 32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(72) \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+12]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ _argvec[2+11] = (unsigned long)arg11; \
+ _argvec[2+12] = (unsigned long)arg12; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "stw 2,-8(11)\n\t" /* save tocptr */ \
+ "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \
+ VG_EXPAND_FRAME_BY_trashes_r3(72) \
+ /* arg12 */ \
+ "lwz 3,48(11)\n\t" \
+ "stw 3,68(1)\n\t" \
+ /* arg11 */ \
+ "lwz 3,44(11)\n\t" \
+ "stw 3,64(1)\n\t" \
+ /* arg10 */ \
+ "lwz 3,40(11)\n\t" \
+ "stw 3,60(1)\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,56(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3, 4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4, 8(11)\n\t" /* arg2->r4 */ \
+ "lwz 5, 12(11)\n\t" /* arg3->r5 */ \
+ "lwz 6, 16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7, 20(11)\n\t" /* arg5->r7 */ \
+ "lwz 8, 24(11)\n\t" /* arg6->r8 */ \
+ "lwz 9, 28(11)\n\t" /* arg7->r9 */ \
+ "lwz 10, 32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "lwz 2,-8(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(72) \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_ppc32_aix5 */
+
+/* ------------------------ ppc64-aix5 ------------------------- */
+
+#if defined(PLAT_ppc64_aix5)
+
+/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS \
+ "lr", "ctr", "xer", \
+ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \
+ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \
+ "r11", "r12", "r13"
+
+/* Expand the stack frame, copying enough info that unwinding
+ still works. Trashes r3. */
+
+#define VG_EXPAND_FRAME_BY_trashes_r3(_n_fr) \
+ "addi 1,1,-" #_n_fr "\n\t" \
+ "ld 3," #_n_fr "(1)\n\t" \
+ "std 3,0(1)\n\t"
+
+#define VG_CONTRACT_FRAME_BY(_n_fr) \
+ "addi 1,1," #_n_fr "\n\t"
+
+/* These CALL_FN_ macros assume that on ppc64-aix5, sizeof(unsigned
+ long) == 8. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+0]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+1]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+2]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+3]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+4]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+5]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+6]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+7]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+8]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+9]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ VG_EXPAND_FRAME_BY_trashes_r3(128) \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(128) \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+10]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ VG_EXPAND_FRAME_BY_trashes_r3(128) \
+ /* arg10 */ \
+ "ld 3,80(11)\n\t" \
+ "std 3,120(1)\n\t" \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(128) \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+11]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ _argvec[2+11] = (unsigned long)arg11; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ VG_EXPAND_FRAME_BY_trashes_r3(144) \
+ /* arg11 */ \
+ "ld 3,88(11)\n\t" \
+ "std 3,128(1)\n\t" \
+ /* arg10 */ \
+ "ld 3,80(11)\n\t" \
+ "std 3,120(1)\n\t" \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(144) \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+12]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ _argvec[2+11] = (unsigned long)arg11; \
+ _argvec[2+12] = (unsigned long)arg12; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ VG_EXPAND_FRAME_BY_trashes_r3(512) \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ VG_EXPAND_FRAME_BY_trashes_r3(144) \
+ /* arg12 */ \
+ "ld 3,96(11)\n\t" \
+ "std 3,136(1)\n\t" \
+ /* arg11 */ \
+ "ld 3,88(11)\n\t" \
+ "std 3,128(1)\n\t" \
+ /* arg10 */ \
+ "ld 3,80(11)\n\t" \
+ "std 3,120(1)\n\t" \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VG_CONTRACT_FRAME_BY(144) \
+ VG_CONTRACT_FRAME_BY(512) \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_ppc64_aix5 */
+
+
+/* ------------------------------------------------------------------ */
+/* ARCHITECTURE INDEPENDENT MACROS for CLIENT REQUESTS. */
+/* */
+/* ------------------------------------------------------------------ */
+
+/* Some request codes. There are many more of these, but most are not
+ exposed to end-user view. These are the public ones, all of the
+ form 0x1000 + small_number.
+
+ Core ones are in the range 0x00000000--0x0000ffff. The non-public
+ ones start at 0x2000.
+*/
+
+/* These macros are used by tools -- they must be public, but don't
+ embed them into other programs. */
+#define VG_USERREQ_TOOL_BASE(a,b) \
+ ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16))
+#define VG_IS_TOOL_USERREQ(a, b, v) \
+ (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000))
+
+/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !!
+ This enum comprises an ABI exported by Valgrind to programs
+ which use client requests. DO NOT CHANGE THE ORDER OF THESE
+ ENTRIES, NOR DELETE ANY -- add new ones at the end. */
+typedef
+ enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001,
+ VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002,
+
+ /* These allow any function to be called from the simulated
+ CPU but run on the real CPU. Nb: the first arg passed to
+ the function is always the ThreadId of the running
+ thread! So CLIENT_CALL0 actually requires a 1 arg
+ function, etc. */
+ VG_USERREQ__CLIENT_CALL0 = 0x1101,
+ VG_USERREQ__CLIENT_CALL1 = 0x1102,
+ VG_USERREQ__CLIENT_CALL2 = 0x1103,
+ VG_USERREQ__CLIENT_CALL3 = 0x1104,
+
+ /* Can be useful in regression testing suites -- eg. can
+ send Valgrind's output to /dev/null and still count
+ errors. */
+ VG_USERREQ__COUNT_ERRORS = 0x1201,
+
+ /* These are useful and can be interpreted by any tool that
+ tracks malloc() et al, by using vg_replace_malloc.c. */
+ VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301,
+ VG_USERREQ__FREELIKE_BLOCK = 0x1302,
+ /* Memory pool support. */
+ VG_USERREQ__CREATE_MEMPOOL = 0x1303,
+ VG_USERREQ__DESTROY_MEMPOOL = 0x1304,
+ VG_USERREQ__MEMPOOL_ALLOC = 0x1305,
+ VG_USERREQ__MEMPOOL_FREE = 0x1306,
+ VG_USERREQ__MEMPOOL_TRIM = 0x1307,
+ VG_USERREQ__MOVE_MEMPOOL = 0x1308,
+ VG_USERREQ__MEMPOOL_CHANGE = 0x1309,
+ VG_USERREQ__MEMPOOL_EXISTS = 0x130a,
+
+ /* Allow printfs to valgrind log. */
+ /* The first two pass the va_list argument by value, which
+ assumes it is the same size as or smaller than a UWord,
+ which generally isn't the case. Hence are deprecated.
+ The second two pass the vargs by reference and so are
+ immune to this problem. */
+ /* both :: char* fmt, va_list vargs (DEPRECATED) */
+ VG_USERREQ__PRINTF = 0x1401,
+ VG_USERREQ__PRINTF_BACKTRACE = 0x1402,
+ /* both :: char* fmt, va_list* vargs */
+ VG_USERREQ__PRINTF_VALIST_BY_REF = 0x1403,
+ VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF = 0x1404,
+
+ /* Stack support. */
+ VG_USERREQ__STACK_REGISTER = 0x1501,
+ VG_USERREQ__STACK_DEREGISTER = 0x1502,
+ VG_USERREQ__STACK_CHANGE = 0x1503,
+
+ /* Wine support */
+ VG_USERREQ__LOAD_PDB_DEBUGINFO = 0x1601,
+
+ /* Querying of debug info. */
+ VG_USERREQ__MAP_IP_TO_SRCLOC = 0x1701
+ } Vg_ClientRequest;
+
+#if !defined(__GNUC__)
+# define __extension__ /* */
+#endif
+
+
+/*
+ * VALGRIND_DO_CLIENT_REQUEST_EXPR(): a C expression that invokes a Valgrind
+ * client request and whose value equals the client request result.
+ */
+
+#if defined(NVALGRIND)
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ (_zzq_default)
+
+#else /*defined(NVALGRIND)*/
+
+#if defined(_MSC_VER)
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ (vg_VALGRIND_DO_CLIENT_REQUEST_EXPR((uintptr_t)(_zzq_default), \
+ (_zzq_request), (uintptr_t)(_zzq_arg1), (uintptr_t)(_zzq_arg2), \
+ (uintptr_t)(_zzq_arg3), (uintptr_t)(_zzq_arg4), \
+ (uintptr_t)(_zzq_arg5)))
+
+static __inline unsigned
+vg_VALGRIND_DO_CLIENT_REQUEST_EXPR(uintptr_t _zzq_default,
+ unsigned _zzq_request, uintptr_t _zzq_arg1,
+ uintptr_t _zzq_arg2, uintptr_t _zzq_arg3,
+ uintptr_t _zzq_arg4, uintptr_t _zzq_arg5)
+{
+ unsigned _zzq_rlval;
+ VALGRIND_DO_CLIENT_REQUEST(_zzq_rlval, _zzq_default, _zzq_request,
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5);
+ return _zzq_rlval;
+}
+
+#else /*defined(_MSC_VER)*/
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ (__extension__({unsigned int _zzq_rlval; \
+ VALGRIND_DO_CLIENT_REQUEST(_zzq_rlval, _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ _zzq_rlval; \
+ }))
+
+#endif /*defined(_MSC_VER)*/
+
+#endif /*defined(NVALGRIND)*/
+
+
+/* Returns the number of Valgrinds this code is running under. That
+ is, 0 if running natively, 1 if running under Valgrind, 2 if
+ running under Valgrind which is running under another Valgrind,
+ etc. */
+#define RUNNING_ON_VALGRIND \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* if not */, \
+ VG_USERREQ__RUNNING_ON_VALGRIND, \
+ 0, 0, 0, 0, 0) \
+
+
+/* Discard translation of code in the range [_qzz_addr .. _qzz_addr +
+ _qzz_len - 1]. Useful if you are debugging a JITter or some such,
+ since it provides a way to make sure valgrind will retranslate the
+ invalidated area. Returns no value. */
+#define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__DISCARD_TRANSLATIONS, \
+ _qzz_addr, _qzz_len, 0, 0, 0); \
+ }
+
+
+/* These requests are for getting Valgrind itself to print something.
+ Possibly with a backtrace. This is a really ugly hack. The return value
+ is the number of characters printed, excluding the "**<pid>** " part at the
+ start and the backtrace (if present). */
+
+#if defined(NVALGRIND)
+
+# define VALGRIND_PRINTF(...)
+# define VALGRIND_PRINTF_BACKTRACE(...)
+
+#else /* NVALGRIND */
+
+#if !defined(_MSC_VER)
+/* Modern GCC will optimize the static routine out if unused,
+ and unused attribute will shut down warnings about it. */
+static int VALGRIND_PRINTF(const char *format, ...)
+ __attribute__((format(__printf__, 1, 2), __unused__));
+#endif
+static int
+#if defined(_MSC_VER)
+__inline
+#endif
+VALGRIND_PRINTF(const char *format, ...)
+{
+ unsigned long _qzz_res;
+ va_list vargs;
+ va_start(vargs, format);
+#if defined(_MSC_VER)
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0,
+ VG_USERREQ__PRINTF_VALIST_BY_REF,
+ (uintptr_t)format,
+ (uintptr_t)&vargs,
+ 0, 0, 0);
+#else
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0,
+ VG_USERREQ__PRINTF_VALIST_BY_REF,
+ (unsigned long)format,
+ (unsigned long)&vargs,
+ 0, 0, 0);
+#endif
+ va_end(vargs);
+ return (int)_qzz_res;
+}
+
+#if !defined(_MSC_VER)
+static int VALGRIND_PRINTF_BACKTRACE(const char *format, ...)
+ __attribute__((format(__printf__, 1, 2), __unused__));
+#endif
+static int
+#if defined(_MSC_VER)
+__inline
+#endif
+VALGRIND_PRINTF_BACKTRACE(const char *format, ...)
+{
+ unsigned long _qzz_res;
+ va_list vargs;
+ va_start(vargs, format);
+#if defined(_MSC_VER)
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0,
+ VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF,
+ (uintptr_t)format,
+ (uintptr_t)&vargs,
+ 0, 0, 0);
+#else
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0,
+ VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF,
+ (unsigned long)format,
+ (unsigned long)&vargs,
+ 0, 0, 0);
+#endif
+ va_end(vargs);
+ return (int)_qzz_res;
+}
+
+#endif /* NVALGRIND */
+
+
+/* These requests allow control to move from the simulated CPU to the
+ real CPU, calling an arbitary function.
+
+ Note that the current ThreadId is inserted as the first argument.
+ So this call:
+
+ VALGRIND_NON_SIMD_CALL2(f, arg1, arg2)
+
+ requires f to have this signature:
+
+ Word f(Word tid, Word arg1, Word arg2)
+
+ where "Word" is a word-sized type.
+
+ Note that these client requests are not entirely reliable. For example,
+ if you call a function with them that subsequently calls printf(),
+ there's a high chance Valgrind will crash. Generally, your prospects of
+ these working are made higher if the called function does not refer to
+ any global variables, and does not refer to any libc or other functions
+ (printf et al). Any kind of entanglement with libc or dynamic linking is
+ likely to have a bad outcome, for tricky reasons which we've grappled
+ with a lot in the past.
+*/
+#define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \
+ __extension__ \
+ ({unsigned long _qyy_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \
+ VG_USERREQ__CLIENT_CALL0, \
+ _qyy_fn, \
+ 0, 0, 0, 0); \
+ _qyy_res; \
+ })
+
+#define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \
+ __extension__ \
+ ({unsigned long _qyy_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \
+ VG_USERREQ__CLIENT_CALL1, \
+ _qyy_fn, \
+ _qyy_arg1, 0, 0, 0); \
+ _qyy_res; \
+ })
+
+#define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \
+ __extension__ \
+ ({unsigned long _qyy_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \
+ VG_USERREQ__CLIENT_CALL2, \
+ _qyy_fn, \
+ _qyy_arg1, _qyy_arg2, 0, 0); \
+ _qyy_res; \
+ })
+
+#define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \
+ __extension__ \
+ ({unsigned long _qyy_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \
+ VG_USERREQ__CLIENT_CALL3, \
+ _qyy_fn, \
+ _qyy_arg1, _qyy_arg2, \
+ _qyy_arg3, 0); \
+ _qyy_res; \
+ })
+
+
+/* Counts the number of errors that have been recorded by a tool. Nb:
+ the tool must record the errors with VG_(maybe_record_error)() or
+ VG_(unique_error)() for them to be counted. */
+#define VALGRIND_COUNT_ERRORS \
+ __extension__ \
+ ({unsigned int _qyy_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \
+ VG_USERREQ__COUNT_ERRORS, \
+ 0, 0, 0, 0, 0); \
+ _qyy_res; \
+ })
+
+/* Several Valgrind tools (Memcheck, Massif, Helgrind, DRD) rely on knowing
+ when heap blocks are allocated in order to give accurate results. This
+ happens automatically for the standard allocator functions such as
+ malloc(), calloc(), realloc(), memalign(), new, new[], free(), delete,
+ delete[], etc.
+
+ But if your program uses a custom allocator, this doesn't automatically
+ happen, and Valgrind will not do as well. For example, if you allocate
+ superblocks with mmap() and then allocates chunks of the superblocks, all
+ Valgrind's observations will be at the mmap() level and it won't know that
+ the chunks should be considered separate entities. In Memcheck's case,
+ that means you probably won't get heap block overrun detection (because
+ there won't be redzones marked as unaddressable) and you definitely won't
+ get any leak detection.
+
+ The following client requests allow a custom allocator to be annotated so
+ that it can be handled accurately by Valgrind.
+
+ VALGRIND_MALLOCLIKE_BLOCK marks a region of memory as having been allocated
+ by a malloc()-like function. For Memcheck (an illustrative case), this
+ does two things:
+
+ - It records that the block has been allocated. This means any addresses
+ within the block mentioned in error messages will be
+ identified as belonging to the block. It also means that if the block
+ isn't freed it will be detected by the leak checker.
+
+ - It marks the block as being addressable and undefined (if 'is_zeroed' is
+ not set), or addressable and defined (if 'is_zeroed' is set). This
+ controls how accesses to the block by the program are handled.
+
+ 'addr' is the start of the usable block (ie. after any
+ redzone), 'sizeB' is its size. 'rzB' is the redzone size if the allocator
+ can apply redzones -- these are blocks of padding at the start and end of
+ each block. Adding redzones is recommended as it makes it much more likely
+ Valgrind will spot block overruns. `is_zeroed' indicates if the memory is
+ zeroed (or filled with another predictable value), as is the case for
+ calloc().
+
+ VALGRIND_MALLOCLIKE_BLOCK should be put immediately after the point where a
+ heap block -- that will be used by the client program -- is allocated.
+ It's best to put it at the outermost level of the allocator if possible;
+ for example, if you have a function my_alloc() which calls
+ internal_alloc(), and the client request is put inside internal_alloc(),
+ stack traces relating to the heap block will contain entries for both
+ my_alloc() and internal_alloc(), which is probably not what you want.
+
+ For Memcheck users: if you use VALGRIND_MALLOCLIKE_BLOCK to carve out
+ custom blocks from within a heap block, B, that has been allocated with
+ malloc/calloc/new/etc, then block B will be *ignored* during leak-checking
+ -- the custom blocks will take precedence.
+
+ VALGRIND_FREELIKE_BLOCK is the partner to VALGRIND_MALLOCLIKE_BLOCK. For
+ Memcheck, it does two things:
+
+ - It records that the block has been deallocated. This assumes that the
+ block was annotated as having been allocated via
+ VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued.
+
+ - It marks the block as being unaddressable.
+
+ VALGRIND_FREELIKE_BLOCK should be put immediately after the point where a
+ heap block is deallocated.
+
+ In many cases, these two client requests will not be enough to get your
+ allocator working well with Memcheck. More specifically, if your allocator
+ writes to freed blocks in any way then a VALGRIND_MAKE_MEM_UNDEFINED call
+ will be necessary to mark the memory as addressable just before the zeroing
+ occurs, otherwise you'll get a lot of invalid write errors. For example,
+ you'll need to do this if your allocator recycles freed blocks, but it
+ zeroes them before handing them back out (via VALGRIND_MALLOCLIKE_BLOCK).
+ Alternatively, if your allocator reuses freed blocks for allocator-internal
+ data structures, VALGRIND_MAKE_MEM_UNDEFINED calls will also be necessary.
+
+ Really, what's happening is a blurring of the lines between the client
+ program and the allocator... after VALGRIND_FREELIKE_BLOCK is called, the
+ memory should be considered unaddressable to the client program, but the
+ allocator knows more than the rest of the client program and so may be able
+ to safely access it. Extra client requests are necessary for Valgrind to
+ understand the distinction between the allocator and the rest of the
+ program.
+
+ Note: there is currently no VALGRIND_REALLOCLIKE_BLOCK client request; it
+ has to be emulated with MALLOCLIKE/FREELIKE and memory copying.
+
+ Ignored if addr == 0.
+*/
+#define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__MALLOCLIKE_BLOCK, \
+ addr, sizeB, rzB, is_zeroed, 0); \
+ }
+
+/* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details.
+ Ignored if addr == 0.
+*/
+#define VALGRIND_FREELIKE_BLOCK(addr, rzB) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__FREELIKE_BLOCK, \
+ addr, rzB, 0, 0, 0); \
+ }
+
+/* Create a memory pool. */
+#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__CREATE_MEMPOOL, \
+ pool, rzB, is_zeroed, 0, 0); \
+ }
+
+/* Destroy a memory pool. */
+#define VALGRIND_DESTROY_MEMPOOL(pool) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__DESTROY_MEMPOOL, \
+ pool, 0, 0, 0, 0); \
+ }
+
+/* Associate a piece of memory with a memory pool. */
+#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__MEMPOOL_ALLOC, \
+ pool, addr, size, 0, 0); \
+ }
+
+/* Disassociate a piece of memory from a memory pool. */
+#define VALGRIND_MEMPOOL_FREE(pool, addr) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__MEMPOOL_FREE, \
+ pool, addr, 0, 0, 0); \
+ }
+
+/* Disassociate any pieces outside a particular range. */
+#define VALGRIND_MEMPOOL_TRIM(pool, addr, size) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__MEMPOOL_TRIM, \
+ pool, addr, size, 0, 0); \
+ }
+
+/* Resize and/or move a piece associated with a memory pool. */
+#define VALGRIND_MOVE_MEMPOOL(poolA, poolB) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__MOVE_MEMPOOL, \
+ poolA, poolB, 0, 0, 0); \
+ }
+
+/* Resize and/or move a piece associated with a memory pool. */
+#define VALGRIND_MEMPOOL_CHANGE(pool, addrA, addrB, size) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__MEMPOOL_CHANGE, \
+ pool, addrA, addrB, size, 0); \
+ }
+
+/* Return 1 if a mempool exists, else 0. */
+#define VALGRIND_MEMPOOL_EXISTS(pool) \
+ __extension__ \
+ ({unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__MEMPOOL_EXISTS, \
+ pool, 0, 0, 0, 0); \
+ _qzz_res; \
+ })
+
+/* Mark a piece of memory as being a stack. Returns a stack id. */
+#define VALGRIND_STACK_REGISTER(start, end) \
+ __extension__ \
+ ({unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__STACK_REGISTER, \
+ start, end, 0, 0, 0); \
+ _qzz_res; \
+ })
+
+/* Unmark the piece of memory associated with a stack id as being a
+ stack. */
+#define VALGRIND_STACK_DEREGISTER(id) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__STACK_DEREGISTER, \
+ id, 0, 0, 0, 0); \
+ }
+
+/* Change the start and end address of the stack id. */
+#define VALGRIND_STACK_CHANGE(id, start, end) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__STACK_CHANGE, \
+ id, start, end, 0, 0); \
+ }
+
+/* Load PDB debug info for Wine PE image_map. */
+#define VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, delta) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__LOAD_PDB_DEBUGINFO, \
+ fd, ptr, total_size, delta, 0); \
+ }
+
+/* Map a code address to a source file name and line number. buf64
+ must point to a 64-byte buffer in the caller's address space. The
+ result will be dumped in there and is guaranteed to be zero
+ terminated. If no info is found, the first byte is set to zero. */
+#define VALGRIND_MAP_IP_TO_SRCLOC(addr, buf64) \
+ {unsigned int _qzz_res; \
+ VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \
+ VG_USERREQ__MAP_IP_TO_SRCLOC, \
+ addr, buf64, 0, 0, 0); \
+ }
+
+
+#undef PLAT_x86_linux
+#undef PLAT_amd64_linux
+#undef PLAT_ppc32_linux
+#undef PLAT_ppc64_linux
+#undef PLAT_arm_linux
+#undef PLAT_ppc32_aix5
+#undef PLAT_ppc64_aix5
+
+#endif /* __VALGRIND_H */
diff --git a/src/base/third_party/xdg_mime/LICENSE b/src/base/third_party/xdg_mime/LICENSE
new file mode 100644
index 0000000..55fedcf
--- /dev/null
+++ b/src/base/third_party/xdg_mime/LICENSE
@@ -0,0 +1,168 @@
+Licensed under the Academic Free License version 2.0 (below)
+Or under the following terms:
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the
+Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+
+
+--------------------------------------------------------------------------------
+Academic Free License v. 2.0
+--------------------------------------------------------------------------------
+
+This Academic Free License (the "License") applies to any original work of
+authorship (the "Original Work") whose owner (the "Licensor") has placed the
+following notice immediately following the copyright notice for the Original
+Work:
+
+Licensed under the Academic Free License version 2.0
+1) Grant of Copyright License. Licensor hereby grants You a world-wide,
+royalty-free, non-exclusive, perpetual, sublicenseable license to do the
+following:
+
+a) to reproduce the Original Work in copies;
+b) to prepare derivative works ("Derivative Works") based upon the Original
+ Work;
+c) to distribute copies of the Original Work and Derivative Works to the
+ public;
+d) to perform the Original Work publicly; and
+e) to display the Original Work publicly.
+
+2) Grant of Patent License. Licensor hereby grants You a world-wide,
+royalty-free, non-exclusive, perpetual, sublicenseable license, under patent
+claims owned or controlled by the Licensor that are embodied in the Original
+Work as furnished by the Licensor, to make, use, sell and offer for sale the
+Original Work and Derivative Works.
+
+3) Grant of Source Code License. The term "Source Code" means the preferred
+form of the Original Work for making modifications to it and all available
+documentation describing how to modify the Original Work. Licensor hereby
+agrees to provide a machine-readable copy of the Source Code of the Original
+Work along with each copy of the Original Work that Licensor distributes.
+Licensor reserves the right to satisfy this obligation by placing a
+machine-readable copy of the Source Code in an information repository
+reasonably calculated to permit inexpensive and convenient access by You for as
+long as Licensor continues to distribute the Original Work, and by publishing
+the address of that information repository in a notice immediately following
+the copyright notice that applies to the Original Work.
+
+4) Exclusions From License Grant. Neither the names of Licensor, nor the names
+of any contributors to the Original Work, nor any of their trademarks or
+service marks, may be used to endorse or promote products derived from this
+Original Work without express prior written permission of the Licensor. Nothing
+in this License shall be deemed to grant any rights to trademarks, copyrights,
+patents, trade secrets or any other intellectual property of Licensor except as
+expressly stated herein. No patent license is granted to make, use, sell or
+offer to sell embodiments of any patent claims other than the licensed claims
+defined in Section 2. No right is granted to the trademarks of Licensor even if
+such marks are included in the Original Work. Nothing in this License shall be
+interpreted to prohibit Licensor from licensing under different terms from this
+License any Original Work that Licensor otherwise would have a right to
+license.
+
+5) This section intentionally omitted.
+
+6) Attribution Rights. You must retain, in the Source Code of any Derivative
+Works that You create, all copyright, patent or trademark notices from the
+Source Code of the Original Work, as well as any notices of licensing and any
+descriptive text identified therein as an "Attribution Notice." You must cause
+the Source Code for any Derivative Works that You create to carry a prominent
+Attribution Notice reasonably calculated to inform recipients that You have
+modified the Original Work.
+
+7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that
+the copyright in and to the Original Work and the patent rights granted herein
+by Licensor are owned by the Licensor or are sublicensed to You under the terms
+of this License with the permission of the contributor(s) of those copyrights
+and patent rights. Except as expressly stated in the immediately proceeding
+sentence, the Original Work is provided under this License on an "AS IS" BASIS
+and WITHOUT WARRANTY, either express or implied, including, without limitation,
+the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU.
+This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No
+license to Original Work is granted hereunder except under this disclaimer.
+
+8) Limitation of Liability. Under no circumstances and under no legal theory,
+whether in tort (including negligence), contract, or otherwise, shall the
+Licensor be liable to any person for any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License
+or the use of the Original Work including, without limitation, damages for loss
+of goodwill, work stoppage, computer failure or malfunction, or any and all
+other commercial damages or losses. This limitation of liability shall not
+apply to liability for death or personal injury resulting from Licensor's
+negligence to the extent applicable law prohibits such limitation. Some
+jurisdictions do not allow the exclusion or limitation of incidental or
+consequential damages, so this exclusion and limitation may not apply to You.
+
+9) Acceptance and Termination. If You distribute copies of the Original Work or
+a Derivative Work, You must make a reasonable effort under the circumstances to
+obtain the express assent of recipients to the terms of this License. Nothing
+else but this License (or another written agreement between Licensor and You)
+grants You permission to create Derivative Works based upon the Original Work
+or to exercise any of the rights granted in Section 1 herein, and any attempt
+to do so except under the terms of this License (or another written agreement
+between Licensor and You) is expressly prohibited by U.S. copyright law, the
+equivalent laws of other countries, and by international treaty. Therefore, by
+exercising any of the rights granted to You in Section 1 herein, You indicate
+Your acceptance of this License and all of its terms and conditions.
+
+10) Termination for Patent Action. This License shall terminate automatically
+and You may no longer exercise any of the rights granted to You by this License
+as of the date You commence an action, including a cross-claim or counterclaim,
+for patent infringement (i) against Licensor with respect to a patent
+applicable to software or (ii) against any entity with respect to a patent
+applicable to the Original Work (but excluding combinations of the Original
+Work with other software or hardware).
+
+11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this
+License may be brought only in the courts of a jurisdiction wherein the
+Licensor resides or in which Licensor conducts its primary business, and under
+the laws of that jurisdiction excluding its conflict-of-law provisions. The
+application of the United Nations Convention on Contracts for the International
+Sale of Goods is expressly excluded. Any use of the Original Work outside the
+scope of this License or after its termination shall be subject to the
+requirements and penalties of the U.S. Copyright Act, 17 U.S.C. 101 et seq.,
+the equivalent laws of other countries, and international treaty. This section
+shall survive the termination of this License.
+
+12) Attorneys Fees. In any action to enforce the terms of this License or
+seeking damages relating thereto, the prevailing party shall be entitled to
+recover its costs and expenses, including, without limitation, reasonable
+attorneys' fees and costs incurred in connection with such action, including
+any appeal of such action. This section shall survive the termination of this
+License.
+
+13) Miscellaneous. This License represents the complete agreement concerning
+the subject matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent necessary to
+make it enforceable.
+
+14) Definition of "You" in This License. "You" throughout this License, whether
+in upper or lower case, means an individual or a legal entity exercising rights
+under, and complying with all of the terms of, this License. For legal
+entities, "You" includes any entity that controls, is controlled by, or is
+under common control with you. For purposes of this definition, "control" means
+(i) the power, direct or indirect, to cause the direction or management of such
+entity, whether by contract or otherwise, or (ii) ownership of fifty percent
+(50%) or more of the outstanding shares, or (iii) beneficial ownership of such
+entity.
+
+15) Right to Use. You may use the Original Work in all ways not otherwise
+restricted or conditioned by this License or by law, and Licensor promises not
+to interfere with or be responsible for such uses by You.
+
+This license is Copyright (C) 2003 Lawrence E. Rosen. All rights reserved.
+Permission is hereby granted to copy and distribute this license without
+modification. This license may not be modified without the express written
+permission of its copyright owner.
diff --git a/src/base/third_party/xdg_mime/README b/src/base/third_party/xdg_mime/README
new file mode 100644
index 0000000..e7f3f68
--- /dev/null
+++ b/src/base/third_party/xdg_mime/README
@@ -0,0 +1,8 @@
+This module is a simple module that parses the proposed MIME spec listed
+at http://freedesktop.org/. It is currently targetted at version 0.12.
+There are no formal releases planned for this module, and it is not
+intended to be installed at this time. Rather, it is meant to be used
+by other libraries or applications to add support for the MIME system.
+
+It is dual-licensed under the terms of the GNU Lesser General Public
+License, and the Academic Free License, version 2.0.
diff --git a/src/base/third_party/xdg_mime/README.chromium b/src/base/third_party/xdg_mime/README.chromium
new file mode 100644
index 0000000..29d239a
--- /dev/null
+++ b/src/base/third_party/xdg_mime/README.chromium
@@ -0,0 +1,11 @@
+Name: xdg-mime
+URL: http://freedesktop.org
+License: Academic Free License version 2.0 or LGPL v2
+
+The code in this directory is synced from:
+git://anongit.freedesktop.org/xdg/xdgmime
+@ 2cdd8d36d7930d5a594587286cb1949ff62f7027 on 2012/08/06.
+
+In addition, we have the following patch(es):
+- compile.patch: small tweaks to make the code compile.
+- Added a LICENSE file.
diff --git a/src/base/third_party/xdg_mime/compile.patch b/src/base/third_party/xdg_mime/compile.patch
new file mode 100644
index 0000000..cd055ed
--- /dev/null
+++ b/src/base/third_party/xdg_mime/compile.patch
@@ -0,0 +1,17 @@
+--- a/xdgmimecache.c
++++ b/xdgmimecache.c
+@@ -40,6 +40,8 @@
+
+ #include <netinet/in.h> /* for ntohl/ntohs */
+
++#define HAVE_MMAP 1
++
+ #ifdef HAVE_MMAP
+ #include <sys/mman.h>
+ #else
+@@ -1000,5 +1002,3 @@
+ dump_glob_node (cache, offset + 20 * j, 0);
+ }
+ }
+-
+-
diff --git a/src/base/third_party/xdg_mime/xdgmime.c b/src/base/third_party/xdg_mime/xdgmime.c
new file mode 100644
index 0000000..c7b16bb
--- /dev/null
+++ b/src/base/third_party/xdg_mime/xdgmime.c
@@ -0,0 +1,934 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmime.c: XDG Mime Spec mime resolver. Based on version 0.11 of the spec.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003,2004 Red Hat, Inc.
+ * Copyright (C) 2003,2004 Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "xdgmime.h"
+#include "xdgmimeint.h"
+#include "xdgmimeglob.h"
+#include "xdgmimemagic.h"
+#include "xdgmimealias.h"
+#include "xdgmimeicon.h"
+#include "xdgmimeparent.h"
+#include "xdgmimecache.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <assert.h>
+
+typedef struct XdgDirTimeList XdgDirTimeList;
+typedef struct XdgCallbackList XdgCallbackList;
+
+static int need_reread = TRUE;
+static time_t last_stat_time = 0;
+
+static XdgGlobHash *global_hash = NULL;
+static XdgMimeMagic *global_magic = NULL;
+static XdgAliasList *alias_list = NULL;
+static XdgParentList *parent_list = NULL;
+static XdgDirTimeList *dir_time_list = NULL;
+static XdgCallbackList *callback_list = NULL;
+static XdgIconList *icon_list = NULL;
+static XdgIconList *generic_icon_list = NULL;
+
+XdgMimeCache **_caches = NULL;
+static int n_caches = 0;
+
+const char xdg_mime_type_unknown[] = "application/octet-stream";
+const char xdg_mime_type_empty[] = "application/x-zerosize";
+const char xdg_mime_type_textplain[] = "text/plain";
+
+
+enum
+{
+ XDG_CHECKED_UNCHECKED,
+ XDG_CHECKED_VALID,
+ XDG_CHECKED_INVALID
+};
+
+struct XdgDirTimeList
+{
+ time_t mtime;
+ char *directory_name;
+ int checked;
+ XdgDirTimeList *next;
+};
+
+struct XdgCallbackList
+{
+ XdgCallbackList *next;
+ XdgCallbackList *prev;
+ int callback_id;
+ XdgMimeCallback callback;
+ void *data;
+ XdgMimeDestroy destroy;
+};
+
+/* Function called by xdg_run_command_on_dirs. If it returns TRUE, further
+ * directories aren't looked at */
+typedef int (*XdgDirectoryFunc) (const char *directory,
+ void *user_data);
+
+static void
+xdg_dir_time_list_add (char *file_name,
+ time_t mtime)
+{
+ XdgDirTimeList *list;
+
+ for (list = dir_time_list; list; list = list->next)
+ {
+ if (strcmp (list->directory_name, file_name) == 0)
+ {
+ free (file_name);
+ return;
+ }
+ }
+
+ list = calloc (1, sizeof (XdgDirTimeList));
+ list->checked = XDG_CHECKED_UNCHECKED;
+ list->directory_name = file_name;
+ list->mtime = mtime;
+ list->next = dir_time_list;
+ dir_time_list = list;
+}
+
+static void
+xdg_dir_time_list_free (XdgDirTimeList *list)
+{
+ XdgDirTimeList *next;
+
+ while (list)
+ {
+ next = list->next;
+ free (list->directory_name);
+ free (list);
+ list = next;
+ }
+}
+
+static int
+xdg_mime_init_from_directory (const char *directory)
+{
+ char *file_name;
+ struct stat st;
+
+ assert (directory != NULL);
+
+ file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1);
+ strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache");
+ if (stat (file_name, &st) == 0)
+ {
+ XdgMimeCache *cache = _xdg_mime_cache_new_from_file (file_name);
+
+ if (cache != NULL)
+ {
+ xdg_dir_time_list_add (file_name, st.st_mtime);
+
+ _caches = realloc (_caches, sizeof (XdgMimeCache *) * (n_caches + 2));
+ _caches[n_caches] = cache;
+ _caches[n_caches + 1] = NULL;
+ n_caches++;
+
+ return FALSE;
+ }
+ }
+ free (file_name);
+
+ file_name = malloc (strlen (directory) + strlen ("/mime/globs2") + 1);
+ strcpy (file_name, directory); strcat (file_name, "/mime/globs2");
+ if (stat (file_name, &st) == 0)
+ {
+ _xdg_mime_glob_read_from_file (global_hash, file_name, TRUE);
+ xdg_dir_time_list_add (file_name, st.st_mtime);
+ }
+ else
+ {
+ free (file_name);
+ file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1);
+ strcpy (file_name, directory); strcat (file_name, "/mime/globs");
+ if (stat (file_name, &st) == 0)
+ {
+ _xdg_mime_glob_read_from_file (global_hash, file_name, FALSE);
+ xdg_dir_time_list_add (file_name, st.st_mtime);
+ }
+ else
+ {
+ free (file_name);
+ }
+ }
+
+ file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1);
+ strcpy (file_name, directory); strcat (file_name, "/mime/magic");
+ if (stat (file_name, &st) == 0)
+ {
+ _xdg_mime_magic_read_from_file (global_magic, file_name);
+ xdg_dir_time_list_add (file_name, st.st_mtime);
+ }
+ else
+ {
+ free (file_name);
+ }
+
+ file_name = malloc (strlen (directory) + strlen ("/mime/aliases") + 1);
+ strcpy (file_name, directory); strcat (file_name, "/mime/aliases");
+ _xdg_mime_alias_read_from_file (alias_list, file_name);
+ free (file_name);
+
+ file_name = malloc (strlen (directory) + strlen ("/mime/subclasses") + 1);
+ strcpy (file_name, directory); strcat (file_name, "/mime/subclasses");
+ _xdg_mime_parent_read_from_file (parent_list, file_name);
+ free (file_name);
+
+ file_name = malloc (strlen (directory) + strlen ("/mime/icons") + 1);
+ strcpy (file_name, directory); strcat (file_name, "/mime/icons");
+ _xdg_mime_icon_read_from_file (icon_list, file_name);
+ free (file_name);
+
+ file_name = malloc (strlen (directory) + strlen ("/mime/generic-icons") + 1);
+ strcpy (file_name, directory); strcat (file_name, "/mime/generic-icons");
+ _xdg_mime_icon_read_from_file (generic_icon_list, file_name);
+ free (file_name);
+
+ return FALSE; /* Keep processing */
+}
+
+/* Runs a command on all the directories in the search path */
+static void
+xdg_run_command_on_dirs (XdgDirectoryFunc func,
+ void *user_data)
+{
+ const char *xdg_data_home;
+ const char *xdg_data_dirs;
+ const char *ptr;
+
+ xdg_data_home = getenv ("XDG_DATA_HOME");
+ if (xdg_data_home)
+ {
+ if ((func) (xdg_data_home, user_data))
+ return;
+ }
+ else
+ {
+ const char *home;
+
+ home = getenv ("HOME");
+ if (home != NULL)
+ {
+ char *guessed_xdg_home;
+ int stop_processing;
+
+ guessed_xdg_home = malloc (strlen (home) + strlen ("/.local/share/") + 1);
+ strcpy (guessed_xdg_home, home);
+ strcat (guessed_xdg_home, "/.local/share/");
+ stop_processing = (func) (guessed_xdg_home, user_data);
+ free (guessed_xdg_home);
+
+ if (stop_processing)
+ return;
+ }
+ }
+
+ xdg_data_dirs = getenv ("XDG_DATA_DIRS");
+ if (xdg_data_dirs == NULL)
+ xdg_data_dirs = "/usr/local/share/:/usr/share/";
+
+ ptr = xdg_data_dirs;
+
+ while (*ptr != '\000')
+ {
+ const char *end_ptr;
+ char *dir;
+ int len;
+ int stop_processing;
+
+ end_ptr = ptr;
+ while (*end_ptr != ':' && *end_ptr != '\000')
+ end_ptr ++;
+
+ if (end_ptr == ptr)
+ {
+ ptr++;
+ continue;
+ }
+
+ if (*end_ptr == ':')
+ len = end_ptr - ptr;
+ else
+ len = end_ptr - ptr + 1;
+ dir = malloc (len + 1);
+ strncpy (dir, ptr, len);
+ dir[len] = '\0';
+ stop_processing = (func) (dir, user_data);
+ free (dir);
+
+ if (stop_processing)
+ return;
+
+ ptr = end_ptr;
+ }
+}
+
+/* Checks file_path to make sure it has the same mtime as last time it was
+ * checked. If it has a different mtime, or if the file doesn't exist, it
+ * returns FALSE.
+ *
+ * FIXME: This doesn't protect against permission changes.
+ */
+static int
+xdg_check_file (const char *file_path,
+ int *exists)
+{
+ struct stat st;
+
+ /* If the file exists */
+ if (stat (file_path, &st) == 0)
+ {
+ XdgDirTimeList *list;
+
+ if (exists)
+ *exists = TRUE;
+
+ for (list = dir_time_list; list; list = list->next)
+ {
+ if (! strcmp (list->directory_name, file_path))
+ {
+ if (st.st_mtime == list->mtime)
+ list->checked = XDG_CHECKED_VALID;
+ else
+ list->checked = XDG_CHECKED_INVALID;
+
+ return (list->checked != XDG_CHECKED_VALID);
+ }
+ }
+ return TRUE;
+ }
+
+ if (exists)
+ *exists = FALSE;
+
+ return FALSE;
+}
+
+static int
+xdg_check_dir (const char *directory,
+ int *invalid_dir_list)
+{
+ int invalid, exists;
+ char *file_name;
+
+ assert (directory != NULL);
+
+ /* Check the mime.cache file */
+ file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1);
+ strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache");
+ invalid = xdg_check_file (file_name, &exists);
+ free (file_name);
+ if (invalid)
+ {
+ *invalid_dir_list = TRUE;
+ return TRUE;
+ }
+ else if (exists)
+ {
+ return FALSE;
+ }
+
+ /* Check the globs file */
+ file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1);
+ strcpy (file_name, directory); strcat (file_name, "/mime/globs");
+ invalid = xdg_check_file (file_name, NULL);
+ free (file_name);
+ if (invalid)
+ {
+ *invalid_dir_list = TRUE;
+ return TRUE;
+ }
+
+ /* Check the magic file */
+ file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1);
+ strcpy (file_name, directory); strcat (file_name, "/mime/magic");
+ invalid = xdg_check_file (file_name, NULL);
+ free (file_name);
+ if (invalid)
+ {
+ *invalid_dir_list = TRUE;
+ return TRUE;
+ }
+
+ return FALSE; /* Keep processing */
+}
+
+/* Walks through all the mime files stat()ing them to see if they've changed.
+ * Returns TRUE if they have. */
+static int
+xdg_check_dirs (void)
+{
+ XdgDirTimeList *list;
+ int invalid_dir_list = FALSE;
+
+ for (list = dir_time_list; list; list = list->next)
+ list->checked = XDG_CHECKED_UNCHECKED;
+
+ xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_check_dir,
+ &invalid_dir_list);
+
+ if (invalid_dir_list)
+ return TRUE;
+
+ for (list = dir_time_list; list; list = list->next)
+ {
+ if (list->checked != XDG_CHECKED_VALID)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* We want to avoid stat()ing on every single mime call, so we only look for
+ * newer files every 5 seconds. This will return TRUE if we need to reread the
+ * mime data from disk.
+ */
+static int
+xdg_check_time_and_dirs (void)
+{
+ struct timeval tv;
+ time_t current_time;
+ int retval = FALSE;
+
+ gettimeofday (&tv, NULL);
+ current_time = tv.tv_sec;
+
+ if (current_time >= last_stat_time + 5)
+ {
+ retval = xdg_check_dirs ();
+ last_stat_time = current_time;
+ }
+
+ return retval;
+}
+
+/* Called in every public function. It reloads the hash function if need be.
+ */
+static void
+xdg_mime_init (void)
+{
+ if (xdg_check_time_and_dirs ())
+ {
+ xdg_mime_shutdown ();
+ }
+
+ if (need_reread)
+ {
+ global_hash = _xdg_glob_hash_new ();
+ global_magic = _xdg_mime_magic_new ();
+ alias_list = _xdg_mime_alias_list_new ();
+ parent_list = _xdg_mime_parent_list_new ();
+ icon_list = _xdg_mime_icon_list_new ();
+ generic_icon_list = _xdg_mime_icon_list_new ();
+
+ xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_mime_init_from_directory,
+ NULL);
+
+ need_reread = FALSE;
+ }
+}
+
+const char *
+xdg_mime_get_mime_type_for_data (const void *data,
+ size_t len,
+ int *result_prio)
+{
+ const char *mime_type;
+
+ if (len == 0)
+ {
+ *result_prio = 100;
+ return XDG_MIME_TYPE_EMPTY;
+ }
+
+ xdg_mime_init ();
+
+ if (_caches)
+ mime_type = _xdg_mime_cache_get_mime_type_for_data (data, len, result_prio);
+ else
+ mime_type = _xdg_mime_magic_lookup_data (global_magic, data, len, result_prio, NULL, 0);
+
+ if (mime_type)
+ return mime_type;
+
+ return _xdg_binary_or_text_fallback(data, len);
+}
+
+const char *
+xdg_mime_get_mime_type_for_file (const char *file_name,
+ struct stat *statbuf)
+{
+ const char *mime_type;
+ /* currently, only a few globs occur twice, and none
+ * more often, so 5 seems plenty.
+ */
+ const char *mime_types[5];
+ FILE *file;
+ unsigned char *data;
+ int max_extent;
+ int bytes_read;
+ struct stat buf;
+ const char *base_name;
+ int n;
+
+ if (file_name == NULL)
+ return NULL;
+ if (! _xdg_utf8_validate (file_name))
+ return NULL;
+
+ xdg_mime_init ();
+
+ if (_caches)
+ return _xdg_mime_cache_get_mime_type_for_file (file_name, statbuf);
+
+ base_name = _xdg_get_base_name (file_name);
+ n = _xdg_glob_hash_lookup_file_name (global_hash, base_name, mime_types, 5);
+
+ if (n == 1)
+ return mime_types[0];
+
+ if (!statbuf)
+ {
+ if (stat (file_name, &buf) != 0)
+ return XDG_MIME_TYPE_UNKNOWN;
+
+ statbuf = &buf;
+ }
+
+ if (!S_ISREG (statbuf->st_mode))
+ return XDG_MIME_TYPE_UNKNOWN;
+
+ /* FIXME: Need to make sure that max_extent isn't totally broken. This could
+ * be large and need getting from a stream instead of just reading it all
+ * in. */
+ max_extent = _xdg_mime_magic_get_buffer_extents (global_magic);
+ data = malloc (max_extent);
+ if (data == NULL)
+ return XDG_MIME_TYPE_UNKNOWN;
+
+ file = fopen (file_name, "r");
+ if (file == NULL)
+ {
+ free (data);
+ return XDG_MIME_TYPE_UNKNOWN;
+ }
+
+ bytes_read = fread (data, 1, max_extent, file);
+ if (ferror (file))
+ {
+ free (data);
+ fclose (file);
+ return XDG_MIME_TYPE_UNKNOWN;
+ }
+
+ mime_type = _xdg_mime_magic_lookup_data (global_magic, data, bytes_read, NULL,
+ mime_types, n);
+
+ free (data);
+ fclose (file);
+
+ if (mime_type)
+ return mime_type;
+
+ return _xdg_binary_or_text_fallback(data, bytes_read);
+}
+
+const char *
+xdg_mime_get_mime_type_from_file_name (const char *file_name)
+{
+ const char *mime_type;
+
+ xdg_mime_init ();
+
+ if (_caches)
+ return _xdg_mime_cache_get_mime_type_from_file_name (file_name);
+
+ if (_xdg_glob_hash_lookup_file_name (global_hash, file_name, &mime_type, 1))
+ return mime_type;
+ else
+ return XDG_MIME_TYPE_UNKNOWN;
+}
+
+int
+xdg_mime_get_mime_types_from_file_name (const char *file_name,
+ const char *mime_types[],
+ int n_mime_types)
+{
+ xdg_mime_init ();
+
+ if (_caches)
+ return _xdg_mime_cache_get_mime_types_from_file_name (file_name, mime_types, n_mime_types);
+
+ return _xdg_glob_hash_lookup_file_name (global_hash, file_name, mime_types, n_mime_types);
+}
+
+int
+xdg_mime_is_valid_mime_type (const char *mime_type)
+{
+ /* FIXME: We should make this a better test
+ */
+ return _xdg_utf8_validate (mime_type);
+}
+
+void
+xdg_mime_shutdown (void)
+{
+ XdgCallbackList *list;
+
+ /* FIXME: Need to make this (and the whole library) thread safe */
+ if (dir_time_list)
+ {
+ xdg_dir_time_list_free (dir_time_list);
+ dir_time_list = NULL;
+ }
+
+ if (global_hash)
+ {
+ _xdg_glob_hash_free (global_hash);
+ global_hash = NULL;
+ }
+ if (global_magic)
+ {
+ _xdg_mime_magic_free (global_magic);
+ global_magic = NULL;
+ }
+
+ if (alias_list)
+ {
+ _xdg_mime_alias_list_free (alias_list);
+ alias_list = NULL;
+ }
+
+ if (parent_list)
+ {
+ _xdg_mime_parent_list_free (parent_list);
+ parent_list = NULL;
+ }
+
+ if (icon_list)
+ {
+ _xdg_mime_icon_list_free (icon_list);
+ icon_list = NULL;
+ }
+
+ if (generic_icon_list)
+ {
+ _xdg_mime_icon_list_free (generic_icon_list);
+ generic_icon_list = NULL;
+ }
+
+ if (_caches)
+ {
+ int i;
+
+ for (i = 0; i < n_caches; i++)
+ _xdg_mime_cache_unref (_caches[i]);
+ free (_caches);
+ _caches = NULL;
+ n_caches = 0;
+ }
+
+ for (list = callback_list; list; list = list->next)
+ (list->callback) (list->data);
+
+ need_reread = TRUE;
+}
+
+int
+xdg_mime_get_max_buffer_extents (void)
+{
+ xdg_mime_init ();
+
+ if (_caches)
+ return _xdg_mime_cache_get_max_buffer_extents ();
+
+ return _xdg_mime_magic_get_buffer_extents (global_magic);
+}
+
+const char *
+_xdg_mime_unalias_mime_type (const char *mime_type)
+{
+ const char *lookup;
+
+ if (_caches)
+ return _xdg_mime_cache_unalias_mime_type (mime_type);
+
+ if ((lookup = _xdg_mime_alias_list_lookup (alias_list, mime_type)) != NULL)
+ return lookup;
+
+ return mime_type;
+}
+
+const char *
+xdg_mime_unalias_mime_type (const char *mime_type)
+{
+ xdg_mime_init ();
+
+ return _xdg_mime_unalias_mime_type (mime_type);
+}
+
+int
+_xdg_mime_mime_type_equal (const char *mime_a,
+ const char *mime_b)
+{
+ const char *unalias_a, *unalias_b;
+
+ unalias_a = _xdg_mime_unalias_mime_type (mime_a);
+ unalias_b = _xdg_mime_unalias_mime_type (mime_b);
+
+ if (strcmp (unalias_a, unalias_b) == 0)
+ return 1;
+
+ return 0;
+}
+
+int
+xdg_mime_mime_type_equal (const char *mime_a,
+ const char *mime_b)
+{
+ xdg_mime_init ();
+
+ return _xdg_mime_mime_type_equal (mime_a, mime_b);
+}
+
+int
+xdg_mime_media_type_equal (const char *mime_a,
+ const char *mime_b)
+{
+ char *sep;
+
+ sep = strchr (mime_a, '/');
+
+ if (sep && strncmp (mime_a, mime_b, sep - mime_a + 1) == 0)
+ return 1;
+
+ return 0;
+}
+
+#if 1
+static int
+xdg_mime_is_super_type (const char *mime)
+{
+ int length;
+ const char *type;
+
+ length = strlen (mime);
+ type = &(mime[length - 2]);
+
+ if (strcmp (type, "/*") == 0)
+ return 1;
+
+ return 0;
+}
+#endif
+
+int
+_xdg_mime_mime_type_subclass (const char *mime,
+ const char *base)
+{
+ const char *umime, *ubase;
+ const char **parents;
+
+ if (_caches)
+ return _xdg_mime_cache_mime_type_subclass (mime, base);
+
+ umime = _xdg_mime_unalias_mime_type (mime);
+ ubase = _xdg_mime_unalias_mime_type (base);
+
+ if (strcmp (umime, ubase) == 0)
+ return 1;
+
+#if 1
+ /* Handle supertypes */
+ if (xdg_mime_is_super_type (ubase) &&
+ xdg_mime_media_type_equal (umime, ubase))
+ return 1;
+#endif
+
+ /* Handle special cases text/plain and application/octet-stream */
+ if (strcmp (ubase, "text/plain") == 0 &&
+ strncmp (umime, "text/", 5) == 0)
+ return 1;
+
+ if (strcmp (ubase, "application/octet-stream") == 0)
+ return 1;
+
+ parents = _xdg_mime_parent_list_lookup (parent_list, umime);
+ for (; parents && *parents; parents++)
+ {
+ if (_xdg_mime_mime_type_subclass (*parents, ubase))
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+xdg_mime_mime_type_subclass (const char *mime,
+ const char *base)
+{
+ xdg_mime_init ();
+
+ return _xdg_mime_mime_type_subclass (mime, base);
+}
+
+char **
+xdg_mime_list_mime_parents (const char *mime)
+{
+ const char **parents;
+ char **result;
+ int i, n;
+
+ if (_caches)
+ return _xdg_mime_cache_list_mime_parents (mime);
+
+ parents = xdg_mime_get_mime_parents (mime);
+
+ if (!parents)
+ return NULL;
+
+ for (i = 0; parents[i]; i++) ;
+
+ n = (i + 1) * sizeof (char *);
+ result = (char **) malloc (n);
+ memcpy (result, parents, n);
+
+ return result;
+}
+
+const char **
+xdg_mime_get_mime_parents (const char *mime)
+{
+ const char *umime;
+
+ xdg_mime_init ();
+
+ umime = _xdg_mime_unalias_mime_type (mime);
+
+ return _xdg_mime_parent_list_lookup (parent_list, umime);
+}
+
+void
+xdg_mime_dump (void)
+{
+ xdg_mime_init();
+
+ printf ("*** ALIASES ***\n\n");
+ _xdg_mime_alias_list_dump (alias_list);
+ printf ("\n*** PARENTS ***\n\n");
+ _xdg_mime_parent_list_dump (parent_list);
+ printf ("\n*** CACHE ***\n\n");
+ _xdg_glob_hash_dump (global_hash);
+ printf ("\n*** GLOBS ***\n\n");
+ _xdg_glob_hash_dump (global_hash);
+ printf ("\n*** GLOBS REVERSE TREE ***\n\n");
+ _xdg_mime_cache_glob_dump ();
+}
+
+
+/* Registers a function to be called every time the mime database reloads its files
+ */
+int
+xdg_mime_register_reload_callback (XdgMimeCallback callback,
+ void *data,
+ XdgMimeDestroy destroy)
+{
+ XdgCallbackList *list_el;
+ static int callback_id = 1;
+
+ /* Make a new list element */
+ list_el = calloc (1, sizeof (XdgCallbackList));
+ list_el->callback_id = callback_id;
+ list_el->callback = callback;
+ list_el->data = data;
+ list_el->destroy = destroy;
+ list_el->next = callback_list;
+ if (list_el->next)
+ list_el->next->prev = list_el;
+
+ callback_list = list_el;
+ callback_id ++;
+
+ return callback_id - 1;
+}
+
+void
+xdg_mime_remove_callback (int callback_id)
+{
+ XdgCallbackList *list;
+
+ for (list = callback_list; list; list = list->next)
+ {
+ if (list->callback_id == callback_id)
+ {
+ if (list->next)
+ list->next = list->prev;
+
+ if (list->prev)
+ list->prev->next = list->next;
+ else
+ callback_list = list->next;
+
+ /* invoke the destroy handler */
+ (list->destroy) (list->data);
+ free (list);
+ return;
+ }
+ }
+}
+
+const char *
+xdg_mime_get_icon (const char *mime)
+{
+ xdg_mime_init ();
+
+ if (_caches)
+ return _xdg_mime_cache_get_icon (mime);
+
+ return _xdg_mime_icon_list_lookup (icon_list, mime);
+}
+
+const char *
+xdg_mime_get_generic_icon (const char *mime)
+{
+ xdg_mime_init ();
+
+ if (_caches)
+ return _xdg_mime_cache_get_generic_icon (mime);
+
+ return _xdg_mime_icon_list_lookup (generic_icon_list, mime);
+}
diff --git a/src/base/third_party/xdg_mime/xdgmime.h b/src/base/third_party/xdg_mime/xdgmime.h
new file mode 100644
index 0000000..6a34edf
--- /dev/null
+++ b/src/base/third_party/xdg_mime/xdgmime.h
@@ -0,0 +1,133 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmime.h: XDG Mime Spec mime resolver. Based on version 0.11 of the spec.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __XDG_MIME_H__
+#define __XDG_MIME_H__
+
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifdef XDG_PREFIX
+#define XDG_ENTRY(func) _XDG_ENTRY2(XDG_PREFIX,func)
+#define _XDG_ENTRY2(prefix,func) _XDG_ENTRY3(prefix,func)
+#define _XDG_ENTRY3(prefix,func) prefix##_##func
+
+#define XDG_RESERVED_ENTRY(func) _XDG_RESERVED_ENTRY2(XDG_PREFIX,func)
+#define _XDG_RESERVED_ENTRY2(prefix,func) _XDG_RESERVED_ENTRY3(prefix,func)
+#define _XDG_RESERVED_ENTRY3(prefix,func) _##prefix##_##func
+#endif
+
+typedef void (*XdgMimeCallback) (void *user_data);
+typedef void (*XdgMimeDestroy) (void *user_data);
+
+
+#ifdef XDG_PREFIX
+#define xdg_mime_get_mime_type_for_data XDG_ENTRY(get_mime_type_for_data)
+#define xdg_mime_get_mime_type_for_file XDG_ENTRY(get_mime_type_for_file)
+#define xdg_mime_get_mime_type_from_file_name XDG_ENTRY(get_mime_type_from_file_name)
+#define xdg_mime_get_mime_types_from_file_name XDG_ENTRY(get_mime_types_from_file_name)
+#define xdg_mime_is_valid_mime_type XDG_ENTRY(is_valid_mime_type)
+#define xdg_mime_mime_type_equal XDG_ENTRY(mime_type_equal)
+#define xdg_mime_media_type_equal XDG_ENTRY(media_type_equal)
+#define xdg_mime_mime_type_subclass XDG_ENTRY(mime_type_subclass)
+#define xdg_mime_get_mime_parents XDG_ENTRY(get_mime_parents)
+#define xdg_mime_list_mime_parents XDG_ENTRY(list_mime_parents)
+#define xdg_mime_unalias_mime_type XDG_ENTRY(unalias_mime_type)
+#define xdg_mime_get_max_buffer_extents XDG_ENTRY(get_max_buffer_extents)
+#define xdg_mime_shutdown XDG_ENTRY(shutdown)
+#define xdg_mime_dump XDG_ENTRY(dump)
+#define xdg_mime_register_reload_callback XDG_ENTRY(register_reload_callback)
+#define xdg_mime_remove_callback XDG_ENTRY(remove_callback)
+#define xdg_mime_type_unknown XDG_ENTRY(type_unknown)
+#define xdg_mime_type_empty XDG_ENTRY(type_empty)
+#define xdg_mime_type_textplain XDG_ENTRY(type_textplain)
+#define xdg_mime_get_icon XDG_ENTRY(get_icon)
+#define xdg_mime_get_generic_icon XDG_ENTRY(get_generic_icon)
+
+#define _xdg_mime_mime_type_equal XDG_RESERVED_ENTRY(mime_type_equal)
+#define _xdg_mime_mime_type_subclass XDG_RESERVED_ENTRY(mime_type_subclass)
+#define _xdg_mime_unalias_mime_type XDG_RESERVED_ENTRY(unalias_mime_type)
+#endif
+
+extern const char xdg_mime_type_unknown[];
+extern const char xdg_mime_type_empty[];
+extern const char xdg_mime_type_textplain[];
+#define XDG_MIME_TYPE_UNKNOWN xdg_mime_type_unknown
+#define XDG_MIME_TYPE_EMPTY xdg_mime_type_empty
+#define XDG_MIME_TYPE_TEXTPLAIN xdg_mime_type_textplain
+
+const char *xdg_mime_get_mime_type_for_data (const void *data,
+ size_t len,
+ int *result_prio);
+const char *xdg_mime_get_mime_type_for_file (const char *file_name,
+ struct stat *statbuf);
+const char *xdg_mime_get_mime_type_from_file_name (const char *file_name);
+int xdg_mime_get_mime_types_from_file_name(const char *file_name,
+ const char *mime_types[],
+ int n_mime_types);
+int xdg_mime_is_valid_mime_type (const char *mime_type);
+int xdg_mime_mime_type_equal (const char *mime_a,
+ const char *mime_b);
+int xdg_mime_media_type_equal (const char *mime_a,
+ const char *mime_b);
+int xdg_mime_mime_type_subclass (const char *mime_a,
+ const char *mime_b);
+ /* xdg_mime_get_mime_parents() is deprecated since it does
+ * not work correctly with caches. Use xdg_mime_list_parents()
+ * instead, but notice that that function expects you to free
+ * the array it returns.
+ */
+const char **xdg_mime_get_mime_parents (const char *mime);
+char ** xdg_mime_list_mime_parents (const char *mime);
+const char *xdg_mime_unalias_mime_type (const char *mime);
+const char *xdg_mime_get_icon (const char *mime);
+const char *xdg_mime_get_generic_icon (const char *mime);
+int xdg_mime_get_max_buffer_extents (void);
+void xdg_mime_shutdown (void);
+void xdg_mime_dump (void);
+int xdg_mime_register_reload_callback (XdgMimeCallback callback,
+ void *data,
+ XdgMimeDestroy destroy);
+void xdg_mime_remove_callback (int callback_id);
+
+ /* Private versions of functions that don't call xdg_mime_init () */
+int _xdg_mime_mime_type_equal (const char *mime_a,
+ const char *mime_b);
+int _xdg_mime_mime_type_subclass (const char *mime,
+ const char *base);
+const char *_xdg_mime_unalias_mime_type (const char *mime);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __XDG_MIME_H__ */
diff --git a/src/base/third_party/xdg_mime/xdgmimealias.c b/src/base/third_party/xdg_mime/xdgmimealias.c
new file mode 100644
index 0000000..07d89eb
--- /dev/null
+++ b/src/base/third_party/xdg_mime/xdgmimealias.c
@@ -0,0 +1,184 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimealias.c: Private file. Datastructure for storing the aliases.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ * Copyright (C) 2004 Matthias Clasen <mclasen@redhat.com>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "xdgmimealias.h"
+#include "xdgmimeint.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <fnmatch.h>
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+typedef struct XdgAlias XdgAlias;
+
+struct XdgAlias
+{
+ char *alias;
+ char *mime_type;
+};
+
+struct XdgAliasList
+{
+ struct XdgAlias *aliases;
+ int n_aliases;
+};
+
+XdgAliasList *
+_xdg_mime_alias_list_new (void)
+{
+ XdgAliasList *list;
+
+ list = malloc (sizeof (XdgAliasList));
+
+ list->aliases = NULL;
+ list->n_aliases = 0;
+
+ return list;
+}
+
+void
+_xdg_mime_alias_list_free (XdgAliasList *list)
+{
+ int i;
+
+ if (list->aliases)
+ {
+ for (i = 0; i < list->n_aliases; i++)
+ {
+ free (list->aliases[i].alias);
+ free (list->aliases[i].mime_type);
+ }
+ free (list->aliases);
+ }
+ free (list);
+}
+
+static int
+alias_entry_cmp (const void *v1, const void *v2)
+{
+ return strcmp (((XdgAlias *)v1)->alias, ((XdgAlias *)v2)->alias);
+}
+
+const char *
+_xdg_mime_alias_list_lookup (XdgAliasList *list,
+ const char *alias)
+{
+ XdgAlias *entry;
+ XdgAlias key;
+
+ if (list->n_aliases > 0)
+ {
+ key.alias = (char *)alias;
+ key.mime_type = NULL;
+
+ entry = bsearch (&key, list->aliases, list->n_aliases,
+ sizeof (XdgAlias), alias_entry_cmp);
+ if (entry)
+ return entry->mime_type;
+ }
+
+ return NULL;
+}
+
+void
+_xdg_mime_alias_read_from_file (XdgAliasList *list,
+ const char *file_name)
+{
+ FILE *file;
+ char line[255];
+ int alloc;
+
+ file = fopen (file_name, "r");
+
+ if (file == NULL)
+ return;
+
+ /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
+ * Blah */
+ alloc = list->n_aliases + 16;
+ list->aliases = realloc (list->aliases, alloc * sizeof (XdgAlias));
+ while (fgets (line, 255, file) != NULL)
+ {
+ char *sep;
+ if (line[0] == '#')
+ continue;
+
+ sep = strchr (line, ' ');
+ if (sep == NULL)
+ continue;
+ *(sep++) = '\000';
+ sep[strlen (sep) -1] = '\000';
+ if (list->n_aliases == alloc)
+ {
+ alloc <<= 1;
+ list->aliases = realloc (list->aliases,
+ alloc * sizeof (XdgAlias));
+ }
+ list->aliases[list->n_aliases].alias = strdup (line);
+ list->aliases[list->n_aliases].mime_type = strdup (sep);
+ list->n_aliases++;
+ }
+ list->aliases = realloc (list->aliases,
+ list->n_aliases * sizeof (XdgAlias));
+
+ fclose (file);
+
+ if (list->n_aliases > 1)
+ qsort (list->aliases, list->n_aliases,
+ sizeof (XdgAlias), alias_entry_cmp);
+}
+
+
+void
+_xdg_mime_alias_list_dump (XdgAliasList *list)
+{
+ int i;
+
+ if (list->aliases)
+ {
+ for (i = 0; i < list->n_aliases; i++)
+ {
+ printf ("%s %s\n",
+ list->aliases[i].alias,
+ list->aliases[i].mime_type);
+ }
+ }
+}
+
+
diff --git a/src/base/third_party/xdg_mime/xdgmimealias.h b/src/base/third_party/xdg_mime/xdgmimealias.h
new file mode 100644
index 0000000..3c28012
--- /dev/null
+++ b/src/base/third_party/xdg_mime/xdgmimealias.h
@@ -0,0 +1,51 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimealias.h: Private file. Datastructure for storing the aliases.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ * Copyright (C) 200 Matthias Clasen <mclasen@redhat.com>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XDG_MIME_ALIAS_H__
+#define __XDG_MIME_ALIAS_H__
+
+#include "xdgmime.h"
+
+typedef struct XdgAliasList XdgAliasList;
+
+#ifdef XDG_PREFIX
+#define _xdg_mime_alias_read_from_file XDG_RESERVED_ENTRY(alias_read_from_file)
+#define _xdg_mime_alias_list_new XDG_RESERVED_ENTRY(alias_list_new)
+#define _xdg_mime_alias_list_free XDG_RESERVED_ENTRY(alias_list_free)
+#define _xdg_mime_alias_list_lookup XDG_RESERVED_ENTRY(alias_list_lookup)
+#define _xdg_mime_alias_list_dump XDG_RESERVED_ENTRY(alias_list_dump)
+#endif
+
+void _xdg_mime_alias_read_from_file (XdgAliasList *list,
+ const char *file_name);
+XdgAliasList *_xdg_mime_alias_list_new (void);
+void _xdg_mime_alias_list_free (XdgAliasList *list);
+const char *_xdg_mime_alias_list_lookup (XdgAliasList *list,
+ const char *alias);
+void _xdg_mime_alias_list_dump (XdgAliasList *list);
+
+#endif /* __XDG_MIME_ALIAS_H__ */
diff --git a/src/base/third_party/xdg_mime/xdgmimecache.c b/src/base/third_party/xdg_mime/xdgmimecache.c
new file mode 100644
index 0000000..ddb8754
--- /dev/null
+++ b/src/base/third_party/xdg_mime/xdgmimecache.c
@@ -0,0 +1,1069 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimealias.c: Private file. mmappable caches for mime data
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2005 Matthias Clasen <mclasen@redhat.com>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <fnmatch.h>
+#include <assert.h>
+
+#include <netinet/in.h> /* for ntohl/ntohs */
+
+#define HAVE_MMAP 1
+
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#else
+#warning Building xdgmime without MMAP support. Binary "mime.cache" files will not be used.
+#endif
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "xdgmimecache.h"
+#include "xdgmimeint.h"
+
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+#ifndef _O_BINARY
+#define _O_BINARY 0
+#endif
+
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *) -1)
+#endif
+
+#define MAJOR_VERSION 1
+#define MINOR_VERSION_MIN 1
+#define MINOR_VERSION_MAX 2
+
+struct _XdgMimeCache
+{
+ int ref_count;
+ int minor;
+
+ size_t size;
+ char *buffer;
+};
+
+#define GET_UINT16(cache,offset) (ntohs(*(xdg_uint16_t*)((cache) + (offset))))
+#define GET_UINT32(cache,offset) (ntohl(*(xdg_uint32_t*)((cache) + (offset))))
+
+XdgMimeCache *
+_xdg_mime_cache_ref (XdgMimeCache *cache)
+{
+ cache->ref_count++;
+ return cache;
+}
+
+void
+_xdg_mime_cache_unref (XdgMimeCache *cache)
+{
+ cache->ref_count--;
+
+ if (cache->ref_count == 0)
+ {
+#ifdef HAVE_MMAP
+ munmap (cache->buffer, cache->size);
+#endif
+ free (cache);
+ }
+}
+
+XdgMimeCache *
+_xdg_mime_cache_new_from_file (const char *file_name)
+{
+ XdgMimeCache *cache = NULL;
+
+#ifdef HAVE_MMAP
+ int fd = -1;
+ struct stat st;
+ char *buffer = NULL;
+ int minor;
+
+ /* Open the file and map it into memory */
+ fd = open (file_name, O_RDONLY|_O_BINARY, 0);
+
+ if (fd < 0)
+ return NULL;
+
+ if (fstat (fd, &st) < 0 || st.st_size < 4)
+ goto done;
+
+ buffer = (char *) mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+
+ if (buffer == MAP_FAILED)
+ goto done;
+
+ minor = GET_UINT16 (buffer, 2);
+ /* Verify version */
+ if (GET_UINT16 (buffer, 0) != MAJOR_VERSION ||
+ (minor < MINOR_VERSION_MIN ||
+ minor > MINOR_VERSION_MAX))
+ {
+ munmap (buffer, st.st_size);
+
+ goto done;
+ }
+
+ cache = (XdgMimeCache *) malloc (sizeof (XdgMimeCache));
+ cache->minor = minor;
+ cache->ref_count = 1;
+ cache->buffer = buffer;
+ cache->size = st.st_size;
+
+ done:
+ if (fd != -1)
+ close (fd);
+
+#endif /* HAVE_MMAP */
+
+ return cache;
+}
+
+static int
+cache_magic_matchlet_compare_to_data (XdgMimeCache *cache,
+ xdg_uint32_t offset,
+ const void *data,
+ size_t len)
+{
+ xdg_uint32_t range_start = GET_UINT32 (cache->buffer, offset);
+ xdg_uint32_t range_length = GET_UINT32 (cache->buffer, offset + 4);
+ xdg_uint32_t data_length = GET_UINT32 (cache->buffer, offset + 12);
+ xdg_uint32_t data_offset = GET_UINT32 (cache->buffer, offset + 16);
+ xdg_uint32_t mask_offset = GET_UINT32 (cache->buffer, offset + 20);
+
+ int i, j;
+
+ for (i = range_start; i < range_start + range_length; i++)
+ {
+ int valid_matchlet = TRUE;
+
+ if (i + data_length > len)
+ return FALSE;
+
+ if (mask_offset)
+ {
+ for (j = 0; j < data_length; j++)
+ {
+ if ((((unsigned char *)cache->buffer)[data_offset + j] & ((unsigned char *)cache->buffer)[mask_offset + j]) !=
+ ((((unsigned char *) data)[j + i]) & ((unsigned char *)cache->buffer)[mask_offset + j]))
+ {
+ valid_matchlet = FALSE;
+ break;
+ }
+ }
+ }
+ else
+ {
+ valid_matchlet = memcmp(cache->buffer + data_offset, data + i, data_length) == 0;
+ }
+
+ if (valid_matchlet)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int
+cache_magic_matchlet_compare (XdgMimeCache *cache,
+ xdg_uint32_t offset,
+ const void *data,
+ size_t len)
+{
+ xdg_uint32_t n_children = GET_UINT32 (cache->buffer, offset + 24);
+ xdg_uint32_t child_offset = GET_UINT32 (cache->buffer, offset + 28);
+
+ int i;
+
+ if (cache_magic_matchlet_compare_to_data (cache, offset, data, len))
+ {
+ if (n_children == 0)
+ return TRUE;
+
+ for (i = 0; i < n_children; i++)
+ {
+ if (cache_magic_matchlet_compare (cache, child_offset + 32 * i,
+ data, len))
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static const char *
+cache_magic_compare_to_data (XdgMimeCache *cache,
+ xdg_uint32_t offset,
+ const void *data,
+ size_t len,
+ int *prio)
+{
+ xdg_uint32_t priority = GET_UINT32 (cache->buffer, offset);
+ xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, offset + 4);
+ xdg_uint32_t n_matchlets = GET_UINT32 (cache->buffer, offset + 8);
+ xdg_uint32_t matchlet_offset = GET_UINT32 (cache->buffer, offset + 12);
+
+ int i;
+
+ for (i = 0; i < n_matchlets; i++)
+ {
+ if (cache_magic_matchlet_compare (cache, matchlet_offset + i * 32,
+ data, len))
+ {
+ *prio = priority;
+
+ return cache->buffer + mimetype_offset;
+ }
+ }
+
+ return NULL;
+}
+
+static const char *
+cache_magic_lookup_data (XdgMimeCache *cache,
+ const void *data,
+ size_t len,
+ int *prio,
+ const char *mime_types[],
+ int n_mime_types)
+{
+ xdg_uint32_t list_offset;
+ xdg_uint32_t n_entries;
+ xdg_uint32_t offset;
+
+ int j, n;
+
+ *prio = 0;
+
+ list_offset = GET_UINT32 (cache->buffer, 24);
+ n_entries = GET_UINT32 (cache->buffer, list_offset);
+ offset = GET_UINT32 (cache->buffer, list_offset + 8);
+
+ for (j = 0; j < n_entries; j++)
+ {
+ const char *match;
+
+ match = cache_magic_compare_to_data (cache, offset + 16 * j,
+ data, len, prio);
+ if (match)
+ return match;
+ else
+ {
+ xdg_uint32_t mimetype_offset;
+ const char *non_match;
+
+ mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * j + 4);
+ non_match = cache->buffer + mimetype_offset;
+
+ for (n = 0; n < n_mime_types; n++)
+ {
+ if (mime_types[n] &&
+ _xdg_mime_mime_type_equal (mime_types[n], non_match))
+ mime_types[n] = NULL;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static const char *
+cache_alias_lookup (const char *alias)
+{
+ const char *ptr;
+ int i, min, max, mid, cmp;
+
+ for (i = 0; _caches[i]; i++)
+ {
+ XdgMimeCache *cache = _caches[i];
+ xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 4);
+ xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+ xdg_uint32_t offset;
+
+ min = 0;
+ max = n_entries - 1;
+ while (max >= min)
+ {
+ mid = (min + max) / 2;
+
+ offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
+ ptr = cache->buffer + offset;
+ cmp = strcmp (ptr, alias);
+
+ if (cmp < 0)
+ min = mid + 1;
+ else if (cmp > 0)
+ max = mid - 1;
+ else
+ {
+ offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
+ return cache->buffer + offset;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+typedef struct {
+ const char *mime;
+ int weight;
+} MimeWeight;
+
+static int
+cache_glob_lookup_literal (const char *file_name,
+ const char *mime_types[],
+ int n_mime_types,
+ int case_sensitive_check)
+{
+ const char *ptr;
+ int i, min, max, mid, cmp;
+
+ for (i = 0; _caches[i]; i++)
+ {
+ XdgMimeCache *cache = _caches[i];
+ xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 12);
+ xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+ xdg_uint32_t offset;
+
+ min = 0;
+ max = n_entries - 1;
+ while (max >= min)
+ {
+ mid = (min + max) / 2;
+
+ offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid);
+ ptr = cache->buffer + offset;
+ cmp = strcmp (ptr, file_name);
+
+ if (cmp < 0)
+ min = mid + 1;
+ else if (cmp > 0)
+ max = mid - 1;
+ else
+ {
+ int weight = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid + 8);
+ int case_sensitive = weight & 0x100;
+ weight = weight & 0xff;
+
+ if (case_sensitive_check || !case_sensitive)
+ {
+ offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid + 4);
+ mime_types[0] = (const char *)(cache->buffer + offset);
+
+ return 1;
+ }
+ return 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+cache_glob_lookup_fnmatch (const char *file_name,
+ MimeWeight mime_types[],
+ int n_mime_types,
+ int case_sensitive_check)
+{
+ const char *mime_type;
+ const char *ptr;
+
+ int i, j, n;
+
+ n = 0;
+ for (i = 0; _caches[i]; i++)
+ {
+ XdgMimeCache *cache = _caches[i];
+
+ xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 20);
+ xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+
+ for (j = 0; j < n_entries && n < n_mime_types; j++)
+ {
+ xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j);
+ xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 4);
+ int weight = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 8);
+ int case_sensitive = weight & 0x100;
+ weight = weight & 0xff;
+ ptr = cache->buffer + offset;
+ mime_type = cache->buffer + mimetype_offset;
+ if (case_sensitive_check || !case_sensitive)
+ {
+ /* FIXME: Not UTF-8 safe */
+ if (fnmatch (ptr, file_name, 0) == 0)
+ {
+ mime_types[n].mime = mime_type;
+ mime_types[n].weight = weight;
+ n++;
+ }
+ }
+ }
+
+ if (n > 0)
+ return n;
+ }
+
+ return 0;
+}
+
+static int
+cache_glob_node_lookup_suffix (XdgMimeCache *cache,
+ xdg_uint32_t n_entries,
+ xdg_uint32_t offset,
+ const char *file_name,
+ int len,
+ int case_sensitive_check,
+ MimeWeight mime_types[],
+ int n_mime_types)
+{
+ xdg_unichar_t character;
+ xdg_unichar_t match_char;
+ xdg_uint32_t mimetype_offset;
+ xdg_uint32_t n_children;
+ xdg_uint32_t child_offset;
+ int weight;
+ int case_sensitive;
+
+ int min, max, mid, n, i;
+
+ character = file_name[len - 1];
+
+ assert (character != 0);
+
+ min = 0;
+ max = n_entries - 1;
+ while (max >= min)
+ {
+ mid = (min + max) / 2;
+ match_char = GET_UINT32 (cache->buffer, offset + 12 * mid);
+ if (match_char < character)
+ min = mid + 1;
+ else if (match_char > character)
+ max = mid - 1;
+ else
+ {
+ len--;
+ n = 0;
+ n_children = GET_UINT32 (cache->buffer, offset + 12 * mid + 4);
+ child_offset = GET_UINT32 (cache->buffer, offset + 12 * mid + 8);
+
+ if (len > 0)
+ {
+ n = cache_glob_node_lookup_suffix (cache,
+ n_children, child_offset,
+ file_name, len,
+ case_sensitive_check,
+ mime_types,
+ n_mime_types);
+ }
+ if (n == 0)
+ {
+ i = 0;
+ while (n < n_mime_types && i < n_children)
+ {
+ match_char = GET_UINT32 (cache->buffer, child_offset + 12 * i);
+ if (match_char != 0)
+ break;
+
+ mimetype_offset = GET_UINT32 (cache->buffer, child_offset + 12 * i + 4);
+ weight = GET_UINT32 (cache->buffer, child_offset + 12 * i + 8);
+ case_sensitive = weight & 0x100;
+ weight = weight & 0xff;
+
+ if (case_sensitive_check || !case_sensitive)
+ {
+ mime_types[n].mime = cache->buffer + mimetype_offset;
+ mime_types[n].weight = weight;
+ n++;
+ }
+ i++;
+ }
+ }
+ return n;
+ }
+ }
+ return 0;
+}
+
+static int
+cache_glob_lookup_suffix (const char *file_name,
+ int len,
+ int ignore_case,
+ MimeWeight mime_types[],
+ int n_mime_types)
+{
+ int i, n;
+
+ for (i = 0; _caches[i]; i++)
+ {
+ XdgMimeCache *cache = _caches[i];
+
+ xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 16);
+ xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+ xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4);
+
+ n = cache_glob_node_lookup_suffix (cache,
+ n_entries, offset,
+ file_name, len,
+ ignore_case,
+ mime_types,
+ n_mime_types);
+ if (n > 0)
+ return n;
+ }
+
+ return 0;
+}
+
+static int compare_mime_weight (const void *a, const void *b)
+{
+ const MimeWeight *aa = (const MimeWeight *)a;
+ const MimeWeight *bb = (const MimeWeight *)b;
+
+ return bb->weight - aa->weight;
+}
+
+#define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z')
+static char *
+ascii_tolower (const char *str)
+{
+ char *p, *lower;
+
+ lower = strdup (str);
+ p = lower;
+ while (*p != 0)
+ {
+ char c = *p;
+ *p++ = ISUPPER (c) ? c - 'A' + 'a' : c;
+ }
+ return lower;
+}
+
+static int
+cache_glob_lookup_file_name (const char *file_name,
+ const char *mime_types[],
+ int n_mime_types)
+{
+ int n;
+ MimeWeight mimes[10];
+ int n_mimes = 10;
+ int i;
+ int len;
+ char *lower_case;
+
+ assert (file_name != NULL && n_mime_types > 0);
+
+ /* First, check the literals */
+
+ lower_case = ascii_tolower (file_name);
+
+ n = cache_glob_lookup_literal (lower_case, mime_types, n_mime_types, FALSE);
+ if (n > 0)
+ {
+ free (lower_case);
+ return n;
+ }
+
+ n = cache_glob_lookup_literal (file_name, mime_types, n_mime_types, TRUE);
+ if (n > 0)
+ {
+ free (lower_case);
+ return n;
+ }
+
+ len = strlen (file_name);
+ n = cache_glob_lookup_suffix (lower_case, len, FALSE, mimes, n_mimes);
+ if (n == 0)
+ n = cache_glob_lookup_suffix (file_name, len, TRUE, mimes, n_mimes);
+
+ /* Last, try fnmatch */
+ if (n == 0)
+ n = cache_glob_lookup_fnmatch (lower_case, mimes, n_mimes, FALSE);
+ if (n == 0)
+ n = cache_glob_lookup_fnmatch (file_name, mimes, n_mimes, TRUE);
+
+ free (lower_case);
+
+ qsort (mimes, n, sizeof (MimeWeight), compare_mime_weight);
+
+ if (n_mime_types < n)
+ n = n_mime_types;
+
+ for (i = 0; i < n; i++)
+ mime_types[i] = mimes[i].mime;
+
+ return n;
+}
+
+int
+_xdg_mime_cache_get_max_buffer_extents (void)
+{
+ xdg_uint32_t offset;
+ xdg_uint32_t max_extent;
+ int i;
+
+ max_extent = 0;
+ for (i = 0; _caches[i]; i++)
+ {
+ XdgMimeCache *cache = _caches[i];
+
+ offset = GET_UINT32 (cache->buffer, 24);
+ max_extent = MAX (max_extent, GET_UINT32 (cache->buffer, offset + 4));
+ }
+
+ return max_extent;
+}
+
+static const char *
+cache_get_mime_type_for_data (const void *data,
+ size_t len,
+ int *result_prio,
+ const char *mime_types[],
+ int n_mime_types)
+{
+ const char *mime_type;
+ int i, n, priority;
+
+ priority = 0;
+ mime_type = NULL;
+ for (i = 0; _caches[i]; i++)
+ {
+ XdgMimeCache *cache = _caches[i];
+
+ int prio;
+ const char *match;
+
+ match = cache_magic_lookup_data (cache, data, len, &prio,
+ mime_types, n_mime_types);
+ if (prio > priority)
+ {
+ priority = prio;
+ mime_type = match;
+ }
+ }
+
+ if (result_prio)
+ *result_prio = priority;
+
+ if (priority > 0)
+ {
+ /* Pick glob-result R where mime_type inherits from R */
+ for (n = 0; n < n_mime_types; n++)
+ {
+ if (mime_types[n] && _xdg_mime_cache_mime_type_subclass(mime_types[n], mime_type))
+ return mime_types[n];
+ }
+
+ /* Return magic match */
+ return mime_type;
+ }
+
+ /* Pick first glob result, as fallback */
+ for (n = 0; n < n_mime_types; n++)
+ {
+ if (mime_types[n])
+ return mime_types[n];
+ }
+
+ return NULL;
+}
+
+const char *
+_xdg_mime_cache_get_mime_type_for_data (const void *data,
+ size_t len,
+ int *result_prio)
+{
+ return cache_get_mime_type_for_data (data, len, result_prio, NULL, 0);
+}
+
+const char *
+_xdg_mime_cache_get_mime_type_for_file (const char *file_name,
+ struct stat *statbuf)
+{
+ const char *mime_type;
+ const char *mime_types[10];
+ FILE *file;
+ unsigned char *data;
+ int max_extent;
+ int bytes_read;
+ struct stat buf;
+ const char *base_name;
+ int n;
+
+ if (file_name == NULL)
+ return NULL;
+
+ if (! _xdg_utf8_validate (file_name))
+ return NULL;
+
+ base_name = _xdg_get_base_name (file_name);
+ n = cache_glob_lookup_file_name (base_name, mime_types, 10);
+
+ if (n == 1)
+ return mime_types[0];
+
+ if (!statbuf)
+ {
+ if (stat (file_name, &buf) != 0)
+ return XDG_MIME_TYPE_UNKNOWN;
+
+ statbuf = &buf;
+ }
+
+ if (statbuf->st_size == 0)
+ return XDG_MIME_TYPE_EMPTY;
+
+ if (!S_ISREG (statbuf->st_mode))
+ return XDG_MIME_TYPE_UNKNOWN;
+
+ /* FIXME: Need to make sure that max_extent isn't totally broken. This could
+ * be large and need getting from a stream instead of just reading it all
+ * in. */
+ max_extent = _xdg_mime_cache_get_max_buffer_extents ();
+ data = malloc (max_extent);
+ if (data == NULL)
+ return XDG_MIME_TYPE_UNKNOWN;
+
+ file = fopen (file_name, "r");
+ if (file == NULL)
+ {
+ free (data);
+ return XDG_MIME_TYPE_UNKNOWN;
+ }
+
+ bytes_read = fread (data, 1, max_extent, file);
+ if (ferror (file))
+ {
+ free (data);
+ fclose (file);
+ return XDG_MIME_TYPE_UNKNOWN;
+ }
+
+ mime_type = cache_get_mime_type_for_data (data, bytes_read, NULL,
+ mime_types, n);
+
+ if (!mime_type)
+ mime_type = _xdg_binary_or_text_fallback(data, bytes_read);
+
+ free (data);
+ fclose (file);
+
+ return mime_type;
+}
+
+const char *
+_xdg_mime_cache_get_mime_type_from_file_name (const char *file_name)
+{
+ const char *mime_type;
+
+ if (cache_glob_lookup_file_name (file_name, &mime_type, 1))
+ return mime_type;
+ else
+ return XDG_MIME_TYPE_UNKNOWN;
+}
+
+int
+_xdg_mime_cache_get_mime_types_from_file_name (const char *file_name,
+ const char *mime_types[],
+ int n_mime_types)
+{
+ return cache_glob_lookup_file_name (file_name, mime_types, n_mime_types);
+}
+
+#if 1
+static int
+is_super_type (const char *mime)
+{
+ int length;
+ const char *type;
+
+ length = strlen (mime);
+ type = &(mime[length - 2]);
+
+ if (strcmp (type, "/*") == 0)
+ return 1;
+
+ return 0;
+}
+#endif
+
+int
+_xdg_mime_cache_mime_type_subclass (const char *mime,
+ const char *base)
+{
+ const char *umime, *ubase;
+
+ int i, j, min, max, med, cmp;
+
+ umime = _xdg_mime_cache_unalias_mime_type (mime);
+ ubase = _xdg_mime_cache_unalias_mime_type (base);
+
+ if (strcmp (umime, ubase) == 0)
+ return 1;
+
+ /* We really want to handle text/ * in GtkFileFilter, so we just
+ * turn on the supertype matching
+ */
+#if 1
+ /* Handle supertypes */
+ if (is_super_type (ubase) &&
+ xdg_mime_media_type_equal (umime, ubase))
+ return 1;
+#endif
+
+ /* Handle special cases text/plain and application/octet-stream */
+ if (strcmp (ubase, "text/plain") == 0 &&
+ strncmp (umime, "text/", 5) == 0)
+ return 1;
+
+ if (strcmp (ubase, "application/octet-stream") == 0)
+ return 1;
+
+ for (i = 0; _caches[i]; i++)
+ {
+ XdgMimeCache *cache = _caches[i];
+
+ xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
+ xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+ xdg_uint32_t offset, n_parents, parent_offset;
+
+ min = 0;
+ max = n_entries - 1;
+ while (max >= min)
+ {
+ med = (min + max)/2;
+
+ offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med);
+ cmp = strcmp (cache->buffer + offset, umime);
+ if (cmp < 0)
+ min = med + 1;
+ else if (cmp > 0)
+ max = med - 1;
+ else
+ {
+ offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med + 4);
+ n_parents = GET_UINT32 (cache->buffer, offset);
+
+ for (j = 0; j < n_parents; j++)
+ {
+ parent_offset = GET_UINT32 (cache->buffer, offset + 4 + 4 * j);
+ if (_xdg_mime_cache_mime_type_subclass (cache->buffer + parent_offset, ubase))
+ return 1;
+ }
+
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+const char *
+_xdg_mime_cache_unalias_mime_type (const char *mime)
+{
+ const char *lookup;
+
+ lookup = cache_alias_lookup (mime);
+
+ if (lookup)
+ return lookup;
+
+ return mime;
+}
+
+char **
+_xdg_mime_cache_list_mime_parents (const char *mime)
+{
+ int i, j, k, l, p;
+ char *all_parents[128]; /* we'll stop at 128 */
+ char **result;
+
+ mime = xdg_mime_unalias_mime_type (mime);
+
+ p = 0;
+ for (i = 0; _caches[i]; i++)
+ {
+ XdgMimeCache *cache = _caches[i];
+
+ xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
+ xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+
+ for (j = 0; j < n_entries; j++)
+ {
+ xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j);
+ xdg_uint32_t parents_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j + 4);
+
+ if (strcmp (cache->buffer + mimetype_offset, mime) == 0)
+ {
+ xdg_uint32_t parent_mime_offset;
+ xdg_uint32_t n_parents = GET_UINT32 (cache->buffer, parents_offset);
+
+ for (k = 0; k < n_parents && p < 127; k++)
+ {
+ parent_mime_offset = GET_UINT32 (cache->buffer, parents_offset + 4 + 4 * k);
+
+ /* Don't add same parent multiple times.
+ * This can happen for instance if the same type is listed in multiple directories
+ */
+ for (l = 0; l < p; l++)
+ {
+ if (strcmp (all_parents[l], cache->buffer + parent_mime_offset) == 0)
+ break;
+ }
+
+ if (l == p)
+ all_parents[p++] = cache->buffer + parent_mime_offset;
+ }
+
+ break;
+ }
+ }
+ }
+ all_parents[p++] = NULL;
+
+ result = (char **) malloc (p * sizeof (char *));
+ memcpy (result, all_parents, p * sizeof (char *));
+
+ return result;
+}
+
+static const char *
+cache_lookup_icon (const char *mime, int header)
+{
+ const char *ptr;
+ int i, min, max, mid, cmp;
+
+ for (i = 0; _caches[i]; i++)
+ {
+ XdgMimeCache *cache = _caches[i];
+ xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, header);
+ xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+ xdg_uint32_t offset;
+
+ min = 0;
+ max = n_entries - 1;
+ while (max >= min)
+ {
+ mid = (min + max) / 2;
+
+ offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
+ ptr = cache->buffer + offset;
+ cmp = strcmp (ptr, mime);
+
+ if (cmp < 0)
+ min = mid + 1;
+ else if (cmp > 0)
+ max = mid - 1;
+ else
+ {
+ offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
+ return cache->buffer + offset;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+const char *
+_xdg_mime_cache_get_generic_icon (const char *mime)
+{
+ return cache_lookup_icon (mime, 36);
+}
+
+const char *
+_xdg_mime_cache_get_icon (const char *mime)
+{
+ return cache_lookup_icon (mime, 32);
+}
+
+static void
+dump_glob_node (XdgMimeCache *cache,
+ xdg_uint32_t offset,
+ int depth)
+{
+ xdg_unichar_t character;
+ xdg_uint32_t mime_offset;
+ xdg_uint32_t n_children;
+ xdg_uint32_t child_offset;
+ int i;
+
+ character = GET_UINT32 (cache->buffer, offset);
+ mime_offset = GET_UINT32 (cache->buffer, offset + 4);
+ n_children = GET_UINT32 (cache->buffer, offset + 8);
+ child_offset = GET_UINT32 (cache->buffer, offset + 12);
+ for (i = 0; i < depth; i++)
+ printf (" ");
+ printf ("%c", character);
+ if (mime_offset)
+ printf (" - %s", cache->buffer + mime_offset);
+ printf ("\n");
+ if (child_offset)
+ {
+ for (i = 0; i < n_children; i++)
+ dump_glob_node (cache, child_offset + 20 * i, depth + 1);
+ }
+}
+
+void
+_xdg_mime_cache_glob_dump (void)
+{
+ int i, j;
+ for (i = 0; _caches[i]; i++)
+ {
+ XdgMimeCache *cache = _caches[i];
+ xdg_uint32_t list_offset;
+ xdg_uint32_t n_entries;
+ xdg_uint32_t offset;
+ list_offset = GET_UINT32 (cache->buffer, 16);
+ n_entries = GET_UINT32 (cache->buffer, list_offset);
+ offset = GET_UINT32 (cache->buffer, list_offset + 4);
+ for (j = 0; j < n_entries; j++)
+ dump_glob_node (cache, offset + 20 * j, 0);
+ }
+}
diff --git a/src/base/third_party/xdg_mime/xdgmimecache.h b/src/base/third_party/xdg_mime/xdgmimecache.h
new file mode 100644
index 0000000..27f42d0
--- /dev/null
+++ b/src/base/third_party/xdg_mime/xdgmimecache.h
@@ -0,0 +1,81 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimecache.h: Private file. Datastructure for mmapped caches.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2005 Matthias Clasen <mclasen@redhat.com>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XDG_MIME_CACHE_H__
+#define __XDG_MIME_CACHE_H__
+
+#include "xdgmime.h"
+
+typedef struct _XdgMimeCache XdgMimeCache;
+
+#ifdef XDG_PREFIX
+#define _xdg_mime_cache_new_from_file XDG_RESERVED_ENTRY(cache_new_from_file)
+#define _xdg_mime_cache_ref XDG_RESERVED_ENTRY(cache_ref)
+#define _xdg_mime_cache_unref XDG_RESERVED_ENTRY(cache_unref)
+#define _xdg_mime_cache_get_max_buffer_extents XDG_RESERVED_ENTRY(cache_get_max_buffer_extents)
+#define _xdg_mime_cache_get_mime_type_for_data XDG_RESERVED_ENTRY(cache_get_mime_type_for_data)
+#define _xdg_mime_cache_get_mime_type_for_file XDG_RESERVED_ENTRY(cache_get_mime_type_for_file)
+#define _xdg_mime_cache_get_mime_type_from_file_name XDG_RESERVED_ENTRY(cache_get_mime_type_from_file_name)
+#define _xdg_mime_cache_get_mime_types_from_file_name XDG_RESERVED_ENTRY(cache_get_mime_types_from_file_name)
+#define _xdg_mime_cache_list_mime_parents XDG_RESERVED_ENTRY(cache_list_mime_parents)
+#define _xdg_mime_cache_mime_type_subclass XDG_RESERVED_ENTRY(cache_mime_type_subclass)
+#define _xdg_mime_cache_unalias_mime_type XDG_RESERVED_ENTRY(cache_unalias_mime_type)
+#define _xdg_mime_cache_get_icon XDG_RESERVED_ENTRY(cache_get_icon)
+#define _xdg_mime_cache_get_generic_icon XDG_RESERVED_ENTRY(cache_get_generic_icon)
+#define _xdg_mime_cache_glob_dump XDG_RESERVED_ENTRY(cache_glob_dump)
+#endif
+
+extern XdgMimeCache **_caches;
+
+XdgMimeCache *_xdg_mime_cache_new_from_file (const char *file_name);
+XdgMimeCache *_xdg_mime_cache_ref (XdgMimeCache *cache);
+void _xdg_mime_cache_unref (XdgMimeCache *cache);
+
+
+const char *_xdg_mime_cache_get_mime_type_for_data (const void *data,
+ size_t len,
+ int *result_prio);
+const char *_xdg_mime_cache_get_mime_type_for_file (const char *file_name,
+ struct stat *statbuf);
+int _xdg_mime_cache_get_mime_types_from_file_name (const char *file_name,
+ const char *mime_types[],
+ int n_mime_types);
+const char *_xdg_mime_cache_get_mime_type_from_file_name (const char *file_name);
+int _xdg_mime_cache_is_valid_mime_type (const char *mime_type);
+int _xdg_mime_cache_mime_type_equal (const char *mime_a,
+ const char *mime_b);
+int _xdg_mime_cache_media_type_equal (const char *mime_a,
+ const char *mime_b);
+int _xdg_mime_cache_mime_type_subclass (const char *mime_a,
+ const char *mime_b);
+char **_xdg_mime_cache_list_mime_parents (const char *mime);
+const char *_xdg_mime_cache_unalias_mime_type (const char *mime);
+int _xdg_mime_cache_get_max_buffer_extents (void);
+const char *_xdg_mime_cache_get_icon (const char *mime);
+const char *_xdg_mime_cache_get_generic_icon (const char *mime);
+void _xdg_mime_cache_glob_dump (void);
+
+#endif /* __XDG_MIME_CACHE_H__ */
diff --git a/src/base/third_party/xdg_mime/xdgmimeglob.c b/src/base/third_party/xdg_mime/xdgmimeglob.c
new file mode 100644
index 0000000..f8434bc
--- /dev/null
+++ b/src/base/third_party/xdg_mime/xdgmimeglob.c
@@ -0,0 +1,691 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimeglob.c: Private file. Datastructure for storing the globs.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "xdgmimeglob.h"
+#include "xdgmimeint.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <fnmatch.h>
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+typedef struct XdgGlobHashNode XdgGlobHashNode;
+typedef struct XdgGlobList XdgGlobList;
+
+struct XdgGlobHashNode
+{
+ xdg_unichar_t character;
+ const char *mime_type;
+ int weight;
+ int case_sensitive;
+ XdgGlobHashNode *next;
+ XdgGlobHashNode *child;
+};
+struct XdgGlobList
+{
+ const char *data;
+ const char *mime_type;
+ int weight;
+ int case_sensitive;
+ XdgGlobList *next;
+};
+
+struct XdgGlobHash
+{
+ XdgGlobList *literal_list;
+ XdgGlobHashNode *simple_node;
+ XdgGlobList *full_list;
+};
+
+
+/* XdgGlobList
+ */
+static XdgGlobList *
+_xdg_glob_list_new (void)
+{
+ XdgGlobList *new_element;
+
+ new_element = calloc (1, sizeof (XdgGlobList));
+
+ return new_element;
+}
+
+/* Frees glob_list and all of it's children */
+static void
+_xdg_glob_list_free (XdgGlobList *glob_list)
+{
+ XdgGlobList *ptr, *next;
+
+ ptr = glob_list;
+
+ while (ptr != NULL)
+ {
+ next = ptr->next;
+
+ if (ptr->data)
+ free ((void *) ptr->data);
+ if (ptr->mime_type)
+ free ((void *) ptr->mime_type);
+ free (ptr);
+
+ ptr = next;
+ }
+}
+
+static XdgGlobList *
+_xdg_glob_list_append (XdgGlobList *glob_list,
+ void *data,
+ const char *mime_type,
+ int weight,
+ int case_sensitive)
+{
+ XdgGlobList *new_element;
+ XdgGlobList *tmp_element;
+
+ tmp_element = glob_list;
+ while (tmp_element != NULL)
+ {
+ if (strcmp (tmp_element->data, data) == 0 &&
+ strcmp (tmp_element->mime_type, mime_type) == 0)
+ return glob_list;
+
+ tmp_element = tmp_element->next;
+ }
+
+ new_element = _xdg_glob_list_new ();
+ new_element->data = data;
+ new_element->mime_type = mime_type;
+ new_element->weight = weight;
+ new_element->case_sensitive = case_sensitive;
+ if (glob_list == NULL)
+ return new_element;
+
+ tmp_element = glob_list;
+ while (tmp_element->next != NULL)
+ tmp_element = tmp_element->next;
+
+ tmp_element->next = new_element;
+
+ return glob_list;
+}
+
+/* XdgGlobHashNode
+ */
+
+static XdgGlobHashNode *
+_xdg_glob_hash_node_new (void)
+{
+ XdgGlobHashNode *glob_hash_node;
+
+ glob_hash_node = calloc (1, sizeof (XdgGlobHashNode));
+
+ return glob_hash_node;
+}
+
+static void
+_xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node,
+ int depth)
+{
+ int i;
+ for (i = 0; i < depth; i++)
+ printf (" ");
+
+ printf ("%c", (char)glob_hash_node->character);
+ if (glob_hash_node->mime_type)
+ printf (" - %s %d\n", glob_hash_node->mime_type, glob_hash_node->weight);
+ else
+ printf ("\n");
+ if (glob_hash_node->child)
+ _xdg_glob_hash_node_dump (glob_hash_node->child, depth + 1);
+ if (glob_hash_node->next)
+ _xdg_glob_hash_node_dump (glob_hash_node->next, depth);
+}
+
+static XdgGlobHashNode *
+_xdg_glob_hash_insert_ucs4 (XdgGlobHashNode *glob_hash_node,
+ xdg_unichar_t *text,
+ const char *mime_type,
+ int weight,
+ int case_sensitive)
+{
+ XdgGlobHashNode *node;
+ xdg_unichar_t character;
+
+ character = text[0];
+
+ if ((glob_hash_node == NULL) ||
+ (character < glob_hash_node->character))
+ {
+ node = _xdg_glob_hash_node_new ();
+ node->character = character;
+ node->next = glob_hash_node;
+ glob_hash_node = node;
+ }
+ else if (character == glob_hash_node->character)
+ {
+ node = glob_hash_node;
+ }
+ else
+ {
+ XdgGlobHashNode *prev_node;
+ int found_node = FALSE;
+
+ /* Look for the first character of text in glob_hash_node, and insert it if we
+ * have to.*/
+ prev_node = glob_hash_node;
+ node = prev_node->next;
+
+ while (node != NULL)
+ {
+ if (character < node->character)
+ {
+ node = _xdg_glob_hash_node_new ();
+ node->character = character;
+ node->next = prev_node->next;
+ prev_node->next = node;
+
+ found_node = TRUE;
+ break;
+ }
+ else if (character == node->character)
+ {
+ found_node = TRUE;
+ break;
+ }
+ prev_node = node;
+ node = node->next;
+ }
+
+ if (! found_node)
+ {
+ node = _xdg_glob_hash_node_new ();
+ node->character = character;
+ node->next = prev_node->next;
+ prev_node->next = node;
+ }
+ }
+
+ text++;
+ if (*text == 0)
+ {
+ if (node->mime_type)
+ {
+ if (strcmp (node->mime_type, mime_type) != 0)
+ {
+ XdgGlobHashNode *child;
+ int found_node = FALSE;
+
+ child = node->child;
+ while (child && child->character == 0)
+ {
+ if (strcmp (child->mime_type, mime_type) == 0)
+ {
+ found_node = TRUE;
+ break;
+ }
+ child = child->next;
+ }
+
+ if (!found_node)
+ {
+ child = _xdg_glob_hash_node_new ();
+ child->character = 0;
+ child->mime_type = strdup (mime_type);
+ child->weight = weight;
+ child->case_sensitive = case_sensitive;
+ child->child = NULL;
+ child->next = node->child;
+ node->child = child;
+ }
+ }
+ }
+ else
+ {
+ node->mime_type = strdup (mime_type);
+ node->weight = weight;
+ node->case_sensitive = case_sensitive;
+ }
+ }
+ else
+ {
+ node->child = _xdg_glob_hash_insert_ucs4 (node->child, text, mime_type, weight, case_sensitive);
+ }
+ return glob_hash_node;
+}
+
+/* glob must be valid UTF-8 */
+static XdgGlobHashNode *
+_xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node,
+ const char *text,
+ const char *mime_type,
+ int weight,
+ int case_sensitive)
+{
+ XdgGlobHashNode *node;
+ xdg_unichar_t *unitext;
+ int len;
+
+ unitext = _xdg_convert_to_ucs4 (text, &len);
+ _xdg_reverse_ucs4 (unitext, len);
+ node = _xdg_glob_hash_insert_ucs4 (glob_hash_node, unitext, mime_type, weight, case_sensitive);
+ free (unitext);
+ return node;
+}
+
+typedef struct {
+ const char *mime;
+ int weight;
+} MimeWeight;
+
+static int
+_xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node,
+ const char *file_name,
+ int len,
+ int case_sensitive_check,
+ MimeWeight mime_types[],
+ int n_mime_types)
+{
+ int n;
+ XdgGlobHashNode *node;
+ xdg_unichar_t character;
+
+ if (glob_hash_node == NULL)
+ return 0;
+
+ character = file_name[len - 1];
+
+ for (node = glob_hash_node; node && character >= node->character; node = node->next)
+ {
+ if (character == node->character)
+ {
+ len--;
+ n = 0;
+ if (len > 0)
+ {
+ n = _xdg_glob_hash_node_lookup_file_name (node->child,
+ file_name,
+ len,
+ case_sensitive_check,
+ mime_types,
+ n_mime_types);
+ }
+ if (n == 0)
+ {
+ if (node->mime_type &&
+ (case_sensitive_check ||
+ !node->case_sensitive))
+ {
+ mime_types[n].mime = node->mime_type;
+ mime_types[n].weight = node->weight;
+ n++;
+ }
+ node = node->child;
+ while (n < n_mime_types && node && node->character == 0)
+ {
+ if (node->mime_type &&
+ (case_sensitive_check ||
+ !node->case_sensitive))
+ {
+ mime_types[n].mime = node->mime_type;
+ mime_types[n].weight = node->weight;
+ n++;
+ }
+ node = node->next;
+ }
+ }
+ return n;
+ }
+ }
+
+ return 0;
+}
+
+static int compare_mime_weight (const void *a, const void *b)
+{
+ const MimeWeight *aa = (const MimeWeight *)a;
+ const MimeWeight *bb = (const MimeWeight *)b;
+
+ return bb->weight - aa->weight;
+}
+
+#define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z')
+static char *
+ascii_tolower (const char *str)
+{
+ char *p, *lower;
+
+ lower = strdup (str);
+ p = lower;
+ while (*p != 0)
+ {
+ char c = *p;
+ *p++ = ISUPPER (c) ? c - 'A' + 'a' : c;
+ }
+ return lower;
+}
+
+int
+_xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
+ const char *file_name,
+ const char *mime_types[],
+ int n_mime_types)
+{
+ XdgGlobList *list;
+ int i, n;
+ MimeWeight mimes[10];
+ int n_mimes = 10;
+ int len;
+ char *lower_case;
+
+ /* First, check the literals */
+
+ assert (file_name != NULL && n_mime_types > 0);
+
+ n = 0;
+
+ lower_case = ascii_tolower (file_name);
+
+ for (list = glob_hash->literal_list; list; list = list->next)
+ {
+ if (strcmp ((const char *)list->data, file_name) == 0)
+ {
+ mime_types[0] = list->mime_type;
+ free (lower_case);
+ return 1;
+ }
+ }
+
+ for (list = glob_hash->literal_list; list; list = list->next)
+ {
+ if (!list->case_sensitive &&
+ strcmp ((const char *)list->data, lower_case) == 0)
+ {
+ mime_types[0] = list->mime_type;
+ free (lower_case);
+ return 1;
+ }
+ }
+
+
+ len = strlen (file_name);
+ n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, lower_case, len, FALSE,
+ mimes, n_mimes);
+ if (n == 0)
+ n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, file_name, len, TRUE,
+ mimes, n_mimes);
+
+ if (n == 0)
+ {
+ for (list = glob_hash->full_list; list && n < n_mime_types; list = list->next)
+ {
+ if (fnmatch ((const char *)list->data, file_name, 0) == 0)
+ {
+ mimes[n].mime = list->mime_type;
+ mimes[n].weight = list->weight;
+ n++;
+ }
+ }
+ }
+ free (lower_case);
+
+ qsort (mimes, n, sizeof (MimeWeight), compare_mime_weight);
+
+ if (n_mime_types < n)
+ n = n_mime_types;
+
+ for (i = 0; i < n; i++)
+ mime_types[i] = mimes[i].mime;
+
+ return n;
+}
+
+
+
+/* XdgGlobHash
+ */
+
+XdgGlobHash *
+_xdg_glob_hash_new (void)
+{
+ XdgGlobHash *glob_hash;
+
+ glob_hash = calloc (1, sizeof (XdgGlobHash));
+
+ return glob_hash;
+}
+
+
+static void
+_xdg_glob_hash_free_nodes (XdgGlobHashNode *node)
+{
+ if (node)
+ {
+ if (node->child)
+ _xdg_glob_hash_free_nodes (node->child);
+ if (node->next)
+ _xdg_glob_hash_free_nodes (node->next);
+ if (node->mime_type)
+ free ((void *) node->mime_type);
+ free (node);
+ }
+}
+
+void
+_xdg_glob_hash_free (XdgGlobHash *glob_hash)
+{
+ _xdg_glob_list_free (glob_hash->literal_list);
+ _xdg_glob_list_free (glob_hash->full_list);
+ _xdg_glob_hash_free_nodes (glob_hash->simple_node);
+ free (glob_hash);
+}
+
+XdgGlobType
+_xdg_glob_determine_type (const char *glob)
+{
+ const char *ptr;
+ int maybe_in_simple_glob = FALSE;
+ int first_char = TRUE;
+
+ ptr = glob;
+
+ while (*ptr != '\0')
+ {
+ if (*ptr == '*' && first_char)
+ maybe_in_simple_glob = TRUE;
+ else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*')
+ return XDG_GLOB_FULL;
+
+ first_char = FALSE;
+ ptr = _xdg_utf8_next_char (ptr);
+ }
+ if (maybe_in_simple_glob)
+ return XDG_GLOB_SIMPLE;
+ else
+ return XDG_GLOB_LITERAL;
+}
+
+/* glob must be valid UTF-8 */
+void
+_xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
+ const char *glob,
+ const char *mime_type,
+ int weight,
+ int case_sensitive)
+{
+ XdgGlobType type;
+
+ assert (glob_hash != NULL);
+ assert (glob != NULL);
+
+ type = _xdg_glob_determine_type (glob);
+
+ switch (type)
+ {
+ case XDG_GLOB_LITERAL:
+ glob_hash->literal_list = _xdg_glob_list_append (glob_hash->literal_list, strdup (glob), strdup (mime_type), weight, case_sensitive);
+ break;
+ case XDG_GLOB_SIMPLE:
+ glob_hash->simple_node = _xdg_glob_hash_insert_text (glob_hash->simple_node, glob + 1, mime_type, weight, case_sensitive);
+ break;
+ case XDG_GLOB_FULL:
+ glob_hash->full_list = _xdg_glob_list_append (glob_hash->full_list, strdup (glob), strdup (mime_type), weight, case_sensitive);
+ break;
+ }
+}
+
+void
+_xdg_glob_hash_dump (XdgGlobHash *glob_hash)
+{
+ XdgGlobList *list;
+ printf ("LITERAL STRINGS\n");
+ if (!glob_hash || glob_hash->literal_list == NULL)
+ {
+ printf (" None\n");
+ }
+ else
+ {
+ for (list = glob_hash->literal_list; list; list = list->next)
+ printf (" %s - %s %d\n", (char *)list->data, list->mime_type, list->weight);
+ }
+ printf ("\nSIMPLE GLOBS\n");
+ if (!glob_hash || glob_hash->simple_node == NULL)
+ {
+ printf (" None\n");
+ }
+ else
+ {
+ _xdg_glob_hash_node_dump (glob_hash->simple_node, 4);
+ }
+
+ printf ("\nFULL GLOBS\n");
+ if (!glob_hash || glob_hash->full_list == NULL)
+ {
+ printf (" None\n");
+ }
+ else
+ {
+ for (list = glob_hash->full_list; list; list = list->next)
+ printf (" %s - %s %d\n", (char *)list->data, list->mime_type, list->weight);
+ }
+}
+
+
+void
+_xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash,
+ const char *file_name,
+ int version_two)
+{
+ FILE *glob_file;
+ char line[255];
+ char *p;
+
+ glob_file = fopen (file_name, "r");
+
+ if (glob_file == NULL)
+ return;
+
+ /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
+ * Blah */
+ while (fgets (line, 255, glob_file) != NULL)
+ {
+ char *colon;
+ char *mimetype, *glob, *end;
+ int weight;
+ int case_sensitive;
+
+ if (line[0] == '#' || line[0] == 0)
+ continue;
+
+ end = line + strlen(line) - 1;
+ if (*end == '\n')
+ *end = 0;
+
+ p = line;
+ if (version_two)
+ {
+ colon = strchr (p, ':');
+ if (colon == NULL)
+ continue;
+ *colon = 0;
+ weight = atoi (p);
+ p = colon + 1;
+ }
+ else
+ weight = 50;
+
+ colon = strchr (p, ':');
+ if (colon == NULL)
+ continue;
+ *colon = 0;
+
+ mimetype = p;
+ p = colon + 1;
+ glob = p;
+ case_sensitive = FALSE;
+
+ colon = strchr (p, ':');
+ if (version_two && colon != NULL)
+ {
+ char *flag;
+
+ /* We got flags */
+ *colon = 0;
+ p = colon + 1;
+
+ /* Flags end at next colon */
+ colon = strchr (p, ':');
+ if (colon != NULL)
+ *colon = 0;
+
+ flag = strstr (p, "cs");
+ if (flag != NULL &&
+ /* Start or after comma */
+ (flag == p ||
+ flag[-1] == ',') &&
+ /* ends with comma or end of string */
+ (flag[2] == 0 ||
+ flag[2] == ','))
+ case_sensitive = TRUE;
+ }
+
+ _xdg_glob_hash_append_glob (glob_hash, glob, mimetype, weight, case_sensitive);
+ }
+
+ fclose (glob_file);
+}
diff --git a/src/base/third_party/xdg_mime/xdgmimeglob.h b/src/base/third_party/xdg_mime/xdgmimeglob.h
new file mode 100644
index 0000000..0018292
--- /dev/null
+++ b/src/base/third_party/xdg_mime/xdgmimeglob.h
@@ -0,0 +1,70 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimeglob.h: Private file. Datastructure for storing the globs.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XDG_MIME_GLOB_H__
+#define __XDG_MIME_GLOB_H__
+
+#include "xdgmime.h"
+
+typedef struct XdgGlobHash XdgGlobHash;
+
+typedef enum
+{
+ XDG_GLOB_LITERAL, /* Makefile */
+ XDG_GLOB_SIMPLE, /* *.gif */
+ XDG_GLOB_FULL /* x*.[ch] */
+} XdgGlobType;
+
+
+#ifdef XDG_PREFIX
+#define _xdg_mime_glob_read_from_file XDG_RESERVED_ENTRY(glob_read_from_file)
+#define _xdg_glob_hash_new XDG_RESERVED_ENTRY(hash_new)
+#define _xdg_glob_hash_free XDG_RESERVED_ENTRY(hash_free)
+#define _xdg_glob_hash_lookup_file_name XDG_RESERVED_ENTRY(hash_lookup_file_name)
+#define _xdg_glob_hash_append_glob XDG_RESERVED_ENTRY(hash_append_glob)
+#define _xdg_glob_determine_type XDG_RESERVED_ENTRY(determine_type)
+#define _xdg_glob_hash_dump XDG_RESERVED_ENTRY(hash_dump)
+#endif
+
+void _xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash,
+ const char *file_name,
+ int version_two);
+XdgGlobHash *_xdg_glob_hash_new (void);
+void _xdg_glob_hash_free (XdgGlobHash *glob_hash);
+int _xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
+ const char *text,
+ const char *mime_types[],
+ int n_mime_types);
+void _xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
+ const char *glob,
+ const char *mime_type,
+ int weight,
+ int case_sensitive);
+XdgGlobType _xdg_glob_determine_type (const char *glob);
+void _xdg_glob_hash_dump (XdgGlobHash *glob_hash);
+
+#endif /* __XDG_MIME_GLOB_H__ */
diff --git a/src/base/third_party/xdg_mime/xdgmimeicon.c b/src/base/third_party/xdg_mime/xdgmimeicon.c
new file mode 100644
index 0000000..05c9473
--- /dev/null
+++ b/src/base/third_party/xdg_mime/xdgmimeicon.c
@@ -0,0 +1,183 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimeicon.c: Private file. Datastructure for storing the aliases.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "xdgmimeicon.h"
+#include "xdgmimeint.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <fnmatch.h>
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+typedef struct XdgIcon XdgIcon;
+
+struct XdgIcon
+{
+ char *mime_type;
+ char *icon_name;
+};
+
+struct XdgIconList
+{
+ struct XdgIcon *icons;
+ int n_icons;
+};
+
+XdgIconList *
+_xdg_mime_icon_list_new (void)
+{
+ XdgIconList *list;
+
+ list = malloc (sizeof (XdgIconList));
+
+ list->icons = NULL;
+ list->n_icons = 0;
+
+ return list;
+}
+
+void
+_xdg_mime_icon_list_free (XdgIconList *list)
+{
+ int i;
+
+ if (list->icons)
+ {
+ for (i = 0; i < list->n_icons; i++)
+ {
+ free (list->icons[i].mime_type);
+ free (list->icons[i].icon_name);
+ }
+ free (list->icons);
+ }
+ free (list);
+}
+
+static int
+icon_entry_cmp (const void *v1, const void *v2)
+{
+ return strcmp (((XdgIcon *)v1)->mime_type, ((XdgIcon *)v2)->mime_type);
+}
+
+const char *
+_xdg_mime_icon_list_lookup (XdgIconList *list,
+ const char *mime_type)
+{
+ XdgIcon *entry;
+ XdgIcon key;
+
+ if (list->n_icons > 0)
+ {
+ key.mime_type = (char *)mime_type;
+ key.icon_name = NULL;
+
+ entry = bsearch (&key, list->icons, list->n_icons,
+ sizeof (XdgIcon), icon_entry_cmp);
+ if (entry)
+ return entry->icon_name;
+ }
+
+ return NULL;
+}
+
+void
+_xdg_mime_icon_read_from_file (XdgIconList *list,
+ const char *file_name)
+{
+ FILE *file;
+ char line[255];
+ int alloc;
+
+ file = fopen (file_name, "r");
+
+ if (file == NULL)
+ return;
+
+ /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
+ * Blah */
+ alloc = list->n_icons + 16;
+ list->icons = realloc (list->icons, alloc * sizeof (XdgIcon));
+ while (fgets (line, 255, file) != NULL)
+ {
+ char *sep;
+ if (line[0] == '#')
+ continue;
+
+ sep = strchr (line, ':');
+ if (sep == NULL)
+ continue;
+ *(sep++) = '\000';
+ sep[strlen (sep) -1] = '\000';
+ if (list->n_icons == alloc)
+ {
+ alloc <<= 1;
+ list->icons = realloc (list->icons,
+ alloc * sizeof (XdgIcon));
+ }
+ list->icons[list->n_icons].mime_type = strdup (line);
+ list->icons[list->n_icons].icon_name = strdup (sep);
+ list->n_icons++;
+ }
+ list->icons = realloc (list->icons,
+ list->n_icons * sizeof (XdgIcon));
+
+ fclose (file);
+
+ if (list->n_icons > 1)
+ qsort (list->icons, list->n_icons,
+ sizeof (XdgIcon), icon_entry_cmp);
+}
+
+
+void
+_xdg_mime_icon_list_dump (XdgIconList *list)
+{
+ int i;
+
+ if (list->icons)
+ {
+ for (i = 0; i < list->n_icons; i++)
+ {
+ printf ("%s %s\n",
+ list->icons[i].mime_type,
+ list->icons[i].icon_name);
+ }
+ }
+}
+
+
diff --git a/src/base/third_party/xdg_mime/xdgmimeicon.h b/src/base/third_party/xdg_mime/xdgmimeicon.h
new file mode 100644
index 0000000..b5f2583
--- /dev/null
+++ b/src/base/third_party/xdg_mime/xdgmimeicon.h
@@ -0,0 +1,50 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimeicon.h: Private file. Datastructure for storing the aliases.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XDG_MIME_ICON_H__
+#define __XDG_MIME_ICON_H__
+
+#include "xdgmime.h"
+
+typedef struct XdgIconList XdgIconList;
+
+#ifdef XDG_PREFIX
+#define _xdg_mime_icon_read_from_file XDG_ENTRY(icon_read_from_file)
+#define _xdg_mime_icon_list_new XDG_ENTRY(icon_list_new)
+#define _xdg_mime_icon_list_free XDG_ENTRY(icon_list_free)
+#define _xdg_mime_icon_list_lookup XDG_ENTRY(icon_list_lookup)
+#define _xdg_mime_icon_list_dump XDG_ENTRY(icon_list_dump)
+#endif
+
+void _xdg_mime_icon_read_from_file (XdgIconList *list,
+ const char *file_name);
+XdgIconList *_xdg_mime_icon_list_new (void);
+void _xdg_mime_icon_list_free (XdgIconList *list);
+const char *_xdg_mime_icon_list_lookup (XdgIconList *list,
+ const char *mime);
+void _xdg_mime_icon_list_dump (XdgIconList *list);
+
+#endif /* __XDG_MIME_ICON_H__ */
diff --git a/src/base/third_party/xdg_mime/xdgmimeint.c b/src/base/third_party/xdg_mime/xdgmimeint.c
new file mode 100644
index 0000000..cf789d9
--- /dev/null
+++ b/src/base/third_party/xdg_mime/xdgmimeint.c
@@ -0,0 +1,206 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimeint.c: Internal defines and functions.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "xdgmimeint.h"
+#include <ctype.h>
+#include <string.h>
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+static const char _xdg_utf8_skip_data[256] = {
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
+};
+
+const char * const _xdg_utf8_skip = _xdg_utf8_skip_data;
+
+
+
+/* Returns the number of unprocessed characters. */
+xdg_unichar_t
+_xdg_utf8_to_ucs4(const char *source)
+{
+ xdg_unichar_t ucs32;
+ if( ! ( *source & 0x80 ) )
+ {
+ ucs32 = *source;
+ }
+ else
+ {
+ int bytelength = 0;
+ xdg_unichar_t result;
+ if ( ! (*source & 0x40) )
+ {
+ ucs32 = *source;
+ }
+ else
+ {
+ if ( ! (*source & 0x20) )
+ {
+ result = *source++ & 0x1F;
+ bytelength = 2;
+ }
+ else if ( ! (*source & 0x10) )
+ {
+ result = *source++ & 0x0F;
+ bytelength = 3;
+ }
+ else if ( ! (*source & 0x08) )
+ {
+ result = *source++ & 0x07;
+ bytelength = 4;
+ }
+ else if ( ! (*source & 0x04) )
+ {
+ result = *source++ & 0x03;
+ bytelength = 5;
+ }
+ else if ( ! (*source & 0x02) )
+ {
+ result = *source++ & 0x01;
+ bytelength = 6;
+ }
+ else
+ {
+ result = *source++;
+ bytelength = 1;
+ }
+
+ for ( bytelength --; bytelength > 0; bytelength -- )
+ {
+ result <<= 6;
+ result |= *source++ & 0x3F;
+ }
+ ucs32 = result;
+ }
+ }
+ return ucs32;
+}
+
+
+/* hullo. this is great code. don't rewrite it */
+
+xdg_unichar_t
+_xdg_ucs4_to_lower (xdg_unichar_t source)
+{
+ /* FIXME: Do a real to_upper sometime */
+ /* CaseFolding-3.2.0.txt has a table of rules. */
+ if ((source & 0xFF) == source)
+ return (xdg_unichar_t) tolower ((unsigned char) source);
+ return source;
+}
+
+int
+_xdg_utf8_validate (const char *source)
+{
+ /* FIXME: actually write */
+ return TRUE;
+}
+
+const char *
+_xdg_get_base_name (const char *file_name)
+{
+ const char *base_name;
+
+ if (file_name == NULL)
+ return NULL;
+
+ base_name = strrchr (file_name, '/');
+
+ if (base_name == NULL)
+ return file_name;
+ else
+ return base_name + 1;
+}
+
+xdg_unichar_t *
+_xdg_convert_to_ucs4 (const char *source, int *len)
+{
+ xdg_unichar_t *out;
+ int i;
+ const char *p;
+
+ out = malloc (sizeof (xdg_unichar_t) * (strlen (source) + 1));
+
+ p = source;
+ i = 0;
+ while (*p)
+ {
+ out[i++] = _xdg_utf8_to_ucs4 (p);
+ p = _xdg_utf8_next_char (p);
+ }
+ out[i] = 0;
+ *len = i;
+
+ return out;
+}
+
+void
+_xdg_reverse_ucs4 (xdg_unichar_t *source, int len)
+{
+ xdg_unichar_t c;
+ int i;
+
+ for (i = 0; i < len - i - 1; i++)
+ {
+ c = source[i];
+ source[i] = source[len - i - 1];
+ source[len - i - 1] = c;
+ }
+}
+
+const char *
+_xdg_binary_or_text_fallback(const void *data, size_t len)
+{
+ unsigned char *chardata;
+ int i;
+
+ chardata = (unsigned char *) data;
+ for (i = 0; i < 32 && i < len; ++i)
+ {
+ if (chardata[i] < 32 && chardata[i] != 9 && chardata[i] != 10 && chardata[i] != 13)
+ return XDG_MIME_TYPE_UNKNOWN; /* binary data */
+ }
+
+ return XDG_MIME_TYPE_TEXTPLAIN;
+}
diff --git a/src/base/third_party/xdg_mime/xdgmimeint.h b/src/base/third_party/xdg_mime/xdgmimeint.h
new file mode 100644
index 0000000..9e8b2cb
--- /dev/null
+++ b/src/base/third_party/xdg_mime/xdgmimeint.h
@@ -0,0 +1,78 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimeint.h: Internal defines and functions.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XDG_MIME_INT_H__
+#define __XDG_MIME_INT_H__
+
+#include "xdgmime.h"
+
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+/* FIXME: Needs to be configure check */
+typedef unsigned int xdg_unichar_t;
+typedef unsigned char xdg_uchar8_t;
+typedef unsigned short xdg_uint16_t;
+typedef unsigned int xdg_uint32_t;
+
+#ifdef XDG_PREFIX
+#define _xdg_utf8_skip XDG_RESERVED_ENTRY(utf8_skip)
+#define _xdg_utf8_to_ucs4 XDG_RESERVED_ENTRY(utf8_to_ucs4)
+#define _xdg_ucs4_to_lower XDG_RESERVED_ENTRY(ucs4_to_lower)
+#define _xdg_utf8_validate XDG_RESERVED_ENTRY(utf8_validate)
+#define _xdg_get_base_name XDG_RESERVED_ENTRY(get_base_name)
+#define _xdg_convert_to_ucs4 XDG_RESERVED_ENTRY(convert_to_ucs4)
+#define _xdg_reverse_ucs4 XDG_RESERVED_ENTRY(reverse_ucs4)
+#endif
+
+#define SWAP_BE16_TO_LE16(val) (xdg_uint16_t)(((xdg_uint16_t)(val) << 8)|((xdg_uint16_t)(val) >> 8))
+
+#define SWAP_BE32_TO_LE32(val) (xdg_uint32_t)((((xdg_uint32_t)(val) & 0xFF000000U) >> 24) | \
+ (((xdg_uint32_t)(val) & 0x00FF0000U) >> 8) | \
+ (((xdg_uint32_t)(val) & 0x0000FF00U) << 8) | \
+ (((xdg_uint32_t)(val) & 0x000000FFU) << 24))
+/* UTF-8 utils
+ */
+extern const char *const _xdg_utf8_skip;
+#define _xdg_utf8_next_char(p) (char *)((p) + _xdg_utf8_skip[*(unsigned char *)(p)])
+#define _xdg_utf8_char_size(p) (int) (_xdg_utf8_skip[*(unsigned char *)(p)])
+
+xdg_unichar_t _xdg_utf8_to_ucs4 (const char *source);
+xdg_unichar_t _xdg_ucs4_to_lower (xdg_unichar_t source);
+int _xdg_utf8_validate (const char *source);
+xdg_unichar_t *_xdg_convert_to_ucs4 (const char *source, int *len);
+void _xdg_reverse_ucs4 (xdg_unichar_t *source, int len);
+const char *_xdg_get_base_name (const char *file_name);
+const char *_xdg_binary_or_text_fallback(const void *data, size_t len);
+
+#endif /* __XDG_MIME_INT_H__ */
diff --git a/src/base/third_party/xdg_mime/xdgmimemagic.c b/src/base/third_party/xdg_mime/xdgmimemagic.c
new file mode 100644
index 0000000..a2320f5
--- /dev/null
+++ b/src/base/third_party/xdg_mime/xdgmimemagic.c
@@ -0,0 +1,813 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimemagic.: Private file. Datastructure for storing magic files.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include "xdgmimemagic.h"
+#include "xdgmimeint.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+#if !defined getc_unlocked && !defined HAVE_GETC_UNLOCKED
+# define getc_unlocked(fp) getc (fp)
+#endif
+
+typedef struct XdgMimeMagicMatch XdgMimeMagicMatch;
+typedef struct XdgMimeMagicMatchlet XdgMimeMagicMatchlet;
+
+typedef enum
+{
+ XDG_MIME_MAGIC_SECTION,
+ XDG_MIME_MAGIC_MAGIC,
+ XDG_MIME_MAGIC_ERROR,
+ XDG_MIME_MAGIC_EOF
+} XdgMimeMagicState;
+
+struct XdgMimeMagicMatch
+{
+ const char *mime_type;
+ int priority;
+ XdgMimeMagicMatchlet *matchlet;
+ XdgMimeMagicMatch *next;
+};
+
+
+struct XdgMimeMagicMatchlet
+{
+ int indent;
+ int offset;
+ unsigned int value_length;
+ unsigned char *value;
+ unsigned char *mask;
+ unsigned int range_length;
+ unsigned int word_size;
+ XdgMimeMagicMatchlet *next;
+};
+
+
+struct XdgMimeMagic
+{
+ XdgMimeMagicMatch *match_list;
+ int max_extent;
+};
+
+static XdgMimeMagicMatch *
+_xdg_mime_magic_match_new (void)
+{
+ return calloc (1, sizeof (XdgMimeMagicMatch));
+}
+
+
+static XdgMimeMagicMatchlet *
+_xdg_mime_magic_matchlet_new (void)
+{
+ XdgMimeMagicMatchlet *matchlet;
+
+ matchlet = malloc (sizeof (XdgMimeMagicMatchlet));
+
+ matchlet->indent = 0;
+ matchlet->offset = 0;
+ matchlet->value_length = 0;
+ matchlet->value = NULL;
+ matchlet->mask = NULL;
+ matchlet->range_length = 1;
+ matchlet->word_size = 1;
+ matchlet->next = NULL;
+
+ return matchlet;
+}
+
+
+static void
+_xdg_mime_magic_matchlet_free (XdgMimeMagicMatchlet *mime_magic_matchlet)
+{
+ if (mime_magic_matchlet)
+ {
+ if (mime_magic_matchlet->next)
+ _xdg_mime_magic_matchlet_free (mime_magic_matchlet->next);
+ if (mime_magic_matchlet->value)
+ free (mime_magic_matchlet->value);
+ if (mime_magic_matchlet->mask)
+ free (mime_magic_matchlet->mask);
+ free (mime_magic_matchlet);
+ }
+}
+
+
+/* Frees mime_magic_match and the remainder of its list
+ */
+static void
+_xdg_mime_magic_match_free (XdgMimeMagicMatch *mime_magic_match)
+{
+ XdgMimeMagicMatch *ptr, *next;
+
+ ptr = mime_magic_match;
+ while (ptr)
+ {
+ next = ptr->next;
+
+ if (ptr->mime_type)
+ free ((void *) ptr->mime_type);
+ if (ptr->matchlet)
+ _xdg_mime_magic_matchlet_free (ptr->matchlet);
+ free (ptr);
+
+ ptr = next;
+ }
+}
+
+/* Reads in a hunk of data until a newline character or a '\000' is hit. The
+ * returned string is null terminated, and doesn't include the newline.
+ */
+static unsigned char *
+_xdg_mime_magic_read_to_newline (FILE *magic_file,
+ int *end_of_file)
+{
+ unsigned char *retval;
+ int c;
+ int len, pos;
+
+ len = 128;
+ pos = 0;
+ retval = malloc (len);
+ *end_of_file = FALSE;
+
+ while (TRUE)
+ {
+ c = getc_unlocked (magic_file);
+ if (c == EOF)
+ {
+ *end_of_file = TRUE;
+ break;
+ }
+ if (c == '\n' || c == '\000')
+ break;
+ retval[pos++] = (unsigned char) c;
+ if (pos % 128 == 127)
+ {
+ len = len + 128;
+ retval = realloc (retval, len);
+ }
+ }
+
+ retval[pos] = '\000';
+ return retval;
+}
+
+/* Returns the number read from the file, or -1 if no number could be read.
+ */
+static int
+_xdg_mime_magic_read_a_number (FILE *magic_file,
+ int *end_of_file)
+{
+ /* LONG_MAX is about 20 characters on my system */
+#define MAX_NUMBER_SIZE 30
+ char number_string[MAX_NUMBER_SIZE + 1];
+ int pos = 0;
+ int c;
+ long retval = -1;
+
+ while (TRUE)
+ {
+ c = getc_unlocked (magic_file);
+
+ if (c == EOF)
+ {
+ *end_of_file = TRUE;
+ break;
+ }
+ if (! isdigit (c))
+ {
+ ungetc (c, magic_file);
+ break;
+ }
+ number_string[pos] = (char) c;
+ pos++;
+ if (pos == MAX_NUMBER_SIZE)
+ break;
+ }
+ if (pos > 0)
+ {
+ number_string[pos] = '\000';
+ errno = 0;
+ retval = strtol (number_string, NULL, 10);
+
+ if ((retval < INT_MIN) || (retval > INT_MAX) || (errno != 0))
+ return -1;
+ }
+
+ return retval;
+}
+
+/* Headers are of the format:
+ * [<priority>:<mime-type>]
+ */
+static XdgMimeMagicState
+_xdg_mime_magic_parse_header (FILE *magic_file, XdgMimeMagicMatch *match)
+{
+ int c;
+ char *buffer;
+ char *end_ptr;
+ int end_of_file = 0;
+
+ assert (magic_file != NULL);
+ assert (match != NULL);
+
+ c = getc_unlocked (magic_file);
+ if (c == EOF)
+ return XDG_MIME_MAGIC_EOF;
+ if (c != '[')
+ return XDG_MIME_MAGIC_ERROR;
+
+ match->priority = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+ if (end_of_file)
+ return XDG_MIME_MAGIC_EOF;
+ if (match->priority == -1)
+ return XDG_MIME_MAGIC_ERROR;
+
+ c = getc_unlocked (magic_file);
+ if (c == EOF)
+ return XDG_MIME_MAGIC_EOF;
+ if (c != ':')
+ return XDG_MIME_MAGIC_ERROR;
+
+ buffer = (char *)_xdg_mime_magic_read_to_newline (magic_file, &end_of_file);
+ if (end_of_file)
+ return XDG_MIME_MAGIC_EOF;
+
+ end_ptr = buffer;
+ while (*end_ptr != ']' && *end_ptr != '\000' && *end_ptr != '\n')
+ end_ptr++;
+ if (*end_ptr != ']')
+ {
+ free (buffer);
+ return XDG_MIME_MAGIC_ERROR;
+ }
+ *end_ptr = '\000';
+
+ match->mime_type = strdup (buffer);
+ free (buffer);
+
+ return XDG_MIME_MAGIC_MAGIC;
+}
+
+static XdgMimeMagicState
+_xdg_mime_magic_parse_error (FILE *magic_file)
+{
+ int c;
+
+ while (1)
+ {
+ c = getc_unlocked (magic_file);
+ if (c == EOF)
+ return XDG_MIME_MAGIC_EOF;
+ if (c == '\n')
+ return XDG_MIME_MAGIC_SECTION;
+ }
+}
+
+/* Headers are of the format:
+ * [ indent ] ">" start-offset "=" value
+ * [ "&" mask ] [ "~" word-size ] [ "+" range-length ] "\n"
+ */
+static XdgMimeMagicState
+_xdg_mime_magic_parse_magic_line (FILE *magic_file,
+ XdgMimeMagicMatch *match)
+{
+ XdgMimeMagicMatchlet *matchlet;
+ int c;
+ int end_of_file;
+ int indent = 0;
+ int bytes_read;
+
+ assert (magic_file != NULL);
+
+ /* Sniff the buffer to make sure it's a valid line */
+ c = getc_unlocked (magic_file);
+ if (c == EOF)
+ return XDG_MIME_MAGIC_EOF;
+ else if (c == '[')
+ {
+ ungetc (c, magic_file);
+ return XDG_MIME_MAGIC_SECTION;
+ }
+ else if (c == '\n')
+ return XDG_MIME_MAGIC_MAGIC;
+
+ /* At this point, it must be a digit or a '>' */
+ end_of_file = FALSE;
+ if (isdigit (c))
+ {
+ ungetc (c, magic_file);
+ indent = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+ if (end_of_file)
+ return XDG_MIME_MAGIC_EOF;
+ if (indent == -1)
+ return XDG_MIME_MAGIC_ERROR;
+ c = getc_unlocked (magic_file);
+ if (c == EOF)
+ return XDG_MIME_MAGIC_EOF;
+ }
+
+ if (c != '>')
+ return XDG_MIME_MAGIC_ERROR;
+
+ matchlet = _xdg_mime_magic_matchlet_new ();
+ matchlet->indent = indent;
+ matchlet->offset = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+ if (end_of_file)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_EOF;
+ }
+ if (matchlet->offset == -1)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_ERROR;
+ }
+ c = getc_unlocked (magic_file);
+ if (c == EOF)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_EOF;
+ }
+ else if (c != '=')
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_ERROR;
+ }
+
+ /* Next two bytes determine how long the value is */
+ matchlet->value_length = 0;
+ c = getc_unlocked (magic_file);
+ if (c == EOF)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_EOF;
+ }
+ matchlet->value_length = c & 0xFF;
+ matchlet->value_length = matchlet->value_length << 8;
+
+ c = getc_unlocked (magic_file);
+ if (c == EOF)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_EOF;
+ }
+ matchlet->value_length = matchlet->value_length + (c & 0xFF);
+
+ matchlet->value = malloc (matchlet->value_length);
+
+ /* OOM */
+ if (matchlet->value == NULL)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_ERROR;
+ }
+ bytes_read = fread (matchlet->value, 1, matchlet->value_length, magic_file);
+ if (bytes_read != matchlet->value_length)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ if (feof (magic_file))
+ return XDG_MIME_MAGIC_EOF;
+ else
+ return XDG_MIME_MAGIC_ERROR;
+ }
+
+ c = getc_unlocked (magic_file);
+ if (c == '&')
+ {
+ matchlet->mask = malloc (matchlet->value_length);
+ /* OOM */
+ if (matchlet->mask == NULL)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_ERROR;
+ }
+ bytes_read = fread (matchlet->mask, 1, matchlet->value_length, magic_file);
+ if (bytes_read != matchlet->value_length)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ if (feof (magic_file))
+ return XDG_MIME_MAGIC_EOF;
+ else
+ return XDG_MIME_MAGIC_ERROR;
+ }
+ c = getc_unlocked (magic_file);
+ }
+
+ if (c == '~')
+ {
+ matchlet->word_size = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+ if (end_of_file)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_EOF;
+ }
+ if (matchlet->word_size != 0 &&
+ matchlet->word_size != 1 &&
+ matchlet->word_size != 2 &&
+ matchlet->word_size != 4)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_ERROR;
+ }
+ c = getc_unlocked (magic_file);
+ }
+
+ if (c == '+')
+ {
+ matchlet->range_length = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+ if (end_of_file)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_EOF;
+ }
+ if (matchlet->range_length == -1)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_ERROR;
+ }
+ c = getc_unlocked (magic_file);
+ }
+
+
+ if (c == '\n')
+ {
+ /* We clean up the matchlet, byte swapping if needed */
+ if (matchlet->word_size > 1)
+ {
+ int i;
+ if (matchlet->value_length % matchlet->word_size != 0)
+ {
+ _xdg_mime_magic_matchlet_free (matchlet);
+ return XDG_MIME_MAGIC_ERROR;
+ }
+ /* FIXME: need to get this defined in a <config.h> style file */
+#if LITTLE_ENDIAN
+ for (i = 0; i < matchlet->value_length; i = i + matchlet->word_size)
+ {
+ if (matchlet->word_size == 2)
+ *((xdg_uint16_t *) matchlet->value + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->value + i)));
+ else if (matchlet->word_size == 4)
+ *((xdg_uint32_t *) matchlet->value + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->value + i)));
+ if (matchlet->mask)
+ {
+ if (matchlet->word_size == 2)
+ *((xdg_uint16_t *) matchlet->mask + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->mask + i)));
+ else if (matchlet->word_size == 4)
+ *((xdg_uint32_t *) matchlet->mask + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->mask + i)));
+
+ }
+ }
+#endif
+ }
+
+ matchlet->next = match->matchlet;
+ match->matchlet = matchlet;
+
+
+ return XDG_MIME_MAGIC_MAGIC;
+ }
+
+ _xdg_mime_magic_matchlet_free (matchlet);
+ if (c == EOF)
+ return XDG_MIME_MAGIC_EOF;
+
+ return XDG_MIME_MAGIC_ERROR;
+}
+
+static int
+_xdg_mime_magic_matchlet_compare_to_data (XdgMimeMagicMatchlet *matchlet,
+ const void *data,
+ size_t len)
+{
+ int i, j;
+ for (i = matchlet->offset; i < matchlet->offset + matchlet->range_length; i++)
+ {
+ int valid_matchlet = TRUE;
+
+ if (i + matchlet->value_length > len)
+ return FALSE;
+
+ if (matchlet->mask)
+ {
+ for (j = 0; j < matchlet->value_length; j++)
+ {
+ if ((matchlet->value[j] & matchlet->mask[j]) !=
+ ((((unsigned char *) data)[j + i]) & matchlet->mask[j]))
+ {
+ valid_matchlet = FALSE;
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (j = 0; j < matchlet->value_length; j++)
+ {
+ if (matchlet->value[j] != ((unsigned char *) data)[j + i])
+ {
+ valid_matchlet = FALSE;
+ break;
+ }
+ }
+ }
+ if (valid_matchlet)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int
+_xdg_mime_magic_matchlet_compare_level (XdgMimeMagicMatchlet *matchlet,
+ const void *data,
+ size_t len,
+ int indent)
+{
+ while ((matchlet != NULL) && (matchlet->indent == indent))
+ {
+ if (_xdg_mime_magic_matchlet_compare_to_data (matchlet, data, len))
+ {
+ if ((matchlet->next == NULL) ||
+ (matchlet->next->indent <= indent))
+ return TRUE;
+
+ if (_xdg_mime_magic_matchlet_compare_level (matchlet->next,
+ data,
+ len,
+ indent + 1))
+ return TRUE;
+ }
+
+ do
+ {
+ matchlet = matchlet->next;
+ }
+ while (matchlet && matchlet->indent > indent);
+ }
+
+ return FALSE;
+}
+
+static int
+_xdg_mime_magic_match_compare_to_data (XdgMimeMagicMatch *match,
+ const void *data,
+ size_t len)
+{
+ return _xdg_mime_magic_matchlet_compare_level (match->matchlet, data, len, 0);
+}
+
+static void
+_xdg_mime_magic_insert_match (XdgMimeMagic *mime_magic,
+ XdgMimeMagicMatch *match)
+{
+ XdgMimeMagicMatch *list;
+
+ if (mime_magic->match_list == NULL)
+ {
+ mime_magic->match_list = match;
+ return;
+ }
+
+ if (match->priority > mime_magic->match_list->priority)
+ {
+ match->next = mime_magic->match_list;
+ mime_magic->match_list = match;
+ return;
+ }
+
+ list = mime_magic->match_list;
+ while (list->next != NULL)
+ {
+ if (list->next->priority < match->priority)
+ {
+ match->next = list->next;
+ list->next = match;
+ return;
+ }
+ list = list->next;
+ }
+ list->next = match;
+ match->next = NULL;
+}
+
+XdgMimeMagic *
+_xdg_mime_magic_new (void)
+{
+ return calloc (1, sizeof (XdgMimeMagic));
+}
+
+void
+_xdg_mime_magic_free (XdgMimeMagic *mime_magic)
+{
+ if (mime_magic) {
+ _xdg_mime_magic_match_free (mime_magic->match_list);
+ free (mime_magic);
+ }
+}
+
+int
+_xdg_mime_magic_get_buffer_extents (XdgMimeMagic *mime_magic)
+{
+ return mime_magic->max_extent;
+}
+
+const char *
+_xdg_mime_magic_lookup_data (XdgMimeMagic *mime_magic,
+ const void *data,
+ size_t len,
+ int *result_prio,
+ const char *mime_types[],
+ int n_mime_types)
+{
+ XdgMimeMagicMatch *match;
+ const char *mime_type;
+ int n;
+ int prio;
+
+ prio = 0;
+ mime_type = NULL;
+ for (match = mime_magic->match_list; match; match = match->next)
+ {
+ if (_xdg_mime_magic_match_compare_to_data (match, data, len))
+ {
+ prio = match->priority;
+ mime_type = match->mime_type;
+ break;
+ }
+ else
+ {
+ for (n = 0; n < n_mime_types; n++)
+ {
+ if (mime_types[n] &&
+ _xdg_mime_mime_type_equal (mime_types[n], match->mime_type))
+ mime_types[n] = NULL;
+ }
+ }
+ }
+
+ if (mime_type == NULL)
+ {
+ for (n = 0; n < n_mime_types; n++)
+ {
+ if (mime_types[n])
+ mime_type = mime_types[n];
+ }
+ }
+
+ if (result_prio)
+ *result_prio = prio;
+
+ return mime_type;
+}
+
+static void
+_xdg_mime_update_mime_magic_extents (XdgMimeMagic *mime_magic)
+{
+ XdgMimeMagicMatch *match;
+ int max_extent = 0;
+
+ for (match = mime_magic->match_list; match; match = match->next)
+ {
+ XdgMimeMagicMatchlet *matchlet;
+
+ for (matchlet = match->matchlet; matchlet; matchlet = matchlet->next)
+ {
+ int extent;
+
+ extent = matchlet->value_length + matchlet->offset + matchlet->range_length;
+ if (max_extent < extent)
+ max_extent = extent;
+ }
+ }
+
+ mime_magic->max_extent = max_extent;
+}
+
+static XdgMimeMagicMatchlet *
+_xdg_mime_magic_matchlet_mirror (XdgMimeMagicMatchlet *matchlets)
+{
+ XdgMimeMagicMatchlet *new_list;
+ XdgMimeMagicMatchlet *tmp;
+
+ if ((matchlets == NULL) || (matchlets->next == NULL))
+ return matchlets;
+
+ new_list = NULL;
+ tmp = matchlets;
+ while (tmp != NULL)
+ {
+ XdgMimeMagicMatchlet *matchlet;
+
+ matchlet = tmp;
+ tmp = tmp->next;
+ matchlet->next = new_list;
+ new_list = matchlet;
+ }
+
+ return new_list;
+
+}
+
+static void
+_xdg_mime_magic_read_magic_file (XdgMimeMagic *mime_magic,
+ FILE *magic_file)
+{
+ XdgMimeMagicState state;
+ XdgMimeMagicMatch *match = NULL; /* Quiet compiler */
+
+ state = XDG_MIME_MAGIC_SECTION;
+
+ while (state != XDG_MIME_MAGIC_EOF)
+ {
+ switch (state)
+ {
+ case XDG_MIME_MAGIC_SECTION:
+ match = _xdg_mime_magic_match_new ();
+ state = _xdg_mime_magic_parse_header (magic_file, match);
+ if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR)
+ _xdg_mime_magic_match_free (match);
+ break;
+ case XDG_MIME_MAGIC_MAGIC:
+ state = _xdg_mime_magic_parse_magic_line (magic_file, match);
+ if (state == XDG_MIME_MAGIC_SECTION ||
+ (state == XDG_MIME_MAGIC_EOF && match->mime_type))
+ {
+ match->matchlet = _xdg_mime_magic_matchlet_mirror (match->matchlet);
+ _xdg_mime_magic_insert_match (mime_magic, match);
+ }
+ else if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR)
+ _xdg_mime_magic_match_free (match);
+ break;
+ case XDG_MIME_MAGIC_ERROR:
+ state = _xdg_mime_magic_parse_error (magic_file);
+ break;
+ case XDG_MIME_MAGIC_EOF:
+ default:
+ /* Make the compiler happy */
+ assert (0);
+ }
+ }
+ _xdg_mime_update_mime_magic_extents (mime_magic);
+}
+
+void
+_xdg_mime_magic_read_from_file (XdgMimeMagic *mime_magic,
+ const char *file_name)
+{
+ FILE *magic_file;
+ char header[12];
+
+ magic_file = fopen (file_name, "r");
+
+ if (magic_file == NULL)
+ return;
+
+ if (fread (header, 1, 12, magic_file) == 12)
+ {
+ if (memcmp ("MIME-Magic\0\n", header, 12) == 0)
+ _xdg_mime_magic_read_magic_file (mime_magic, magic_file);
+ }
+
+ fclose (magic_file);
+}
diff --git a/src/base/third_party/xdg_mime/xdgmimemagic.h b/src/base/third_party/xdg_mime/xdgmimemagic.h
new file mode 100644
index 0000000..35c8039
--- /dev/null
+++ b/src/base/third_party/xdg_mime/xdgmimemagic.h
@@ -0,0 +1,57 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimemagic.h: Private file. Datastructure for storing the magic files.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XDG_MIME_MAGIC_H__
+#define __XDG_MIME_MAGIC_H__
+
+#include <unistd.h>
+#include "xdgmime.h"
+typedef struct XdgMimeMagic XdgMimeMagic;
+
+#ifdef XDG_PREFIX
+#define _xdg_mime_glob_read_from_file XDG_RESERVED_ENTRY(glob_read_from_file)
+#define _xdg_mime_magic_new XDG_RESERVED_ENTRY(magic_new)
+#define _xdg_mime_magic_read_from_file XDG_RESERVED_ENTRY(magic_read_from_file)
+#define _xdg_mime_magic_free XDG_RESERVED_ENTRY(magic_free)
+#define _xdg_mime_magic_get_buffer_extents XDG_RESERVED_ENTRY(magic_get_buffer_extents)
+#define _xdg_mime_magic_lookup_data XDG_RESERVED_ENTRY(magic_lookup_data)
+#endif
+
+
+XdgMimeMagic *_xdg_mime_magic_new (void);
+void _xdg_mime_magic_read_from_file (XdgMimeMagic *mime_magic,
+ const char *file_name);
+void _xdg_mime_magic_free (XdgMimeMagic *mime_magic);
+int _xdg_mime_magic_get_buffer_extents (XdgMimeMagic *mime_magic);
+const char *_xdg_mime_magic_lookup_data (XdgMimeMagic *mime_magic,
+ const void *data,
+ size_t len,
+ int *result_prio,
+ const char *mime_types[],
+ int n_mime_types);
+
+#endif /* __XDG_MIME_MAGIC_H__ */
diff --git a/src/base/third_party/xdg_mime/xdgmimeparent.c b/src/base/third_party/xdg_mime/xdgmimeparent.c
new file mode 100644
index 0000000..511bbac
--- /dev/null
+++ b/src/base/third_party/xdg_mime/xdgmimeparent.c
@@ -0,0 +1,219 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimealias.c: Private file. Datastructure for storing the hierarchy.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ * Copyright (C) 2004 Matthias Clasen <mclasen@redhat.com>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "xdgmimeparent.h"
+#include "xdgmimeint.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <fnmatch.h>
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+typedef struct XdgMimeParents XdgMimeParents;
+
+struct XdgMimeParents
+{
+ char *mime;
+ char **parents;
+ int n_parents;
+};
+
+struct XdgParentList
+{
+ struct XdgMimeParents *parents;
+ int n_mimes;
+};
+
+XdgParentList *
+_xdg_mime_parent_list_new (void)
+{
+ XdgParentList *list;
+
+ list = malloc (sizeof (XdgParentList));
+
+ list->parents = NULL;
+ list->n_mimes = 0;
+
+ return list;
+}
+
+void
+_xdg_mime_parent_list_free (XdgParentList *list)
+{
+ int i;
+ char **p;
+
+ if (list->parents)
+ {
+ for (i = 0; i < list->n_mimes; i++)
+ {
+ for (p = list->parents[i].parents; *p; p++)
+ free (*p);
+
+ free (list->parents[i].parents);
+ free (list->parents[i].mime);
+ }
+ free (list->parents);
+ }
+ free (list);
+}
+
+static int
+parent_entry_cmp (const void *v1, const void *v2)
+{
+ return strcmp (((XdgMimeParents *)v1)->mime, ((XdgMimeParents *)v2)->mime);
+}
+
+const char **
+_xdg_mime_parent_list_lookup (XdgParentList *list,
+ const char *mime)
+{
+ XdgMimeParents *entry;
+ XdgMimeParents key;
+
+ if (list->n_mimes > 0)
+ {
+ key.mime = (char *)mime;
+ key.parents = NULL;
+
+ entry = bsearch (&key, list->parents, list->n_mimes,
+ sizeof (XdgMimeParents), &parent_entry_cmp);
+ if (entry)
+ return (const char **)entry->parents;
+ }
+
+ return NULL;
+}
+
+void
+_xdg_mime_parent_read_from_file (XdgParentList *list,
+ const char *file_name)
+{
+ FILE *file;
+ char line[255];
+ int i, alloc;
+ XdgMimeParents *entry;
+
+ file = fopen (file_name, "r");
+
+ if (file == NULL)
+ return;
+
+ /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
+ * Blah */
+ alloc = list->n_mimes + 16;
+ list->parents = realloc (list->parents, alloc * sizeof (XdgMimeParents));
+ while (fgets (line, 255, file) != NULL)
+ {
+ char *sep;
+ if (line[0] == '#')
+ continue;
+
+ sep = strchr (line, ' ');
+ if (sep == NULL)
+ continue;
+ *(sep++) = '\000';
+ sep[strlen (sep) -1] = '\000';
+ entry = NULL;
+ for (i = 0; i < list->n_mimes; i++)
+ {
+ if (strcmp (list->parents[i].mime, line) == 0)
+ {
+ entry = &(list->parents[i]);
+ break;
+ }
+ }
+
+ if (!entry)
+ {
+ if (list->n_mimes == alloc)
+ {
+ alloc <<= 1;
+ list->parents = realloc (list->parents,
+ alloc * sizeof (XdgMimeParents));
+ }
+ list->parents[list->n_mimes].mime = strdup (line);
+ list->parents[list->n_mimes].parents = NULL;
+ entry = &(list->parents[list->n_mimes]);
+ list->n_mimes++;
+ }
+
+ if (!entry->parents)
+ {
+ entry->n_parents = 1;
+ entry->parents = malloc ((entry->n_parents + 1) * sizeof (char *));
+ }
+ else
+ {
+ entry->n_parents += 1;
+ entry->parents = realloc (entry->parents,
+ (entry->n_parents + 2) * sizeof (char *));
+ }
+ entry->parents[entry->n_parents - 1] = strdup (sep);
+ entry->parents[entry->n_parents] = NULL;
+ }
+
+ list->parents = realloc (list->parents,
+ list->n_mimes * sizeof (XdgMimeParents));
+
+ fclose (file);
+
+ if (list->n_mimes > 1)
+ qsort (list->parents, list->n_mimes,
+ sizeof (XdgMimeParents), &parent_entry_cmp);
+}
+
+
+void
+_xdg_mime_parent_list_dump (XdgParentList *list)
+{
+ int i;
+ char **p;
+
+ if (list->parents)
+ {
+ for (i = 0; i < list->n_mimes; i++)
+ {
+ for (p = list->parents[i].parents; *p; p++)
+ printf ("%s %s\n", list->parents[i].mime, *p);
+ }
+ }
+}
+
+
diff --git a/src/base/third_party/xdg_mime/xdgmimeparent.h b/src/base/third_party/xdg_mime/xdgmimeparent.h
new file mode 100644
index 0000000..b564f41
--- /dev/null
+++ b/src/base/third_party/xdg_mime/xdgmimeparent.h
@@ -0,0 +1,51 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimeparent.h: Private file. Datastructure for storing the hierarchy.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ * Copyright (C) 200 Matthias Clasen <mclasen@redhat.com>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XDG_MIME_PARENT_H__
+#define __XDG_MIME_PARENT_H__
+
+#include "xdgmime.h"
+
+typedef struct XdgParentList XdgParentList;
+
+#ifdef XDG_PREFIX
+#define _xdg_mime_parent_read_from_file XDG_RESERVED_ENTRY(parent_read_from_file)
+#define _xdg_mime_parent_list_new XDG_RESERVED_ENTRY(parent_list_new)
+#define _xdg_mime_parent_list_free XDG_RESERVED_ENTRY(parent_list_free)
+#define _xdg_mime_parent_list_lookup XDG_RESERVED_ENTRY(parent_list_lookup)
+#define _xdg_mime_parent_list_dump XDG_RESERVED_ENTRY(parent_list_dump)
+#endif
+
+void _xdg_mime_parent_read_from_file (XdgParentList *list,
+ const char *file_name);
+XdgParentList *_xdg_mime_parent_list_new (void);
+void _xdg_mime_parent_list_free (XdgParentList *list);
+const char **_xdg_mime_parent_list_lookup (XdgParentList *list,
+ const char *mime);
+void _xdg_mime_parent_list_dump (XdgParentList *list);
+
+#endif /* __XDG_MIME_PARENT_H__ */
diff --git a/src/base/third_party/xdg_user_dirs/LICENSE b/src/base/third_party/xdg_user_dirs/LICENSE
new file mode 100644
index 0000000..540e803
--- /dev/null
+++ b/src/base/third_party/xdg_user_dirs/LICENSE
@@ -0,0 +1,21 @@
+ Copyright (c) 2007 Red Hat, inc
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
diff --git a/src/base/third_party/xdg_user_dirs/README.chromium b/src/base/third_party/xdg_user_dirs/README.chromium
new file mode 100644
index 0000000..b4cf6d7
--- /dev/null
+++ b/src/base/third_party/xdg_user_dirs/README.chromium
@@ -0,0 +1,7 @@
+Name: xdg-user-dirs
+URL: http://www.freedesktop.org/wiki/Software/xdg-user-dirs
+License: MIT
+
+This directory include xdg-user-dir-lookup.c renamed as xdg_user_dir_lookup.cc
+from xdg-user-dirs 0.10. We made xdg_user_dir_lookup() non-static and added a
+xdg_user_dir_lookup.h.
diff --git a/src/base/third_party/xdg_user_dirs/xdg_user_dir_lookup.cc b/src/base/third_party/xdg_user_dirs/xdg_user_dir_lookup.cc
new file mode 100644
index 0000000..343f70c
--- /dev/null
+++ b/src/base/third_party/xdg_user_dirs/xdg_user_dir_lookup.cc
@@ -0,0 +1,232 @@
+/*
+ This file is not licenced under the GPL like the rest of the code.
+ Its is under the MIT license, to encourage reuse by cut-and-paste.
+
+ Copyright (c) 2007 Red Hat, inc
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * xdg_user_dir_lookup_with_fallback:
+ * @type: a string specifying the type of directory
+ * @fallback: value to use if the directory isn't specified by the user
+ * @returns: a newly allocated absolute pathname
+ *
+ * Looks up a XDG user directory of the specified type.
+ * Example of types are "DESKTOP" and "DOWNLOAD".
+ *
+ * In case the user hasn't specified any directory for the specified
+ * type the value returned is @fallback.
+ *
+ * The return value is newly allocated and must be freed with
+ * free(). The return value is never NULL if @fallback != NULL, unless
+ * out of memory.
+ **/
+static char *
+xdg_user_dir_lookup_with_fallback (const char *type, const char *fallback)
+{
+ FILE *file;
+ char *home_dir, *config_home, *config_file;
+ char buffer[512];
+ char *user_dir;
+ char *p, *d;
+ int len;
+ int relative;
+
+ home_dir = getenv ("HOME");
+
+ if (home_dir == NULL)
+ goto error;
+
+ config_home = getenv ("XDG_CONFIG_HOME");
+ if (config_home == NULL || config_home[0] == 0)
+ {
+ config_file = (char*) malloc (strlen (home_dir) + strlen ("/.config/user-dirs.dirs") + 1);
+ if (config_file == NULL)
+ goto error;
+
+ strcpy (config_file, home_dir);
+ strcat (config_file, "/.config/user-dirs.dirs");
+ }
+ else
+ {
+ config_file = (char*) malloc (strlen (config_home) + strlen ("/user-dirs.dirs") + 1);
+ if (config_file == NULL)
+ goto error;
+
+ strcpy (config_file, config_home);
+ strcat (config_file, "/user-dirs.dirs");
+ }
+
+ file = fopen (config_file, "r");
+ free (config_file);
+ if (file == NULL)
+ goto error;
+
+ user_dir = NULL;
+ while (fgets (buffer, sizeof (buffer), file))
+ {
+ /* Remove newline at end */
+ len = strlen (buffer);
+ if (len > 0 && buffer[len-1] == '\n')
+ buffer[len-1] = 0;
+
+ p = buffer;
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (strncmp (p, "XDG_", 4) != 0)
+ continue;
+ p += 4;
+ if (strncmp (p, type, strlen (type)) != 0)
+ continue;
+ p += strlen (type);
+ if (strncmp (p, "_DIR", 4) != 0)
+ continue;
+ p += 4;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (*p != '=')
+ continue;
+ p++;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (*p != '"')
+ continue;
+ p++;
+
+ relative = 0;
+ if (strncmp (p, "$HOME/", 6) == 0)
+ {
+ p += 6;
+ relative = 1;
+ }
+ else if (*p != '/')
+ continue;
+
+ if (relative)
+ {
+ user_dir = (char*) malloc (strlen (home_dir) + 1 + strlen (p) + 1);
+ if (user_dir == NULL)
+ goto error2;
+
+ strcpy (user_dir, home_dir);
+ strcat (user_dir, "/");
+ }
+ else
+ {
+ user_dir = (char*) malloc (strlen (p) + 1);
+ if (user_dir == NULL)
+ goto error2;
+
+ *user_dir = 0;
+ }
+
+ d = user_dir + strlen (user_dir);
+ while (*p && *p != '"')
+ {
+ if ((*p == '\\') && (*(p+1) != 0))
+ p++;
+ *d++ = *p++;
+ }
+ *d = 0;
+ }
+error2:
+ fclose (file);
+
+ if (user_dir)
+ return user_dir;
+
+ error:
+ if (fallback)
+ return strdup (fallback);
+ return NULL;
+}
+
+/**
+ * xdg_user_dir_lookup:
+ * @type: a string specifying the type of directory
+ * @returns: a newly allocated absolute pathname
+ *
+ * Looks up a XDG user directory of the specified type.
+ * Example of types are "DESKTOP" and "DOWNLOAD".
+ *
+ * The return value is always != NULL (unless out of memory),
+ * and if a directory
+ * for the type is not specified by the user the default
+ * is the home directory. Except for DESKTOP which defaults
+ * to ~/Desktop.
+ *
+ * The return value is newly allocated and must be freed with
+ * free().
+ **/
+char *
+xdg_user_dir_lookup (const char *type)
+{
+ char *dir, *home_dir, *user_dir;
+
+ dir = xdg_user_dir_lookup_with_fallback (type, NULL);
+ if (dir != NULL)
+ return dir;
+
+ home_dir = getenv ("HOME");
+
+ if (home_dir == NULL)
+ return strdup ("/tmp");
+
+ /* Special case desktop for historical compatibility */
+ if (strcmp (type, "DESKTOP") == 0)
+ {
+ user_dir = (char*) malloc (strlen (home_dir) + strlen ("/Desktop") + 1);
+ if (user_dir == NULL)
+ return NULL;
+
+ strcpy (user_dir, home_dir);
+ strcat (user_dir, "/Desktop");
+ return user_dir;
+ }
+
+ return strdup (home_dir);
+}
+
+#ifdef STANDALONE_XDG_USER_DIR_LOOKUP
+int
+main (int argc, char *argv[])
+{
+ if (argc != 2)
+ {
+ fprintf (stderr, "Usage %s <dir-type>\n", argv[0]);
+ exit (1);
+ }
+
+ printf ("%s\n", xdg_user_dir_lookup (argv[1]));
+ return 0;
+}
+#endif
diff --git a/src/base/third_party/xdg_user_dirs/xdg_user_dir_lookup.h b/src/base/third_party/xdg_user_dirs/xdg_user_dir_lookup.h
new file mode 100644
index 0000000..9e81e1b
--- /dev/null
+++ b/src/base/third_party/xdg_user_dirs/xdg_user_dir_lookup.h
@@ -0,0 +1,33 @@
+/*
+ This file is not licenced under the GPL like the rest of the code.
+ Its is under the MIT license, to encourage reuse by cut-and-paste.
+
+ Copyright (c) 2007 Red Hat, inc
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+
+#ifndef CHROME_THIRD_PARTY_XDG_USER_DIRS_XDG_USER_DIR_LOOKUP_H_
+#define CHROME_THIRD_PARTY_XDG_USER_DIRS_XDG_USER_DIR_LOOKUP_H_
+
+char* xdg_user_dir_lookup(const char *type);
+
+#endif // CHROME_THIRD_PARTY_XDG_USER_DIRS_XDG_USER_DIR_LOOKUP_H_
diff --git a/src/base/thread_task_runner_handle.cc b/src/base/thread_task_runner_handle.cc
new file mode 100644
index 0000000..d1b07aa
--- /dev/null
+++ b/src/base/thread_task_runner_handle.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 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/thread_task_runner_handle.h"
+
+#include "base/lazy_instance.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_local.h"
+
+namespace base {
+
+namespace {
+
+base::LazyInstance<base::ThreadLocalPointer<ThreadTaskRunnerHandle> >
+ lazy_tls_ptr = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+// static
+scoped_refptr<SingleThreadTaskRunner> ThreadTaskRunnerHandle::Get() {
+ ThreadTaskRunnerHandle* current = lazy_tls_ptr.Pointer()->Get();
+ DCHECK(current);
+ return current->task_runner_;
+}
+
+ThreadTaskRunnerHandle::ThreadTaskRunnerHandle(
+ const scoped_refptr<SingleThreadTaskRunner>& task_runner)
+ : task_runner_(task_runner) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ DCHECK(!lazy_tls_ptr.Pointer()->Get());
+ lazy_tls_ptr.Pointer()->Set(this);
+}
+
+ThreadTaskRunnerHandle::~ThreadTaskRunnerHandle() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ DCHECK_EQ(lazy_tls_ptr.Pointer()->Get(), this);
+ lazy_tls_ptr.Pointer()->Set(NULL);
+}
+
+} // namespace base
diff --git a/src/base/thread_task_runner_handle.h b/src/base/thread_task_runner_handle.h
new file mode 100644
index 0000000..599d15b
--- /dev/null
+++ b/src/base/thread_task_runner_handle.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2012 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_THREAD_TASK_RUNNER_HANDLE_H_
+#define BASE_THREAD_TASK_RUNNER_HANDLE_H_
+
+#include "base/base_export.h"
+#include "base/memory/ref_counted.h"
+
+namespace base {
+
+class SingleThreadTaskRunner;
+
+// ThreadTaskRunnerHandle stores a reference to the main task runner
+// for each thread. Not more than one of these objects can be created
+// per thread. After an instance of this object is created the Get()
+// function will return the |task_runner| specified in the
+// constructor.
+class BASE_EXPORT ThreadTaskRunnerHandle {
+ public:
+ // Gets the SingleThreadTaskRunner for the current thread.
+ static scoped_refptr<SingleThreadTaskRunner> Get();
+
+ ThreadTaskRunnerHandle(
+ const scoped_refptr<SingleThreadTaskRunner>& task_runner);
+ ~ThreadTaskRunnerHandle();
+
+ private:
+ scoped_refptr<SingleThreadTaskRunner> task_runner_;
+};
+
+} // namespace base
+
+#endif // BASE_THREAD_TASK_RUNNER_HANDLE_H_
diff --git a/src/base/threading/non_thread_safe.h b/src/base/threading/non_thread_safe.h
new file mode 100644
index 0000000..cf7a418
--- /dev/null
+++ b/src/base/threading/non_thread_safe.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 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_THREADING_NON_THREAD_SAFE_H_
+#define BASE_THREADING_NON_THREAD_SAFE_H_
+
+// Classes deriving from NonThreadSafe may need to suppress MSVC warning 4275:
+// non dll-interface class 'Bar' used as base for dll-interface class 'Foo'.
+// There is a specific macro to do it: NON_EXPORTED_BASE(), defined in
+// compiler_specific.h
+#include "base/compiler_specific.h"
+
+// See comment at top of thread_checker.h
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
+#define ENABLE_NON_THREAD_SAFE 1
+#else
+#define ENABLE_NON_THREAD_SAFE 0
+#endif
+
+#if ENABLE_NON_THREAD_SAFE
+#include "base/threading/non_thread_safe_impl.h"
+#endif
+
+namespace base {
+
+// Do nothing implementation of NonThreadSafe, for release mode.
+//
+// Note: You should almost always use the NonThreadSafe class to get
+// the right version of the class for your build configuration.
+class NonThreadSafeDoNothing {
+ public:
+ bool CalledOnValidThread() const {
+ return true;
+ }
+
+ protected:
+ ~NonThreadSafeDoNothing() {}
+ void DetachFromThread() {}
+};
+
+// NonThreadSafe is a helper class used to help verify that methods of a
+// class are called from the same thread. One can inherit from this class
+// and use CalledOnValidThread() to verify.
+//
+// This is intended to be used with classes that appear to be thread safe, but
+// aren't. For example, a service or a singleton like the preferences system.
+//
+// Example:
+// class MyClass : public base::NonThreadSafe {
+// public:
+// void Foo() {
+// DCHECK(CalledOnValidThread());
+// ... (do stuff) ...
+// }
+// }
+//
+// Note that base::ThreadChecker offers identical functionality to
+// NonThreadSafe, but does not require inheritence. In general, it is preferable
+// to have a base::ThreadChecker as a member, rather than inherit from
+// NonThreadSafe. For more details about when to choose one over the other, see
+// the documentation for base::ThreadChecker.
+#if ENABLE_NON_THREAD_SAFE
+typedef NonThreadSafeImpl NonThreadSafe;
+#else
+typedef NonThreadSafeDoNothing NonThreadSafe;
+#endif // ENABLE_NON_THREAD_SAFE
+
+#undef ENABLE_NON_THREAD_SAFE
+
+} // namespace base
+
+#endif // BASE_NON_THREAD_SAFE_H_
diff --git a/src/base/threading/non_thread_safe_impl.cc b/src/base/threading/non_thread_safe_impl.cc
new file mode 100644
index 0000000..7e729d9
--- /dev/null
+++ b/src/base/threading/non_thread_safe_impl.cc
@@ -0,0 +1,23 @@
+// Copyright (c) 2012 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/threading/non_thread_safe_impl.h"
+
+#include "base/logging.h"
+
+namespace base {
+
+bool NonThreadSafeImpl::CalledOnValidThread() const {
+ return thread_checker_.CalledOnValidThread();
+}
+
+NonThreadSafeImpl::~NonThreadSafeImpl() {
+ DCHECK(CalledOnValidThread());
+}
+
+void NonThreadSafeImpl::DetachFromThread() {
+ thread_checker_.DetachFromThread();
+}
+
+} // namespace base
diff --git a/src/base/threading/non_thread_safe_impl.h b/src/base/threading/non_thread_safe_impl.h
new file mode 100644
index 0000000..a3a356d
--- /dev/null
+++ b/src/base/threading/non_thread_safe_impl.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 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_THREADING_NON_THREAD_SAFE_IMPL_H_
+#define BASE_THREADING_NON_THREAD_SAFE_IMPL_H_
+
+#include "base/base_export.h"
+#include "base/threading/thread_checker_impl.h"
+
+namespace base {
+
+// Full implementation of NonThreadSafe, for debug mode or for occasional
+// temporary use in release mode e.g. when you need to CHECK on a thread
+// bug that only occurs in the wild.
+//
+// Note: You should almost always use the NonThreadSafe class to get
+// the right version of the class for your build configuration.
+class BASE_EXPORT NonThreadSafeImpl {
+ public:
+ bool CalledOnValidThread() const;
+
+ protected:
+ ~NonThreadSafeImpl();
+
+ // Changes the thread that is checked for in CalledOnValidThread. The next
+ // call to CalledOnValidThread will attach this class to a new thread. It is
+ // up to the NonThreadSafe derived class to decide to expose this or not.
+ // This may be useful when an object may be created on one thread and then
+ // used exclusively on another thread.
+ void DetachFromThread();
+
+ private:
+ ThreadCheckerImpl thread_checker_;
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_NON_THREAD_SAFE_IMPL_H_
diff --git a/src/base/threading/non_thread_safe_unittest.cc b/src/base/threading/non_thread_safe_unittest.cc
new file mode 100644
index 0000000..ee31701
--- /dev/null
+++ b/src/base/threading/non_thread_safe_unittest.cc
@@ -0,0 +1,166 @@
+// Copyright (c) 2012 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/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/threading/simple_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Duplicated from base/threading/non_thread_safe.h so that we can be
+// good citizens there and undef the macro.
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
+#define ENABLE_NON_THREAD_SAFE 1
+#else
+#define ENABLE_NON_THREAD_SAFE 0
+#endif
+
+namespace base {
+
+namespace {
+
+// Simple class to exersice the basics of NonThreadSafe.
+// Both the destructor and DoStuff should verify that they were
+// called on the same thread as the constructor.
+class NonThreadSafeClass : public NonThreadSafe {
+ public:
+ NonThreadSafeClass() {}
+
+ // Verifies that it was called on the same thread as the constructor.
+ void DoStuff() {
+ DCHECK(CalledOnValidThread());
+ }
+
+ void DetachFromThread() {
+ NonThreadSafe::DetachFromThread();
+ }
+
+ static void MethodOnDifferentThreadImpl();
+ static void DestructorOnDifferentThreadImpl();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NonThreadSafeClass);
+};
+
+// Calls NonThreadSafeClass::DoStuff on another thread.
+class CallDoStuffOnThread : public SimpleThread {
+ public:
+ CallDoStuffOnThread(NonThreadSafeClass* non_thread_safe_class)
+ : SimpleThread("call_do_stuff_on_thread"),
+ non_thread_safe_class_(non_thread_safe_class) {
+ }
+
+ virtual void Run() OVERRIDE {
+ non_thread_safe_class_->DoStuff();
+ }
+
+ private:
+ NonThreadSafeClass* non_thread_safe_class_;
+
+ DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread);
+};
+
+// Deletes NonThreadSafeClass on a different thread.
+class DeleteNonThreadSafeClassOnThread : public SimpleThread {
+ public:
+ DeleteNonThreadSafeClassOnThread(NonThreadSafeClass* non_thread_safe_class)
+ : SimpleThread("delete_non_thread_safe_class_on_thread"),
+ non_thread_safe_class_(non_thread_safe_class) {
+ }
+
+ virtual void Run() OVERRIDE {
+ non_thread_safe_class_.reset();
+ }
+
+ private:
+ scoped_ptr<NonThreadSafeClass> non_thread_safe_class_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeleteNonThreadSafeClassOnThread);
+};
+
+} // namespace
+
+TEST(NonThreadSafeTest, CallsAllowedOnSameThread) {
+ scoped_ptr<NonThreadSafeClass> non_thread_safe_class(
+ new NonThreadSafeClass);
+
+ // Verify that DoStuff doesn't assert.
+ non_thread_safe_class->DoStuff();
+
+ // Verify that the destructor doesn't assert.
+ non_thread_safe_class.reset();
+}
+
+TEST(NonThreadSafeTest, DetachThenDestructOnDifferentThread) {
+ scoped_ptr<NonThreadSafeClass> non_thread_safe_class(
+ new NonThreadSafeClass);
+
+ // Verify that the destructor doesn't assert when called on a different thread
+ // after a detach.
+ non_thread_safe_class->DetachFromThread();
+ DeleteNonThreadSafeClassOnThread delete_on_thread(
+ non_thread_safe_class.release());
+
+ delete_on_thread.Start();
+ delete_on_thread.Join();
+}
+
+#if GTEST_HAS_DEATH_TEST || !ENABLE_NON_THREAD_SAFE
+
+void NonThreadSafeClass::MethodOnDifferentThreadImpl() {
+ scoped_ptr<NonThreadSafeClass> non_thread_safe_class(
+ new NonThreadSafeClass);
+
+ // Verify that DoStuff asserts in debug builds only when called
+ // on a different thread.
+ CallDoStuffOnThread call_on_thread(non_thread_safe_class.get());
+
+ call_on_thread.Start();
+ call_on_thread.Join();
+}
+
+#if ENABLE_NON_THREAD_SAFE
+TEST(NonThreadSafeDeathTest, MethodNotAllowedOnDifferentThreadInDebug) {
+ ASSERT_DEATH({
+ NonThreadSafeClass::MethodOnDifferentThreadImpl();
+ }, "");
+}
+#else
+TEST(NonThreadSafeTest, MethodAllowedOnDifferentThreadInRelease) {
+ NonThreadSafeClass::MethodOnDifferentThreadImpl();
+}
+#endif // ENABLE_NON_THREAD_SAFE
+
+void NonThreadSafeClass::DestructorOnDifferentThreadImpl() {
+ scoped_ptr<NonThreadSafeClass> non_thread_safe_class(
+ new NonThreadSafeClass);
+
+ // Verify that the destructor asserts in debug builds only
+ // when called on a different thread.
+ DeleteNonThreadSafeClassOnThread delete_on_thread(
+ non_thread_safe_class.release());
+
+ delete_on_thread.Start();
+ delete_on_thread.Join();
+}
+
+#if ENABLE_NON_THREAD_SAFE
+TEST(NonThreadSafeDeathTest, DestructorNotAllowedOnDifferentThreadInDebug) {
+ ASSERT_DEATH({
+ NonThreadSafeClass::DestructorOnDifferentThreadImpl();
+ }, "");
+}
+#else
+TEST(NonThreadSafeTest, DestructorAllowedOnDifferentThreadInRelease) {
+ NonThreadSafeClass::DestructorOnDifferentThreadImpl();
+}
+#endif // ENABLE_NON_THREAD_SAFE
+
+#endif // GTEST_HAS_DEATH_TEST || !ENABLE_NON_THREAD_SAFE
+
+// Just in case we ever get lumped together with other compilation units.
+#undef ENABLE_NON_THREAD_SAFE
+
+} // namespace base
diff --git a/src/base/threading/platform_thread.h b/src/base/threading/platform_thread.h
new file mode 100644
index 0000000..89452c1
--- /dev/null
+++ b/src/base/threading/platform_thread.h
@@ -0,0 +1,181 @@
+// Copyright (c) 2012 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.
+
+// WARNING: You should *NOT* be using this class directly. PlatformThread is
+// the low-level platform-specific abstraction to the OS's threading interface.
+// You should instead be using a message-loop driven Thread, see thread.h.
+
+#ifndef BASE_THREADING_PLATFORM_THREAD_H_
+#define BASE_THREADING_PLATFORM_THREAD_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/time.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_POSIX)
+#include <pthread.h>
+#include <unistd.h>
+#elif defined(OS_STARBOARD)
+#include "starboard/thread.h"
+#endif
+
+namespace base {
+
+// PlatformThreadHandle should not be assumed to be a numeric type, since the
+// standard intends to allow pthread_t to be a structure. This means you
+// should not initialize it to a value, like 0. If it's a member variable, the
+// constructor can safely "value initialize" using () in the initializer list.
+//
+// ... Note that PlatformThreadHandle is assumed to be a scalar type below.
+#if defined(OS_WIN)
+typedef DWORD PlatformThreadId;
+typedef void* PlatformThreadHandle; // HANDLE
+const PlatformThreadHandle kNullThreadHandle = NULL;
+const PlatformThreadId kInvalidThreadId = 0;
+#elif defined(OS_POSIX)
+typedef pthread_t PlatformThreadHandle;
+const PlatformThreadHandle kNullThreadHandle = 0;
+typedef pid_t PlatformThreadId;
+const PlatformThreadId kInvalidThreadId = 0;
+#if defined(COBALT)
+typedef int ThreadAffinity;
+const ThreadAffinity kNoThreadAffinity = -1;
+#endif
+#elif defined(OS_STARBOARD)
+typedef SbThread PlatformThreadHandle;
+const PlatformThreadHandle kNullThreadHandle = kSbThreadInvalid;
+typedef SbThreadId PlatformThreadId;
+const PlatformThreadId kInvalidThreadId = kSbThreadInvalidId;
+typedef SbThreadAffinity ThreadAffinity;
+const ThreadAffinity kNoThreadAffinity = kSbThreadNoAffinity;
+#endif
+
+// Valid values for SetThreadPriority()
+enum ThreadPriority {
+#if defined(OS_STARBOARD)
+ // See the priorities defined in "starboard/thread.h"
+
+ kThreadPriority_Default = kSbThreadNoPriority,
+ kThreadPriority_Lowest = kSbThreadPriorityLowest,
+ kThreadPriority_Low = kSbThreadPriorityLow,
+ kThreadPriority_Normal = kSbThreadPriorityNormal,
+ kThreadPriority_High = kSbThreadPriorityHigh,
+ kThreadPriority_Highest = kSbThreadPriorityHighest,
+ kThreadPriority_RealTime = kSbThreadPriorityRealTime,
+ kThreadPriority_RealtimeAudio = kThreadPriority_RealTime,
+#elif defined(COBALT)
+ // Cobalt on non-Starboard platforms.
+
+ kThreadPriority_Default = -1,
+ kThreadPriority_Lowest = 0,
+ kThreadPriority_Low,
+ kThreadPriority_Normal,
+ kThreadPriority_High,
+ kThreadPriority_Highest,
+ kThreadPriority_RealTime,
+ kThreadPriority_RealtimeAudio = kThreadPriority_RealTime,
+#else
+ // The original Chromium priorities.
+
+ kThreadPriority_Normal,
+ // Suitable for low-latency, glitch-resistant audio.
+ kThreadPriority_RealtimeAudio,
+#endif
+};
+
+// A namespace for low-level thread functions.
+class BASE_EXPORT PlatformThread {
+ public:
+ // Implement this interface to run code on a background thread. Your
+ // ThreadMain method will be called on the newly created thread.
+ class BASE_EXPORT Delegate {
+ public:
+ virtual void ThreadMain() = 0;
+
+ protected:
+ virtual ~Delegate() {}
+ };
+
+#if defined(__LB_SHELL__) || defined(OS_STARBOARD)
+ struct PlatformThreadOptions {
+ // <= 0 indicates default stack size
+ int64_t stack_size;
+
+ // The priority to start the thread at, or kThreadPriority_Default for the
+ // default priority assignment policy.
+ ThreadPriority priority;
+
+ // The CPU index to pin the thread to, or kNoThreadAffinity for no affinity.
+ ThreadAffinity affinity;
+ };
+#endif
+
+ // Gets the current thread id, which may be useful for logging purposes.
+ static PlatformThreadId CurrentId();
+
+ // Yield the current thread so another thread can be scheduled.
+ static void YieldCurrentThread();
+
+ // Sleeps for the specified duration.
+ static void Sleep(base::TimeDelta duration);
+
+ // Sets the thread name visible to debuggers/tools. This has no effect
+ // otherwise. This name pointer is not copied internally. Thus, it must stay
+ // valid until the thread ends.
+ static void SetName(const char* name);
+
+ // Gets the thread name, if previously set by SetName.
+ static const char* GetName();
+
+ // Creates a new thread. The |stack_size| parameter can be 0 to indicate
+ // that the default stack size should be used. Upon success,
+ // |*thread_handle| will be assigned a handle to the newly created thread,
+ // and |delegate|'s ThreadMain method will be executed on the newly created
+ // thread.
+ // NOTE: When you are done with the thread handle, you must call Join to
+ // release system resources associated with the thread. You must ensure that
+ // the Delegate object outlives the thread.
+ static bool Create(size_t stack_size, Delegate* delegate,
+ PlatformThreadHandle* thread_handle);
+
+ // CreateWithPriority() does the same thing as Create() except the priority of
+ // the thread is set based on |priority|. Can be used in place of Create()
+ // followed by SetThreadPriority(). SetThreadPriority() has not been
+ // implemented on the Linux platform yet, this is the only way to get a high
+ // priority thread on Linux.
+ static bool CreateWithPriority(size_t stack_size, Delegate* delegate,
+ PlatformThreadHandle* thread_handle,
+ ThreadPriority priority);
+
+ // CreateNonJoinable() does the same thing as Create() except the thread
+ // cannot be Join()'d. Therefore, it also does not output a
+ // PlatformThreadHandle.
+ static bool CreateNonJoinable(size_t stack_size, Delegate* delegate);
+
+#if defined(__LB_SHELL__) || defined(OS_STARBOARD)
+ static bool CreateWithOptions(const PlatformThreadOptions& options,
+ Delegate* delegate,
+ PlatformThreadHandle* thread_handle);
+#endif
+
+ // Joins with a thread created via the Create function. This function blocks
+ // the caller until the designated thread exits. This will invalidate
+ // |thread_handle|.
+ static void Join(PlatformThreadHandle thread_handle);
+
+ // Sets the priority of the thread specified in |handle| to |priority|.
+ // This does not work on Linux, use CreateWithPriority() instead.
+ static void SetThreadPriority(PlatformThreadHandle handle,
+ ThreadPriority priority);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformThread);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_PLATFORM_THREAD_H_
diff --git a/src/base/threading/platform_thread_mac.mm b/src/base/threading/platform_thread_mac.mm
new file mode 100644
index 0000000..96c6720
--- /dev/null
+++ b/src/base/threading/platform_thread_mac.mm
@@ -0,0 +1,190 @@
+// Copyright (c) 2012 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/threading/platform_thread.h"
+
+#import <Foundation/Foundation.h>
+#include <dlfcn.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <mach/thread_policy.h>
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/thread_local.h"
+#include "base/tracked_objects.h"
+
+namespace base {
+
+namespace {
+
+LazyInstance<ThreadLocalPointer<char> >::Leaky
+ current_thread_name = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+// If Cocoa is to be used on more than one thread, it must know that the
+// application is multithreaded. Since it's possible to enter Cocoa code
+// from threads created by pthread_thread_create, Cocoa won't necessarily
+// be aware that the application is multithreaded. Spawning an NSThread is
+// enough to get Cocoa to set up for multithreaded operation, so this is done
+// if necessary before pthread_thread_create spawns any threads.
+//
+// http://developer.apple.com/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/chapter_4_section_4.html
+void InitThreading() {
+ static BOOL multithreaded = [NSThread isMultiThreaded];
+ if (!multithreaded) {
+ // +[NSObject class] is idempotent.
+ [NSThread detachNewThreadSelector:@selector(class)
+ toTarget:[NSObject class]
+ withObject:nil];
+ multithreaded = YES;
+
+ DCHECK([NSThread isMultiThreaded]);
+ }
+}
+
+// static
+void PlatformThread::SetName(const char* name) {
+ current_thread_name.Pointer()->Set(const_cast<char*>(name));
+ tracked_objects::ThreadData::InitializeThreadContext(name);
+
+ // pthread_setname_np is only available in 10.6 or later, so test
+ // for it at runtime.
+ int (*dynamic_pthread_setname_np)(const char*);
+ *reinterpret_cast<void**>(&dynamic_pthread_setname_np) =
+ dlsym(RTLD_DEFAULT, "pthread_setname_np");
+ if (!dynamic_pthread_setname_np)
+ return;
+
+ // Mac OS X does not expose the length limit of the name, so
+ // hardcode it.
+ const int kMaxNameLength = 63;
+ std::string shortened_name = std::string(name).substr(0, kMaxNameLength);
+ // pthread_setname() fails (harmlessly) in the sandbox, ignore when it does.
+ // See http://crbug.com/47058
+ dynamic_pthread_setname_np(shortened_name.c_str());
+}
+
+// static
+const char* PlatformThread::GetName() {
+ return current_thread_name.Pointer()->Get();
+}
+
+namespace {
+
+void SetPriorityNormal(mach_port_t mach_thread_id) {
+ // Make thread standard policy.
+ // Please note that this call could fail in rare cases depending
+ // on runtime conditions.
+ thread_standard_policy policy;
+ kern_return_t result = thread_policy_set(mach_thread_id,
+ THREAD_STANDARD_POLICY,
+ (thread_policy_t)&policy,
+ THREAD_STANDARD_POLICY_COUNT);
+
+ if (result != KERN_SUCCESS)
+ DVLOG(1) << "thread_policy_set() failure: " << result;
+}
+
+// Enables time-contraint policy and priority suitable for low-latency,
+// glitch-resistant audio.
+void SetPriorityRealtimeAudio(mach_port_t mach_thread_id) {
+ kern_return_t result;
+
+ // Increase thread priority to real-time.
+
+ // Please note that the thread_policy_set() calls may fail in
+ // rare cases if the kernel decides the system is under heavy load
+ // and is unable to handle boosting the thread priority.
+ // In these cases we just return early and go on with life.
+
+ // Make thread fixed priority.
+ thread_extended_policy_data_t policy;
+ policy.timeshare = 0; // Set to 1 for a non-fixed thread.
+ result = thread_policy_set(mach_thread_id,
+ THREAD_EXTENDED_POLICY,
+ (thread_policy_t)&policy,
+ THREAD_EXTENDED_POLICY_COUNT);
+ if (result != KERN_SUCCESS) {
+ DVLOG(1) << "thread_policy_set() failure: " << result;
+ return;
+ }
+
+ // Set to relatively high priority.
+ thread_precedence_policy_data_t precedence;
+ precedence.importance = 63;
+ result = thread_policy_set(mach_thread_id,
+ THREAD_PRECEDENCE_POLICY,
+ (thread_policy_t)&precedence,
+ THREAD_PRECEDENCE_POLICY_COUNT);
+ if (result != KERN_SUCCESS) {
+ DVLOG(1) << "thread_policy_set() failure: " << result;
+ return;
+ }
+
+ // Most important, set real-time constraints.
+
+ // Define the guaranteed and max fraction of time for the audio thread.
+ // These "duty cycle" values can range from 0 to 1. A value of 0.5
+ // means the scheduler would give half the time to the thread.
+ // These values have empirically been found to yield good behavior.
+ // Good means that audio performance is high and other threads won't starve.
+ const double kGuaranteedAudioDutyCycle = 0.75;
+ const double kMaxAudioDutyCycle = 0.85;
+
+ // Define constants determining how much time the audio thread can
+ // use in a given time quantum. All times are in milliseconds.
+
+ // About 128 frames @44.1KHz
+ const double kTimeQuantum = 2.9;
+
+ // Time guaranteed each quantum.
+ const double kAudioTimeNeeded = kGuaranteedAudioDutyCycle * kTimeQuantum;
+
+ // Maximum time each quantum.
+ const double kMaxTimeAllowed = kMaxAudioDutyCycle * kTimeQuantum;
+
+ // Get the conversion factor from milliseconds to absolute time
+ // which is what the time-constraints call needs.
+ mach_timebase_info_data_t tb_info;
+ mach_timebase_info(&tb_info);
+ double ms_to_abs_time =
+ ((double)tb_info.denom / (double)tb_info.numer) * 1000000;
+
+ thread_time_constraint_policy_data_t time_constraints;
+ time_constraints.period = kTimeQuantum * ms_to_abs_time;
+ time_constraints.computation = kAudioTimeNeeded * ms_to_abs_time;
+ time_constraints.constraint = kMaxTimeAllowed * ms_to_abs_time;
+ time_constraints.preemptible = 0;
+
+ result = thread_policy_set(mach_thread_id,
+ THREAD_TIME_CONSTRAINT_POLICY,
+ (thread_policy_t)&time_constraints,
+ THREAD_TIME_CONSTRAINT_POLICY_COUNT);
+ if (result != KERN_SUCCESS)
+ DVLOG(1) << "thread_policy_set() failure: " << result;
+
+ return;
+}
+
+} // anonymous namespace
+
+// static
+void PlatformThread::SetThreadPriority(PlatformThreadHandle handle,
+ ThreadPriority priority) {
+ // Convert from pthread_t to mach thread identifier.
+ mach_port_t mach_thread_id = pthread_mach_thread_np(handle);
+
+ switch (priority) {
+ case kThreadPriority_Normal:
+ SetPriorityNormal(mach_thread_id);
+ break;
+ case kThreadPriority_RealtimeAudio:
+ SetPriorityRealtimeAudio(mach_thread_id);
+ break;
+ }
+}
+
+} // namespace base
diff --git a/src/base/threading/platform_thread_posix.cc b/src/base/threading/platform_thread_posix.cc
new file mode 100644
index 0000000..038ab04
--- /dev/null
+++ b/src/base/threading/platform_thread_posix.cc
@@ -0,0 +1,286 @@
+// Copyright (c) 2012 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/threading/platform_thread.h"
+
+#include <errno.h>
+#include <sched.h>
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/safe_strerror_posix.h"
+#include "base/threading/thread_local.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/tracked_objects.h"
+
+#if defined(OS_MACOSX)
+#include <sys/resource.h>
+#include <algorithm>
+#endif
+
+#if defined(OS_LINUX)
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+
+#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
+#include "base/android/jni_android.h"
+#endif
+
+// TODO(bbudge) Use time.h when NaCl toolchain supports _POSIX_TIMERS
+#if defined(OS_NACL)
+#include <sys/nacl_syscalls.h>
+#endif
+
+namespace base {
+
+#if defined(OS_MACOSX)
+void InitThreading();
+#endif
+
+namespace {
+
+#if !defined(OS_MACOSX)
+// Mac name code is in in platform_thread_mac.mm.
+LazyInstance<ThreadLocalPointer<char> >::Leaky
+ current_thread_name = LAZY_INSTANCE_INITIALIZER;
+#endif
+
+struct ThreadParams {
+ PlatformThread::Delegate* delegate;
+ bool joinable;
+};
+
+void* ThreadFunc(void* params) {
+ ThreadParams* thread_params = static_cast<ThreadParams*>(params);
+ PlatformThread::Delegate* delegate = thread_params->delegate;
+ if (!thread_params->joinable)
+ base::ThreadRestrictions::SetSingletonAllowed(false);
+ delete thread_params;
+ delegate->ThreadMain();
+#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
+ base::android::DetachFromVM();
+#endif
+ return NULL;
+}
+
+bool CreateThread(size_t stack_size, bool joinable,
+ PlatformThread::Delegate* delegate,
+ PlatformThreadHandle* thread_handle,
+ ThreadPriority priority) {
+#if defined(OS_MACOSX)
+ base::InitThreading();
+#endif // OS_MACOSX
+
+ bool success = false;
+ pthread_attr_t attributes;
+ pthread_attr_init(&attributes);
+
+ // Pthreads are joinable by default, so only specify the detached attribute if
+ // the thread should be non-joinable.
+ if (!joinable) {
+ pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED);
+ }
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+ // The Mac OS X default for a pthread stack size is 512kB.
+ // Libc-594.1.4/pthreads/pthread.c's pthread_attr_init uses
+ // DEFAULT_STACK_SIZE for this purpose.
+ //
+ // 512kB isn't quite generous enough for some deeply recursive threads that
+ // otherwise request the default stack size by specifying 0. Here, adopt
+ // glibc's behavior as on Linux, which is to use the current stack size
+ // limit (ulimit -s) as the default stack size. See
+ // glibc-2.11.1/nptl/nptl-init.c's __pthread_initialize_minimal_internal. To
+ // avoid setting the limit below the Mac OS X default or the minimum usable
+ // stack size, these values are also considered. If any of these values
+ // can't be determined, or if stack size is unlimited (ulimit -s unlimited),
+ // stack_size is left at 0 to get the system default.
+ //
+ // Mac OS X normally only applies ulimit -s to the main thread stack. On
+ // contemporary OS X and Linux systems alike, this value is generally 8MB
+ // or in that neighborhood.
+ if (stack_size == 0) {
+ size_t default_stack_size;
+ struct rlimit stack_rlimit;
+ if (pthread_attr_getstacksize(&attributes, &default_stack_size) == 0 &&
+ getrlimit(RLIMIT_STACK, &stack_rlimit) == 0 &&
+ stack_rlimit.rlim_cur != RLIM_INFINITY) {
+ stack_size = std::max(std::max(default_stack_size,
+ static_cast<size_t>(PTHREAD_STACK_MIN)),
+ static_cast<size_t>(stack_rlimit.rlim_cur));
+ }
+ }
+#endif // OS_MACOSX && !OS_IOS
+
+ if (stack_size > 0)
+ pthread_attr_setstacksize(&attributes, stack_size);
+
+ ThreadParams* params = new ThreadParams;
+ params->delegate = delegate;
+ params->joinable = joinable;
+ success = !pthread_create(thread_handle, &attributes, ThreadFunc, params);
+
+ if (priority != kThreadPriority_Normal) {
+#if defined(OS_LINUX)
+ if (priority == kThreadPriority_RealtimeAudio) {
+ // Linux isn't posix compliant with setpriority(2), it will set a thread
+ // priority if it is passed a tid, not affecting the rest of the threads
+ // in the process. Setting this priority will only succeed if the user
+ // has been granted permission to adjust nice values on the system.
+ const int kNiceSetting = -10;
+ if (setpriority(PRIO_PROCESS, PlatformThread::CurrentId(), kNiceSetting))
+ DVLOG(1) << "Failed to set nice value of thread to " << kNiceSetting;
+ } else {
+ NOTREACHED() << "Unknown thread priority.";
+ }
+#else
+ PlatformThread::SetThreadPriority(*thread_handle, priority);
+#endif
+ }
+
+ pthread_attr_destroy(&attributes);
+ if (!success)
+ delete params;
+ return success;
+}
+
+} // namespace
+
+// static
+PlatformThreadId PlatformThread::CurrentId() {
+ // Pthreads doesn't have the concept of a thread ID, so we have to reach down
+ // into the kernel.
+#if defined(OS_LINUX)
+ return syscall(__NR_gettid);
+#elif defined(OS_ANDROID) || defined(__LB_ANDROID__)
+ return gettid();
+#elif defined(OS_SOLARIS)
+ return pthread_self();
+#elif defined(OS_NACL) && defined(__GLIBC__)
+ return pthread_self();
+#elif defined(OS_NACL) && !defined(__GLIBC__)
+ // Pointers are 32-bits in NaCl.
+ return reinterpret_cast<int32>(pthread_self());
+#elif defined(OS_POSIX)
+ return reinterpret_cast<int64>(pthread_self());
+#endif
+}
+
+// static
+void PlatformThread::YieldCurrentThread() {
+ sched_yield();
+}
+
+// static
+void PlatformThread::Sleep(TimeDelta duration) {
+ struct timespec sleep_time, remaining;
+
+ // Break the duration into seconds and nanoseconds.
+ // NOTE: TimeDelta's microseconds are int64s while timespec's
+ // nanoseconds are longs, so this unpacking must prevent overflow.
+ sleep_time.tv_sec = duration.InSeconds();
+ duration -= TimeDelta::FromSeconds(sleep_time.tv_sec);
+ sleep_time.tv_nsec = duration.InMicroseconds() * 1000; // nanoseconds
+
+ while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR)
+ sleep_time = remaining;
+}
+
+#if defined(OS_LINUX)
+// static
+void PlatformThread::SetName(const char* name) {
+ // have to cast away const because ThreadLocalPointer does not support const
+ // void*
+ current_thread_name.Pointer()->Set(const_cast<char*>(name));
+ tracked_objects::ThreadData::InitializeThreadContext(name);
+
+ // On linux we can get the thread names to show up in the debugger by setting
+ // the process name for the LWP. We don't want to do this for the main
+ // thread because that would rename the process, causing tools like killall
+ // to stop working.
+ if (PlatformThread::CurrentId() == getpid())
+ return;
+
+ // http://0pointer.de/blog/projects/name-your-threads.html
+ // Set the name for the LWP (which gets truncated to 15 characters).
+ // Note that glibc also has a 'pthread_setname_np' api, but it may not be
+ // available everywhere and it's only benefit over using prctl directly is
+ // that it can set the name of threads other than the current thread.
+ int err = prctl(PR_SET_NAME, name);
+ // We expect EPERM failures in sandboxed processes, just ignore those.
+ if (err < 0 && errno != EPERM)
+ DPLOG(ERROR) << "prctl(PR_SET_NAME)";
+}
+#elif defined(OS_MACOSX)
+// Mac is implemented in platform_thread_mac.mm.
+#else
+// static
+void PlatformThread::SetName(const char* name) {
+ // have to cast away const because ThreadLocalPointer does not support const
+ // void*
+ current_thread_name.Pointer()->Set(const_cast<char*>(name));
+ tracked_objects::ThreadData::InitializeThreadContext(name);
+
+ // (This should be relatively simple to implement for the BSDs; I
+ // just don't have one handy to test the code on.)
+}
+#endif // defined(OS_LINUX)
+
+
+#if !defined(OS_MACOSX)
+// Mac is implemented in platform_thread_mac.mm.
+// static
+const char* PlatformThread::GetName() {
+ return current_thread_name.Pointer()->Get();
+}
+#endif
+
+// static
+bool PlatformThread::Create(size_t stack_size, Delegate* delegate,
+ PlatformThreadHandle* thread_handle) {
+ return CreateThread(stack_size, true /* joinable thread */,
+ delegate, thread_handle, kThreadPriority_Normal);
+}
+
+// static
+bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate,
+ PlatformThreadHandle* thread_handle,
+ ThreadPriority priority) {
+ return CreateThread(stack_size, true, // joinable thread
+ delegate, thread_handle, priority);
+}
+
+// static
+bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) {
+ PlatformThreadHandle unused;
+
+ bool result = CreateThread(stack_size, false /* non-joinable thread */,
+ delegate, &unused, kThreadPriority_Normal);
+ return result;
+}
+
+// static
+void PlatformThread::Join(PlatformThreadHandle thread_handle) {
+ // Joining another thread may block the current thread for a long time, since
+ // the thread referred to by |thread_handle| may still be running long-lived /
+ // blocking tasks.
+ base::ThreadRestrictions::AssertIOAllowed();
+ pthread_join(thread_handle, NULL);
+}
+
+#if !defined(OS_MACOSX)
+// Mac OS X uses lower-level mach APIs.
+
+// static
+void PlatformThread::SetThreadPriority(PlatformThreadHandle, ThreadPriority) {
+ // TODO(crogers): Implement, see http://crbug.com/116172
+}
+#endif
+
+} // namespace base
diff --git a/src/base/threading/platform_thread_starboard.cc b/src/base/threading/platform_thread_starboard.cc
new file mode 100644
index 0000000..20f5cdf
--- /dev/null
+++ b/src/base/threading/platform_thread_starboard.cc
@@ -0,0 +1,147 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/threading/platform_thread.h"
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/thread_local.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/tracked_objects.h"
+#include "starboard/thread.h"
+
+namespace base {
+
+namespace {
+
+LazyInstance< ThreadLocalPointer<char> >::Leaky current_thread_name =
+ LAZY_INSTANCE_INITIALIZER;
+
+struct ThreadParams {
+ PlatformThread::Delegate* delegate;
+ bool joinable;
+};
+
+void* ThreadFunc(void* params) {
+ ThreadParams* thread_params = static_cast<ThreadParams*>(params);
+ PlatformThread::Delegate* delegate = thread_params->delegate;
+ if (!thread_params->joinable) {
+ base::ThreadRestrictions::SetSingletonAllowed(false);
+ }
+
+ delete thread_params;
+ delegate->ThreadMain();
+ return NULL;
+}
+
+bool CreateThread(size_t stack_size,
+ SbThreadPriority priority,
+ SbThreadAffinity affinity,
+ bool joinable,
+ const char* name,
+ PlatformThread::Delegate* delegate,
+ PlatformThreadHandle* thread_handle) {
+ ThreadParams* params = new ThreadParams;
+ params->delegate = delegate;
+ params->joinable = joinable;
+
+ SbThread thread = SbThreadCreate(stack_size, priority, affinity, joinable,
+ name, ThreadFunc, params);
+ if (SbThreadIsValid(thread)) {
+ if (thread_handle) {
+ *thread_handle = thread;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+inline SbThreadPriority toSbPriority(ThreadPriority priority) {
+ return static_cast<SbThreadPriority>(priority);
+}
+
+} // namespace
+
+// static
+PlatformThreadId PlatformThread::CurrentId() {
+ return SbThreadGetId();
+}
+
+// static
+void PlatformThread::YieldCurrentThread() {
+ SbThreadYield();
+}
+
+// static
+void PlatformThread::Sleep(TimeDelta duration) {
+ SbThreadSleep(duration.ToSbTime());
+}
+
+// static
+void PlatformThread::SetName(const char* name) {
+ // have to cast away const because ThreadLocalPointer does not support const
+ // void*
+ current_thread_name.Pointer()->Set(const_cast<char*>(name));
+ SbThreadSetName(name);
+}
+
+// static
+const char* PlatformThread::GetName() {
+ return current_thread_name.Pointer()->Get();
+}
+
+// static
+bool PlatformThread::Create(size_t stack_size, Delegate* delegate,
+ PlatformThreadHandle* thread_handle) {
+ return CreateThread(stack_size, kSbThreadNoPriority, kSbThreadNoAffinity,
+ true /* joinable thread */, NULL, delegate,
+ thread_handle);
+}
+
+// static
+bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate,
+ PlatformThreadHandle* thread_handle,
+ ThreadPriority priority) {
+ return CreateThread(stack_size, toSbPriority(priority), kSbThreadNoAffinity,
+ true /* joinable thread */, NULL, delegate,
+ thread_handle);
+}
+
+// static
+bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) {
+ return CreateThread(stack_size, kSbThreadNoPriority, kSbThreadNoAffinity,
+ false /* joinable thread */, NULL, delegate, NULL);
+}
+
+// static
+void PlatformThread::Join(PlatformThreadHandle thread_handle) {
+ // Joining another thread may block the current thread for a long time, since
+ // the thread referred to by |thread_handle| may still be running long-lived /
+ // blocking tasks.
+ base::ThreadRestrictions::AssertIOAllowed();
+ SbThreadJoin(thread_handle, NULL);
+}
+
+// static
+bool PlatformThread::CreateWithOptions(const PlatformThreadOptions& options,
+ Delegate* delegate,
+ PlatformThreadHandle* thread_handle) {
+ return CreateThread(options.stack_size, toSbPriority(options.priority),
+ options.affinity, true /* joinable */, NULL, delegate,
+ thread_handle);
+}
+
+} // namespace base
diff --git a/src/base/threading/platform_thread_unittest.cc b/src/base/threading/platform_thread_unittest.cc
new file mode 100644
index 0000000..e37709a
--- /dev/null
+++ b/src/base/threading/platform_thread_unittest.cc
@@ -0,0 +1,121 @@
+// Copyright (c) 2012 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/compiler_specific.h"
+#include "base/threading/platform_thread.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+// Trivial tests that thread runs and doesn't crash on create and join ---------
+
+class TrivialThread : public PlatformThread::Delegate {
+ public:
+ TrivialThread() : did_run_(false) {}
+
+ virtual void ThreadMain() OVERRIDE {
+ did_run_ = true;
+ }
+
+ bool did_run() const { return did_run_; }
+
+ private:
+ bool did_run_;
+
+ DISALLOW_COPY_AND_ASSIGN(TrivialThread);
+};
+
+TEST(PlatformThreadTest, Trivial) {
+ TrivialThread thread;
+ PlatformThreadHandle handle = kNullThreadHandle;
+
+ ASSERT_FALSE(thread.did_run());
+ ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
+ PlatformThread::Join(handle);
+ ASSERT_TRUE(thread.did_run());
+}
+
+TEST(PlatformThreadTest, TrivialTimesTen) {
+ TrivialThread thread[10];
+ PlatformThreadHandle handle[arraysize(thread)];
+
+ for (size_t n = 0; n < arraysize(thread); n++)
+ ASSERT_FALSE(thread[n].did_run());
+ for (size_t n = 0; n < arraysize(thread); n++)
+ ASSERT_TRUE(PlatformThread::Create(0, &thread[n], &handle[n]));
+ for (size_t n = 0; n < arraysize(thread); n++)
+ PlatformThread::Join(handle[n]);
+ for (size_t n = 0; n < arraysize(thread); n++)
+ ASSERT_TRUE(thread[n].did_run());
+}
+
+// Tests of basic thread functions ---------------------------------------------
+
+class FunctionTestThread : public TrivialThread {
+ public:
+ FunctionTestThread() : thread_id_(0) {}
+
+ virtual void ThreadMain() OVERRIDE {
+ thread_id_ = PlatformThread::CurrentId();
+ PlatformThread::YieldCurrentThread();
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(50));
+
+ // Make sure that the thread ID is the same across calls.
+ EXPECT_EQ(thread_id_, PlatformThread::CurrentId());
+
+ TrivialThread::ThreadMain();
+ }
+
+ PlatformThreadId thread_id() const { return thread_id_; }
+
+ private:
+ PlatformThreadId thread_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(FunctionTestThread);
+};
+
+TEST(PlatformThreadTest, Function) {
+ PlatformThreadId main_thread_id = PlatformThread::CurrentId();
+
+ FunctionTestThread thread;
+ PlatformThreadHandle handle = kNullThreadHandle;
+
+ ASSERT_FALSE(thread.did_run());
+ ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
+ PlatformThread::Join(handle);
+ ASSERT_TRUE(thread.did_run());
+ EXPECT_NE(thread.thread_id(), main_thread_id);
+
+ // Make sure that the thread ID is the same across calls.
+ EXPECT_EQ(main_thread_id, PlatformThread::CurrentId());
+}
+
+TEST(PlatformThreadTest, FunctionTimesTen) {
+ PlatformThreadId main_thread_id = PlatformThread::CurrentId();
+
+ FunctionTestThread thread[10];
+ PlatformThreadHandle handle[arraysize(thread)];
+
+ for (size_t n = 0; n < arraysize(thread); n++)
+ ASSERT_FALSE(thread[n].did_run());
+ for (size_t n = 0; n < arraysize(thread); n++)
+ ASSERT_TRUE(PlatformThread::Create(0, &thread[n], &handle[n]));
+ for (size_t n = 0; n < arraysize(thread); n++)
+ PlatformThread::Join(handle[n]);
+ for (size_t n = 0; n < arraysize(thread); n++) {
+ ASSERT_TRUE(thread[n].did_run());
+ EXPECT_NE(thread[n].thread_id(), main_thread_id);
+
+ // Make sure no two threads get the same ID.
+ for (size_t i = 0; i < n; ++i) {
+ EXPECT_NE(thread[i].thread_id(), thread[n].thread_id());
+ }
+ }
+
+ // Make sure that the thread ID is the same across calls.
+ EXPECT_EQ(main_thread_id, PlatformThread::CurrentId());
+}
+
+} // namespace base
diff --git a/src/base/threading/platform_thread_win.cc b/src/base/threading/platform_thread_win.cc
new file mode 100644
index 0000000..82981ad
--- /dev/null
+++ b/src/base/threading/platform_thread_win.cc
@@ -0,0 +1,207 @@
+// Copyright (c) 2012 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/threading/platform_thread.h"
+
+#include "base/debug/alias.h"
+#include "base/debug/profiler.h"
+#include "base/logging.h"
+#include "base/threading/thread_local.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/tracked_objects.h"
+
+#include "base/win/windows_version.h"
+
+namespace base {
+
+namespace {
+
+static ThreadLocalPointer<char> current_thread_name;
+
+// The information on how to set the thread name comes from
+// a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx
+const DWORD kVCThreadNameException = 0x406D1388;
+
+typedef struct tagTHREADNAME_INFO {
+ DWORD dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to name (in user addr space).
+ DWORD dwThreadID; // Thread ID (-1=caller thread).
+ DWORD dwFlags; // Reserved for future use, must be zero.
+} THREADNAME_INFO;
+
+// This function has try handling, so it is separated out of its caller.
+void SetNameInternal(PlatformThreadId thread_id, const char* name) {
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = name;
+ info.dwThreadID = thread_id;
+ info.dwFlags = 0;
+
+ __try {
+ RaiseException(kVCThreadNameException, 0, sizeof(info)/sizeof(DWORD),
+ reinterpret_cast<DWORD_PTR*>(&info));
+ } __except(EXCEPTION_CONTINUE_EXECUTION) {
+ }
+}
+
+struct ThreadParams {
+ PlatformThread::Delegate* delegate;
+ bool joinable;
+};
+
+DWORD __stdcall ThreadFunc(void* params) {
+ ThreadParams* thread_params = static_cast<ThreadParams*>(params);
+ PlatformThread::Delegate* delegate = thread_params->delegate;
+ if (!thread_params->joinable)
+ base::ThreadRestrictions::SetSingletonAllowed(false);
+ delete thread_params;
+ delegate->ThreadMain();
+ return NULL;
+}
+
+// CreateThreadInternal() matches PlatformThread::Create(), except that
+// |out_thread_handle| may be NULL, in which case a non-joinable thread is
+// created.
+bool CreateThreadInternal(size_t stack_size,
+ PlatformThread::Delegate* delegate,
+ PlatformThreadHandle* out_thread_handle) {
+ PlatformThreadHandle thread_handle;
+ unsigned int flags = 0;
+ if (stack_size > 0 && base::win::GetVersion() >= base::win::VERSION_XP) {
+ flags = STACK_SIZE_PARAM_IS_A_RESERVATION;
+ } else {
+ stack_size = 0;
+ }
+
+ ThreadParams* params = new ThreadParams;
+ params->delegate = delegate;
+ params->joinable = out_thread_handle != NULL;
+
+ // Using CreateThread here vs _beginthreadex makes thread creation a bit
+ // faster and doesn't require the loader lock to be available. Our code will
+ // have to work running on CreateThread() threads anyway, since we run code
+ // on the Windows thread pool, etc. For some background on the difference:
+ // http://www.microsoft.com/msj/1099/win32/win321099.aspx
+ thread_handle = CreateThread(
+ NULL, stack_size, ThreadFunc, params, flags, NULL);
+ if (!thread_handle) {
+ delete params;
+ return false;
+ }
+
+ if (out_thread_handle)
+ *out_thread_handle = thread_handle;
+ else
+ CloseHandle(thread_handle);
+ return true;
+}
+
+} // namespace
+
+// static
+PlatformThreadId PlatformThread::CurrentId() {
+ return GetCurrentThreadId();
+}
+
+// static
+void PlatformThread::YieldCurrentThread() {
+ ::Sleep(0);
+}
+
+// static
+void PlatformThread::Sleep(TimeDelta duration) {
+ ::Sleep(duration.InMillisecondsRoundedUp());
+}
+
+// static
+void PlatformThread::SetName(const char* name) {
+ current_thread_name.Set(const_cast<char*>(name));
+
+ // On Windows only, we don't need to tell the profiler about the "BrokerEvent"
+ // thread, as it exists only in the chrome.exe image, and never spawns or runs
+ // tasks (items which could be profiled). This test avoids the notification,
+ // which would also (as a side effect) initialize the profiler in this unused
+ // context, including setting up thread local storage, etc. The performance
+ // impact is not terrible, but there is no reason to do initialize it.
+ if (0 != strcmp(name, "BrokerEvent"))
+ tracked_objects::ThreadData::InitializeThreadContext(name);
+
+ // The debugger needs to be around to catch the name in the exception. If
+ // there isn't a debugger, we are just needlessly throwing an exception.
+ // If this image file is instrumented, we raise the exception anyway
+ // to provide the profiler with human-readable thread names.
+ if (!::IsDebuggerPresent() && !base::debug::IsBinaryInstrumented())
+ return;
+
+ SetNameInternal(CurrentId(), name);
+}
+
+// static
+const char* PlatformThread::GetName() {
+ return current_thread_name.Get();
+}
+
+// static
+bool PlatformThread::Create(size_t stack_size, Delegate* delegate,
+ PlatformThreadHandle* thread_handle) {
+ DCHECK(thread_handle);
+ return CreateThreadInternal(stack_size, delegate, thread_handle);
+}
+
+// static
+bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate,
+ PlatformThreadHandle* thread_handle,
+ ThreadPriority priority) {
+ bool result = Create(stack_size, delegate, thread_handle);
+ if (result)
+ SetThreadPriority(*thread_handle, priority);
+ return result;
+}
+
+// static
+bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) {
+ return CreateThreadInternal(stack_size, delegate, NULL);
+}
+
+// static
+void PlatformThread::Join(PlatformThreadHandle thread_handle) {
+ DCHECK(thread_handle);
+ // TODO(willchan): Enable this check once I can get it to work for Windows
+ // shutdown.
+ // Joining another thread may block the current thread for a long time, since
+ // the thread referred to by |thread_handle| may still be running long-lived /
+ // blocking tasks.
+#if 0
+ base::ThreadRestrictions::AssertIOAllowed();
+#endif
+
+ // Wait for the thread to exit. It should already have terminated but make
+ // sure this assumption is valid.
+ DWORD result = WaitForSingleObject(thread_handle, INFINITE);
+ if (result != WAIT_OBJECT_0) {
+ // Debug info for bug 127931.
+ DWORD error = GetLastError();
+ debug::Alias(&error);
+ debug::Alias(&result);
+ debug::Alias(&thread_handle);
+ CHECK(false);
+ }
+
+ CloseHandle(thread_handle);
+}
+
+// static
+void PlatformThread::SetThreadPriority(PlatformThreadHandle handle,
+ ThreadPriority priority) {
+ switch (priority) {
+ case kThreadPriority_Normal:
+ ::SetThreadPriority(handle, THREAD_PRIORITY_NORMAL);
+ break;
+ case kThreadPriority_RealtimeAudio:
+ ::SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL);
+ break;
+ }
+}
+
+} // namespace base
diff --git a/src/base/threading/post_task_and_reply_impl.cc b/src/base/threading/post_task_and_reply_impl.cc
new file mode 100644
index 0000000..192198b
--- /dev/null
+++ b/src/base/threading/post_task_and_reply_impl.cc
@@ -0,0 +1,90 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/post_task_and_reply_impl.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/message_loop_proxy.h"
+
+namespace base {
+
+namespace {
+
+// This relay class remembers the MessageLoop that it was created on, and
+// ensures that both the |task| and |reply| Closures are deleted on this same
+// thread. Also, |task| is guaranteed to be deleted before |reply| is run or
+// deleted.
+//
+// If this is not possible because the originating MessageLoop is no longer
+// available, the the |task| and |reply| Closures are leaked. Leaking is
+// considered preferable to having a thread-safetey violations caused by
+// invoking the Closure destructor on the wrong thread.
+class PostTaskAndReplyRelay {
+ public:
+ PostTaskAndReplyRelay(const tracked_objects::Location& from_here,
+ const Closure& task, const Closure& reply)
+ : from_here_(from_here),
+ origin_loop_(MessageLoopProxy::current()) {
+ task_ = task;
+ reply_ = reply;
+ }
+
+ ~PostTaskAndReplyRelay() {
+ DCHECK(origin_loop_->BelongsToCurrentThread());
+ task_.Reset();
+ reply_.Reset();
+ }
+
+ void Run() {
+ task_.Run();
+ origin_loop_->PostTask(
+ from_here_,
+ Bind(&PostTaskAndReplyRelay::RunReplyAndSelfDestruct,
+ base::Unretained(this)));
+ }
+
+ private:
+ void RunReplyAndSelfDestruct() {
+ DCHECK(origin_loop_->BelongsToCurrentThread());
+
+ // Force |task_| to be released before |reply_| is to ensure that no one
+ // accidentally depends on |task_| keeping one of its arguments alive while
+ // |reply_| is executing.
+ task_.Reset();
+
+ reply_.Run();
+
+ // Cue mission impossible theme.
+ delete this;
+ }
+
+ tracked_objects::Location from_here_;
+ scoped_refptr<MessageLoopProxy> origin_loop_;
+ Closure reply_;
+ Closure task_;
+};
+
+} // namespace
+
+namespace internal {
+
+bool PostTaskAndReplyImpl::PostTaskAndReply(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ const Closure& reply) {
+ PostTaskAndReplyRelay* relay =
+ new PostTaskAndReplyRelay(from_here, task, reply);
+ if (!PostTask(from_here, Bind(&PostTaskAndReplyRelay::Run,
+ Unretained(relay)))) {
+ delete relay;
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace internal
+
+} // namespace base
diff --git a/src/base/threading/post_task_and_reply_impl.h b/src/base/threading/post_task_and_reply_impl.h
new file mode 100644
index 0000000..076a46d
--- /dev/null
+++ b/src/base/threading/post_task_and_reply_impl.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains the implementation shared by
+// MessageLoopProxy::PostTaskAndReply and WorkerPool::PostTaskAndReply.
+
+#ifndef BASE_THREADING_POST_TASK_AND_REPLY_IMPL_H_
+#define BASE_THREADING_POST_TASK_AND_REPLY_IMPL_H_
+
+#include "base/callback_forward.h"
+#include "base/location.h"
+
+namespace base {
+namespace internal {
+
+// Inherit from this in a class that implements PostTask appropriately
+// for sending to a destination thread.
+//
+// Note that 'reply' will always get posted back to your current
+// MessageLoop.
+//
+// If you're looking for a concrete implementation of
+// PostTaskAndReply, you probably want base::MessageLoopProxy, or you
+// may want base::WorkerPool.
+class PostTaskAndReplyImpl {
+ public:
+ // Implementation for MessageLoopProxy::PostTaskAndReply and
+ // WorkerPool::PostTaskAndReply.
+ bool PostTaskAndReply(const tracked_objects::Location& from_here,
+ const Closure& task,
+ const Closure& reply);
+
+ private:
+ virtual bool PostTask(const tracked_objects::Location& from_here,
+ const Closure& task) = 0;
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_THREADING_POST_TASK_AND_REPLY_IMPL_H_
diff --git a/src/base/threading/sequenced_worker_pool.cc b/src/base/threading/sequenced_worker_pool.cc
new file mode 100644
index 0000000..56f908b
--- /dev/null
+++ b/src/base/threading/sequenced_worker_pool.cc
@@ -0,0 +1,1122 @@
+// Copyright (c) 2012 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/threading/sequenced_worker_pool.h"
+
+#include <list>
+#include <map>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/atomicops.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/critical_closure.h"
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "base/memory/linked_ptr.h"
+#include "base/message_loop_proxy.h"
+#include "base/metrics/histogram.h"
+#include "base/stl_util.h"
+#include "base/stringprintf.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/simple_thread.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time.h"
+#include "base/tracked_objects.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_nsautorelease_pool.h"
+#endif
+
+namespace base {
+
+namespace {
+
+struct SequencedTask : public TrackingInfo {
+ SequencedTask()
+ : sequence_token_id(0),
+ trace_id(0),
+ sequence_task_number(0),
+ shutdown_behavior(SequencedWorkerPool::BLOCK_SHUTDOWN) {}
+
+ explicit SequencedTask(const tracked_objects::Location& from_here)
+ : base::TrackingInfo(from_here, TimeTicks()),
+ sequence_token_id(0),
+ trace_id(0),
+ sequence_task_number(0),
+ shutdown_behavior(SequencedWorkerPool::BLOCK_SHUTDOWN) {}
+
+ ~SequencedTask() {}
+
+ int sequence_token_id;
+ int trace_id;
+ int64 sequence_task_number;
+ SequencedWorkerPool::WorkerShutdown shutdown_behavior;
+ tracked_objects::Location posted_from;
+ Closure task;
+
+ // Non-delayed tasks and delayed tasks are managed together by time-to-run
+ // order. We calculate the time by adding the posted time and the given delay.
+ TimeTicks time_to_run;
+};
+
+struct SequencedTaskLessThan {
+ public:
+ bool operator()(const SequencedTask& lhs, const SequencedTask& rhs) const {
+ if (lhs.time_to_run < rhs.time_to_run)
+ return true;
+
+ if (lhs.time_to_run > rhs.time_to_run)
+ return false;
+
+ // If the time happen to match, then we use the sequence number to decide.
+ return lhs.sequence_task_number < rhs.sequence_task_number;
+ }
+};
+
+// SequencedWorkerPoolTaskRunner ---------------------------------------------
+// A TaskRunner which posts tasks to a SequencedWorkerPool with a
+// fixed ShutdownBehavior.
+//
+// Note that this class is RefCountedThreadSafe (inherited from TaskRunner).
+class SequencedWorkerPoolTaskRunner : public TaskRunner {
+ public:
+ SequencedWorkerPoolTaskRunner(
+ const scoped_refptr<SequencedWorkerPool>& pool,
+ SequencedWorkerPool::WorkerShutdown shutdown_behavior);
+
+ // TaskRunner implementation
+ virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) OVERRIDE;
+ virtual bool RunsTasksOnCurrentThread() const OVERRIDE;
+
+ private:
+ virtual ~SequencedWorkerPoolTaskRunner();
+
+ const scoped_refptr<SequencedWorkerPool> pool_;
+
+ const SequencedWorkerPool::WorkerShutdown shutdown_behavior_;
+
+ DISALLOW_COPY_AND_ASSIGN(SequencedWorkerPoolTaskRunner);
+};
+
+SequencedWorkerPoolTaskRunner::SequencedWorkerPoolTaskRunner(
+ const scoped_refptr<SequencedWorkerPool>& pool,
+ SequencedWorkerPool::WorkerShutdown shutdown_behavior)
+ : pool_(pool),
+ shutdown_behavior_(shutdown_behavior) {
+}
+
+SequencedWorkerPoolTaskRunner::~SequencedWorkerPoolTaskRunner() {
+}
+
+bool SequencedWorkerPoolTaskRunner::PostDelayedTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) {
+ if (delay == TimeDelta()) {
+ return pool_->PostWorkerTaskWithShutdownBehavior(
+ from_here, task, shutdown_behavior_);
+ }
+ return pool_->PostDelayedWorkerTask(from_here, task, delay);
+}
+
+bool SequencedWorkerPoolTaskRunner::RunsTasksOnCurrentThread() const {
+ return pool_->RunsTasksOnCurrentThread();
+}
+
+// SequencedWorkerPoolSequencedTaskRunner ------------------------------------
+// A SequencedTaskRunner which posts tasks to a SequencedWorkerPool with a
+// fixed sequence token.
+//
+// Note that this class is RefCountedThreadSafe (inherited from TaskRunner).
+class SequencedWorkerPoolSequencedTaskRunner : public SequencedTaskRunner {
+ public:
+ SequencedWorkerPoolSequencedTaskRunner(
+ const scoped_refptr<SequencedWorkerPool>& pool,
+ SequencedWorkerPool::SequenceToken token,
+ SequencedWorkerPool::WorkerShutdown shutdown_behavior);
+
+ // TaskRunner implementation
+ virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) OVERRIDE;
+ virtual bool RunsTasksOnCurrentThread() const OVERRIDE;
+
+ // SequencedTaskRunner implementation
+ virtual bool PostNonNestableDelayedTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) OVERRIDE;
+
+ private:
+ virtual ~SequencedWorkerPoolSequencedTaskRunner();
+
+ const scoped_refptr<SequencedWorkerPool> pool_;
+
+ const SequencedWorkerPool::SequenceToken token_;
+
+ const SequencedWorkerPool::WorkerShutdown shutdown_behavior_;
+
+ DISALLOW_COPY_AND_ASSIGN(SequencedWorkerPoolSequencedTaskRunner);
+};
+
+SequencedWorkerPoolSequencedTaskRunner::SequencedWorkerPoolSequencedTaskRunner(
+ const scoped_refptr<SequencedWorkerPool>& pool,
+ SequencedWorkerPool::SequenceToken token,
+ SequencedWorkerPool::WorkerShutdown shutdown_behavior)
+ : pool_(pool),
+ token_(token),
+ shutdown_behavior_(shutdown_behavior) {
+}
+
+SequencedWorkerPoolSequencedTaskRunner::
+~SequencedWorkerPoolSequencedTaskRunner() {
+}
+
+bool SequencedWorkerPoolSequencedTaskRunner::PostDelayedTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) {
+ if (delay == TimeDelta()) {
+ return pool_->PostSequencedWorkerTaskWithShutdownBehavior(
+ token_, from_here, task, shutdown_behavior_);
+ }
+ return pool_->PostDelayedSequencedWorkerTask(token_, from_here, task, delay);
+}
+
+bool SequencedWorkerPoolSequencedTaskRunner::RunsTasksOnCurrentThread() const {
+ return pool_->IsRunningSequenceOnCurrentThread(token_);
+}
+
+bool SequencedWorkerPoolSequencedTaskRunner::PostNonNestableDelayedTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) {
+ // There's no way to run nested tasks, so simply forward to
+ // PostDelayedTask.
+ return PostDelayedTask(from_here, task, delay);
+}
+
+// Create a process-wide unique ID to represent this task in trace events. This
+// will be mangled with a Process ID hash to reduce the likelyhood of colliding
+// with MessageLoop pointers on other processes.
+uint64 GetTaskTraceID(const SequencedTask& task,
+ void* pool) {
+ return (static_cast<uint64>(task.trace_id) << 32) |
+ static_cast<uint64>(reinterpret_cast<intptr_t>(pool));
+}
+
+} // namespace
+
+// Worker ---------------------------------------------------------------------
+
+class SequencedWorkerPool::Worker : public SimpleThread {
+ public:
+ // Hold a (cyclic) ref to |worker_pool|, since we want to keep it
+ // around as long as we are running.
+ Worker(const scoped_refptr<SequencedWorkerPool>& worker_pool,
+ int thread_number,
+ const std::string& thread_name_prefix);
+ virtual ~Worker();
+
+ // SimpleThread implementation. This actually runs the background thread.
+ virtual void Run() OVERRIDE;
+
+ void set_running_sequence(SequenceToken token) {
+ running_sequence_ = token;
+ }
+
+ SequenceToken running_sequence() const {
+ return running_sequence_;
+ }
+
+ private:
+ scoped_refptr<SequencedWorkerPool> worker_pool_;
+ SequenceToken running_sequence_;
+
+ DISALLOW_COPY_AND_ASSIGN(Worker);
+};
+
+// Inner ----------------------------------------------------------------------
+
+class SequencedWorkerPool::Inner {
+ public:
+ // Take a raw pointer to |worker| to avoid cycles (since we're owned
+ // by it).
+ Inner(SequencedWorkerPool* worker_pool, size_t max_threads,
+ const std::string& thread_name_prefix,
+ TestingObserver* observer);
+
+ ~Inner();
+
+ SequenceToken GetSequenceToken();
+
+ SequenceToken GetNamedSequenceToken(const std::string& name);
+
+ // This function accepts a name and an ID. If the name is null, the
+ // token ID is used. This allows us to implement the optional name lookup
+ // from a single function without having to enter the lock a separate time.
+ bool PostTask(const std::string* optional_token_name,
+ SequenceToken sequence_token,
+ WorkerShutdown shutdown_behavior,
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay);
+
+ bool RunsTasksOnCurrentThread() const;
+
+ bool IsRunningSequenceOnCurrentThread(SequenceToken sequence_token) const;
+
+ void FlushForTesting();
+
+ void SignalHasWorkForTesting();
+
+ int GetWorkSignalCountForTesting() const;
+
+ void Shutdown();
+
+ // Runs the worker loop on the background thread.
+ void ThreadLoop(Worker* this_worker);
+
+ private:
+ enum GetWorkStatus {
+ GET_WORK_FOUND,
+ GET_WORK_NOT_FOUND,
+ GET_WORK_WAIT,
+ };
+
+ // Returns whether there are no more pending tasks and all threads
+ // are idle. Must be called under lock.
+ bool IsIdle() const;
+
+ // Called from within the lock, this converts the given token name into a
+ // token ID, creating a new one if necessary.
+ int LockedGetNamedTokenID(const std::string& name);
+
+ // Called from within the lock, this returns the next sequence task number.
+ int64 LockedGetNextSequenceTaskNumber();
+
+ // Gets new task. There are 3 cases depending on the return value:
+ //
+ // 1) If the return value is |GET_WORK_FOUND|, |task| is filled in and should
+ // be run immediately.
+ // 2) If the return value is |GET_WORK_NOT_FOUND|, there are no tasks to run,
+ // and |task| is not filled in. In this case, the caller should wait until
+ // a task is posted.
+ // 3) If the return value is |GET_WORK_WAIT|, there are no tasks to run
+ // immediately, and |task| is not filled in. Likewise, |wait_time| is
+ // filled in the time to wait until the next task to run. In this case, the
+ // caller should wait the time.
+ //
+ // In any case, the calling code should clear the given
+ // delete_these_outside_lock vector the next time the lock is released.
+ // See the implementation for a more detailed description.
+ GetWorkStatus GetWork(SequencedTask* task,
+ TimeDelta* wait_time,
+ std::vector<Closure>* delete_these_outside_lock);
+
+ // Peforms init and cleanup around running the given task. WillRun...
+ // returns the value from PrepareToStartAdditionalThreadIfNecessary.
+ // The calling code should call FinishStartingAdditionalThread once the
+ // lock is released if the return values is nonzero.
+ int WillRunWorkerTask(const SequencedTask& task);
+ void DidRunWorkerTask(const SequencedTask& task);
+
+ // Returns true if there are no threads currently running the given
+ // sequence token.
+ bool IsSequenceTokenRunnable(int sequence_token_id) const;
+
+ // Checks if all threads are busy and the addition of one more could run an
+ // additional task waiting in the queue. This must be called from within
+ // the lock.
+ //
+ // If another thread is helpful, this will mark the thread as being in the
+ // process of starting and returns the index of the new thread which will be
+ // 0 or more. The caller should then call FinishStartingAdditionalThread to
+ // complete initialization once the lock is released.
+ //
+ // If another thread is not necessary, returne 0;
+ //
+ // See the implementedion for more.
+ int PrepareToStartAdditionalThreadIfHelpful();
+
+ // The second part of thread creation after
+ // PrepareToStartAdditionalThreadIfHelpful with the thread number it
+ // generated. This actually creates the thread and should be called outside
+ // the lock to avoid blocking important work starting a thread in the lock.
+ void FinishStartingAdditionalThread(int thread_number);
+
+ // Signal |has_work_| and increment |has_work_signal_count_|.
+ void SignalHasWork();
+
+ // Checks whether there is work left that's blocking shutdown. Must be
+ // called inside the lock.
+ bool CanShutdown() const;
+
+ SequencedWorkerPool* const worker_pool_;
+
+ // The last sequence number used. Managed by GetSequenceToken, since this
+ // only does threadsafe increment operations, you do not need to hold the
+ // lock.
+ volatile subtle::Atomic32 last_sequence_number_;
+
+ // This lock protects |everything in this class|. Do not read or modify
+ // anything without holding this lock. Do not block while holding this
+ // lock.
+ mutable Lock lock_;
+
+ // Condition variable that is waited on by worker threads until new
+ // tasks are posted or shutdown starts.
+ ConditionVariable has_work_cv_;
+
+ // Condition variable that is waited on by non-worker threads (in
+ // FlushForTesting()) until IsIdle() goes to true.
+ ConditionVariable is_idle_cv_;
+
+ // Condition variable that is waited on by non-worker threads (in
+ // Shutdown()) until CanShutdown() goes to true.
+ ConditionVariable can_shutdown_cv_;
+
+ // The maximum number of worker threads we'll create.
+ const size_t max_threads_;
+
+ const std::string thread_name_prefix_;
+
+ // Associates all known sequence token names with their IDs.
+ std::map<std::string, int> named_sequence_tokens_;
+
+ // Owning pointers to all threads we've created so far, indexed by
+ // ID. Since we lazily create threads, this may be less than
+ // max_threads_ and will be initially empty.
+ typedef std::map<PlatformThreadId, linked_ptr<Worker> > ThreadMap;
+ ThreadMap threads_;
+
+ // Set to true when we're in the process of creating another thread.
+ // See PrepareToStartAdditionalThreadIfHelpful for more.
+ bool thread_being_created_;
+
+ // Number of threads currently waiting for work.
+ size_t waiting_thread_count_;
+
+ // Number of threads currently running tasks that have the BLOCK_SHUTDOWN
+ // or SKIP_ON_SHUTDOWN flag set.
+ size_t blocking_shutdown_thread_count_;
+
+ // A set of all pending tasks in time-to-run order. These are tasks that are
+ // either waiting for a thread to run on, waiting for their time to run,
+ // or blocked on a previous task in their sequence. We have to iterate over
+ // the tasks by time-to-run order, so we use the set instead of the
+ // traditional priority_queue.
+ typedef std::set<SequencedTask, SequencedTaskLessThan> PendingTaskSet;
+ PendingTaskSet pending_tasks_;
+
+ // The next sequence number for a new sequenced task.
+ int64 next_sequence_task_number_;
+
+ // Number of tasks in the pending_tasks_ list that are marked as blocking
+ // shutdown.
+ size_t blocking_shutdown_pending_task_count_;
+
+ // Lists all sequence tokens currently executing.
+ std::set<int> current_sequences_;
+
+ // An ID for each posted task to distinguish the task from others in traces.
+ int trace_id_;
+
+ // Set when Shutdown is called and no further tasks should be
+ // allowed, though we may still be running existing tasks.
+ bool shutdown_called_;
+
+ TestingObserver* const testing_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(Inner);
+};
+
+// Worker definitions ---------------------------------------------------------
+
+SequencedWorkerPool::Worker::Worker(
+ const scoped_refptr<SequencedWorkerPool>& worker_pool,
+ int thread_number,
+ const std::string& prefix)
+ : SimpleThread(
+ prefix + StringPrintf("Worker%d", thread_number).c_str()),
+ worker_pool_(worker_pool) {
+ Start();
+}
+
+SequencedWorkerPool::Worker::~Worker() {
+}
+
+void SequencedWorkerPool::Worker::Run() {
+ // Just jump back to the Inner object to run the thread, since it has all the
+ // tracking information and queues. It might be more natural to implement
+ // using DelegateSimpleThread and have Inner implement the Delegate to avoid
+ // having these worker objects at all, but that method lacks the ability to
+ // send thread-specific information easily to the thread loop.
+ worker_pool_->inner_->ThreadLoop(this);
+ // Release our cyclic reference once we're done.
+ worker_pool_ = NULL;
+}
+
+// Inner definitions ---------------------------------------------------------
+
+SequencedWorkerPool::Inner::Inner(
+ SequencedWorkerPool* worker_pool,
+ size_t max_threads,
+ const std::string& thread_name_prefix,
+ TestingObserver* observer)
+ : worker_pool_(worker_pool),
+ last_sequence_number_(0),
+ lock_(),
+ has_work_cv_(&lock_),
+ is_idle_cv_(&lock_),
+ can_shutdown_cv_(&lock_),
+ max_threads_(max_threads),
+ thread_name_prefix_(thread_name_prefix),
+ thread_being_created_(false),
+ waiting_thread_count_(0),
+ blocking_shutdown_thread_count_(0),
+ next_sequence_task_number_(0),
+ blocking_shutdown_pending_task_count_(0),
+ trace_id_(0),
+ shutdown_called_(false),
+ testing_observer_(observer) {}
+
+SequencedWorkerPool::Inner::~Inner() {
+ // You must call Shutdown() before destroying the pool.
+ DCHECK(shutdown_called_);
+
+ // Need to explicitly join with the threads before they're destroyed or else
+ // they will be running when our object is half torn down.
+ for (ThreadMap::iterator it = threads_.begin(); it != threads_.end(); ++it)
+ it->second->Join();
+ threads_.clear();
+
+ if (testing_observer_)
+ testing_observer_->OnDestruct();
+}
+
+SequencedWorkerPool::SequenceToken
+SequencedWorkerPool::Inner::GetSequenceToken() {
+ subtle::Atomic32 result =
+ subtle::NoBarrier_AtomicIncrement(&last_sequence_number_, 1);
+ return SequenceToken(static_cast<int>(result));
+}
+
+SequencedWorkerPool::SequenceToken
+SequencedWorkerPool::Inner::GetNamedSequenceToken(const std::string& name) {
+ AutoLock lock(lock_);
+ return SequenceToken(LockedGetNamedTokenID(name));
+}
+
+bool SequencedWorkerPool::Inner::PostTask(
+ const std::string* optional_token_name,
+ SequenceToken sequence_token,
+ WorkerShutdown shutdown_behavior,
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) {
+ DCHECK(delay == TimeDelta() || shutdown_behavior == SKIP_ON_SHUTDOWN);
+ SequencedTask sequenced(from_here);
+ sequenced.sequence_token_id = sequence_token.id_;
+ sequenced.shutdown_behavior = shutdown_behavior;
+ sequenced.posted_from = from_here;
+ sequenced.task =
+ shutdown_behavior == BLOCK_SHUTDOWN ?
+ base::MakeCriticalClosure(task) : task;
+ sequenced.time_to_run = TimeTicks::Now() + delay;
+
+ int create_thread_id = 0;
+ {
+ AutoLock lock(lock_);
+ if (shutdown_called_)
+ return false;
+
+ // The trace_id is used for identifying the task in about:tracing.
+ sequenced.trace_id = trace_id_++;
+
+ TRACE_EVENT_FLOW_BEGIN0("task", "SequencedWorkerPool::PostTask",
+ TRACE_ID_MANGLE(GetTaskTraceID(sequenced, static_cast<void*>(this))));
+
+ sequenced.sequence_task_number = LockedGetNextSequenceTaskNumber();
+
+ // Now that we have the lock, apply the named token rules.
+ if (optional_token_name)
+ sequenced.sequence_token_id = LockedGetNamedTokenID(*optional_token_name);
+
+ pending_tasks_.insert(sequenced);
+ if (shutdown_behavior == BLOCK_SHUTDOWN)
+ blocking_shutdown_pending_task_count_++;
+
+ create_thread_id = PrepareToStartAdditionalThreadIfHelpful();
+ }
+
+ // Actually start the additional thread or signal an existing one now that
+ // we're outside the lock.
+ if (create_thread_id)
+ FinishStartingAdditionalThread(create_thread_id);
+ else
+ SignalHasWork();
+
+ return true;
+}
+
+bool SequencedWorkerPool::Inner::RunsTasksOnCurrentThread() const {
+ AutoLock lock(lock_);
+ return ContainsKey(threads_, PlatformThread::CurrentId());
+}
+
+bool SequencedWorkerPool::Inner::IsRunningSequenceOnCurrentThread(
+ SequenceToken sequence_token) const {
+ AutoLock lock(lock_);
+ ThreadMap::const_iterator found = threads_.find(PlatformThread::CurrentId());
+ if (found == threads_.end())
+ return false;
+ return found->second->running_sequence().Equals(sequence_token);
+}
+
+void SequencedWorkerPool::Inner::FlushForTesting() {
+ AutoLock lock(lock_);
+ while (!IsIdle())
+ is_idle_cv_.Wait();
+}
+
+void SequencedWorkerPool::Inner::SignalHasWorkForTesting() {
+ SignalHasWork();
+}
+
+void SequencedWorkerPool::Inner::Shutdown() {
+ // Mark us as terminated and go through and drop all tasks that aren't
+ // required to run on shutdown. Since no new tasks will get posted once the
+ // terminated flag is set, this ensures that all remaining tasks are required
+ // for shutdown whenever the termianted_ flag is set.
+ {
+ AutoLock lock(lock_);
+
+ if (shutdown_called_)
+ return;
+ shutdown_called_ = true;
+
+ // Tickle the threads. This will wake up a waiting one so it will know that
+ // it can exit, which in turn will wake up any other waiting ones.
+ SignalHasWork();
+
+ // There are no pending or running tasks blocking shutdown, we're done.
+ if (CanShutdown())
+ return;
+ }
+
+ // If we're here, then something is blocking shutdown. So wait for
+ // CanShutdown() to go to true.
+
+ if (testing_observer_)
+ testing_observer_->WillWaitForShutdown();
+
+ TimeTicks shutdown_wait_begin = TimeTicks::Now();
+
+ {
+ base::ThreadRestrictions::ScopedAllowWait allow_wait;
+ AutoLock lock(lock_);
+ while (!CanShutdown())
+ can_shutdown_cv_.Wait();
+ }
+ UMA_HISTOGRAM_TIMES("SequencedWorkerPool.ShutdownDelayTime",
+ TimeTicks::Now() - shutdown_wait_begin);
+}
+
+void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) {
+ {
+ AutoLock lock(lock_);
+ DCHECK(thread_being_created_);
+ thread_being_created_ = false;
+ std::pair<ThreadMap::iterator, bool> result =
+ threads_.insert(
+ std::make_pair(this_worker->tid(), make_linked_ptr(this_worker)));
+ DCHECK(result.second);
+
+ while (true) {
+#if defined(OS_MACOSX)
+ base::mac::ScopedNSAutoreleasePool autorelease_pool;
+#endif
+
+ // See GetWork for what delete_these_outside_lock is doing.
+ SequencedTask task;
+ TimeDelta wait_time;
+ std::vector<Closure> delete_these_outside_lock;
+ GetWorkStatus status =
+ GetWork(&task, &wait_time, &delete_these_outside_lock);
+ if (status == GET_WORK_FOUND) {
+ TRACE_EVENT_FLOW_END0("task", "SequencedWorkerPool::PostTask",
+ TRACE_ID_MANGLE(GetTaskTraceID(task, static_cast<void*>(this))));
+ TRACE_EVENT2("task", "SequencedWorkerPool::ThreadLoop",
+ "src_file", task.posted_from.file_name(),
+ "src_func", task.posted_from.function_name());
+ int new_thread_id = WillRunWorkerTask(task);
+ {
+ AutoUnlock unlock(lock_);
+ // There may be more work available, so wake up another
+ // worker thread. (Technically not required, since we
+ // already get a signal for each new task, but it doesn't
+ // hurt.)
+ SignalHasWork();
+ delete_these_outside_lock.clear();
+
+ // Complete thread creation outside the lock if necessary.
+ if (new_thread_id)
+ FinishStartingAdditionalThread(new_thread_id);
+
+ this_worker->set_running_sequence(
+ SequenceToken(task.sequence_token_id));
+
+ tracked_objects::TrackedTime start_time =
+ tracked_objects::ThreadData::NowForStartOfRun(task.birth_tally);
+
+ task.task.Run();
+
+ tracked_objects::ThreadData::TallyRunOnNamedThreadIfTracking(task,
+ start_time, tracked_objects::ThreadData::NowForEndOfRun());
+
+ this_worker->set_running_sequence(SequenceToken());
+
+ // Make sure our task is erased outside the lock for the same reason
+ // we do this with delete_these_oustide_lock.
+ task.task = Closure();
+ }
+ DidRunWorkerTask(task); // Must be done inside the lock.
+ } else {
+ // When we're terminating and there's no more work, we can
+ // shut down. You can't get more tasks posted once
+ // shutdown_called_ is set. There may be some tasks stuck
+ // behind running ones with the same sequence token, but
+ // additional threads won't help this case.
+ if (shutdown_called_ &&
+ blocking_shutdown_pending_task_count_ == 0)
+ break;
+ waiting_thread_count_++;
+ // This is the only time that IsIdle() can go to true.
+ if (IsIdle())
+ is_idle_cv_.Signal();
+
+ switch (status) {
+ case GET_WORK_NOT_FOUND:
+ has_work_cv_.Wait();
+ break;
+ case GET_WORK_WAIT:
+ has_work_cv_.TimedWait(wait_time);
+ break;
+ default:
+ NOTREACHED();
+ }
+ waiting_thread_count_--;
+ }
+ }
+ } // Release lock_.
+
+ // We noticed we should exit. Wake up the next worker so it knows it should
+ // exit as well (because the Shutdown() code only signals once).
+ SignalHasWork();
+
+ // Possibly unblock shutdown.
+ can_shutdown_cv_.Signal();
+}
+
+bool SequencedWorkerPool::Inner::IsIdle() const {
+ lock_.AssertAcquired();
+ return pending_tasks_.empty() && waiting_thread_count_ == threads_.size();
+}
+
+int SequencedWorkerPool::Inner::LockedGetNamedTokenID(
+ const std::string& name) {
+ lock_.AssertAcquired();
+ DCHECK(!name.empty());
+
+ std::map<std::string, int>::const_iterator found =
+ named_sequence_tokens_.find(name);
+ if (found != named_sequence_tokens_.end())
+ return found->second; // Got an existing one.
+
+ // Create a new one for this name.
+ SequenceToken result = GetSequenceToken();
+ named_sequence_tokens_.insert(std::make_pair(name, result.id_));
+ return result.id_;
+}
+
+int64 SequencedWorkerPool::Inner::LockedGetNextSequenceTaskNumber() {
+ lock_.AssertAcquired();
+ // We assume that we never create enough tasks to wrap around.
+ return next_sequence_task_number_++;
+}
+
+SequencedWorkerPool::Inner::GetWorkStatus SequencedWorkerPool::Inner::GetWork(
+ SequencedTask* task,
+ TimeDelta* wait_time,
+ std::vector<Closure>* delete_these_outside_lock) {
+ lock_.AssertAcquired();
+
+ UMA_HISTOGRAM_COUNTS_100("SequencedWorkerPool.TaskCount",
+ static_cast<int>(pending_tasks_.size()));
+
+ // Find the next task with a sequence token that's not currently in use.
+ // If the token is in use, that means another thread is running something
+ // in that sequence, and we can't run it without going out-of-order.
+ //
+ // This algorithm is simple and fair, but inefficient in some cases. For
+ // example, say somebody schedules 1000 slow tasks with the same sequence
+ // number. We'll have to go through all those tasks each time we feel like
+ // there might be work to schedule. If this proves to be a problem, we
+ // should make this more efficient.
+ //
+ // One possible enhancement would be to keep a map from sequence ID to a
+ // list of pending but currently blocked SequencedTasks for that ID.
+ // When a worker finishes a task of one sequence token, it can pick up the
+ // next one from that token right away.
+ //
+ // This may lead to starvation if there are sufficient numbers of sequences
+ // in use. To alleviate this, we could add an incrementing priority counter
+ // to each SequencedTask. Then maintain a priority_queue of all runnable
+ // tasks, sorted by priority counter. When a sequenced task is completed
+ // we would pop the head element off of that tasks pending list and add it
+ // to the priority queue. Then we would run the first item in the priority
+ // queue.
+
+ GetWorkStatus status = GET_WORK_NOT_FOUND;
+ int unrunnable_tasks = 0;
+ PendingTaskSet::iterator i = pending_tasks_.begin();
+ // We assume that the loop below doesn't take too long and so we can just do
+ // a single call to TimeTicks::Now().
+ const TimeTicks current_time = TimeTicks::Now();
+ while (i != pending_tasks_.end()) {
+ if (!IsSequenceTokenRunnable(i->sequence_token_id)) {
+ unrunnable_tasks++;
+ ++i;
+ continue;
+ }
+
+ if (i->time_to_run > current_time) {
+ // The time to run has not come yet.
+ *wait_time = i->time_to_run - current_time;
+ status = GET_WORK_WAIT;
+ break;
+ }
+
+ if (shutdown_called_ && i->shutdown_behavior != BLOCK_SHUTDOWN) {
+ // We're shutting down and the task we just found isn't blocking
+ // shutdown. Delete it and get more work.
+ //
+ // Note that we do not want to delete unrunnable tasks. Deleting a task
+ // can have side effects (like freeing some objects) and deleting a
+ // task that's supposed to run after one that's currently running could
+ // cause an obscure crash.
+ //
+ // We really want to delete these tasks outside the lock in case the
+ // closures are holding refs to objects that want to post work from
+ // their destructorss (which would deadlock). The closures are
+ // internally refcounted, so we just need to keep a copy of them alive
+ // until the lock is exited. The calling code can just clear() the
+ // vector they passed to us once the lock is exited to make this
+ // happen.
+ delete_these_outside_lock->push_back(i->task);
+ pending_tasks_.erase(i++);
+ } else {
+ // Found a runnable task.
+ *task = *i;
+ pending_tasks_.erase(i);
+ if (task->shutdown_behavior == BLOCK_SHUTDOWN) {
+ blocking_shutdown_pending_task_count_--;
+ }
+
+ status = GET_WORK_FOUND;
+ break;
+ }
+ }
+
+ // Track the number of tasks we had to skip over to see if we should be
+ // making this more efficient. If this number ever becomes large or is
+ // frequently "some", we should consider the optimization above.
+ UMA_HISTOGRAM_COUNTS_100("SequencedWorkerPool.UnrunnableTaskCount",
+ unrunnable_tasks);
+ return status;
+}
+
+int SequencedWorkerPool::Inner::WillRunWorkerTask(const SequencedTask& task) {
+ lock_.AssertAcquired();
+
+ // Mark the task's sequence number as in use.
+ if (task.sequence_token_id)
+ current_sequences_.insert(task.sequence_token_id);
+
+ // Ensure that threads running tasks posted with either SKIP_ON_SHUTDOWN
+ // or BLOCK_SHUTDOWN will prevent shutdown until that task or thread
+ // completes.
+ if (task.shutdown_behavior != CONTINUE_ON_SHUTDOWN)
+ blocking_shutdown_thread_count_++;
+
+ // We just picked up a task. Since StartAdditionalThreadIfHelpful only
+ // creates a new thread if there is no free one, there is a race when posting
+ // tasks that many tasks could have been posted before a thread started
+ // running them, so only one thread would have been created. So we also check
+ // whether we should create more threads after removing our task from the
+ // queue, which also has the nice side effect of creating the workers from
+ // background threads rather than the main thread of the app.
+ //
+ // If another thread wasn't created, we want to wake up an existing thread
+ // if there is one waiting to pick up the next task.
+ //
+ // Note that we really need to do this *before* running the task, not
+ // after. Otherwise, if more than one task is posted, the creation of the
+ // second thread (since we only create one at a time) will be blocked by
+ // the execution of the first task, which could be arbitrarily long.
+ return PrepareToStartAdditionalThreadIfHelpful();
+}
+
+void SequencedWorkerPool::Inner::DidRunWorkerTask(const SequencedTask& task) {
+ lock_.AssertAcquired();
+
+ if (task.shutdown_behavior != CONTINUE_ON_SHUTDOWN) {
+ DCHECK_GT(blocking_shutdown_thread_count_, 0u);
+ blocking_shutdown_thread_count_--;
+ }
+
+ if (task.sequence_token_id)
+ current_sequences_.erase(task.sequence_token_id);
+}
+
+bool SequencedWorkerPool::Inner::IsSequenceTokenRunnable(
+ int sequence_token_id) const {
+ lock_.AssertAcquired();
+ return !sequence_token_id ||
+ current_sequences_.find(sequence_token_id) ==
+ current_sequences_.end();
+}
+
+int SequencedWorkerPool::Inner::PrepareToStartAdditionalThreadIfHelpful() {
+ lock_.AssertAcquired();
+ // How thread creation works:
+ //
+ // We'de like to avoid creating threads with the lock held. However, we
+ // need to be sure that we have an accurate accounting of the threads for
+ // proper Joining and deltion on shutdown.
+ //
+ // We need to figure out if we need another thread with the lock held, which
+ // is what this function does. It then marks us as in the process of creating
+ // a thread. When we do shutdown, we wait until the thread_being_created_
+ // flag is cleared, which ensures that the new thread is properly added to
+ // all the data structures and we can't leak it. Once shutdown starts, we'll
+ // refuse to create more threads or they would be leaked.
+ //
+ // Note that this creates a mostly benign race condition on shutdown that
+ // will cause fewer workers to be created than one would expect. It isn't
+ // much of an issue in real life, but affects some tests. Since we only spawn
+ // one worker at a time, the following sequence of events can happen:
+ //
+ // 1. Main thread posts a bunch of unrelated tasks that would normally be
+ // run on separate threads.
+ // 2. The first task post causes us to start a worker. Other tasks do not
+ // cause a worker to start since one is pending.
+ // 3. Main thread initiates shutdown.
+ // 4. No more threads are created since the shutdown_called_ flag is set.
+ //
+ // The result is that one may expect that max_threads_ workers to be created
+ // given the workload, but in reality fewer may be created because the
+ // sequence of thread creation on the background threads is racing with the
+ // shutdown call.
+ if (!shutdown_called_ &&
+ !thread_being_created_ &&
+ threads_.size() < max_threads_ &&
+ waiting_thread_count_ == 0) {
+ // We could use an additional thread if there's work to be done.
+ for (PendingTaskSet::const_iterator i = pending_tasks_.begin();
+ i != pending_tasks_.end(); ++i) {
+ if (IsSequenceTokenRunnable(i->sequence_token_id)) {
+ // Found a runnable task, mark the thread as being started.
+ thread_being_created_ = true;
+ return static_cast<int>(threads_.size() + 1);
+ }
+ }
+ }
+ return 0;
+}
+
+void SequencedWorkerPool::Inner::FinishStartingAdditionalThread(
+ int thread_number) {
+ // Called outside of the lock.
+ DCHECK(thread_number > 0);
+
+ // The worker is assigned to the list when the thread actually starts, which
+ // will manage the memory of the pointer.
+ new Worker(worker_pool_, thread_number, thread_name_prefix_);
+}
+
+void SequencedWorkerPool::Inner::SignalHasWork() {
+ has_work_cv_.Signal();
+ if (testing_observer_) {
+ testing_observer_->OnHasWork();
+ }
+}
+
+bool SequencedWorkerPool::Inner::CanShutdown() const {
+ lock_.AssertAcquired();
+ // See PrepareToStartAdditionalThreadIfHelpful for how thread creation works.
+ return !thread_being_created_ &&
+ blocking_shutdown_thread_count_ == 0 &&
+ blocking_shutdown_pending_task_count_ == 0;
+}
+
+// SequencedWorkerPool --------------------------------------------------------
+
+SequencedWorkerPool::SequencedWorkerPool(
+ size_t max_threads,
+ const std::string& thread_name_prefix)
+ : constructor_message_loop_(MessageLoopProxy::current()),
+ inner_(new Inner(ALLOW_THIS_IN_INITIALIZER_LIST(this),
+ max_threads, thread_name_prefix, NULL)) {
+}
+
+SequencedWorkerPool::SequencedWorkerPool(
+ size_t max_threads,
+ const std::string& thread_name_prefix,
+ TestingObserver* observer)
+ : constructor_message_loop_(MessageLoopProxy::current()),
+ inner_(new Inner(ALLOW_THIS_IN_INITIALIZER_LIST(this),
+ max_threads, thread_name_prefix, observer)) {
+}
+
+SequencedWorkerPool::~SequencedWorkerPool() {}
+
+void SequencedWorkerPool::OnDestruct() const {
+ DCHECK(constructor_message_loop_.get());
+ // Avoid deleting ourselves on a worker thread (which would
+ // deadlock).
+ if (RunsTasksOnCurrentThread()) {
+ constructor_message_loop_->DeleteSoon(FROM_HERE, this);
+ } else {
+ delete this;
+ }
+}
+
+SequencedWorkerPool::SequenceToken SequencedWorkerPool::GetSequenceToken() {
+ return inner_->GetSequenceToken();
+}
+
+SequencedWorkerPool::SequenceToken SequencedWorkerPool::GetNamedSequenceToken(
+ const std::string& name) {
+ return inner_->GetNamedSequenceToken(name);
+}
+
+scoped_refptr<SequencedTaskRunner> SequencedWorkerPool::GetSequencedTaskRunner(
+ SequenceToken token) {
+ return GetSequencedTaskRunnerWithShutdownBehavior(token, BLOCK_SHUTDOWN);
+}
+
+scoped_refptr<SequencedTaskRunner>
+SequencedWorkerPool::GetSequencedTaskRunnerWithShutdownBehavior(
+ SequenceToken token, WorkerShutdown shutdown_behavior) {
+ return new SequencedWorkerPoolSequencedTaskRunner(
+ this, token, shutdown_behavior);
+}
+
+scoped_refptr<TaskRunner>
+SequencedWorkerPool::GetTaskRunnerWithShutdownBehavior(
+ WorkerShutdown shutdown_behavior) {
+ return new SequencedWorkerPoolTaskRunner(this, shutdown_behavior);
+}
+
+bool SequencedWorkerPool::PostWorkerTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task) {
+ return inner_->PostTask(NULL, SequenceToken(), BLOCK_SHUTDOWN,
+ from_here, task, TimeDelta());
+}
+
+bool SequencedWorkerPool::PostDelayedWorkerTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) {
+ WorkerShutdown shutdown_behavior =
+ delay == TimeDelta() ? BLOCK_SHUTDOWN : SKIP_ON_SHUTDOWN;
+ return inner_->PostTask(NULL, SequenceToken(), shutdown_behavior,
+ from_here, task, delay);
+}
+
+bool SequencedWorkerPool::PostWorkerTaskWithShutdownBehavior(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ WorkerShutdown shutdown_behavior) {
+ return inner_->PostTask(NULL, SequenceToken(), shutdown_behavior,
+ from_here, task, TimeDelta());
+}
+
+bool SequencedWorkerPool::PostSequencedWorkerTask(
+ SequenceToken sequence_token,
+ const tracked_objects::Location& from_here,
+ const Closure& task) {
+ return inner_->PostTask(NULL, sequence_token, BLOCK_SHUTDOWN,
+ from_here, task, TimeDelta());
+}
+
+bool SequencedWorkerPool::PostDelayedSequencedWorkerTask(
+ SequenceToken sequence_token,
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) {
+ WorkerShutdown shutdown_behavior =
+ delay == TimeDelta() ? BLOCK_SHUTDOWN : SKIP_ON_SHUTDOWN;
+ return inner_->PostTask(NULL, sequence_token, shutdown_behavior,
+ from_here, task, delay);
+}
+
+bool SequencedWorkerPool::PostNamedSequencedWorkerTask(
+ const std::string& token_name,
+ const tracked_objects::Location& from_here,
+ const Closure& task) {
+ DCHECK(!token_name.empty());
+ return inner_->PostTask(&token_name, SequenceToken(), BLOCK_SHUTDOWN,
+ from_here, task, TimeDelta());
+}
+
+bool SequencedWorkerPool::PostSequencedWorkerTaskWithShutdownBehavior(
+ SequenceToken sequence_token,
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ WorkerShutdown shutdown_behavior) {
+ return inner_->PostTask(NULL, sequence_token, shutdown_behavior,
+ from_here, task, TimeDelta());
+}
+
+bool SequencedWorkerPool::PostDelayedTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) {
+ return PostDelayedWorkerTask(from_here, task, delay);
+}
+
+bool SequencedWorkerPool::RunsTasksOnCurrentThread() const {
+ return inner_->RunsTasksOnCurrentThread();
+}
+
+bool SequencedWorkerPool::IsRunningSequenceOnCurrentThread(
+ SequenceToken sequence_token) const {
+ return inner_->IsRunningSequenceOnCurrentThread(sequence_token);
+}
+
+void SequencedWorkerPool::FlushForTesting() {
+ inner_->FlushForTesting();
+}
+
+void SequencedWorkerPool::SignalHasWorkForTesting() {
+ inner_->SignalHasWorkForTesting();
+}
+
+void SequencedWorkerPool::Shutdown() {
+ DCHECK(constructor_message_loop_->BelongsToCurrentThread());
+ inner_->Shutdown();
+}
+
+} // namespace base
diff --git a/src/base/threading/sequenced_worker_pool.h b/src/base/threading/sequenced_worker_pool.h
new file mode 100644
index 0000000..1a26a4a
--- /dev/null
+++ b/src/base/threading/sequenced_worker_pool.h
@@ -0,0 +1,336 @@
+// Copyright (c) 2012 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_THREADING_SEQUENCED_WORKER_POOL_H_
+#define BASE_THREADING_SEQUENCED_WORKER_POOL_H_
+
+#include <cstddef>
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/task_runner.h"
+#include "base/sequenced_task_runner.h"
+
+namespace tracked_objects {
+class Location;
+} // namespace tracked_objects
+
+namespace base {
+
+class MessageLoopProxy;
+
+template <class T> class DeleteHelper;
+
+class SequencedTaskRunner;
+
+// A worker thread pool that enforces ordering between sets of tasks. It also
+// allows you to specify what should happen to your tasks on shutdown.
+//
+// To enforce ordering, get a unique sequence token from the pool and post all
+// tasks you want to order with the token. All tasks with the same token are
+// guaranteed to execute serially, though not necessarily on the same thread.
+// This means that:
+//
+// - No two tasks with the same token will run at the same time.
+//
+// - Given two tasks T1 and T2 with the same token such that T2 will
+// run after T1, then T2 will start after T1 is destroyed.
+//
+// - If T2 will run after T1, then all memory changes in T1 and T1's
+// destruction will be visible to T2.
+//
+// Example:
+// SequencedWorkerPool::SequenceToken token = pool.GetSequenceToken();
+// pool.PostSequencedWorkerTask(token, SequencedWorkerPool::SKIP_ON_SHUTDOWN,
+// FROM_HERE, base::Bind(...));
+// pool.PostSequencedWorkerTask(token, SequencedWorkerPool::SKIP_ON_SHUTDOWN,
+// FROM_HERE, base::Bind(...));
+//
+// You can make named sequence tokens to make it easier to share a token
+// across different components.
+//
+// You can also post tasks to the pool without ordering using PostWorkerTask.
+// These will be executed in an unspecified order. The order of execution
+// between tasks with different sequence tokens is also unspecified.
+//
+// This class is designed to be leaked on shutdown to allow the
+// CONTINUE_ON_SHUTDOWN behavior to be implemented. To enforce the
+// BLOCK_SHUTDOWN behavior, you must call Shutdown() which will wait until
+// the necessary tasks have completed.
+//
+// Implementation note: This does not use a base::WorkerPool since that does
+// not enforce shutdown semantics or allow us to specify how many worker
+// threads to run. For the typical use case of random background work, we don't
+// necessarily want to be super aggressive about creating threads.
+//
+// Note that SequencedWorkerPool is RefCountedThreadSafe (inherited
+// from TaskRunner).
+class BASE_EXPORT SequencedWorkerPool : public TaskRunner {
+ public:
+ // Defines what should happen to a task posted to the worker pool on
+ // shutdown.
+ enum WorkerShutdown {
+ // Tasks posted with this mode which have not run at shutdown will be
+ // deleted rather than run, and any tasks with this mode running at
+ // shutdown will be ignored (the worker thread will not be joined).
+ //
+ // This option provides a nice way to post stuff you don't want blocking
+ // shutdown. For example, you might be doing a slow DNS lookup and if it's
+ // blocked on the OS, you may not want to stop shutdown, since the result
+ // doesn't really matter at that point.
+ //
+ // However, you need to be very careful what you do in your callback when
+ // you use this option. Since the thread will continue to run until the OS
+ // terminates the process, the app can be in the process of tearing down
+ // when you're running. This means any singletons or global objects you
+ // use may suddenly become invalid out from under you. For this reason,
+ // it's best to use this only for slow but simple operations like the DNS
+ // example.
+ CONTINUE_ON_SHUTDOWN,
+
+ // Tasks posted with this mode that have not started executing at
+ // shutdown will be deleted rather than executed. However, any tasks that
+ // have already begun executing when shutdown is called will be allowed
+ // to continue, and will block shutdown until completion.
+ //
+ // Note: Because Shutdown() may block while these tasks are executing,
+ // care must be taken to ensure that they do not block on the thread that
+ // called Shutdown(), as this may lead to deadlock.
+ SKIP_ON_SHUTDOWN,
+
+ // Tasks posted with this mode will block shutdown until they're
+ // executed. Since this can have significant performance implications,
+ // use sparingly.
+ //
+ // Generally, this should be used only for user data, for example, a task
+ // writing a preference file.
+ //
+ // If a task is posted during shutdown, it will not get run since the
+ // workers may already be stopped. In this case, the post operation will
+ // fail (return false) and the task will be deleted.
+ BLOCK_SHUTDOWN,
+ };
+
+ private:
+ class Inner;
+
+ public:
+ // Opaque identifier that defines sequencing of tasks posted to the worker
+ // pool.
+ class SequenceToken {
+ public:
+ SequenceToken() : id_(0) {}
+ ~SequenceToken() {}
+
+ bool Equals(const SequenceToken& other) const {
+ return id_ == other.id_;
+ }
+
+ private:
+ friend class SequencedWorkerPool;
+
+ explicit SequenceToken(int id) : id_(id) {}
+
+ int id_;
+ };
+
+ // Allows tests to perform certain actions.
+ class TestingObserver {
+ public:
+ virtual ~TestingObserver() {}
+ virtual void OnHasWork() = 0;
+ virtual void WillWaitForShutdown() = 0;
+ virtual void OnDestruct() = 0;
+ };
+
+ // When constructing a SequencedWorkerPool, there must be a
+ // MessageLoop on the current thread unless you plan to deliberately
+ // leak it.
+
+ // Pass the maximum number of threads (they will be lazily created as needed)
+ // and a prefix for the thread name to aid in debugging.
+ SequencedWorkerPool(size_t max_threads,
+ const std::string& thread_name_prefix);
+
+ // Like above, but with |observer| for testing. Does not take
+ // ownership of |observer|.
+ SequencedWorkerPool(size_t max_threads,
+ const std::string& thread_name_prefix,
+ TestingObserver* observer);
+
+ // Returns a unique token that can be used to sequence tasks posted to
+ // PostSequencedWorkerTask(). Valid tokens are alwys nonzero.
+ SequenceToken GetSequenceToken();
+
+ // Returns the sequence token associated with the given name. Calling this
+ // function multiple times with the same string will always produce the
+ // same sequence token. If the name has not been used before, a new token
+ // will be created.
+ SequenceToken GetNamedSequenceToken(const std::string& name);
+
+ // Returns a SequencedTaskRunner wrapper which posts to this
+ // SequencedWorkerPool using the given sequence token. Tasks with nonzero
+ // delay are posted with SKIP_ON_SHUTDOWN behavior and tasks with zero delay
+ // are posted with BLOCK_SHUTDOWN behavior.
+ scoped_refptr<SequencedTaskRunner> GetSequencedTaskRunner(
+ SequenceToken token);
+
+ // Returns a SequencedTaskRunner wrapper which posts to this
+ // SequencedWorkerPool using the given sequence token. Tasks with nonzero
+ // delay are posted with SKIP_ON_SHUTDOWN behavior and tasks with zero delay
+ // are posted with the given shutdown behavior.
+ scoped_refptr<SequencedTaskRunner> GetSequencedTaskRunnerWithShutdownBehavior(
+ SequenceToken token,
+ WorkerShutdown shutdown_behavior);
+
+ // Returns a TaskRunner wrapper which posts to this SequencedWorkerPool using
+ // the given shutdown behavior. Tasks with nonzero delay are posted with
+ // SKIP_ON_SHUTDOWN behavior and tasks with zero delay are posted with the
+ // given shutdown behavior.
+ scoped_refptr<TaskRunner> GetTaskRunnerWithShutdownBehavior(
+ WorkerShutdown shutdown_behavior);
+
+ // Posts the given task for execution in the worker pool. Tasks posted with
+ // this function will execute in an unspecified order on a background thread.
+ // Returns true if the task was posted. If your tasks have ordering
+ // requirements, see PostSequencedWorkerTask().
+ //
+ // This class will attempt to delete tasks that aren't run
+ // (non-block-shutdown semantics) but can't guarantee that this happens. If
+ // all worker threads are busy running CONTINUE_ON_SHUTDOWN tasks, there
+ // will be no workers available to delete these tasks. And there may be
+ // tasks with the same sequence token behind those CONTINUE_ON_SHUTDOWN
+ // tasks. Deleting those tasks before the previous one has completed could
+ // cause nondeterministic crashes because the task could be keeping some
+ // objects alive which do work in their destructor, which could voilate the
+ // assumptions of the running task.
+ //
+ // The task will be guaranteed to run to completion before shutdown
+ // (BLOCK_SHUTDOWN semantics).
+ //
+ // Returns true if the task was posted successfully. This may fail during
+ // shutdown regardless of the specified ShutdownBehavior.
+ bool PostWorkerTask(const tracked_objects::Location& from_here,
+ const Closure& task);
+
+ // Same as PostWorkerTask but allows a delay to be specified (although doing
+ // so changes the shutdown behavior). The task will be run after the given
+ // delay has elapsed.
+ //
+ // If the delay is nonzero, the task won't be guaranteed to run to completion
+ // before shutdown (SKIP_ON_SHUTDOWN semantics) to avoid shutdown hangs.
+ // If the delay is zero, this behaves exactly like PostWorkerTask, i.e. the
+ // task will be guaranteed to run to completion before shutdown
+ // (BLOCK_SHUTDOWN semantics).
+ bool PostDelayedWorkerTask(const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay);
+
+ // Same as PostWorkerTask but allows specification of the shutdown behavior.
+ bool PostWorkerTaskWithShutdownBehavior(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ WorkerShutdown shutdown_behavior);
+
+ // Like PostWorkerTask above, but provides sequencing semantics. This means
+ // that tasks posted with the same sequence token (see GetSequenceToken())
+ // are guaranteed to execute in order. This is useful in cases where you're
+ // doing operations that may depend on previous ones, like appending to a
+ // file.
+ //
+ // The task will be guaranteed to run to completion before shutdown
+ // (BLOCK_SHUTDOWN semantics).
+ //
+ // Returns true if the task was posted successfully. This may fail during
+ // shutdown regardless of the specified ShutdownBehavior.
+ bool PostSequencedWorkerTask(SequenceToken sequence_token,
+ const tracked_objects::Location& from_here,
+ const Closure& task);
+
+ // Like PostSequencedWorkerTask above, but allows you to specify a named
+ // token, which saves an extra call to GetNamedSequenceToken.
+ bool PostNamedSequencedWorkerTask(const std::string& token_name,
+ const tracked_objects::Location& from_here,
+ const Closure& task);
+
+ // Same as PostSequencedWorkerTask but allows a delay to be specified
+ // (although doing so changes the shutdown behavior). The task will be run
+ // after the given delay has elapsed.
+ //
+ // If the delay is nonzero, the task won't be guaranteed to run to completion
+ // before shutdown (SKIP_ON_SHUTDOWN semantics) to avoid shutdown hangs.
+ // If the delay is zero, this behaves exactly like PostSequencedWorkerTask,
+ // i.e. the task will be guaranteed to run to completion before shutdown
+ // (BLOCK_SHUTDOWN semantics).
+ bool PostDelayedSequencedWorkerTask(
+ SequenceToken sequence_token,
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay);
+
+ // Same as PostSequencedWorkerTask but allows specification of the shutdown
+ // behavior.
+ bool PostSequencedWorkerTaskWithShutdownBehavior(
+ SequenceToken sequence_token,
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ WorkerShutdown shutdown_behavior);
+
+ // TaskRunner implementation. Forwards to PostDelayedWorkerTask().
+ virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) OVERRIDE;
+ virtual bool RunsTasksOnCurrentThread() const OVERRIDE;
+
+ // Returns true if the current thread is processing a task with the given
+ // sequence_token.
+ bool IsRunningSequenceOnCurrentThread(SequenceToken sequence_token) const;
+
+ // Blocks until all pending tasks are complete. This should only be called in
+ // unit tests when you want to validate something that should have happened.
+ //
+ // Note that calling this will not prevent other threads from posting work to
+ // the queue while the calling thread is waiting on Flush(). In this case,
+ // Flush will return only when there's no more work in the queue. Normally,
+ // this doesn't come up since in a test, all the work is being posted from
+ // the main thread.
+ void FlushForTesting();
+
+ // Spuriously signal that there is work to be done.
+ void SignalHasWorkForTesting();
+
+ // Implements the worker pool shutdown. This should be called during app
+ // shutdown, and will discard/join with appropriate tasks before returning.
+ // After this call, subsequent calls to post tasks will fail.
+ //
+ // Must be called from the same thread this object was constructed on.
+ void Shutdown();
+
+ protected:
+ virtual ~SequencedWorkerPool();
+
+ virtual void OnDestruct() const OVERRIDE;
+
+ private:
+ friend class RefCountedThreadSafe<SequencedWorkerPool>;
+ friend class DeleteHelper<SequencedWorkerPool>;
+
+ class Worker;
+
+ const scoped_refptr<MessageLoopProxy> constructor_message_loop_;
+
+ // Avoid pulling in too many headers by putting (almost) everything
+ // into |inner_|.
+ const scoped_ptr<Inner> inner_;
+
+ DISALLOW_COPY_AND_ASSIGN(SequencedWorkerPool);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_SEQUENCED_WORKER_POOL_H_
diff --git a/src/base/threading/sequenced_worker_pool_unittest.cc b/src/base/threading/sequenced_worker_pool_unittest.cc
new file mode 100644
index 0000000..2dcda60
--- /dev/null
+++ b/src/base/threading/sequenced_worker_pool_unittest.cc
@@ -0,0 +1,752 @@
+// Copyright (c) 2012 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/threading/sequenced_worker_pool.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/test/sequenced_worker_pool_owner.h"
+#include "base/test/sequenced_task_runner_test_template.h"
+#include "base/test/task_runner_test_template.h"
+#include "base/threading/platform_thread.h"
+#include "base/tracked_objects.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+// IMPORTANT NOTE:
+//
+// Many of these tests have failure modes where they'll hang forever. These
+// tests should not be flaky, and hangling indicates a type of failure. Do not
+// mark as flaky if they're hanging, it's likely an actual bug.
+
+namespace {
+
+const size_t kNumWorkerThreads = 3;
+
+// Allows a number of threads to all be blocked on the same event, and
+// provides a way to unblock a certain number of them.
+class ThreadBlocker {
+ public:
+ ThreadBlocker() : lock_(), cond_var_(&lock_), unblock_counter_(0) {}
+
+ void Block() {
+ {
+ base::AutoLock lock(lock_);
+ while (unblock_counter_ == 0)
+ cond_var_.Wait();
+ unblock_counter_--;
+ }
+ cond_var_.Signal();
+ }
+
+ void Unblock(size_t count) {
+ {
+ base::AutoLock lock(lock_);
+ DCHECK(unblock_counter_ == 0);
+ unblock_counter_ = count;
+ }
+ cond_var_.Signal();
+ }
+
+ private:
+ base::Lock lock_;
+ base::ConditionVariable cond_var_;
+
+ size_t unblock_counter_;
+};
+
+class TestTracker : public base::RefCountedThreadSafe<TestTracker> {
+ public:
+ TestTracker()
+ : lock_(),
+ cond_var_(&lock_),
+ started_events_(0) {
+ }
+
+ // Each of these tasks appends the argument to the complete sequence vector
+ // so calling code can see what order they finished in.
+ void FastTask(int id) {
+ SignalWorkerDone(id);
+ }
+
+ void SlowTask(int id) {
+ base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
+ SignalWorkerDone(id);
+ }
+
+ void BlockTask(int id, ThreadBlocker* blocker) {
+ // Note that this task has started and signal anybody waiting for that
+ // to happen.
+ {
+ base::AutoLock lock(lock_);
+ started_events_++;
+ }
+ cond_var_.Signal();
+
+ blocker->Block();
+ SignalWorkerDone(id);
+ }
+
+ // Waits until the given number of tasks have started executing.
+ void WaitUntilTasksBlocked(size_t count) {
+ {
+ base::AutoLock lock(lock_);
+ while (started_events_ < count)
+ cond_var_.Wait();
+ }
+ cond_var_.Signal();
+ }
+
+ // Blocks the current thread until at least the given number of tasks are in
+ // the completed vector, and then returns a copy.
+ std::vector<int> WaitUntilTasksComplete(size_t num_tasks) {
+ std::vector<int> ret;
+ {
+ base::AutoLock lock(lock_);
+ while (complete_sequence_.size() < num_tasks)
+ cond_var_.Wait();
+ ret = complete_sequence_;
+ }
+ cond_var_.Signal();
+ return ret;
+ }
+
+ void ClearCompleteSequence() {
+ base::AutoLock lock(lock_);
+ complete_sequence_.clear();
+ started_events_ = 0;
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<TestTracker>;
+ ~TestTracker() {}
+
+ void SignalWorkerDone(int id) {
+ {
+ base::AutoLock lock(lock_);
+ complete_sequence_.push_back(id);
+ }
+ cond_var_.Signal();
+ }
+
+ // Protects the complete_sequence.
+ base::Lock lock_;
+
+ base::ConditionVariable cond_var_;
+
+ // Protected by lock_.
+ std::vector<int> complete_sequence_;
+
+ // Counter of the number of "block" workers that have started.
+ size_t started_events_;
+};
+
+class SequencedWorkerPoolTest : public testing::Test {
+ public:
+ SequencedWorkerPoolTest()
+ : pool_owner_(kNumWorkerThreads, "test"),
+ tracker_(new TestTracker) {
+ }
+
+ virtual ~SequencedWorkerPoolTest() {}
+
+ virtual void SetUp() OVERRIDE {}
+
+ virtual void TearDown() OVERRIDE {
+ pool()->Shutdown();
+ }
+
+ const scoped_refptr<SequencedWorkerPool>& pool() {
+ return pool_owner_.pool();
+ }
+ TestTracker* tracker() { return tracker_.get(); }
+
+ void SetWillWaitForShutdownCallback(const Closure& callback) {
+ pool_owner_.SetWillWaitForShutdownCallback(callback);
+ }
+
+ // Ensures that the given number of worker threads is created by adding
+ // tasks and waiting until they complete. Worker thread creation is
+ // serialized, can happen on background threads asynchronously, and doesn't
+ // happen any more at shutdown. This means that if a test posts a bunch of
+ // tasks and calls shutdown, fewer workers will be created than the test may
+ // expect.
+ //
+ // This function ensures that this condition can't happen so tests can make
+ // assumptions about the number of workers active. See the comment in
+ // PrepareToStartAdditionalThreadIfNecessary in the .cc file for more
+ // details.
+ //
+ // It will post tasks to the queue with id -1. It also assumes this is the
+ // first thing called in a test since it will clear the complete_sequence_.
+ void EnsureAllWorkersCreated() {
+ // Create a bunch of threads, all waiting. This will cause that may
+ // workers to be created.
+ ThreadBlocker blocker;
+ for (size_t i = 0; i < kNumWorkerThreads; i++) {
+ pool()->PostWorkerTask(FROM_HERE,
+ base::Bind(&TestTracker::BlockTask,
+ tracker(), -1, &blocker));
+ }
+ tracker()->WaitUntilTasksBlocked(kNumWorkerThreads);
+
+ // Now wake them up and wait until they're done.
+ blocker.Unblock(kNumWorkerThreads);
+ tracker()->WaitUntilTasksComplete(kNumWorkerThreads);
+
+ // Clean up the task IDs we added.
+ tracker()->ClearCompleteSequence();
+ }
+
+ int has_work_call_count() const {
+ return pool_owner_.has_work_call_count();
+ }
+
+ private:
+ MessageLoop message_loop_;
+ SequencedWorkerPoolOwner pool_owner_;
+ const scoped_refptr<TestTracker> tracker_;
+};
+
+// Checks that the given number of entries are in the tasks to complete of
+// the given tracker, and then signals the given event the given number of
+// times. This is used to wakt up blocked background threads before blocking
+// on shutdown.
+void EnsureTasksToCompleteCountAndUnblock(scoped_refptr<TestTracker> tracker,
+ size_t expected_tasks_to_complete,
+ ThreadBlocker* blocker,
+ size_t threads_to_awake) {
+ EXPECT_EQ(
+ expected_tasks_to_complete,
+ tracker->WaitUntilTasksComplete(expected_tasks_to_complete).size());
+
+ blocker->Unblock(threads_to_awake);
+}
+
+// Tests that same-named tokens have the same ID.
+TEST_F(SequencedWorkerPoolTest, NamedTokens) {
+ const std::string name1("hello");
+ SequencedWorkerPool::SequenceToken token1 =
+ pool()->GetNamedSequenceToken(name1);
+
+ SequencedWorkerPool::SequenceToken token2 = pool()->GetSequenceToken();
+
+ const std::string name3("goodbye");
+ SequencedWorkerPool::SequenceToken token3 =
+ pool()->GetNamedSequenceToken(name3);
+
+ // All 3 tokens should be different.
+ EXPECT_FALSE(token1.Equals(token2));
+ EXPECT_FALSE(token1.Equals(token3));
+ EXPECT_FALSE(token2.Equals(token3));
+
+ // Requesting the same name again should give the same value.
+ SequencedWorkerPool::SequenceToken token1again =
+ pool()->GetNamedSequenceToken(name1);
+ EXPECT_TRUE(token1.Equals(token1again));
+
+ SequencedWorkerPool::SequenceToken token3again =
+ pool()->GetNamedSequenceToken(name3);
+ EXPECT_TRUE(token3.Equals(token3again));
+}
+
+// Tests that posting a bunch of tasks (many more than the number of worker
+// threads) runs them all.
+TEST_F(SequencedWorkerPoolTest, LotsOfTasks) {
+ pool()->PostWorkerTask(FROM_HERE,
+ base::Bind(&TestTracker::SlowTask, tracker(), 0));
+
+ const size_t kNumTasks = 20;
+ for (size_t i = 1; i < kNumTasks; i++) {
+ pool()->PostWorkerTask(FROM_HERE,
+ base::Bind(&TestTracker::FastTask, tracker(), i));
+ }
+
+ std::vector<int> result = tracker()->WaitUntilTasksComplete(kNumTasks);
+ EXPECT_EQ(kNumTasks, result.size());
+}
+
+// Tests that posting a bunch of tasks (many more than the number of
+// worker threads) to two pools simultaneously runs them all twice.
+// This test is meant to shake out any concurrency issues between
+// pools (like histograms).
+TEST_F(SequencedWorkerPoolTest, LotsOfTasksTwoPools) {
+ SequencedWorkerPoolOwner pool1(kNumWorkerThreads, "test1");
+ SequencedWorkerPoolOwner pool2(kNumWorkerThreads, "test2");
+
+ base::Closure slow_task = base::Bind(&TestTracker::SlowTask, tracker(), 0);
+ pool1.pool()->PostWorkerTask(FROM_HERE, slow_task);
+ pool2.pool()->PostWorkerTask(FROM_HERE, slow_task);
+
+ const size_t kNumTasks = 20;
+ for (size_t i = 1; i < kNumTasks; i++) {
+ base::Closure fast_task =
+ base::Bind(&TestTracker::FastTask, tracker(), i);
+ pool1.pool()->PostWorkerTask(FROM_HERE, fast_task);
+ pool2.pool()->PostWorkerTask(FROM_HERE, fast_task);
+ }
+
+ std::vector<int> result =
+ tracker()->WaitUntilTasksComplete(2*kNumTasks);
+ EXPECT_EQ(2 * kNumTasks, result.size());
+
+ pool2.pool()->Shutdown();
+ pool1.pool()->Shutdown();
+}
+
+// Test that tasks with the same sequence token are executed in order but don't
+// affect other tasks.
+TEST_F(SequencedWorkerPoolTest, Sequence) {
+ // Fill all the worker threads except one.
+ const size_t kNumBackgroundTasks = kNumWorkerThreads - 1;
+ ThreadBlocker background_blocker;
+ for (size_t i = 0; i < kNumBackgroundTasks; i++) {
+ pool()->PostWorkerTask(FROM_HERE,
+ base::Bind(&TestTracker::BlockTask,
+ tracker(), i, &background_blocker));
+ }
+ tracker()->WaitUntilTasksBlocked(kNumBackgroundTasks);
+
+ // Create two tasks with the same sequence token, one that will block on the
+ // event, and one which will just complete quickly when it's run. Since there
+ // is one worker thread free, the first task will start and then block, and
+ // the second task should be waiting.
+ ThreadBlocker blocker;
+ SequencedWorkerPool::SequenceToken token1 = pool()->GetSequenceToken();
+ pool()->PostSequencedWorkerTask(
+ token1, FROM_HERE,
+ base::Bind(&TestTracker::BlockTask, tracker(), 100, &blocker));
+ pool()->PostSequencedWorkerTask(
+ token1, FROM_HERE,
+ base::Bind(&TestTracker::FastTask, tracker(), 101));
+ EXPECT_EQ(0u, tracker()->WaitUntilTasksComplete(0).size());
+
+ // Create another two tasks as above with a different token. These will be
+ // blocked since there are no slots to run.
+ SequencedWorkerPool::SequenceToken token2 = pool()->GetSequenceToken();
+ pool()->PostSequencedWorkerTask(
+ token2, FROM_HERE,
+ base::Bind(&TestTracker::FastTask, tracker(), 200));
+ pool()->PostSequencedWorkerTask(
+ token2, FROM_HERE,
+ base::Bind(&TestTracker::FastTask, tracker(), 201));
+ EXPECT_EQ(0u, tracker()->WaitUntilTasksComplete(0).size());
+
+ // Let one background task complete. This should then let both tasks of
+ // token2 run to completion in order. The second task of token1 should still
+ // be blocked.
+ background_blocker.Unblock(1);
+ std::vector<int> result = tracker()->WaitUntilTasksComplete(3);
+ ASSERT_EQ(3u, result.size());
+ EXPECT_EQ(200, result[1]);
+ EXPECT_EQ(201, result[2]);
+
+ // Finish the rest of the background tasks. This should leave some workers
+ // free with the second token1 task still blocked on the first.
+ background_blocker.Unblock(kNumBackgroundTasks - 1);
+ EXPECT_EQ(kNumBackgroundTasks + 2,
+ tracker()->WaitUntilTasksComplete(kNumBackgroundTasks + 2).size());
+
+ // Allow the first task of token1 to complete. This should run the second.
+ blocker.Unblock(1);
+ result = tracker()->WaitUntilTasksComplete(kNumBackgroundTasks + 4);
+ ASSERT_EQ(kNumBackgroundTasks + 4, result.size());
+ EXPECT_EQ(100, result[result.size() - 2]);
+ EXPECT_EQ(101, result[result.size() - 1]);
+}
+
+// Tests that any tasks posted after Shutdown are ignored.
+TEST_F(SequencedWorkerPoolTest, IgnoresAfterShutdown) {
+ // Start tasks to take all the threads and block them.
+ EnsureAllWorkersCreated();
+ ThreadBlocker blocker;
+ for (size_t i = 0; i < kNumWorkerThreads; i++) {
+ pool()->PostWorkerTask(FROM_HERE,
+ base::Bind(&TestTracker::BlockTask,
+ tracker(), i, &blocker));
+ }
+ tracker()->WaitUntilTasksBlocked(kNumWorkerThreads);
+
+ // Shutdown the worker pool. This should discard all non-blocking tasks.
+ SetWillWaitForShutdownCallback(
+ base::Bind(&EnsureTasksToCompleteCountAndUnblock,
+ scoped_refptr<TestTracker>(tracker()), 0,
+ &blocker, kNumWorkerThreads));
+ pool()->Shutdown();
+
+ int old_has_work_call_count = has_work_call_count();
+
+ std::vector<int> result =
+ tracker()->WaitUntilTasksComplete(kNumWorkerThreads);
+
+ // The kNumWorkerThread items should have completed, in no particular
+ // order.
+ ASSERT_EQ(kNumWorkerThreads, result.size());
+ for (size_t i = 0; i < kNumWorkerThreads; i++) {
+ EXPECT_TRUE(std::find(result.begin(), result.end(), static_cast<int>(i)) !=
+ result.end());
+ }
+
+ // No further tasks, regardless of shutdown mode, should be allowed.
+ EXPECT_FALSE(pool()->PostWorkerTaskWithShutdownBehavior(
+ FROM_HERE,
+ base::Bind(&TestTracker::FastTask, tracker(), 100),
+ SequencedWorkerPool::CONTINUE_ON_SHUTDOWN));
+ EXPECT_FALSE(pool()->PostWorkerTaskWithShutdownBehavior(
+ FROM_HERE,
+ base::Bind(&TestTracker::FastTask, tracker(), 101),
+ SequencedWorkerPool::SKIP_ON_SHUTDOWN));
+ EXPECT_FALSE(pool()->PostWorkerTaskWithShutdownBehavior(
+ FROM_HERE,
+ base::Bind(&TestTracker::FastTask, tracker(), 102),
+ SequencedWorkerPool::BLOCK_SHUTDOWN));
+
+ ASSERT_EQ(old_has_work_call_count, has_work_call_count());
+}
+
+// Tests that unrun tasks are discarded properly according to their shutdown
+// mode.
+TEST_F(SequencedWorkerPoolTest, DiscardOnShutdown) {
+ // Start tasks to take all the threads and block them.
+ EnsureAllWorkersCreated();
+ ThreadBlocker blocker;
+ for (size_t i = 0; i < kNumWorkerThreads; i++) {
+ pool()->PostWorkerTask(FROM_HERE,
+ base::Bind(&TestTracker::BlockTask,
+ tracker(), i, &blocker));
+ }
+ tracker()->WaitUntilTasksBlocked(kNumWorkerThreads);
+
+ // Create some tasks with different shutdown modes.
+ pool()->PostWorkerTaskWithShutdownBehavior(
+ FROM_HERE,
+ base::Bind(&TestTracker::FastTask, tracker(), 100),
+ SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
+ pool()->PostWorkerTaskWithShutdownBehavior(
+ FROM_HERE,
+ base::Bind(&TestTracker::FastTask, tracker(), 101),
+ SequencedWorkerPool::SKIP_ON_SHUTDOWN);
+ pool()->PostWorkerTaskWithShutdownBehavior(
+ FROM_HERE,
+ base::Bind(&TestTracker::FastTask, tracker(), 102),
+ SequencedWorkerPool::BLOCK_SHUTDOWN);
+
+ // Shutdown the worker pool. This should discard all non-blocking tasks.
+ SetWillWaitForShutdownCallback(
+ base::Bind(&EnsureTasksToCompleteCountAndUnblock,
+ scoped_refptr<TestTracker>(tracker()), 0,
+ &blocker, kNumWorkerThreads));
+ pool()->Shutdown();
+
+ std::vector<int> result =
+ tracker()->WaitUntilTasksComplete(kNumWorkerThreads + 1);
+
+ // The kNumWorkerThread items should have completed, plus the BLOCK_SHUTDOWN
+ // one, in no particular order.
+ ASSERT_EQ(kNumWorkerThreads + 1, result.size());
+ for (size_t i = 0; i < kNumWorkerThreads; i++) {
+ EXPECT_TRUE(std::find(result.begin(), result.end(), static_cast<int>(i)) !=
+ result.end());
+ }
+ EXPECT_TRUE(std::find(result.begin(), result.end(), 102) != result.end());
+}
+
+// Tests that CONTINUE_ON_SHUTDOWN tasks don't block shutdown.
+TEST_F(SequencedWorkerPoolTest, ContinueOnShutdown) {
+ scoped_refptr<TaskRunner> runner(pool()->GetTaskRunnerWithShutdownBehavior(
+ SequencedWorkerPool::CONTINUE_ON_SHUTDOWN));
+ scoped_refptr<SequencedTaskRunner> sequenced_runner(
+ pool()->GetSequencedTaskRunnerWithShutdownBehavior(
+ pool()->GetSequenceToken(),
+ SequencedWorkerPool::CONTINUE_ON_SHUTDOWN));
+ EnsureAllWorkersCreated();
+ ThreadBlocker blocker;
+ pool()->PostWorkerTaskWithShutdownBehavior(
+ FROM_HERE,
+ base::Bind(&TestTracker::BlockTask,
+ tracker(), 0, &blocker),
+ SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
+ runner->PostTask(
+ FROM_HERE,
+ base::Bind(&TestTracker::BlockTask,
+ tracker(), 1, &blocker));
+ sequenced_runner->PostTask(
+ FROM_HERE,
+ base::Bind(&TestTracker::BlockTask,
+ tracker(), 2, &blocker));
+
+ tracker()->WaitUntilTasksBlocked(3);
+
+ // This should not block. If this test hangs, it means it failed.
+ pool()->Shutdown();
+
+ // The task should not have completed yet.
+ EXPECT_EQ(0u, tracker()->WaitUntilTasksComplete(0).size());
+
+ // Posting more tasks should fail.
+ EXPECT_FALSE(pool()->PostWorkerTaskWithShutdownBehavior(
+ FROM_HERE, base::Bind(&TestTracker::FastTask, tracker(), 0),
+ SequencedWorkerPool::CONTINUE_ON_SHUTDOWN));
+ EXPECT_FALSE(runner->PostTask(
+ FROM_HERE, base::Bind(&TestTracker::FastTask, tracker(), 0)));
+ EXPECT_FALSE(sequenced_runner->PostTask(
+ FROM_HERE, base::Bind(&TestTracker::FastTask, tracker(), 0)));
+
+ // Continue the background thread and make sure the tasks can complete.
+ blocker.Unblock(3);
+ std::vector<int> result = tracker()->WaitUntilTasksComplete(3);
+ EXPECT_EQ(3u, result.size());
+}
+
+// Tests that SKIP_ON_SHUTDOWN tasks that have been started block Shutdown
+// until they stop, but tasks not yet started do not.
+TEST_F(SequencedWorkerPoolTest, SkipOnShutdown) {
+ // Start tasks to take all the threads and block them.
+ EnsureAllWorkersCreated();
+ ThreadBlocker blocker;
+
+ // Now block all the threads with SKIP_ON_SHUTDOWN. Shutdown() should not
+ // return until these tasks have completed.
+ for (size_t i = 0; i < kNumWorkerThreads; i++) {
+ pool()->PostWorkerTaskWithShutdownBehavior(
+ FROM_HERE,
+ base::Bind(&TestTracker::BlockTask, tracker(), i, &blocker),
+ SequencedWorkerPool::SKIP_ON_SHUTDOWN);
+ }
+ tracker()->WaitUntilTasksBlocked(kNumWorkerThreads);
+
+ // Now post an additional task as SKIP_ON_SHUTDOWN, which should not be
+ // executed once Shutdown() has been called.
+ pool()->PostWorkerTaskWithShutdownBehavior(
+ FROM_HERE,
+ base::Bind(&TestTracker::BlockTask,
+ tracker(), 0, &blocker),
+ SequencedWorkerPool::SKIP_ON_SHUTDOWN);
+
+ // This callback will only be invoked if SKIP_ON_SHUTDOWN tasks that have
+ // been started block shutdown.
+ SetWillWaitForShutdownCallback(
+ base::Bind(&EnsureTasksToCompleteCountAndUnblock,
+ scoped_refptr<TestTracker>(tracker()), 0,
+ &blocker, kNumWorkerThreads));
+
+ // No tasks should have completed yet.
+ EXPECT_EQ(0u, tracker()->WaitUntilTasksComplete(0).size());
+
+ // This should not block. If this test hangs, it means it failed.
+ pool()->Shutdown();
+
+ // Shutdown should not return until all of the tasks have completed.
+ std::vector<int> result =
+ tracker()->WaitUntilTasksComplete(kNumWorkerThreads);
+
+ // Only tasks marked SKIP_ON_SHUTDOWN that were already started should be
+ // allowed to complete. No additional non-blocking tasks should have been
+ // started.
+ ASSERT_EQ(kNumWorkerThreads, result.size());
+ for (size_t i = 0; i < kNumWorkerThreads; i++) {
+ EXPECT_TRUE(std::find(result.begin(), result.end(), static_cast<int>(i)) !=
+ result.end());
+ }
+}
+
+// Ensure all worker threads are created, and then trigger a spurious
+// work signal. This shouldn't cause any other work signals to be
+// triggered. This is a regression test for http://crbug.com/117469.
+TEST_F(SequencedWorkerPoolTest, SpuriousWorkSignal) {
+ EnsureAllWorkersCreated();
+ int old_has_work_call_count = has_work_call_count();
+ pool()->SignalHasWorkForTesting();
+ // This is inherently racy, but can only produce false positives.
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
+ EXPECT_EQ(old_has_work_call_count + 1, has_work_call_count());
+}
+
+void IsRunningOnCurrentThreadTask(
+ SequencedWorkerPool::SequenceToken test_positive_token,
+ SequencedWorkerPool::SequenceToken test_negative_token,
+ SequencedWorkerPool* pool,
+ SequencedWorkerPool* unused_pool) {
+ EXPECT_TRUE(pool->RunsTasksOnCurrentThread());
+ EXPECT_TRUE(pool->IsRunningSequenceOnCurrentThread(test_positive_token));
+ EXPECT_FALSE(pool->IsRunningSequenceOnCurrentThread(test_negative_token));
+ EXPECT_FALSE(unused_pool->RunsTasksOnCurrentThread());
+ EXPECT_FALSE(
+ unused_pool->IsRunningSequenceOnCurrentThread(test_positive_token));
+ EXPECT_FALSE(
+ unused_pool->IsRunningSequenceOnCurrentThread(test_negative_token));
+}
+
+// Verify correctness of the IsRunningSequenceOnCurrentThread method.
+TEST_F(SequencedWorkerPoolTest, IsRunningOnCurrentThread) {
+ SequencedWorkerPool::SequenceToken token1 = pool()->GetSequenceToken();
+ SequencedWorkerPool::SequenceToken token2 = pool()->GetSequenceToken();
+ SequencedWorkerPool::SequenceToken unsequenced_token;
+
+ scoped_refptr<SequencedWorkerPool> unused_pool =
+ new SequencedWorkerPool(2, "unused_pool");
+ EXPECT_TRUE(token1.Equals(unused_pool->GetSequenceToken()));
+ EXPECT_TRUE(token2.Equals(unused_pool->GetSequenceToken()));
+
+ EXPECT_FALSE(pool()->RunsTasksOnCurrentThread());
+ EXPECT_FALSE(pool()->IsRunningSequenceOnCurrentThread(token1));
+ EXPECT_FALSE(pool()->IsRunningSequenceOnCurrentThread(token2));
+ EXPECT_FALSE(pool()->IsRunningSequenceOnCurrentThread(unsequenced_token));
+ EXPECT_FALSE(unused_pool->RunsTasksOnCurrentThread());
+ EXPECT_FALSE(unused_pool->IsRunningSequenceOnCurrentThread(token1));
+ EXPECT_FALSE(unused_pool->IsRunningSequenceOnCurrentThread(token2));
+ EXPECT_FALSE(
+ unused_pool->IsRunningSequenceOnCurrentThread(unsequenced_token));
+
+ pool()->PostSequencedWorkerTask(
+ token1, FROM_HERE,
+ base::Bind(&IsRunningOnCurrentThreadTask,
+ token1, token2, pool(), unused_pool));
+ pool()->PostSequencedWorkerTask(
+ token2, FROM_HERE,
+ base::Bind(&IsRunningOnCurrentThreadTask,
+ token2, unsequenced_token, pool(), unused_pool));
+ pool()->PostWorkerTask(
+ FROM_HERE,
+ base::Bind(&IsRunningOnCurrentThreadTask,
+ unsequenced_token, token1, pool(), unused_pool));
+ pool()->Shutdown();
+ unused_pool->Shutdown();
+}
+
+class SequencedWorkerPoolTaskRunnerTestDelegate {
+ public:
+ SequencedWorkerPoolTaskRunnerTestDelegate() {}
+
+ ~SequencedWorkerPoolTaskRunnerTestDelegate() {}
+
+ void StartTaskRunner() {
+ pool_owner_.reset(
+ new SequencedWorkerPoolOwner(10, "SequencedWorkerPoolTaskRunnerTest"));
+ }
+
+ scoped_refptr<SequencedWorkerPool> GetTaskRunner() {
+ return pool_owner_->pool();
+ }
+
+ void StopTaskRunner() {
+ // Make sure all tasks (including delayed ones) are run before shutting
+ // down.
+ pool_owner_->pool()->FlushForTesting();
+ pool_owner_->pool()->Shutdown();
+ // Don't reset |pool_owner_| here, as the test may still hold a
+ // reference to the pool.
+ }
+
+ bool TaskRunnerHandlesNonZeroDelays() const {
+ return true;
+ }
+
+ private:
+ MessageLoop message_loop_;
+ scoped_ptr<SequencedWorkerPoolOwner> pool_owner_;
+};
+
+INSTANTIATE_TYPED_TEST_CASE_P(
+ SequencedWorkerPool, TaskRunnerTest,
+ SequencedWorkerPoolTaskRunnerTestDelegate);
+
+class SequencedWorkerPoolTaskRunnerWithShutdownBehaviorTestDelegate {
+ public:
+ SequencedWorkerPoolTaskRunnerWithShutdownBehaviorTestDelegate() {}
+
+ ~SequencedWorkerPoolTaskRunnerWithShutdownBehaviorTestDelegate() {
+ }
+
+ void StartTaskRunner() {
+ pool_owner_.reset(
+ new SequencedWorkerPoolOwner(10, "SequencedWorkerPoolTaskRunnerTest"));
+ task_runner_ = pool_owner_->pool()->GetTaskRunnerWithShutdownBehavior(
+ SequencedWorkerPool::BLOCK_SHUTDOWN);
+ }
+
+ scoped_refptr<TaskRunner> GetTaskRunner() {
+ return task_runner_;
+ }
+
+ void StopTaskRunner() {
+ // Make sure all tasks (including delayed ones) are run before shutting
+ // down.
+ pool_owner_->pool()->FlushForTesting();
+ pool_owner_->pool()->Shutdown();
+ // Don't reset |pool_owner_| here, as the test may still hold a
+ // reference to the pool.
+ }
+
+ bool TaskRunnerHandlesNonZeroDelays() const {
+ return true;
+ }
+
+ private:
+ MessageLoop message_loop_;
+ scoped_ptr<SequencedWorkerPoolOwner> pool_owner_;
+ scoped_refptr<TaskRunner> task_runner_;
+};
+
+INSTANTIATE_TYPED_TEST_CASE_P(
+ SequencedWorkerPoolTaskRunner, TaskRunnerTest,
+ SequencedWorkerPoolTaskRunnerWithShutdownBehaviorTestDelegate);
+
+class SequencedWorkerPoolSequencedTaskRunnerTestDelegate {
+ public:
+ SequencedWorkerPoolSequencedTaskRunnerTestDelegate() {}
+
+ ~SequencedWorkerPoolSequencedTaskRunnerTestDelegate() {
+ }
+
+ void StartTaskRunner() {
+ pool_owner_.reset(new SequencedWorkerPoolOwner(
+ 10, "SequencedWorkerPoolSequencedTaskRunnerTest"));
+ task_runner_ = pool_owner_->pool()->GetSequencedTaskRunner(
+ pool_owner_->pool()->GetSequenceToken());
+ }
+
+ scoped_refptr<SequencedTaskRunner> GetTaskRunner() {
+ return task_runner_;
+ }
+
+ void StopTaskRunner() {
+ // Make sure all tasks (including delayed ones) are run before shutting
+ // down.
+ pool_owner_->pool()->FlushForTesting();
+ pool_owner_->pool()->Shutdown();
+ // Don't reset |pool_owner_| here, as the test may still hold a
+ // reference to the pool.
+ }
+
+ bool TaskRunnerHandlesNonZeroDelays() const {
+ return true;
+ }
+
+ private:
+ MessageLoop message_loop_;
+ scoped_ptr<SequencedWorkerPoolOwner> pool_owner_;
+ scoped_refptr<SequencedTaskRunner> task_runner_;
+};
+
+INSTANTIATE_TYPED_TEST_CASE_P(
+ SequencedWorkerPoolSequencedTaskRunner, TaskRunnerTest,
+ SequencedWorkerPoolSequencedTaskRunnerTestDelegate);
+
+INSTANTIATE_TYPED_TEST_CASE_P(
+ SequencedWorkerPoolSequencedTaskRunner, SequencedTaskRunnerTest,
+ SequencedWorkerPoolSequencedTaskRunnerTestDelegate);
+
+} // namespace
+
+} // namespace base
diff --git a/src/base/threading/simple_thread.cc b/src/base/threading/simple_thread.cc
new file mode 100644
index 0000000..b8d7713
--- /dev/null
+++ b/src/base/threading/simple_thread.cc
@@ -0,0 +1,168 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/simple_thread.h"
+
+#include "base/logging.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/string_number_conversions.h"
+
+namespace base {
+
+SimpleThread::SimpleThread(const std::string& name_prefix)
+ : name_prefix_(name_prefix), name_(name_prefix),
+ thread_(), event_(true, false), tid_(0), joined_(false) {
+}
+
+SimpleThread::SimpleThread(const std::string& name_prefix,
+ const Options& options)
+ : name_prefix_(name_prefix), name_(name_prefix), options_(options),
+ thread_(), event_(true, false), tid_(0), joined_(false) {
+}
+
+SimpleThread::~SimpleThread() {
+ DCHECK(HasBeenStarted()) << "SimpleThread was never started.";
+ DCHECK(HasBeenJoined()) << "SimpleThread destroyed without being Join()ed.";
+}
+
+void SimpleThread::Start() {
+ DCHECK(!HasBeenStarted()) << "Tried to Start a thread multiple times.";
+#if defined(__LB_SHELL__) || defined(OS_STARBOARD)
+ PlatformThread::PlatformThreadOptions platform_options;
+ platform_options.stack_size = options_.stack_size();
+ platform_options.priority = options_.priority();
+ platform_options.affinity = options_.affinity();
+ bool success = PlatformThread::CreateWithOptions(platform_options,
+ this, &thread_);
+#else
+ bool success = PlatformThread::Create(options_.stack_size(), this, &thread_);
+#endif
+ DCHECK(success);
+ base::ThreadRestrictions::ScopedAllowWait allow_wait;
+ event_.Wait(); // Wait for the thread to complete initialization.
+}
+
+void SimpleThread::Join() {
+ DCHECK(HasBeenStarted()) << "Tried to Join a never-started thread.";
+ DCHECK(!HasBeenJoined()) << "Tried to Join a thread multiple times.";
+ PlatformThread::Join(thread_);
+ joined_ = true;
+}
+
+bool SimpleThread::HasBeenStarted() {
+ base::ThreadRestrictions::ScopedAllowWait allow_wait;
+ return event_.IsSignaled();
+}
+
+void SimpleThread::ThreadMain() {
+ tid_ = PlatformThread::CurrentId();
+ // Construct our full name of the form "name_prefix_/TID".
+ name_.push_back('/');
+ name_.append(IntToString(tid_));
+ PlatformThread::SetName(name_.c_str());
+
+ // We've initialized our new thread, signal that we're done to Start().
+ event_.Signal();
+
+ Run();
+}
+
+DelegateSimpleThread::DelegateSimpleThread(Delegate* delegate,
+ const std::string& name_prefix)
+ : SimpleThread(name_prefix),
+ delegate_(delegate) {
+}
+
+DelegateSimpleThread::DelegateSimpleThread(Delegate* delegate,
+ const std::string& name_prefix,
+ const Options& options)
+ : SimpleThread(name_prefix, options),
+ delegate_(delegate) {
+}
+
+DelegateSimpleThread::~DelegateSimpleThread() {
+}
+
+void DelegateSimpleThread::Run() {
+ DCHECK(delegate_) << "Tried to call Run without a delegate (called twice?)";
+ delegate_->Run();
+ delegate_ = NULL;
+}
+
+DelegateSimpleThreadPool::DelegateSimpleThreadPool(
+ const std::string& name_prefix,
+ int num_threads)
+ : name_prefix_(name_prefix),
+ num_threads_(num_threads),
+ dry_(true, false) {
+}
+
+DelegateSimpleThreadPool::~DelegateSimpleThreadPool() {
+ DCHECK(threads_.empty());
+ DCHECK(delegates_.empty());
+ DCHECK(!dry_.IsSignaled());
+}
+
+void DelegateSimpleThreadPool::Start() {
+ DCHECK(threads_.empty()) << "Start() called with outstanding threads.";
+ for (int i = 0; i < num_threads_; ++i) {
+ DelegateSimpleThread* thread = new DelegateSimpleThread(this, name_prefix_);
+ thread->Start();
+ threads_.push_back(thread);
+ }
+}
+
+void DelegateSimpleThreadPool::JoinAll() {
+ DCHECK(!threads_.empty()) << "JoinAll() called with no outstanding threads.";
+
+ // Tell all our threads to quit their worker loop.
+ AddWork(NULL, num_threads_);
+
+ // Join and destroy all the worker threads.
+ for (int i = 0; i < num_threads_; ++i) {
+ threads_[i]->Join();
+ delete threads_[i];
+ }
+ threads_.clear();
+ DCHECK(delegates_.empty());
+}
+
+void DelegateSimpleThreadPool::AddWork(Delegate* delegate, int repeat_count) {
+ AutoLock locked(lock_);
+ for (int i = 0; i < repeat_count; ++i)
+ delegates_.push(delegate);
+ // If we were empty, signal that we have work now.
+ if (!dry_.IsSignaled())
+ dry_.Signal();
+}
+
+void DelegateSimpleThreadPool::Run() {
+ Delegate* work = NULL;
+
+ while (true) {
+ dry_.Wait();
+ {
+ AutoLock locked(lock_);
+ if (!dry_.IsSignaled())
+ continue;
+
+ DCHECK(!delegates_.empty());
+ work = delegates_.front();
+ delegates_.pop();
+
+ // Signal to any other threads that we're currently out of work.
+ if (delegates_.empty())
+ dry_.Reset();
+ }
+
+ // A NULL delegate pointer signals us to quit.
+ if (!work)
+ break;
+
+ work->Run();
+ }
+}
+
+} // namespace base
diff --git a/src/base/threading/simple_thread.h b/src/base/threading/simple_thread.h
new file mode 100644
index 0000000..2594b18
--- /dev/null
+++ b/src/base/threading/simple_thread.h
@@ -0,0 +1,210 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// WARNING: You should probably be using Thread (thread.h) instead. Thread is
+// Chrome's message-loop based Thread abstraction, and if you are a
+// thread running in the browser, there will likely be assumptions
+// that your thread will have an associated message loop.
+//
+// This is a simple thread interface that backs to a native operating system
+// thread. You should use this only when you want a thread that does not have
+// an associated MessageLoop. Unittesting is the best example of this.
+//
+// The simplest interface to use is DelegateSimpleThread, which will create
+// a new thread, and execute the Delegate's virtual Run() in this new thread
+// until it has completed, exiting the thread.
+//
+// NOTE: You *MUST* call Join on the thread to clean up the underlying thread
+// resources. You are also responsible for destructing the SimpleThread object.
+// It is invalid to destroy a SimpleThread while it is running, or without
+// Start() having been called (and a thread never created). The Delegate
+// object should live as long as a DelegateSimpleThread.
+//
+// Thread Safety: A SimpleThread is not completely thread safe. It is safe to
+// access it from the creating thread or from the newly created thread. This
+// implies that the creator thread should be the thread that calls Join.
+//
+// Example:
+// class MyThreadRunner : public DelegateSimpleThread::Delegate { ... };
+// MyThreadRunner runner;
+// DelegateSimpleThread thread(&runner, "good_name_here");
+// thread.Start();
+// // Start will return after the Thread has been successfully started and
+// // initialized. The newly created thread will invoke runner->Run(), and
+// // run until it returns.
+// thread.Join(); // Wait until the thread has exited. You *MUST* Join!
+// // The SimpleThread object is still valid, however you may not call Join
+// // or Start again.
+
+#ifndef BASE_THREADING_SIMPLE_THREAD_H_
+#define BASE_THREADING_SIMPLE_THREAD_H_
+
+#include <string>
+#include <queue>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/threading/platform_thread.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+
+namespace base {
+
+// This is the base SimpleThread. You can derive from it and implement the
+// virtual Run method, or you can use the DelegateSimpleThread interface.
+class BASE_EXPORT SimpleThread : public PlatformThread::Delegate {
+ public:
+ class BASE_EXPORT Options {
+ public:
+ Options() : stack_size_(0), priority_(kThreadPriority_Default),
+ affinity_(kNoThreadAffinity) { }
+ Options(size_t stack_size, ThreadPriority priority)
+ : stack_size_(stack_size), priority_(priority),
+ affinity_(kNoThreadAffinity) { }
+ Options(size_t stack_size, ThreadPriority priority, ThreadAffinity affinity)
+ : stack_size_(stack_size), priority_(priority), affinity_(affinity) { }
+ ~Options() { }
+
+ // We use the standard compiler-supplied copy constructor.
+
+ // A custom stack size, or 0 for the system default.
+ void set_stack_size(size_t size) { stack_size_ = size; }
+ size_t stack_size() const { return stack_size_; }
+
+ // Thread priority
+ void set_priority(ThreadPriority priority) { priority_ = priority; }
+ ThreadPriority priority() const { return priority_; }
+
+ // Core affinity
+ void set_affinity(ThreadAffinity affinity) { affinity_ = affinity; }
+ ThreadAffinity affinity() const { return affinity_; }
+
+ private:
+ size_t stack_size_;
+ ThreadPriority priority_;
+ ThreadAffinity affinity_;
+ };
+
+ // Create a SimpleThread. |options| should be used to manage any specific
+ // configuration involving the thread creation and management.
+ // Every thread has a name, in the form of |name_prefix|/TID, for example
+ // "my_thread/321". The thread will not be created until Start() is called.
+ explicit SimpleThread(const std::string& name_prefix);
+ SimpleThread(const std::string& name_prefix, const Options& options);
+
+ virtual ~SimpleThread();
+
+ virtual void Start();
+ virtual void Join();
+
+ // Subclasses should override the Run method.
+ virtual void Run() = 0;
+
+ // Return the thread name prefix, or "unnamed" if none was supplied.
+ std::string name_prefix() { return name_prefix_; }
+
+ // Return the completed name including TID, only valid after Start().
+ std::string name() { return name_; }
+
+ // The native thread handle.
+ PlatformThreadHandle thread_handle() { return thread_; }
+
+ // Return the thread id, only valid after Start().
+ PlatformThreadId tid() { return tid_; }
+
+ // Return True if Start() has ever been called.
+ bool HasBeenStarted();
+
+ // Return True if Join() has evern been called.
+ bool HasBeenJoined() { return joined_; }
+
+ // Overridden from PlatformThread::Delegate:
+ virtual void ThreadMain() OVERRIDE;
+
+ // Only set priorities with a careful understanding of the consequences.
+ // This is meant for very limited use cases.
+ void SetThreadPriority(ThreadPriority priority) {
+ PlatformThread::SetThreadPriority(thread_, priority);
+ }
+
+ private:
+ const std::string name_prefix_;
+ std::string name_;
+ const Options options_;
+ PlatformThreadHandle thread_; // PlatformThread handle, invalid after Join!
+ WaitableEvent event_; // Signaled if Start() was ever called.
+ PlatformThreadId tid_; // The backing thread's id.
+ bool joined_; // True if Join has been called.
+};
+
+class BASE_EXPORT DelegateSimpleThread : public SimpleThread {
+ public:
+ class BASE_EXPORT Delegate {
+ public:
+ Delegate() { }
+ virtual ~Delegate() { }
+ virtual void Run() = 0;
+ };
+
+ DelegateSimpleThread(Delegate* delegate,
+ const std::string& name_prefix);
+ DelegateSimpleThread(Delegate* delegate,
+ const std::string& name_prefix,
+ const Options& options);
+
+ virtual ~DelegateSimpleThread();
+ virtual void Run() OVERRIDE;
+ private:
+ Delegate* delegate_;
+};
+
+// DelegateSimpleThreadPool allows you to start up a fixed number of threads,
+// and then add jobs which will be dispatched to the threads. This is
+// convenient when you have a lot of small work that you want done
+// multi-threaded, but don't want to spawn a thread for each small bit of work.
+//
+// You just call AddWork() to add a delegate to the list of work to be done.
+// JoinAll() will make sure that all outstanding work is processed, and wait
+// for everything to finish. You can reuse a pool, so you can call Start()
+// again after you've called JoinAll().
+class BASE_EXPORT DelegateSimpleThreadPool
+ : public DelegateSimpleThread::Delegate {
+ public:
+ typedef DelegateSimpleThread::Delegate Delegate;
+
+ DelegateSimpleThreadPool(const std::string& name_prefix, int num_threads);
+ virtual ~DelegateSimpleThreadPool();
+
+ // Start up all of the underlying threads, and start processing work if we
+ // have any.
+ void Start();
+
+ // Make sure all outstanding work is finished, and wait for and destroy all
+ // of the underlying threads in the pool.
+ void JoinAll();
+
+ // It is safe to AddWork() any time, before or after Start().
+ // Delegate* should always be a valid pointer, NULL is reserved internally.
+ void AddWork(Delegate* work, int repeat_count);
+ void AddWork(Delegate* work) {
+ AddWork(work, 1);
+ }
+
+ // We implement the Delegate interface, for running our internal threads.
+ virtual void Run() OVERRIDE;
+
+ private:
+ const std::string name_prefix_;
+ int num_threads_;
+ std::vector<DelegateSimpleThread*> threads_;
+ std::queue<Delegate*> delegates_;
+ base::Lock lock_; // Locks delegates_
+ WaitableEvent dry_; // Not signaled when there is no work to do.
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_SIMPLE_THREAD_H_
diff --git a/src/base/threading/simple_thread_unittest.cc b/src/base/threading/simple_thread_unittest.cc
new file mode 100644
index 0000000..169e2d7
--- /dev/null
+++ b/src/base/threading/simple_thread_unittest.cc
@@ -0,0 +1,170 @@
+// Copyright (c) 2012 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/atomic_sequence_num.h"
+#include "base/string_number_conversions.h"
+#include "base/threading/simple_thread.h"
+#include "base/synchronization/waitable_event.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class SetIntRunner : public DelegateSimpleThread::Delegate {
+ public:
+ SetIntRunner(int* ptr, int val) : ptr_(ptr), val_(val) { }
+ virtual ~SetIntRunner() { }
+
+ virtual void Run() OVERRIDE {
+ *ptr_ = val_;
+ }
+
+ private:
+ int* ptr_;
+ int val_;
+};
+
+class WaitEventRunner : public DelegateSimpleThread::Delegate {
+ public:
+ explicit WaitEventRunner(WaitableEvent* event) : event_(event) { }
+ virtual ~WaitEventRunner() { }
+
+ virtual void Run() OVERRIDE {
+ EXPECT_FALSE(event_->IsSignaled());
+ event_->Signal();
+ EXPECT_TRUE(event_->IsSignaled());
+ }
+ private:
+ WaitableEvent* event_;
+};
+
+class SeqRunner : public DelegateSimpleThread::Delegate {
+ public:
+ explicit SeqRunner(AtomicSequenceNumber* seq) : seq_(seq) { }
+ virtual void Run() OVERRIDE {
+ seq_->GetNext();
+ }
+
+ private:
+ AtomicSequenceNumber* seq_;
+};
+
+// We count up on a sequence number, firing on the event when we've hit our
+// expected amount, otherwise we wait on the event. This will ensure that we
+// have all threads outstanding until we hit our expected thread pool size.
+class VerifyPoolRunner : public DelegateSimpleThread::Delegate {
+ public:
+ VerifyPoolRunner(AtomicSequenceNumber* seq,
+ int total, WaitableEvent* event)
+ : seq_(seq), total_(total), event_(event) { }
+
+ virtual void Run() OVERRIDE {
+ if (seq_->GetNext() == total_) {
+ event_->Signal();
+ } else {
+ event_->Wait();
+ }
+ }
+
+ private:
+ AtomicSequenceNumber* seq_;
+ int total_;
+ WaitableEvent* event_;
+};
+
+} // namespace
+
+TEST(SimpleThreadTest, CreateAndJoin) {
+ int stack_int = 0;
+
+ SetIntRunner runner(&stack_int, 7);
+ EXPECT_EQ(0, stack_int);
+
+ DelegateSimpleThread thread(&runner, "int_setter");
+ EXPECT_FALSE(thread.HasBeenStarted());
+ EXPECT_FALSE(thread.HasBeenJoined());
+ EXPECT_EQ(0, stack_int);
+
+ thread.Start();
+ EXPECT_TRUE(thread.HasBeenStarted());
+ EXPECT_FALSE(thread.HasBeenJoined());
+
+ thread.Join();
+ EXPECT_TRUE(thread.HasBeenStarted());
+ EXPECT_TRUE(thread.HasBeenJoined());
+ EXPECT_EQ(7, stack_int);
+}
+
+TEST(SimpleThreadTest, WaitForEvent) {
+ // Create a thread, and wait for it to signal us.
+ WaitableEvent event(true, false);
+
+ WaitEventRunner runner(&event);
+ DelegateSimpleThread thread(&runner, "event_waiter");
+
+ EXPECT_FALSE(event.IsSignaled());
+ thread.Start();
+ event.Wait();
+ EXPECT_TRUE(event.IsSignaled());
+ thread.Join();
+}
+
+TEST(SimpleThreadTest, NamedWithOptions) {
+ WaitableEvent event(true, false);
+
+ WaitEventRunner runner(&event);
+ SimpleThread::Options options;
+ DelegateSimpleThread thread(&runner, "event_waiter", options);
+ EXPECT_EQ(thread.name_prefix(), "event_waiter");
+ EXPECT_FALSE(event.IsSignaled());
+
+ thread.Start();
+ EXPECT_EQ(thread.name_prefix(), "event_waiter");
+ EXPECT_EQ(thread.name(),
+ std::string("event_waiter/") + IntToString(thread.tid()));
+ event.Wait();
+
+ EXPECT_TRUE(event.IsSignaled());
+ thread.Join();
+
+ // We keep the name and tid, even after the thread is gone.
+ EXPECT_EQ(thread.name_prefix(), "event_waiter");
+ EXPECT_EQ(thread.name(),
+ std::string("event_waiter/") + IntToString(thread.tid()));
+}
+
+TEST(SimpleThreadTest, ThreadPool) {
+ AtomicSequenceNumber seq;
+ SeqRunner runner(&seq);
+ DelegateSimpleThreadPool pool("seq_runner", 10);
+
+ // Add work before we're running.
+ pool.AddWork(&runner, 300);
+
+ EXPECT_EQ(seq.GetNext(), 0);
+ pool.Start();
+
+ // Add work while we're running.
+ pool.AddWork(&runner, 300);
+
+ pool.JoinAll();
+
+ EXPECT_EQ(seq.GetNext(), 601);
+
+ // We can reuse our pool. Verify that all 10 threads can actually run in
+ // parallel, so this test will only pass if there are actually 10 threads.
+ AtomicSequenceNumber seq2;
+ WaitableEvent event(true, false);
+ // Changing 9 to 10, for example, would cause us JoinAll() to never return.
+ VerifyPoolRunner verifier(&seq2, 9, &event);
+ pool.Start();
+
+ pool.AddWork(&verifier, 10);
+
+ pool.JoinAll();
+ EXPECT_EQ(seq2.GetNext(), 10);
+}
+
+} // namespace base
diff --git a/src/base/threading/thread.cc b/src/base/threading/thread.cc
new file mode 100644
index 0000000..c940775
--- /dev/null
+++ b/src/base/threading/thread.cc
@@ -0,0 +1,233 @@
+// Copyright (c) 2012 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/threading/thread.h"
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/threading/thread_local.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/synchronization/waitable_event.h"
+
+#if defined(OS_WIN)
+#include "base/win/scoped_com_initializer.h"
+#endif
+
+namespace base {
+
+namespace {
+
+// We use this thread-local variable to record whether or not a thread exited
+// because its Stop method was called. This allows us to catch cases where
+// MessageLoop::Quit() is called directly, which is unexpected when using a
+// Thread to setup and run a MessageLoop.
+base::LazyInstance<base::ThreadLocalBoolean> lazy_tls_bool =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+// This is used to trigger the message loop to exit.
+void ThreadQuitHelper() {
+ MessageLoop::current()->Quit();
+ Thread::SetThreadWasQuitProperly(true);
+}
+
+// Used to pass data to ThreadMain. This structure is allocated on the stack
+// from within StartWithOptions.
+struct Thread::StartupData {
+ // We get away with a const reference here because of how we are allocated.
+ const Thread::Options& options;
+
+ // Used to synchronize thread startup.
+ WaitableEvent event;
+
+ explicit StartupData(const Options& opt)
+ : options(opt),
+ event(false, false) {}
+};
+
+Thread::Thread(const char* name)
+ :
+#if defined(OS_WIN)
+ com_status_(NONE),
+#endif
+ started_(false),
+ stopping_(false),
+ running_(false),
+ startup_data_(NULL),
+ thread_(0),
+ message_loop_(NULL),
+ thread_id_(kInvalidThreadId),
+ name_(name) {
+}
+
+Thread::~Thread() {
+ Stop();
+}
+
+bool Thread::Start() {
+ Options options;
+#if defined(OS_WIN)
+ if (com_status_ == STA)
+ options.message_loop_type = MessageLoop::TYPE_UI;
+#endif
+ return StartWithOptions(options);
+}
+
+bool Thread::StartWithOptions(const Options& options) {
+ DCHECK(!message_loop_);
+#if defined(OS_WIN)
+ DCHECK((com_status_ != STA) ||
+ (options.message_loop_type == MessageLoop::TYPE_UI));
+#endif
+
+#if !defined(COBALT)
+ // BUG. This should not be called here. We are currently executing in the
+ // thread that is creating this thread, so this call incorrectly marks the
+ // calling thread as not being quit properly. Instead, set it in
+ // |ThreadMain|.
+ SetThreadWasQuitProperly(false);
+#endif
+
+ StartupData startup_data(options);
+ startup_data_ = &startup_data;
+
+#if defined(__LB_SHELL__) || defined(OS_STARBOARD)
+ PlatformThread::PlatformThreadOptions platform_options;
+ platform_options.stack_size = options.stack_size;
+ platform_options.priority = options.priority;
+ platform_options.affinity = options.affinity;
+ if (!PlatformThread::CreateWithOptions(platform_options, this, &thread_)) {
+#else
+ if (!PlatformThread::Create(options.stack_size, this, &thread_)) {
+#endif
+ DLOG(ERROR) << "failed to create thread";
+ startup_data_ = NULL;
+ return false;
+ }
+
+ // Wait for the thread to start and initialize message_loop_
+ base::ThreadRestrictions::ScopedAllowWait allow_wait;
+ startup_data.event.Wait();
+
+ // set it to NULL so we don't keep a pointer to some object on the stack.
+ startup_data_ = NULL;
+ started_ = true;
+
+ DCHECK(message_loop_);
+ return true;
+}
+
+void Thread::Stop() {
+ if (!started_)
+ return;
+
+ StopSoon();
+
+ // Wait for the thread to exit.
+ //
+ // TODO(darin): Unfortunately, we need to keep message_loop_ around until
+ // the thread exits. Some consumers are abusing the API. Make them stop.
+ //
+ PlatformThread::Join(thread_);
+
+ // The thread should NULL message_loop_ on exit.
+ DCHECK(!message_loop_);
+
+ // The thread no longer needs to be joined.
+ started_ = false;
+
+ stopping_ = false;
+}
+
+void Thread::StopSoon() {
+ // We should only be called on the same thread that started us.
+
+ // Reading thread_id_ without a lock can lead to a benign data race
+ // with ThreadMain, so we annotate it to stay silent under ThreadSanitizer.
+ DCHECK_NE(ANNOTATE_UNPROTECTED_READ(thread_id_), PlatformThread::CurrentId());
+
+ if (stopping_ || !message_loop_)
+ return;
+
+ stopping_ = true;
+ message_loop_->PostTask(FROM_HERE, base::Bind(&ThreadQuitHelper));
+}
+
+bool Thread::IsRunning() const {
+ return running_;
+}
+
+void Thread::Run(MessageLoop* message_loop) {
+ message_loop->Run();
+}
+
+void Thread::SetThreadWasQuitProperly(bool flag) {
+ lazy_tls_bool.Pointer()->Set(flag);
+}
+
+bool Thread::GetThreadWasQuitProperly() {
+ bool quit_properly = true;
+#ifndef NDEBUG
+ quit_properly = lazy_tls_bool.Pointer()->Get();
+#endif
+ return quit_properly;
+}
+
+void Thread::ThreadMain() {
+ {
+#if defined(COBALT)
+ // It is incorrect to call this in |StartWithOptions| - that would mark the
+ // calling thread as not being quit correctly. Instead, set it here.
+ SetThreadWasQuitProperly(false);
+#endif
+
+ // The message loop for this thread.
+ MessageLoop message_loop(startup_data_->options.message_loop_type);
+
+ // Complete the initialization of our Thread object.
+ thread_id_ = PlatformThread::CurrentId();
+ PlatformThread::SetName(name_.c_str());
+ ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector.
+ message_loop.set_thread_name(name_);
+ message_loop_ = &message_loop;
+
+#if defined(OS_WIN)
+ scoped_ptr<win::ScopedCOMInitializer> com_initializer;
+ if (com_status_ != NONE) {
+ com_initializer.reset((com_status_ == STA) ?
+ new win::ScopedCOMInitializer() :
+ new win::ScopedCOMInitializer(win::ScopedCOMInitializer::kMTA));
+ }
+#endif
+
+ // Let the thread do extra initialization.
+ // Let's do this before signaling we are started.
+ Init();
+
+ running_ = true;
+ startup_data_->event.Signal();
+ // startup_data_ can't be touched anymore since the starting thread is now
+ // unlocked.
+
+ Run(message_loop_);
+ running_ = false;
+
+ // Let the thread do extra cleanup.
+ CleanUp();
+
+#if defined(OS_WIN)
+ com_initializer.reset();
+#endif
+
+ // Assert that MessageLoop::Quit was called by ThreadQuitHelper.
+ DCHECK(GetThreadWasQuitProperly());
+
+ // We can't receive messages anymore.
+ message_loop_ = NULL;
+ }
+}
+
+} // namespace base
diff --git a/src/base/threading/thread.h b/src/base/threading/thread.h
new file mode 100644
index 0000000..d0f89af
--- /dev/null
+++ b/src/base/threading/thread.h
@@ -0,0 +1,228 @@
+// Copyright (c) 2012 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_THREADING_THREAD_H_
+#define BASE_THREADING_THREAD_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+// A simple thread abstraction that establishes a MessageLoop on a new thread.
+// The consumer uses the MessageLoop of the thread to cause code to execute on
+// the thread. When this object is destroyed the thread is terminated. All
+// pending tasks queued on the thread's message loop will run to completion
+// before the thread is terminated.
+//
+// WARNING! SUBCLASSES MUST CALL Stop() IN THEIR DESTRUCTORS! See ~Thread().
+//
+// After the thread is stopped, the destruction sequence is:
+//
+// (1) Thread::CleanUp()
+// (2) MessageLoop::~MessageLoop
+// (3.b) MessageLoop::DestructionObserver::WillDestroyCurrentMessageLoop
+class BASE_EXPORT Thread : PlatformThread::Delegate {
+ public:
+ struct Options {
+ Options() : message_loop_type(MessageLoop::TYPE_DEFAULT), stack_size(0)
+ , priority(kThreadPriority_Default), affinity(kNoThreadAffinity) {}
+ Options(MessageLoop::Type type, size_t size)
+ : message_loop_type(type), stack_size(size)
+ , priority(kThreadPriority_Default), affinity(kNoThreadAffinity) {}
+ Options(MessageLoop::Type type, size_t size, ThreadPriority priority)
+ : message_loop_type(type), stack_size(size), priority(priority)
+ , affinity(kNoThreadAffinity) {}
+ Options(MessageLoop::Type type, size_t size, ThreadPriority priority,
+ ThreadAffinity affinity)
+ : message_loop_type(type), stack_size(size), priority(priority)
+ , affinity(affinity) {}
+
+ // Specifies the type of message loop that will be allocated on the thread.
+ MessageLoop::Type message_loop_type;
+
+ // Specifies the maximum stack size that the thread is allowed to use.
+ // This does not necessarily correspond to the thread's initial stack size.
+ // A value of 0 indicates that the default maximum should be used.
+ size_t stack_size;
+
+ // Specifies the initial thread priority.
+ ThreadPriority priority;
+
+ // Specifies the initial thread core affinity.
+ ThreadAffinity affinity;
+ };
+
+ // Constructor.
+ // name is a display string to identify the thread.
+ explicit Thread(const char* name);
+
+ // Destroys the thread, stopping it if necessary.
+ //
+ // NOTE: ALL SUBCLASSES OF Thread MUST CALL Stop() IN THEIR DESTRUCTORS (or
+ // guarantee Stop() is explicitly called before the subclass is destroyed).
+ // This is required to avoid a data race between the destructor modifying the
+ // vtable, and the thread's ThreadMain calling the virtual method Run(). It
+ // also ensures that the CleanUp() virtual method is called on the subclass
+ // before it is destructed.
+ virtual ~Thread();
+
+#if defined(OS_WIN)
+ // Causes the thread to initialize COM. This must be called before calling
+ // Start() or StartWithOptions(). If |use_mta| is false, the thread is also
+ // started with a TYPE_UI message loop. It is an error to call
+ // init_com_with_mta(false) and then StartWithOptions() with any message loop
+ // type other than TYPE_UI.
+ void init_com_with_mta(bool use_mta) {
+ DCHECK(!started_);
+ com_status_ = use_mta ? MTA : STA;
+ }
+#endif
+
+ // Starts the thread. Returns true if the thread was successfully started;
+ // otherwise, returns false. Upon successful return, the message_loop()
+ // getter will return non-null.
+ //
+ // Note: This function can't be called on Windows with the loader lock held;
+ // i.e. during a DllMain, global object construction or destruction, atexit()
+ // callback.
+ bool Start();
+
+ // Starts the thread. Behaves exactly like Start in addition to allow to
+ // override the default options.
+ //
+ // Note: This function can't be called on Windows with the loader lock held;
+ // i.e. during a DllMain, global object construction or destruction, atexit()
+ // callback.
+ bool StartWithOptions(const Options& options);
+
+ // Signals the thread to exit and returns once the thread has exited. After
+ // this method returns, the Thread object is completely reset and may be used
+ // as if it were newly constructed (i.e., Start may be called again).
+ //
+ // Stop may be called multiple times and is simply ignored if the thread is
+ // already stopped.
+ //
+ // NOTE: If you are a consumer of Thread, it is not necessary to call this
+ // before deleting your Thread objects, as the destructor will do it.
+ // IF YOU ARE A SUBCLASS OF Thread, YOU MUST CALL THIS IN YOUR DESTRUCTOR.
+ void Stop();
+
+ // Signals the thread to exit in the near future.
+ //
+ // WARNING: This function is not meant to be commonly used. Use at your own
+ // risk. Calling this function will cause message_loop() to become invalid in
+ // the near future. This function was created to workaround a specific
+ // deadlock on Windows with printer worker thread. In any other case, Stop()
+ // should be used.
+ //
+ // StopSoon should not be called multiple times as it is risky to do so. It
+ // could cause a timing issue in message_loop() access. Call Stop() to reset
+ // the thread object once it is known that the thread has quit.
+ void StopSoon();
+
+ // Returns the message loop for this thread. Use the MessageLoop's
+ // PostTask methods to execute code on the thread. This only returns
+ // non-null after a successful call to Start. After Stop has been called,
+ // this will return NULL.
+ //
+ // NOTE: You must not call this MessageLoop's Quit method directly. Use
+ // the Thread's Stop method instead.
+ //
+ MessageLoop* message_loop() const { return message_loop_; }
+
+ // Returns a MessageLoopProxy for this thread. Use the MessageLoopProxy's
+ // PostTask methods to execute code on the thread. This only returns
+ // non-NULL after a successful call to Start. After Stop has been called,
+ // this will return NULL. Callers can hold on to this even after the thread
+ // is gone.
+ scoped_refptr<MessageLoopProxy> message_loop_proxy() const {
+ return message_loop_ ? message_loop_->message_loop_proxy() : NULL;
+ }
+
+ // Returns the name of this thread (for display in debugger too).
+ const std::string& thread_name() const { return name_; }
+
+ // The native thread handle.
+ PlatformThreadHandle thread_handle() { return thread_; }
+
+ // The thread ID.
+ PlatformThreadId thread_id() const { return thread_id_; }
+
+ // Returns true if the thread has been started, and not yet stopped.
+ bool IsRunning() const;
+
+ protected:
+ // Called just prior to starting the message loop
+ virtual void Init() {}
+
+ // Called to start the message loop
+ virtual void Run(MessageLoop* message_loop);
+
+ // Called just after the message loop ends
+ virtual void CleanUp() {}
+
+ static void SetThreadWasQuitProperly(bool flag);
+ static bool GetThreadWasQuitProperly();
+
+ void set_message_loop(MessageLoop* message_loop) {
+ message_loop_ = message_loop;
+ }
+
+ private:
+#if defined(OS_WIN)
+ enum ComStatus {
+ NONE,
+ STA,
+ MTA,
+ };
+#endif
+
+ // PlatformThread::Delegate methods:
+ virtual void ThreadMain() OVERRIDE;
+
+#if defined(OS_WIN)
+ // Whether this thread needs to initialize COM, and if so, in what mode.
+ ComStatus com_status_;
+#endif
+
+ // Whether we successfully started the thread.
+ bool started_;
+
+ // If true, we're in the middle of stopping, and shouldn't access
+ // |message_loop_|. It may non-NULL and invalid.
+ bool stopping_;
+
+ // True while inside of Run().
+ bool running_;
+
+ // Used to pass data to ThreadMain.
+ struct StartupData;
+ StartupData* startup_data_;
+
+ // The thread's handle.
+ PlatformThreadHandle thread_;
+
+ // The thread's message loop. Valid only while the thread is alive. Set
+ // by the created thread.
+ MessageLoop* message_loop_;
+
+ // Our thread's ID.
+ PlatformThreadId thread_id_;
+
+ // The name of the thread. Used for debugging purposes.
+ std::string name_;
+
+ friend void ThreadQuitHelper();
+
+ DISALLOW_COPY_AND_ASSIGN(Thread);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_H_
diff --git a/src/base/threading/thread_checker.h b/src/base/threading/thread_checker.h
new file mode 100644
index 0000000..d4c9b74
--- /dev/null
+++ b/src/base/threading/thread_checker.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2012 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_THREADING_THREAD_CHECKER_H_
+#define BASE_THREADING_THREAD_CHECKER_H_
+
+// Apart from debug builds, we also enable the thread checker in
+// builds with DCHECK_ALWAYS_ON so that trybots and waterfall bots
+// with this define will get the same level of thread checking as
+// debug bots.
+//
+// Note that this does not perfectly match situations where DCHECK is
+// enabled. For example a non-official release build may have
+// DCHECK_ALWAYS_ON undefined (and therefore ThreadChecker would be
+// disabled) but have DCHECKs enabled at runtime.
+#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
+#define ENABLE_THREAD_CHECKER 1
+#else
+#define ENABLE_THREAD_CHECKER 0
+#endif
+
+#if ENABLE_THREAD_CHECKER
+#include "base/threading/thread_checker_impl.h"
+#endif
+
+namespace base {
+
+// Do nothing implementation, for use in release mode.
+//
+// Note: You should almost always use the ThreadChecker class to get the
+// right version for your build configuration.
+class ThreadCheckerDoNothing {
+ public:
+ bool CalledOnValidThread() const {
+ return true;
+ }
+
+ void DetachFromThread() {}
+};
+
+// ThreadChecker is a helper class used to help verify that some methods of a
+// class are called from the same thread. It provides identical functionality to
+// base::NonThreadSafe, but it is meant to be held as a member variable, rather
+// than inherited from base::NonThreadSafe.
+//
+// While inheriting from base::NonThreadSafe may give a clear indication about
+// the thread-safety of a class, it may also lead to violations of the style
+// guide with regard to multiple inheritence. The choice between having a
+// ThreadChecker member and inheriting from base::NonThreadSafe should be based
+// on whether:
+// - Derived classes need to know the thread they belong to, as opposed to
+// having that functionality fully encapsulated in the base class.
+// - Derived classes should be able to reassign the base class to another
+// thread, via DetachFromThread.
+//
+// If neither of these are true, then having a ThreadChecker member and calling
+// CalledOnValidThread is the preferable solution.
+//
+// Example:
+// class MyClass {
+// public:
+// void Foo() {
+// DCHECK(thread_checker_.CalledOnValidThread());
+// ... (do stuff) ...
+// }
+//
+// private:
+// ThreadChecker thread_checker_;
+// }
+//
+// In Release mode, CalledOnValidThread will always return true.
+#if ENABLE_THREAD_CHECKER
+class ThreadChecker : public ThreadCheckerImpl {
+};
+#else
+class ThreadChecker : public ThreadCheckerDoNothing {
+};
+#endif // ENABLE_THREAD_CHECKER
+
+#undef ENABLE_THREAD_CHECKER
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_CHECKER_H_
diff --git a/src/base/threading/thread_checker_impl.cc b/src/base/threading/thread_checker_impl.cc
new file mode 100644
index 0000000..985433e
--- /dev/null
+++ b/src/base/threading/thread_checker_impl.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_checker_impl.h"
+
+namespace base {
+
+ThreadCheckerImpl::ThreadCheckerImpl()
+ : valid_thread_id_(kInvalidThreadId) {
+ EnsureThreadIdAssigned();
+}
+
+ThreadCheckerImpl::~ThreadCheckerImpl() {}
+
+bool ThreadCheckerImpl::CalledOnValidThread() const {
+ EnsureThreadIdAssigned();
+ AutoLock auto_lock(lock_);
+ return valid_thread_id_ == PlatformThread::CurrentId();
+}
+
+void ThreadCheckerImpl::DetachFromThread() {
+ AutoLock auto_lock(lock_);
+ valid_thread_id_ = kInvalidThreadId;
+}
+
+void ThreadCheckerImpl::EnsureThreadIdAssigned() const {
+ AutoLock auto_lock(lock_);
+ if (valid_thread_id_ != kInvalidThreadId)
+ return;
+ valid_thread_id_ = PlatformThread::CurrentId();
+}
+
+} // namespace base
diff --git a/src/base/threading/thread_checker_impl.h b/src/base/threading/thread_checker_impl.h
new file mode 100644
index 0000000..d33e21f
--- /dev/null
+++ b/src/base/threading/thread_checker_impl.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_THREADING_THREAD_CHECKER_IMPL_H_
+#define BASE_THREADING_THREAD_CHECKER_IMPL_H_
+
+#include "base/base_export.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+// Real implementation of ThreadChecker, for use in debug mode, or
+// for temporary use in release mode (e.g. to CHECK on a threading issue
+// seen only in the wild).
+//
+// Note: You should almost always use the ThreadChecker class to get the
+// right version for your build configuration.
+class BASE_EXPORT ThreadCheckerImpl {
+ public:
+ ThreadCheckerImpl();
+ ~ThreadCheckerImpl();
+
+ bool CalledOnValidThread() const;
+
+ // Changes the thread that is checked for in CalledOnValidThread. This may
+ // be useful when an object may be created on one thread and then used
+ // exclusively on another thread.
+ void DetachFromThread();
+
+ private:
+ void EnsureThreadIdAssigned() const;
+
+#if defined(COBALT) || defined(OS_STARBOARD)
+ // Don't use a mutex since the number of mutexes is limited on some platforms.
+ mutable base::subtle::Atomic32 valid_thread_id_;
+#else
+ mutable base::Lock lock_;
+ // This is mutable so that CalledOnValidThread can set it.
+ // It's guarded by |lock_|.
+ mutable PlatformThreadId valid_thread_id_;
+#endif
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_CHECKER_IMPL_H_
diff --git a/src/base/threading/thread_checker_impl_atomic.cc b/src/base/threading/thread_checker_impl_atomic.cc
new file mode 100644
index 0000000..534cb08
--- /dev/null
+++ b/src/base/threading/thread_checker_impl_atomic.cc
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/threading/thread_checker_impl.h"
+
+#include "base/threading/platform_thread.h"
+
+namespace base {
+namespace {
+
+inline base::subtle::Atomic32 CurrentThreadIdAsAtomic() {
+ return static_cast<base::subtle::Atomic32>(PlatformThread::CurrentId());
+}
+
+const base::subtle::Atomic32 kInvalidThreadIdAtomic =
+ static_cast<base::subtle::Atomic32>(kInvalidThreadId);
+
+} // namespace
+
+ThreadCheckerImpl::ThreadCheckerImpl()
+ : valid_thread_id_(kInvalidThreadIdAtomic) {
+ EnsureThreadIdAssigned();
+}
+
+ThreadCheckerImpl::~ThreadCheckerImpl() {
+}
+
+bool ThreadCheckerImpl::CalledOnValidThread() const {
+ EnsureThreadIdAssigned();
+ base::subtle::Atomic32 current_thread =
+ base::subtle::NoBarrier_Load(&valid_thread_id_);
+ return current_thread == CurrentThreadIdAsAtomic();
+}
+
+void ThreadCheckerImpl::DetachFromThread() {
+ base::subtle::NoBarrier_Store(&valid_thread_id_, kInvalidThreadIdAtomic);
+}
+
+void ThreadCheckerImpl::EnsureThreadIdAssigned() const {
+ // Set valid_thread_id_ to the current thread id if valid_thread_id_ is
+ // currently unset (that is, set to kInvalidThreadIdAtomic).
+ base::subtle::NoBarrier_CompareAndSwap(
+ &valid_thread_id_,
+ kInvalidThreadIdAtomic,
+ CurrentThreadIdAsAtomic());
+}
+
+} // namespace base
diff --git a/src/base/threading/thread_checker_unittest.cc b/src/base/threading/thread_checker_unittest.cc
new file mode 100644
index 0000000..026cfd9
--- /dev/null
+++ b/src/base/threading/thread_checker_unittest.cc
@@ -0,0 +1,182 @@
+// Copyright (c) 2012 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/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "base/threading/simple_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Duplicated from base/threading/thread_checker.h so that we can be
+// good citizens there and undef the macro.
+#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
+#define ENABLE_THREAD_CHECKER 1
+#else
+#define ENABLE_THREAD_CHECKER 0
+#endif
+
+namespace base {
+
+namespace {
+
+// Simple class to exercise the basics of ThreadChecker.
+// Both the destructor and DoStuff should verify that they were
+// called on the same thread as the constructor.
+class ThreadCheckerClass : public ThreadChecker {
+ public:
+ ThreadCheckerClass() {}
+
+ // Verifies that it was called on the same thread as the constructor.
+ void DoStuff() {
+ DCHECK(CalledOnValidThread());
+ }
+
+ void DetachFromThread() {
+ ThreadChecker::DetachFromThread();
+ }
+
+ static void MethodOnDifferentThreadImpl();
+ static void DetachThenCallFromDifferentThreadImpl();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ThreadCheckerClass);
+};
+
+// Calls ThreadCheckerClass::DoStuff on another thread.
+class CallDoStuffOnThread : public base::SimpleThread {
+ public:
+ CallDoStuffOnThread(ThreadCheckerClass* thread_checker_class)
+ : SimpleThread("call_do_stuff_on_thread"),
+ thread_checker_class_(thread_checker_class) {
+ }
+
+ virtual void Run() OVERRIDE {
+ thread_checker_class_->DoStuff();
+ }
+
+ private:
+ ThreadCheckerClass* thread_checker_class_;
+
+ DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread);
+};
+
+// Deletes ThreadCheckerClass on a different thread.
+class DeleteThreadCheckerClassOnThread : public base::SimpleThread {
+ public:
+ DeleteThreadCheckerClassOnThread(ThreadCheckerClass* thread_checker_class)
+ : SimpleThread("delete_thread_checker_class_on_thread"),
+ thread_checker_class_(thread_checker_class) {
+ }
+
+ virtual void Run() OVERRIDE {
+ thread_checker_class_.reset();
+ }
+
+ private:
+ scoped_ptr<ThreadCheckerClass> thread_checker_class_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeleteThreadCheckerClassOnThread);
+};
+
+} // namespace
+
+TEST(ThreadCheckerTest, CallsAllowedOnSameThread) {
+ scoped_ptr<ThreadCheckerClass> thread_checker_class(
+ new ThreadCheckerClass);
+
+ // Verify that DoStuff doesn't assert.
+ thread_checker_class->DoStuff();
+
+ // Verify that the destructor doesn't assert.
+ thread_checker_class.reset();
+}
+
+TEST(ThreadCheckerTest, DestructorAllowedOnDifferentThread) {
+ scoped_ptr<ThreadCheckerClass> thread_checker_class(
+ new ThreadCheckerClass);
+
+ // Verify that the destructor doesn't assert
+ // when called on a different thread.
+ DeleteThreadCheckerClassOnThread delete_on_thread(
+ thread_checker_class.release());
+
+ delete_on_thread.Start();
+ delete_on_thread.Join();
+}
+
+TEST(ThreadCheckerTest, DetachFromThread) {
+ scoped_ptr<ThreadCheckerClass> thread_checker_class(
+ new ThreadCheckerClass);
+
+ // Verify that DoStuff doesn't assert when called on a different thread after
+ // a call to DetachFromThread.
+ thread_checker_class->DetachFromThread();
+ CallDoStuffOnThread call_on_thread(thread_checker_class.get());
+
+ call_on_thread.Start();
+ call_on_thread.Join();
+}
+
+#if GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER
+
+void ThreadCheckerClass::MethodOnDifferentThreadImpl() {
+ scoped_ptr<ThreadCheckerClass> thread_checker_class(
+ new ThreadCheckerClass);
+
+ // DoStuff should assert in debug builds only when called on a
+ // different thread.
+ CallDoStuffOnThread call_on_thread(thread_checker_class.get());
+
+ call_on_thread.Start();
+ call_on_thread.Join();
+}
+
+#if ENABLE_THREAD_CHECKER
+TEST(ThreadCheckerDeathTest, MethodNotAllowedOnDifferentThreadInDebug) {
+ ASSERT_DEATH({
+ ThreadCheckerClass::MethodOnDifferentThreadImpl();
+ }, "");
+}
+#else
+TEST(ThreadCheckerTest, MethodAllowedOnDifferentThreadInRelease) {
+ ThreadCheckerClass::MethodOnDifferentThreadImpl();
+}
+#endif // ENABLE_THREAD_CHECKER
+
+void ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl() {
+ scoped_ptr<ThreadCheckerClass> thread_checker_class(
+ new ThreadCheckerClass);
+
+ // DoStuff doesn't assert when called on a different thread
+ // after a call to DetachFromThread.
+ thread_checker_class->DetachFromThread();
+ CallDoStuffOnThread call_on_thread(thread_checker_class.get());
+
+ call_on_thread.Start();
+ call_on_thread.Join();
+
+ // DoStuff should assert in debug builds only after moving to
+ // another thread.
+ thread_checker_class->DoStuff();
+}
+
+#if ENABLE_THREAD_CHECKER
+TEST(ThreadCheckerDeathTest, DetachFromThreadInDebug) {
+ ASSERT_DEATH({
+ ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
+ }, "");
+}
+#else
+TEST(ThreadCheckerTest, DetachFromThreadInRelease) {
+ ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
+}
+#endif // ENABLE_THREAD_CHECKER
+
+#endif // GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER
+
+// Just in case we ever get lumped together with other compilation units.
+#undef ENABLE_THREAD_CHECKER
+
+} // namespace base
diff --git a/src/base/threading/thread_collision_warner.cc b/src/base/threading/thread_collision_warner.cc
new file mode 100644
index 0000000..547e11c
--- /dev/null
+++ b/src/base/threading/thread_collision_warner.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_collision_warner.h"
+
+#include "base/logging.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+void DCheckAsserter::warn() {
+ NOTREACHED() << "Thread Collision";
+}
+
+static subtle::Atomic32 CurrentThread() {
+ const PlatformThreadId current_thread_id = PlatformThread::CurrentId();
+ // We need to get the thread id into an atomic data type. This might be a
+ // truncating conversion, but any loss-of-information just increases the
+ // chance of a fault negative, not a false positive.
+ const subtle::Atomic32 atomic_thread_id =
+ static_cast<subtle::Atomic32>(current_thread_id);
+
+ return atomic_thread_id;
+}
+
+void ThreadCollisionWarner::EnterSelf() {
+ // If the active thread is 0 then I'll write the current thread ID
+ // if two or more threads arrive here only one will succeed to
+ // write on valid_thread_id_ the current thread ID.
+ subtle::Atomic32 current_thread_id = CurrentThread();
+
+ int previous_value = subtle::NoBarrier_CompareAndSwap(&valid_thread_id_,
+ 0,
+ current_thread_id);
+ if (previous_value != 0 && previous_value != current_thread_id) {
+ // gotcha! a thread is trying to use the same class and that is
+ // not current thread.
+ asserter_->warn();
+ }
+
+ subtle::NoBarrier_AtomicIncrement(&counter_, 1);
+}
+
+void ThreadCollisionWarner::Enter() {
+ subtle::Atomic32 current_thread_id = CurrentThread();
+
+ if (subtle::NoBarrier_CompareAndSwap(&valid_thread_id_,
+ 0,
+ current_thread_id) != 0) {
+ // gotcha! another thread is trying to use the same class.
+ asserter_->warn();
+ }
+
+ subtle::NoBarrier_AtomicIncrement(&counter_, 1);
+}
+
+void ThreadCollisionWarner::Leave() {
+ if (subtle::Barrier_AtomicIncrement(&counter_, -1) == 0) {
+ subtle::NoBarrier_Store(&valid_thread_id_, 0);
+ }
+}
+
+} // namespace base
diff --git a/src/base/threading/thread_collision_warner.h b/src/base/threading/thread_collision_warner.h
new file mode 100644
index 0000000..2181cbd
--- /dev/null
+++ b/src/base/threading/thread_collision_warner.h
@@ -0,0 +1,244 @@
+// Copyright (c) 2012 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_THREADING_THREAD_COLLISION_WARNER_H_
+#define BASE_THREADING_THREAD_COLLISION_WARNER_H_
+
+#include <memory>
+
+#include "base/atomicops.h"
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+
+// A helper class alongside macros to be used to verify assumptions about thread
+// safety of a class.
+//
+// Example: Queue implementation non thread-safe but still usable if clients
+// are synchronized somehow.
+//
+// In this case the macro DFAKE_SCOPED_LOCK has to be
+// used, it checks that if a thread is inside the push/pop then
+// noone else is still inside the pop/push
+//
+// class NonThreadSafeQueue {
+// public:
+// ...
+// void push(int) { DFAKE_SCOPED_LOCK(push_pop_); ... }
+// int pop() { DFAKE_SCOPED_LOCK(push_pop_); ... }
+// ...
+// private:
+// DFAKE_MUTEX(push_pop_);
+// };
+//
+//
+// Example: Queue implementation non thread-safe but still usable if clients
+// are synchronized somehow, it calls a method to "protect" from
+// a "protected" method
+//
+// In this case the macro DFAKE_SCOPED_RECURSIVE_LOCK
+// has to be used, it checks that if a thread is inside the push/pop
+// then noone else is still inside the pop/push
+//
+// class NonThreadSafeQueue {
+// public:
+// void push(int) {
+// DFAKE_SCOPED_LOCK(push_pop_);
+// ...
+// }
+// int pop() {
+// DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
+// bar();
+// ...
+// }
+// void bar() { DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); ... }
+// ...
+// private:
+// DFAKE_MUTEX(push_pop_);
+// };
+//
+//
+// Example: Queue implementation not usable even if clients are synchronized,
+// so only one thread in the class life cycle can use the two members
+// push/pop.
+//
+// In this case the macro DFAKE_SCOPED_LOCK_THREAD_LOCKED pins the
+// specified
+// critical section the first time a thread enters push or pop, from
+// that time on only that thread is allowed to execute push or pop.
+//
+// class NonThreadSafeQueue {
+// public:
+// ...
+// void push(int) { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... }
+// int pop() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... }
+// ...
+// private:
+// DFAKE_MUTEX(push_pop_);
+// };
+//
+//
+// Example: Class that has to be contructed/destroyed on same thread, it has
+// a "shareable" method (with external synchronization) and a not
+// shareable method (even with external synchronization).
+//
+// In this case 3 Critical sections have to be defined
+//
+// class ExoticClass {
+// public:
+// ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... }
+// ~ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... }
+//
+// void Shareable() { DFAKE_SCOPED_LOCK(shareable_section_); ... }
+// void NotShareable() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... }
+// ...
+// private:
+// DFAKE_MUTEX(ctor_dtor_);
+// DFAKE_MUTEX(shareable_section_);
+// };
+
+
+#if !defined(NDEBUG)
+
+// Defines a class member that acts like a mutex. It is used only as a
+// verification tool.
+#define DFAKE_MUTEX(obj) \
+ mutable base::ThreadCollisionWarner obj
+// Asserts the call is never called simultaneously in two threads. Used at
+// member function scope.
+#define DFAKE_SCOPED_LOCK(obj) \
+ base::ThreadCollisionWarner::ScopedCheck s_check_##obj(&obj)
+// Asserts the call is never called simultaneously in two threads. Used at
+// member function scope. Same as DFAKE_SCOPED_LOCK but allows recursive locks.
+#define DFAKE_SCOPED_RECURSIVE_LOCK(obj) \
+ base::ThreadCollisionWarner::ScopedRecursiveCheck sr_check_##obj(&obj)
+// Asserts the code is always executed in the same thread.
+#define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) \
+ base::ThreadCollisionWarner::Check check_##obj(&obj)
+
+#else
+
+#define DFAKE_MUTEX(obj) typedef void InternalFakeMutexType##obj
+#define DFAKE_SCOPED_LOCK(obj) ((void)0)
+#define DFAKE_SCOPED_RECURSIVE_LOCK(obj) ((void)0)
+#define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) ((void)0)
+
+#endif
+
+namespace base {
+
+// The class ThreadCollisionWarner uses an Asserter to notify the collision
+// AsserterBase is the interfaces and DCheckAsserter is the default asserter
+// used. During the unit tests is used another class that doesn't "DCHECK"
+// in case of collision (check thread_collision_warner_unittests.cc)
+struct BASE_EXPORT AsserterBase {
+ virtual ~AsserterBase() {}
+ virtual void warn() = 0;
+};
+
+struct BASE_EXPORT DCheckAsserter : public AsserterBase {
+ virtual ~DCheckAsserter() {}
+ virtual void warn() OVERRIDE;
+};
+
+class BASE_EXPORT ThreadCollisionWarner {
+ public:
+ // The parameter asserter is there only for test purpose
+ ThreadCollisionWarner(AsserterBase* asserter = new DCheckAsserter())
+ : valid_thread_id_(0),
+ counter_(0),
+ asserter_(asserter) {}
+
+ ~ThreadCollisionWarner() {
+ delete asserter_;
+ }
+
+ // This class is meant to be used through the macro
+ // DFAKE_SCOPED_LOCK_THREAD_LOCKED
+ // it doesn't leave the critical section, as opposed to ScopedCheck,
+ // because the critical section being pinned is allowed to be used only
+ // from one thread
+ class BASE_EXPORT Check {
+ public:
+ explicit Check(ThreadCollisionWarner* warner)
+ : warner_(warner) {
+ warner_->EnterSelf();
+ }
+
+ ~Check() {}
+
+ private:
+ ThreadCollisionWarner* warner_;
+
+ DISALLOW_COPY_AND_ASSIGN(Check);
+ };
+
+ // This class is meant to be used through the macro
+ // DFAKE_SCOPED_LOCK
+ class BASE_EXPORT ScopedCheck {
+ public:
+ explicit ScopedCheck(ThreadCollisionWarner* warner)
+ : warner_(warner) {
+ warner_->Enter();
+ }
+
+ ~ScopedCheck() {
+ warner_->Leave();
+ }
+
+ private:
+ ThreadCollisionWarner* warner_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedCheck);
+ };
+
+ // This class is meant to be used through the macro
+ // DFAKE_SCOPED_RECURSIVE_LOCK
+ class BASE_EXPORT ScopedRecursiveCheck {
+ public:
+ explicit ScopedRecursiveCheck(ThreadCollisionWarner* warner)
+ : warner_(warner) {
+ warner_->EnterSelf();
+ }
+
+ ~ScopedRecursiveCheck() {
+ warner_->Leave();
+ }
+
+ private:
+ ThreadCollisionWarner* warner_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedRecursiveCheck);
+ };
+
+ private:
+ // This method stores the current thread identifier and does a DCHECK
+ // if a another thread has already done it, it is safe if same thread
+ // calls this multiple time (recursion allowed).
+ void EnterSelf();
+
+ // Same as EnterSelf but recursion is not allowed.
+ void Enter();
+
+ // Removes the thread_id stored in order to allow other threads to
+ // call EnterSelf or Enter.
+ void Leave();
+
+ // This stores the thread id that is inside the critical section, if the
+ // value is 0 then no thread is inside.
+ volatile subtle::Atomic32 valid_thread_id_;
+
+ // Counter to trace how many time a critical section was "pinned"
+ // (when allowed) in order to unpin it when counter_ reaches 0.
+ volatile subtle::Atomic32 counter_;
+
+ // Here only for class unit tests purpose, during the test I need to not
+ // DCHECK but notify the collision with something else.
+ AsserterBase* asserter_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadCollisionWarner);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_COLLISION_WARNER_H_
diff --git a/src/base/threading/thread_collision_warner_unittest.cc b/src/base/threading/thread_collision_warner_unittest.cc
new file mode 100644
index 0000000..48710a7
--- /dev/null
+++ b/src/base/threading/thread_collision_warner_unittest.cc
@@ -0,0 +1,385 @@
+// Copyright (c) 2012 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/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/simple_thread.h"
+#include "base/threading/thread_collision_warner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// '' : local class member function does not have a body
+MSVC_PUSH_DISABLE_WARNING(4822)
+
+
+#if defined(NDEBUG)
+
+// Would cause a memory leak otherwise.
+#undef DFAKE_MUTEX
+#define DFAKE_MUTEX(obj) scoped_ptr<base::AsserterBase> obj
+
+// In Release, we expect the AsserterBase::warn() to not happen.
+#define EXPECT_NDEBUG_FALSE_DEBUG_TRUE EXPECT_FALSE
+
+#else
+
+// In Debug, we expect the AsserterBase::warn() to happen.
+#define EXPECT_NDEBUG_FALSE_DEBUG_TRUE EXPECT_TRUE
+
+#endif
+
+
+namespace {
+
+// This is the asserter used with ThreadCollisionWarner instead of the default
+// DCheckAsserter. The method fail_state is used to know if a collision took
+// place.
+class AssertReporter : public base::AsserterBase {
+ public:
+ AssertReporter()
+ : failed_(false) {}
+
+ virtual void warn() OVERRIDE {
+ failed_ = true;
+ }
+
+ virtual ~AssertReporter() {}
+
+ bool fail_state() const { return failed_; }
+ void reset() { failed_ = false; }
+
+ private:
+ bool failed_;
+};
+
+} // namespace
+
+TEST(ThreadCollisionTest, BookCriticalSection) {
+ AssertReporter* local_reporter = new AssertReporter();
+
+ base::ThreadCollisionWarner warner(local_reporter);
+ EXPECT_FALSE(local_reporter->fail_state());
+
+ { // Pin section.
+ DFAKE_SCOPED_LOCK_THREAD_LOCKED(warner);
+ EXPECT_FALSE(local_reporter->fail_state());
+ { // Pin section.
+ DFAKE_SCOPED_LOCK_THREAD_LOCKED(warner);
+ EXPECT_FALSE(local_reporter->fail_state());
+ }
+ }
+}
+
+TEST(ThreadCollisionTest, ScopedRecursiveBookCriticalSection) {
+ AssertReporter* local_reporter = new AssertReporter();
+
+ base::ThreadCollisionWarner warner(local_reporter);
+ EXPECT_FALSE(local_reporter->fail_state());
+
+ { // Pin section.
+ DFAKE_SCOPED_RECURSIVE_LOCK(warner);
+ EXPECT_FALSE(local_reporter->fail_state());
+ { // Pin section again (allowed by DFAKE_SCOPED_RECURSIVE_LOCK)
+ DFAKE_SCOPED_RECURSIVE_LOCK(warner);
+ EXPECT_FALSE(local_reporter->fail_state());
+ } // Unpin section.
+ } // Unpin section.
+
+ // Check that section is not pinned
+ { // Pin section.
+ DFAKE_SCOPED_LOCK(warner);
+ EXPECT_FALSE(local_reporter->fail_state());
+ } // Unpin section.
+}
+
+TEST(ThreadCollisionTest, ScopedBookCriticalSection) {
+ AssertReporter* local_reporter = new AssertReporter();
+
+ base::ThreadCollisionWarner warner(local_reporter);
+ EXPECT_FALSE(local_reporter->fail_state());
+
+ { // Pin section.
+ DFAKE_SCOPED_LOCK(warner);
+ EXPECT_FALSE(local_reporter->fail_state());
+ } // Unpin section.
+
+ { // Pin section.
+ DFAKE_SCOPED_LOCK(warner);
+ EXPECT_FALSE(local_reporter->fail_state());
+ {
+ // Pin section again (not allowed by DFAKE_SCOPED_LOCK)
+ DFAKE_SCOPED_LOCK(warner);
+ EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state());
+ // Reset the status of warner for further tests.
+ local_reporter->reset();
+ } // Unpin section.
+ } // Unpin section.
+
+ {
+ // Pin section.
+ DFAKE_SCOPED_LOCK(warner);
+ EXPECT_FALSE(local_reporter->fail_state());
+ } // Unpin section.
+}
+
+TEST(ThreadCollisionTest, MTBookCriticalSectionTest) {
+ class NonThreadSafeQueue {
+ public:
+ explicit NonThreadSafeQueue(base::AsserterBase* asserter)
+ : push_pop_(asserter) {
+ }
+
+ void push(int value) {
+ DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_);
+ }
+
+ int pop() {
+ DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_);
+ return 0;
+ }
+
+ private:
+ DFAKE_MUTEX(push_pop_);
+
+ DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue);
+ };
+
+ class QueueUser : public base::DelegateSimpleThread::Delegate {
+ public:
+ explicit QueueUser(NonThreadSafeQueue& queue)
+ : queue_(queue) {}
+
+ virtual void Run() OVERRIDE {
+ queue_.push(0);
+ queue_.pop();
+ }
+
+ private:
+ NonThreadSafeQueue& queue_;
+ };
+
+ AssertReporter* local_reporter = new AssertReporter();
+
+ NonThreadSafeQueue queue(local_reporter);
+
+ QueueUser queue_user_a(queue);
+ QueueUser queue_user_b(queue);
+
+ base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
+ base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
+
+ thread_a.Start();
+ thread_b.Start();
+
+ thread_a.Join();
+ thread_b.Join();
+
+ EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state());
+}
+
+TEST(ThreadCollisionTest, MTScopedBookCriticalSectionTest) {
+ // Queue with a 5 seconds push execution time, hopefuly the two used threads
+ // in the test will enter the push at same time.
+ class NonThreadSafeQueue {
+ public:
+ explicit NonThreadSafeQueue(base::AsserterBase* asserter)
+ : push_pop_(asserter) {
+ }
+
+ void push(int value) {
+ DFAKE_SCOPED_LOCK(push_pop_);
+ base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(5));
+ }
+
+ int pop() {
+ DFAKE_SCOPED_LOCK(push_pop_);
+ return 0;
+ }
+
+ private:
+ DFAKE_MUTEX(push_pop_);
+
+ DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue);
+ };
+
+ class QueueUser : public base::DelegateSimpleThread::Delegate {
+ public:
+ explicit QueueUser(NonThreadSafeQueue& queue)
+ : queue_(queue) {}
+
+ virtual void Run() OVERRIDE {
+ queue_.push(0);
+ queue_.pop();
+ }
+
+ private:
+ NonThreadSafeQueue& queue_;
+ };
+
+ AssertReporter* local_reporter = new AssertReporter();
+
+ NonThreadSafeQueue queue(local_reporter);
+
+ QueueUser queue_user_a(queue);
+ QueueUser queue_user_b(queue);
+
+ base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
+ base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
+
+ thread_a.Start();
+ thread_b.Start();
+
+ thread_a.Join();
+ thread_b.Join();
+
+ EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state());
+}
+
+TEST(ThreadCollisionTest, MTSynchedScopedBookCriticalSectionTest) {
+ // Queue with a 2 seconds push execution time, hopefuly the two used threads
+ // in the test will enter the push at same time.
+ class NonThreadSafeQueue {
+ public:
+ explicit NonThreadSafeQueue(base::AsserterBase* asserter)
+ : push_pop_(asserter) {
+ }
+
+ void push(int value) {
+ DFAKE_SCOPED_LOCK(push_pop_);
+ base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(2));
+ }
+
+ int pop() {
+ DFAKE_SCOPED_LOCK(push_pop_);
+ return 0;
+ }
+
+ private:
+ DFAKE_MUTEX(push_pop_);
+
+ DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue);
+ };
+
+ // This time the QueueUser class protects the non thread safe queue with
+ // a lock.
+ class QueueUser : public base::DelegateSimpleThread::Delegate {
+ public:
+ QueueUser(NonThreadSafeQueue& queue, base::Lock& lock)
+ : queue_(queue),
+ lock_(lock) {}
+
+ virtual void Run() OVERRIDE {
+ {
+ base::AutoLock auto_lock(lock_);
+ queue_.push(0);
+ }
+ {
+ base::AutoLock auto_lock(lock_);
+ queue_.pop();
+ }
+ }
+ private:
+ NonThreadSafeQueue& queue_;
+ base::Lock& lock_;
+ };
+
+ AssertReporter* local_reporter = new AssertReporter();
+
+ NonThreadSafeQueue queue(local_reporter);
+
+ base::Lock lock;
+
+ QueueUser queue_user_a(queue, lock);
+ QueueUser queue_user_b(queue, lock);
+
+ base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
+ base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
+
+ thread_a.Start();
+ thread_b.Start();
+
+ thread_a.Join();
+ thread_b.Join();
+
+ EXPECT_FALSE(local_reporter->fail_state());
+}
+
+TEST(ThreadCollisionTest, MTSynchedScopedRecursiveBookCriticalSectionTest) {
+ // Queue with a 2 seconds push execution time, hopefuly the two used threads
+ // in the test will enter the push at same time.
+ class NonThreadSafeQueue {
+ public:
+ explicit NonThreadSafeQueue(base::AsserterBase* asserter)
+ : push_pop_(asserter) {
+ }
+
+ void push(int) {
+ DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
+ bar();
+ base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(2));
+ }
+
+ int pop() {
+ DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
+ return 0;
+ }
+
+ void bar() {
+ DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
+ }
+
+ private:
+ DFAKE_MUTEX(push_pop_);
+
+ DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue);
+ };
+
+ // This time the QueueUser class protects the non thread safe queue with
+ // a lock.
+ class QueueUser : public base::DelegateSimpleThread::Delegate {
+ public:
+ QueueUser(NonThreadSafeQueue& queue, base::Lock& lock)
+ : queue_(queue),
+ lock_(lock) {}
+
+ virtual void Run() OVERRIDE {
+ {
+ base::AutoLock auto_lock(lock_);
+ queue_.push(0);
+ }
+ {
+ base::AutoLock auto_lock(lock_);
+ queue_.bar();
+ }
+ {
+ base::AutoLock auto_lock(lock_);
+ queue_.pop();
+ }
+ }
+ private:
+ NonThreadSafeQueue& queue_;
+ base::Lock& lock_;
+ };
+
+ AssertReporter* local_reporter = new AssertReporter();
+
+ NonThreadSafeQueue queue(local_reporter);
+
+ base::Lock lock;
+
+ QueueUser queue_user_a(queue, lock);
+ QueueUser queue_user_b(queue, lock);
+
+ base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
+ base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
+
+ thread_a.Start();
+ thread_b.Start();
+
+ thread_a.Join();
+ thread_b.Join();
+
+ EXPECT_FALSE(local_reporter->fail_state());
+}
diff --git a/src/base/threading/thread_local.h b/src/base/threading/thread_local.h
new file mode 100644
index 0000000..762d77b
--- /dev/null
+++ b/src/base/threading/thread_local.h
@@ -0,0 +1,132 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// WARNING: Thread local storage is a bit tricky to get right. Please make
+// sure that this is really the proper solution for what you're trying to
+// achieve. Don't prematurely optimize, most likely you can just use a Lock.
+//
+// These classes implement a wrapper around the platform's TLS storage
+// mechanism. On construction, they will allocate a TLS slot, and free the
+// TLS slot on destruction. No memory management (creation or destruction) is
+// handled. This means for uses of ThreadLocalPointer, you must correctly
+// manage the memory yourself, these classes will not destroy the pointer for
+// you. There are no at-thread-exit actions taken by these classes.
+//
+// ThreadLocalPointer<Type> wraps a Type*. It performs no creation or
+// destruction, so memory management must be handled elsewhere. The first call
+// to Get() on a thread will return NULL. You can update the pointer with a
+// call to Set().
+//
+// ThreadLocalBoolean wraps a bool. It will default to false if it has never
+// been set otherwise with Set().
+//
+// Thread Safety: An instance of ThreadLocalStorage is completely thread safe
+// once it has been created. If you want to dynamically create an instance,
+// you must of course properly deal with safety and race conditions. This
+// means a function-level static initializer is generally inappropiate.
+//
+// Example usage:
+// // My class is logically attached to a single thread. We cache a pointer
+// // on the thread it was created on, so we can implement current().
+// MyClass::MyClass() {
+// DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() == NULL);
+// Singleton<ThreadLocalPointer<MyClass> >::get()->Set(this);
+// }
+//
+// MyClass::~MyClass() {
+// DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() != NULL);
+// Singleton<ThreadLocalPointer<MyClass> >::get()->Set(NULL);
+// }
+//
+// // Return the current MyClass associated with the calling thread, can be
+// // NULL if there isn't a MyClass associated.
+// MyClass* MyClass::current() {
+// return Singleton<ThreadLocalPointer<MyClass> >::get()->Get();
+// }
+
+#ifndef BASE_THREADING_THREAD_LOCAL_H_
+#define BASE_THREADING_THREAD_LOCAL_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+#if defined(OS_POSIX)
+#include <pthread.h>
+#elif defined(OS_STARBOARD)
+#include "starboard/thread.h"
+#endif
+
+namespace base {
+
+namespace internal {
+
+// Helper functions that abstract the cross-platform APIs. Do not use directly.
+struct BASE_EXPORT ThreadLocalPlatform {
+#if defined(OS_WIN)
+ typedef unsigned long SlotType;
+#elif defined(OS_POSIX)
+ typedef pthread_key_t SlotType;
+#elif defined(OS_STARBOARD)
+ typedef SbThreadLocalKey SlotType;
+#endif
+
+ static void AllocateSlot(SlotType& slot);
+ static void FreeSlot(SlotType& slot);
+ static void* GetValueFromSlot(SlotType& slot);
+ static void SetValueInSlot(SlotType& slot, void* value);
+};
+
+} // namespace internal
+
+template <typename Type>
+class ThreadLocalPointer {
+ public:
+ ThreadLocalPointer() : slot_() {
+ internal::ThreadLocalPlatform::AllocateSlot(slot_);
+ }
+
+ ~ThreadLocalPointer() {
+ internal::ThreadLocalPlatform::FreeSlot(slot_);
+ }
+
+ Type* Get() {
+ return static_cast<Type*>(
+ internal::ThreadLocalPlatform::GetValueFromSlot(slot_));
+ }
+
+ void Set(Type* ptr) {
+ internal::ThreadLocalPlatform::SetValueInSlot(
+ slot_, const_cast<void*>(static_cast<const void*>(ptr)));
+ }
+
+ private:
+ typedef internal::ThreadLocalPlatform::SlotType SlotType;
+
+ SlotType slot_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer<Type>);
+};
+
+class ThreadLocalBoolean {
+ public:
+ ThreadLocalBoolean() { }
+ ~ThreadLocalBoolean() { }
+
+ bool Get() {
+ return tlp_.Get() != NULL;
+ }
+
+ void Set(bool val) {
+ tlp_.Set(val ? this : NULL);
+ }
+
+ private:
+ ThreadLocalPointer<void> tlp_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadLocalBoolean);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_LOCAL_H_
diff --git a/src/base/threading/thread_local_posix.cc b/src/base/threading/thread_local_posix.cc
new file mode 100644
index 0000000..4951006
--- /dev/null
+++ b/src/base/threading/thread_local_posix.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_local.h"
+
+#include <pthread.h>
+
+#include "base/logging.h"
+
+namespace base {
+
+namespace internal {
+
+// static
+void ThreadLocalPlatform::AllocateSlot(SlotType& slot) {
+ int error = pthread_key_create(&slot, NULL);
+ CHECK_EQ(error, 0);
+}
+
+// static
+void ThreadLocalPlatform::FreeSlot(SlotType& slot) {
+ int error = pthread_key_delete(slot);
+ DCHECK_EQ(0, error);
+}
+
+// static
+void* ThreadLocalPlatform::GetValueFromSlot(SlotType& slot) {
+ return pthread_getspecific(slot);
+}
+
+// static
+void ThreadLocalPlatform::SetValueInSlot(SlotType& slot, void* value) {
+ int error = pthread_setspecific(slot, value);
+ DCHECK_EQ(error, 0);
+}
+
+} // namespace internal
+
+} // namespace base
diff --git a/src/base/threading/thread_local_shell.cc b/src/base/threading/thread_local_shell.cc
new file mode 100644
index 0000000..4d09da5
--- /dev/null
+++ b/src/base/threading/thread_local_shell.cc
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2012 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// We use the same code as posix version
+#include "thread_local_posix.cc"
diff --git a/src/base/threading/thread_local_starboard.cc b/src/base/threading/thread_local_starboard.cc
new file mode 100644
index 0000000..e3a96d6
--- /dev/null
+++ b/src/base/threading/thread_local_starboard.cc
@@ -0,0 +1,48 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/threading/thread_local.h"
+
+#include "base/logging.h"
+#include "starboard/thread.h"
+
+namespace base {
+
+namespace internal {
+
+// static
+void ThreadLocalPlatform::AllocateSlot(SlotType& slot) {
+ slot = SbThreadCreateLocalKey(NULL);
+ CHECK_NE(kSbThreadLocalKeyInvalid, slot);
+}
+
+// static
+void ThreadLocalPlatform::FreeSlot(SlotType& slot) {
+ SbThreadDestroyLocalKey(slot);
+}
+
+// static
+void* ThreadLocalPlatform::GetValueFromSlot(SlotType& slot) {
+ return SbThreadGetLocalValue(slot);
+}
+
+// static
+void ThreadLocalPlatform::SetValueInSlot(SlotType& slot, void* value) {
+ bool result = SbThreadSetLocalValue(slot, value);
+ DCHECK(result);
+}
+
+} // namespace internal
+
+} // namespace base
diff --git a/src/base/threading/thread_local_storage.h b/src/base/threading/thread_local_storage.h
new file mode 100644
index 0000000..2240e8a
--- /dev/null
+++ b/src/base/threading/thread_local_storage.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2012 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_THREADING_THREAD_LOCAL_STORAGE_H_
+#define BASE_THREADING_THREAD_LOCAL_STORAGE_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+#if defined(OS_POSIX)
+#include <pthread.h>
+#elif defined(OS_STARBOARD)
+#include "starboard/thread.h"
+#endif
+
+namespace base {
+
+// Wrapper for thread local storage. This class doesn't do much except provide
+// an API for portability.
+class BASE_EXPORT ThreadLocalStorage {
+ public:
+
+ // Prototype for the TLS destructor function, which can be optionally used to
+ // cleanup thread local storage on thread exit. 'value' is the data that is
+ // stored in thread local storage.
+ typedef void (*TLSDestructorFunc)(void* value);
+
+ // StaticSlot uses its own struct initializer-list style static
+ // initialization, as base's LINKER_INITIALIZED requires a constructor and on
+ // some compilers (notably gcc 4.4) this still ends up needing runtime
+ // initialization.
+ #define TLS_INITIALIZER {0}
+
+ // A key representing one value stored in TLS.
+ // Initialize like
+ // ThreadLocalStorage::StaticSlot my_slot = TLS_INITIALIZER;
+ // If you're not using a static variable, use the convenience class
+ // ThreadLocalStorage::Slot (below) instead.
+ struct BASE_EXPORT StaticSlot {
+ // Set up the TLS slot. Called by the constructor.
+ // 'destructor' is a pointer to a function to perform per-thread cleanup of
+ // this object. If set to NULL, no cleanup is done for this TLS slot.
+ // Returns false on error.
+ bool Initialize(TLSDestructorFunc destructor);
+
+ // Free a previously allocated TLS 'slot'.
+ // If a destructor was set for this slot, removes
+ // the destructor so that remaining threads exiting
+ // will not free data.
+ void Free();
+
+ // Get the thread-local value stored in slot 'slot'.
+ // Values are guaranteed to initially be zero.
+ void* Get() const;
+
+ // Set the thread-local value stored in slot 'slot' to
+ // value 'value'.
+ void Set(void* value);
+
+ bool initialized() const { return initialized_; }
+
+ // The internals of this struct should be considered private.
+ bool initialized_;
+#if defined(OS_WIN)
+ int slot_;
+#elif defined(OS_POSIX)
+ pthread_key_t key_;
+#elif defined(OS_STARBOARD)
+ SbThreadLocalKey key_;
+#endif
+
+ };
+
+ // A convenience wrapper around StaticSlot with a constructor. Can be used
+ // as a member variable.
+ class BASE_EXPORT Slot : public StaticSlot {
+ public:
+ // Calls StaticSlot::Initialize().
+ explicit Slot(TLSDestructorFunc destructor = NULL);
+
+ private:
+ using StaticSlot::initialized_;
+#if defined(OS_WIN)
+ using StaticSlot::slot_;
+#elif defined(OS_POSIX)
+ using StaticSlot::key_;
+#elif defined(OS_STARBOARD)
+ using StaticSlot::key_;
+#endif
+ DISALLOW_COPY_AND_ASSIGN(Slot);
+ };
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadLocalStorage);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_LOCAL_STORAGE_H_
diff --git a/src/base/threading/thread_local_storage_posix.cc b/src/base/threading/thread_local_storage_posix.cc
new file mode 100644
index 0000000..75da5a7
--- /dev/null
+++ b/src/base/threading/thread_local_storage_posix.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 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/threading/thread_local_storage.h"
+
+#include "base/logging.h"
+
+namespace base {
+
+ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) {
+ initialized_ = false;
+ key_ = 0;
+ Initialize(destructor);
+}
+
+bool ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) {
+ DCHECK(!initialized_);
+ int error = pthread_key_create(&key_, destructor);
+ if (error) {
+ NOTREACHED();
+ return false;
+ }
+
+ initialized_ = true;
+ return true;
+}
+
+void ThreadLocalStorage::StaticSlot::Free() {
+ DCHECK(initialized_);
+ int error = pthread_key_delete(key_);
+ if (error)
+ NOTREACHED();
+ initialized_ = false;
+}
+
+void* ThreadLocalStorage::StaticSlot::Get() const {
+ DCHECK(initialized_);
+ return pthread_getspecific(key_);
+}
+
+void ThreadLocalStorage::StaticSlot::Set(void* value) {
+ DCHECK(initialized_);
+ int error = pthread_setspecific(key_, value);
+ if (error)
+ NOTREACHED();
+}
+
+} // namespace base
diff --git a/src/base/threading/thread_local_storage_shell.cc b/src/base/threading/thread_local_storage_shell.cc
new file mode 100644
index 0000000..4728401
--- /dev/null
+++ b/src/base/threading/thread_local_storage_shell.cc
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2012 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// We use the same code as posix version
+#include "thread_local_storage_posix.cc"
diff --git a/src/base/threading/thread_local_storage_starboard.cc b/src/base/threading/thread_local_storage_starboard.cc
new file mode 100644
index 0000000..db8e810
--- /dev/null
+++ b/src/base/threading/thread_local_storage_starboard.cc
@@ -0,0 +1,57 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/threading/thread_local_storage.h"
+
+#include "base/logging.h"
+
+namespace base {
+
+ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) {
+ initialized_ = false;
+ key_ = kSbThreadLocalKeyInvalid;
+ Initialize(destructor);
+}
+
+bool ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) {
+ DCHECK(!initialized_);
+ key_ = SbThreadCreateLocalKey(destructor);
+ if (!SbThreadIsValidLocalKey(key_)) {
+ NOTREACHED();
+ return false;
+ }
+
+ initialized_ = true;
+ return true;
+}
+
+void ThreadLocalStorage::StaticSlot::Free() {
+ DCHECK(initialized_);
+ SbThreadDestroyLocalKey(key_);
+ initialized_ = false;
+}
+
+void* ThreadLocalStorage::StaticSlot::Get() const {
+ DCHECK(initialized_);
+ return SbThreadGetLocalValue(key_);
+}
+
+void ThreadLocalStorage::StaticSlot::Set(void* value) {
+ DCHECK(initialized_);
+ if (!SbThreadSetLocalValue(key_, value)) {
+ NOTREACHED();
+ }
+}
+
+} // namespace base
diff --git a/src/base/threading/thread_local_storage_unittest.cc b/src/base/threading/thread_local_storage_unittest.cc
new file mode 100644
index 0000000..c613148
--- /dev/null
+++ b/src/base/threading/thread_local_storage_unittest.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2012 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.
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include <process.h>
+#endif
+
+#include "base/threading/simple_thread.h"
+#include "base/threading/thread_local_storage.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+// Ignore warnings about ptr->int conversions that we use when
+// storing ints into ThreadLocalStorage.
+#pragma warning(disable : 4311 4312)
+#endif
+
+namespace base {
+
+namespace {
+
+const int kInitialTlsValue = 0x5555;
+const int kFinalTlsValue = 0x7777;
+// How many times must a destructor be called before we really are done.
+const int kNumberDestructorCallRepetitions = 3;
+
+static ThreadLocalStorage::StaticSlot tls_slot = TLS_INITIALIZER;
+
+class ThreadLocalStorageRunner : public DelegateSimpleThread::Delegate {
+ public:
+ explicit ThreadLocalStorageRunner(int* tls_value_ptr)
+ : tls_value_ptr_(tls_value_ptr) {}
+
+ virtual ~ThreadLocalStorageRunner() {}
+
+ virtual void Run() OVERRIDE {
+ *tls_value_ptr_ = kInitialTlsValue;
+ tls_slot.Set(tls_value_ptr_);
+
+ int *ptr = static_cast<int*>(tls_slot.Get());
+ EXPECT_EQ(ptr, tls_value_ptr_);
+ EXPECT_EQ(*ptr, kInitialTlsValue);
+ *tls_value_ptr_ = 0;
+
+ ptr = static_cast<int*>(tls_slot.Get());
+ EXPECT_EQ(ptr, tls_value_ptr_);
+ EXPECT_EQ(*ptr, 0);
+
+ *ptr = kFinalTlsValue + kNumberDestructorCallRepetitions;
+ }
+
+ private:
+ int* tls_value_ptr_;
+ DISALLOW_COPY_AND_ASSIGN(ThreadLocalStorageRunner);
+};
+
+
+void ThreadLocalStorageCleanup(void *value) {
+ int *ptr = reinterpret_cast<int*>(value);
+ // Destructors should never be called with a NULL.
+ ASSERT_NE(reinterpret_cast<int*>(NULL), ptr);
+ if (*ptr == kFinalTlsValue)
+ return; // We've been called enough times.
+ ASSERT_LT(kFinalTlsValue, *ptr);
+ ASSERT_GE(kFinalTlsValue + kNumberDestructorCallRepetitions, *ptr);
+ --*ptr; // Move closer to our target.
+ // Tell tls that we're not done with this thread, and still need destruction.
+ tls_slot.Set(value);
+}
+
+} // namespace
+
+TEST(ThreadLocalStorageTest, Basics) {
+ ThreadLocalStorage::Slot slot;
+ slot.Set(reinterpret_cast<void*>(123));
+ int value = reinterpret_cast<intptr_t>(slot.Get());
+ EXPECT_EQ(value, 123);
+ slot.Free();
+}
+
+#if defined(THREAD_SANITIZER)
+// Do not run the test under ThreadSanitizer. Because this test iterates its
+// own TSD destructor for the maximum possible number of times, TSan can't jump
+// in after the last destructor invocation, therefore the destructor remains
+// unsynchronized with the following users of the same TSD slot. This results
+// in race reports between the destructor and functions in other tests.
+#define MAYBE_TLSDestructors DISABLED_TLSDestructors
+#else
+#define MAYBE_TLSDestructors TLSDestructors
+#endif
+TEST(ThreadLocalStorageTest, MAYBE_TLSDestructors) {
+ // Create a TLS index with a destructor. Create a set of
+ // threads that set the TLS, while the destructor cleans it up.
+ // After the threads finish, verify that the value is cleaned up.
+ const int kNumThreads = 5;
+ int values[kNumThreads];
+ ThreadLocalStorageRunner* thread_delegates[kNumThreads];
+ DelegateSimpleThread* threads[kNumThreads];
+
+ tls_slot.Initialize(ThreadLocalStorageCleanup);
+
+ // Spawn the threads.
+ for (int index = 0; index < kNumThreads; index++) {
+ values[index] = kInitialTlsValue;
+ thread_delegates[index] = new ThreadLocalStorageRunner(&values[index]);
+ threads[index] = new DelegateSimpleThread(thread_delegates[index],
+ "tls thread");
+ threads[index]->Start();
+ }
+
+ // Wait for the threads to finish.
+ for (int index = 0; index < kNumThreads; index++) {
+ threads[index]->Join();
+ delete threads[index];
+ delete thread_delegates[index];
+
+ // Verify that the destructor was called and that we reset.
+ EXPECT_EQ(values[index], kFinalTlsValue);
+ }
+ tls_slot.Free(); // Stop doing callbacks to cleanup threads.
+}
+
+} // namespace base
diff --git a/src/base/threading/thread_local_storage_win.cc b/src/base/threading/thread_local_storage_win.cc
new file mode 100644
index 0000000..0ae3cb4
--- /dev/null
+++ b/src/base/threading/thread_local_storage_win.cc
@@ -0,0 +1,277 @@
+// Copyright (c) 2012 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/threading/thread_local_storage.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+
+
+namespace {
+// In order to make TLS destructors work, we need to keep function
+// pointers to the destructor for each TLS that we allocate.
+// We make this work by allocating a single OS-level TLS, which
+// contains an array of slots for the application to use. In
+// parallel, we also allocate an array of destructors, which we
+// keep track of and call when threads terminate.
+
+// g_native_tls_key is the one native TLS that we use. It stores our table.
+long g_native_tls_key = TLS_OUT_OF_INDEXES;
+
+// g_last_used_tls_key is the high-water-mark of allocated thread local storage.
+// Each allocation is an index into our g_tls_destructors[]. Each such index is
+// assigned to the instance variable slot_ in a ThreadLocalStorage::Slot
+// instance. We reserve the value slot_ == 0 to indicate that the corresponding
+// instance of ThreadLocalStorage::Slot has been freed (i.e., destructor called,
+// etc.). This reserved use of 0 is then stated as the initial value of
+// g_last_used_tls_key, so that the first issued index will be 1.
+long g_last_used_tls_key = 0;
+
+// The maximum number of 'slots' in our thread local storage stack.
+const int kThreadLocalStorageSize = 64;
+
+// The maximum number of times to try to clear slots by calling destructors.
+// Use pthread naming convention for clarity.
+const int kMaxDestructorIterations = kThreadLocalStorageSize;
+
+// An array of destructor function pointers for the slots. If a slot has a
+// destructor, it will be stored in its corresponding entry in this array.
+// The elements are volatile to ensure that when the compiler reads the value
+// to potentially call the destructor, it does so once, and that value is tested
+// for null-ness and then used. Yes, that would be a weird de-optimization,
+// but I can imagine some register machines where it was just as easy to
+// re-fetch an array element, and I want to be sure a call to free the key
+// (i.e., null out the destructor entry) that happens on a separate thread can't
+// hurt the racy calls to the destructors on another thread.
+volatile base::ThreadLocalStorage::TLSDestructorFunc
+ g_tls_destructors[kThreadLocalStorageSize];
+
+void** ConstructTlsVector() {
+ if (g_native_tls_key == TLS_OUT_OF_INDEXES) {
+ long value = TlsAlloc();
+ DCHECK(value != TLS_OUT_OF_INDEXES);
+
+ // Atomically test-and-set the tls_key. If the key is TLS_OUT_OF_INDEXES,
+ // go ahead and set it. Otherwise, do nothing, as another
+ // thread already did our dirty work.
+ if (TLS_OUT_OF_INDEXES != InterlockedCompareExchange(
+ &g_native_tls_key, value, TLS_OUT_OF_INDEXES)) {
+ // We've been shortcut. Another thread replaced g_native_tls_key first so
+ // we need to destroy our index and use the one the other thread got
+ // first.
+ TlsFree(value);
+ }
+ }
+ DCHECK(!TlsGetValue(g_native_tls_key));
+
+ // Some allocators, such as TCMalloc, make use of thread local storage.
+ // As a result, any attempt to call new (or malloc) will lazily cause such a
+ // system to initialize, which will include registering for a TLS key. If we
+ // are not careful here, then that request to create a key will call new back,
+ // and we'll have an infinite loop. We avoid that as follows:
+ // Use a stack allocated vector, so that we don't have dependence on our
+ // allocator until our service is in place. (i.e., don't even call new until
+ // after we're setup)
+ void* stack_allocated_tls_data[kThreadLocalStorageSize];
+ memset(stack_allocated_tls_data, 0, sizeof(stack_allocated_tls_data));
+ // Ensure that any rentrant calls change the temp version.
+ TlsSetValue(g_native_tls_key, stack_allocated_tls_data);
+
+ // Allocate an array to store our data.
+ void** tls_data = new void*[kThreadLocalStorageSize];
+ memcpy(tls_data, stack_allocated_tls_data, sizeof(stack_allocated_tls_data));
+ TlsSetValue(g_native_tls_key, tls_data);
+ return tls_data;
+}
+
+// Called when we terminate a thread, this function calls any TLS destructors
+// that are pending for this thread.
+void WinThreadExit() {
+ if (g_native_tls_key == TLS_OUT_OF_INDEXES)
+ return;
+
+ void** tls_data = static_cast<void**>(TlsGetValue(g_native_tls_key));
+ // Maybe we have never initialized TLS for this thread.
+ if (!tls_data)
+ return;
+
+ // Some allocators, such as TCMalloc, use TLS. As a result, when a thread
+ // terminates, one of the destructor calls we make may be to shut down an
+ // allocator. We have to be careful that after we've shutdown all of the
+ // known destructors (perchance including an allocator), that we don't call
+ // the allocator and cause it to resurrect itself (with no possibly destructor
+ // call to follow). We handle this problem as follows:
+ // Switch to using a stack allocated vector, so that we don't have dependence
+ // on our allocator after we have called all g_tls_destructors. (i.e., don't
+ // even call delete[] after we're done with destructors.)
+ void* stack_allocated_tls_data[kThreadLocalStorageSize];
+ memcpy(stack_allocated_tls_data, tls_data, sizeof(stack_allocated_tls_data));
+ // Ensure that any re-entrant calls change the temp version.
+ TlsSetValue(g_native_tls_key, stack_allocated_tls_data);
+ delete[] tls_data; // Our last dependence on an allocator.
+
+ int remaining_attempts = kMaxDestructorIterations;
+ bool need_to_scan_destructors = true;
+ while (need_to_scan_destructors) {
+ need_to_scan_destructors = false;
+ // Try to destroy the first-created-slot (which is slot 1) in our last
+ // destructor call. That user was able to function, and define a slot with
+ // no other services running, so perhaps it is a basic service (like an
+ // allocator) and should also be destroyed last. If we get the order wrong,
+ // then we'll itterate several more times, so it is really not that
+ // critical (but it might help).
+ for (int slot = g_last_used_tls_key; slot > 0; --slot) {
+ void* value = stack_allocated_tls_data[slot];
+ if (value == NULL)
+ continue;
+ base::ThreadLocalStorage::TLSDestructorFunc destructor =
+ g_tls_destructors[slot];
+ if (destructor == NULL)
+ continue;
+ stack_allocated_tls_data[slot] = NULL; // pre-clear the slot.
+ destructor(value);
+ // Any destructor might have called a different service, which then set
+ // a different slot to a non-NULL value. Hence we need to check
+ // the whole vector again. This is a pthread standard.
+ need_to_scan_destructors = true;
+ }
+ if (--remaining_attempts <= 0) {
+ NOTREACHED(); // Destructors might not have been called.
+ break;
+ }
+ }
+
+ // Remove our stack allocated vector.
+ TlsSetValue(g_native_tls_key, NULL);
+}
+
+} // namespace
+
+namespace base {
+
+ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) {
+ initialized_ = false;
+ slot_ = 0;
+ Initialize(destructor);
+}
+
+bool ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) {
+ if (g_native_tls_key == TLS_OUT_OF_INDEXES || !TlsGetValue(g_native_tls_key))
+ ConstructTlsVector();
+
+ // Grab a new slot.
+ slot_ = InterlockedIncrement(&g_last_used_tls_key);
+ DCHECK_GT(slot_, 0);
+ if (slot_ >= kThreadLocalStorageSize) {
+ NOTREACHED();
+ return false;
+ }
+
+ // Setup our destructor.
+ g_tls_destructors[slot_] = destructor;
+ initialized_ = true;
+ return true;
+}
+
+void ThreadLocalStorage::StaticSlot::Free() {
+ // At this time, we don't reclaim old indices for TLS slots.
+ // So all we need to do is wipe the destructor.
+ DCHECK_GT(slot_, 0);
+ DCHECK_LT(slot_, kThreadLocalStorageSize);
+ g_tls_destructors[slot_] = NULL;
+ slot_ = 0;
+ initialized_ = false;
+}
+
+void* ThreadLocalStorage::StaticSlot::Get() const {
+ void** tls_data = static_cast<void**>(TlsGetValue(g_native_tls_key));
+ if (!tls_data)
+ tls_data = ConstructTlsVector();
+ DCHECK_GT(slot_, 0);
+ DCHECK_LT(slot_, kThreadLocalStorageSize);
+ return tls_data[slot_];
+}
+
+void ThreadLocalStorage::StaticSlot::Set(void* value) {
+ void** tls_data = static_cast<void**>(TlsGetValue(g_native_tls_key));
+ if (!tls_data)
+ tls_data = ConstructTlsVector();
+ DCHECK_GT(slot_, 0);
+ DCHECK_LT(slot_, kThreadLocalStorageSize);
+ tls_data[slot_] = value;
+}
+
+} // namespace base
+
+// Thread Termination Callbacks.
+// Windows doesn't support a per-thread destructor with its
+// TLS primitives. So, we build it manually by inserting a
+// function to be called on each thread's exit.
+// This magic is from http://www.codeproject.com/threads/tls.asp
+// and it works for VC++ 7.0 and later.
+
+// Force a reference to _tls_used to make the linker create the TLS directory
+// if it's not already there. (e.g. if __declspec(thread) is not used).
+// Force a reference to p_thread_callback_base to prevent whole program
+// optimization from discarding the variable.
+#ifdef _WIN64
+
+#pragma comment(linker, "/INCLUDE:_tls_used")
+#pragma comment(linker, "/INCLUDE:p_thread_callback_base")
+
+#else // _WIN64
+
+#pragma comment(linker, "/INCLUDE:__tls_used")
+#pragma comment(linker, "/INCLUDE:_p_thread_callback_base")
+
+#endif // _WIN64
+
+// Static callback function to call with each thread termination.
+void NTAPI OnThreadExit(PVOID module, DWORD reason, PVOID reserved) {
+ // On XP SP0 & SP1, the DLL_PROCESS_ATTACH is never seen. It is sent on SP2+
+ // and on W2K and W2K3. So don't assume it is sent.
+ if (DLL_THREAD_DETACH == reason || DLL_PROCESS_DETACH == reason)
+ WinThreadExit();
+}
+
+// .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are
+// called automatically by the OS loader code (not the CRT) when the module is
+// loaded and on thread creation. They are NOT called if the module has been
+// loaded by a LoadLibrary() call. It must have implicitly been loaded at
+// process startup.
+// By implicitly loaded, I mean that it is directly referenced by the main EXE
+// or by one of its dependent DLLs. Delay-loaded DLL doesn't count as being
+// implicitly loaded.
+//
+// See VC\crt\src\tlssup.c for reference.
+
+// extern "C" suppresses C++ name mangling so we know the symbol name for the
+// linker /INCLUDE:symbol pragma above.
+extern "C" {
+// The linker must not discard p_thread_callback_base. (We force a reference
+// to this variable with a linker /INCLUDE:symbol pragma to ensure that.) If
+// this variable is discarded, the OnThreadExit function will never be called.
+#ifdef _WIN64
+
+// .CRT section is merged with .rdata on x64 so it must be constant data.
+#pragma const_seg(".CRT$XLB")
+// When defining a const variable, it must have external linkage to be sure the
+// linker doesn't discard it.
+extern const PIMAGE_TLS_CALLBACK p_thread_callback_base;
+const PIMAGE_TLS_CALLBACK p_thread_callback_base = OnThreadExit;
+
+// Reset the default section.
+#pragma const_seg()
+
+#else // _WIN64
+
+#pragma data_seg(".CRT$XLB")
+PIMAGE_TLS_CALLBACK p_thread_callback_base = OnThreadExit;
+
+// Reset the default section.
+#pragma data_seg()
+
+#endif // _WIN64
+} // extern "C"
diff --git a/src/base/threading/thread_local_unittest.cc b/src/base/threading/thread_local_unittest.cc
new file mode 100644
index 0000000..b125a48
--- /dev/null
+++ b/src/base/threading/thread_local_unittest.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2012 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"
+#include "base/threading/simple_thread.h"
+#include "base/threading/thread_local.h"
+#include "base/synchronization/waitable_event.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class ThreadLocalTesterBase : public base::DelegateSimpleThreadPool::Delegate {
+ public:
+ typedef base::ThreadLocalPointer<ThreadLocalTesterBase> TLPType;
+
+ ThreadLocalTesterBase(TLPType* tlp, base::WaitableEvent* done)
+ : tlp_(tlp),
+ done_(done) {
+ }
+ virtual ~ThreadLocalTesterBase() {}
+
+ protected:
+ TLPType* tlp_;
+ base::WaitableEvent* done_;
+};
+
+class SetThreadLocal : public ThreadLocalTesterBase {
+ public:
+ SetThreadLocal(TLPType* tlp, base::WaitableEvent* done)
+ : ThreadLocalTesterBase(tlp, done),
+ val_(NULL) {
+ }
+ virtual ~SetThreadLocal() {}
+
+ void set_value(ThreadLocalTesterBase* val) { val_ = val; }
+
+ virtual void Run() OVERRIDE {
+ DCHECK(!done_->IsSignaled());
+ tlp_->Set(val_);
+ done_->Signal();
+ }
+
+ private:
+ ThreadLocalTesterBase* val_;
+};
+
+class GetThreadLocal : public ThreadLocalTesterBase {
+ public:
+ GetThreadLocal(TLPType* tlp, base::WaitableEvent* done)
+ : ThreadLocalTesterBase(tlp, done),
+ ptr_(NULL) {
+ }
+ virtual ~GetThreadLocal() {}
+
+ void set_ptr(ThreadLocalTesterBase** ptr) { ptr_ = ptr; }
+
+ virtual void Run() OVERRIDE {
+ DCHECK(!done_->IsSignaled());
+ *ptr_ = tlp_->Get();
+ done_->Signal();
+ }
+
+ private:
+ ThreadLocalTesterBase** ptr_;
+};
+
+} // namespace
+
+// In this test, we start 2 threads which will access a ThreadLocalPointer. We
+// make sure the default is NULL, and the pointers are unique to the threads.
+TEST(ThreadLocalTest, Pointer) {
+ base::DelegateSimpleThreadPool tp1("ThreadLocalTest tp1", 1);
+ base::DelegateSimpleThreadPool tp2("ThreadLocalTest tp1", 1);
+ tp1.Start();
+ tp2.Start();
+
+ base::ThreadLocalPointer<ThreadLocalTesterBase> tlp;
+
+ static ThreadLocalTesterBase* const kBogusPointer =
+ reinterpret_cast<ThreadLocalTesterBase*>(0x1234);
+
+ ThreadLocalTesterBase* tls_val;
+ base::WaitableEvent done(true, false);
+
+ GetThreadLocal getter(&tlp, &done);
+ getter.set_ptr(&tls_val);
+
+ // Check that both threads defaulted to NULL.
+ tls_val = kBogusPointer;
+ done.Reset();
+ tp1.AddWork(&getter);
+ done.Wait();
+ EXPECT_EQ(static_cast<ThreadLocalTesterBase*>(NULL), tls_val);
+
+ tls_val = kBogusPointer;
+ done.Reset();
+ tp2.AddWork(&getter);
+ done.Wait();
+ EXPECT_EQ(static_cast<ThreadLocalTesterBase*>(NULL), tls_val);
+
+
+ SetThreadLocal setter(&tlp, &done);
+ setter.set_value(kBogusPointer);
+
+ // Have thread 1 set their pointer value to kBogusPointer.
+ done.Reset();
+ tp1.AddWork(&setter);
+ done.Wait();
+
+ tls_val = NULL;
+ done.Reset();
+ tp1.AddWork(&getter);
+ done.Wait();
+ EXPECT_EQ(kBogusPointer, tls_val);
+
+ // Make sure thread 2 is still NULL
+ tls_val = kBogusPointer;
+ done.Reset();
+ tp2.AddWork(&getter);
+ done.Wait();
+ EXPECT_EQ(static_cast<ThreadLocalTesterBase*>(NULL), tls_val);
+
+ // Set thread 2 to kBogusPointer + 1.
+ setter.set_value(kBogusPointer + 1);
+
+ done.Reset();
+ tp2.AddWork(&setter);
+ done.Wait();
+
+ tls_val = NULL;
+ done.Reset();
+ tp2.AddWork(&getter);
+ done.Wait();
+ EXPECT_EQ(kBogusPointer + 1, tls_val);
+
+ // Make sure thread 1 is still kBogusPointer.
+ tls_val = NULL;
+ done.Reset();
+ tp1.AddWork(&getter);
+ done.Wait();
+ EXPECT_EQ(kBogusPointer, tls_val);
+
+ tp1.JoinAll();
+ tp2.JoinAll();
+}
+
+TEST(ThreadLocalTest, Boolean) {
+ {
+ base::ThreadLocalBoolean tlb;
+ EXPECT_FALSE(tlb.Get());
+
+ tlb.Set(false);
+ EXPECT_FALSE(tlb.Get());
+
+ tlb.Set(true);
+ EXPECT_TRUE(tlb.Get());
+ }
+
+ // Our slot should have been freed, we're all reset.
+ {
+ base::ThreadLocalBoolean tlb;
+ EXPECT_FALSE(tlb.Get());
+ }
+}
+
+} // namespace base
diff --git a/src/base/threading/thread_local_win.cc b/src/base/threading/thread_local_win.cc
new file mode 100644
index 0000000..56d3a3a
--- /dev/null
+++ b/src/base/threading/thread_local_win.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_local.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+
+namespace base {
+
+namespace internal {
+
+// static
+void ThreadLocalPlatform::AllocateSlot(SlotType& slot) {
+ slot = TlsAlloc();
+ CHECK_NE(slot, TLS_OUT_OF_INDEXES);
+}
+
+// static
+void ThreadLocalPlatform::FreeSlot(SlotType& slot) {
+ if (!TlsFree(slot)) {
+ NOTREACHED() << "Failed to deallocate tls slot with TlsFree().";
+ }
+}
+
+// static
+void* ThreadLocalPlatform::GetValueFromSlot(SlotType& slot) {
+ return TlsGetValue(slot);
+}
+
+// static
+void ThreadLocalPlatform::SetValueInSlot(SlotType& slot, void* value) {
+ if (!TlsSetValue(slot, value)) {
+ LOG(FATAL) << "Failed to TlsSetValue().";
+ }
+}
+
+} // namespace internal
+
+} // namespace base
diff --git a/src/base/threading/thread_restrictions.cc b/src/base/threading/thread_restrictions.cc
new file mode 100644
index 0000000..abf5068
--- /dev/null
+++ b/src/base/threading/thread_restrictions.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2012 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/threading/thread_restrictions.h"
+
+#if ENABLE_THREAD_RESTRICTIONS
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/thread_local.h"
+
+namespace base {
+
+namespace {
+
+LazyInstance<ThreadLocalBoolean>::Leaky
+ g_io_disallowed = LAZY_INSTANCE_INITIALIZER;
+
+LazyInstance<ThreadLocalBoolean>::Leaky
+ g_singleton_disallowed = LAZY_INSTANCE_INITIALIZER;
+
+LazyInstance<ThreadLocalBoolean>::Leaky
+ g_wait_disallowed = LAZY_INSTANCE_INITIALIZER;
+
+} // anonymous namespace
+
+// static
+bool ThreadRestrictions::SetIOAllowed(bool allowed) {
+ bool previous_disallowed = g_io_disallowed.Get().Get();
+ g_io_disallowed.Get().Set(!allowed);
+ return !previous_disallowed;
+}
+
+// static
+void ThreadRestrictions::AssertIOAllowed() {
+ if (g_io_disallowed.Get().Get()) {
+ LOG(FATAL) <<
+ "Function marked as IO-only was called from a thread that "
+ "disallows IO! If this thread really should be allowed to "
+ "make IO calls, adjust the call to "
+ "base::ThreadRestrictions::SetIOAllowed() in this thread's "
+ "startup.";
+ }
+}
+
+#ifdef COBALT
+// static
+bool ThreadRestrictions::GetSingletonAllowed() {
+ return !g_singleton_disallowed.Get().Get();
+}
+#endif // COBALT
+
+// static
+bool ThreadRestrictions::SetSingletonAllowed(bool allowed) {
+ bool previous_disallowed = g_singleton_disallowed.Get().Get();
+ g_singleton_disallowed.Get().Set(!allowed);
+ return !previous_disallowed;
+}
+
+// static
+void ThreadRestrictions::AssertSingletonAllowed() {
+ if (g_singleton_disallowed.Get().Get()) {
+ LOG(FATAL) << "LazyInstance/Singleton is not allowed to be used on this "
+ << "thread. Most likely it's because this thread is not "
+ << "joinable, so AtExitManager may have deleted the object "
+ << "on shutdown, leading to a potential shutdown crash.";
+ }
+}
+
+// static
+void ThreadRestrictions::DisallowWaiting() {
+ g_wait_disallowed.Get().Set(true);
+}
+
+// static
+void ThreadRestrictions::AssertWaitAllowed() {
+ if (g_wait_disallowed.Get().Get()) {
+ LOG(FATAL) << "Waiting is not allowed to be used on this thread to prevent"
+ << "jank and deadlock.";
+ }
+}
+
+bool ThreadRestrictions::SetWaitAllowed(bool allowed) {
+ bool previous_disallowed = g_wait_disallowed.Get().Get();
+ g_wait_disallowed.Get().Set(!allowed);
+ return !previous_disallowed;
+}
+
+} // namespace base
+
+#endif // ENABLE_THREAD_RESTRICTIONS
diff --git a/src/base/threading/thread_restrictions.h b/src/base/threading/thread_restrictions.h
new file mode 100644
index 0000000..7f9f655
--- /dev/null
+++ b/src/base/threading/thread_restrictions.h
@@ -0,0 +1,245 @@
+// Copyright (c) 2012 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_THREADING_THREAD_RESTRICTIONS_H_
+#define BASE_THREADING_THREAD_RESTRICTIONS_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+// See comment at top of thread_checker.h
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
+#define ENABLE_THREAD_RESTRICTIONS 1
+#else
+#define ENABLE_THREAD_RESTRICTIONS 0
+#endif
+
+class AcceleratedPresenter;
+class BrowserProcessImpl;
+class HistogramSynchronizer;
+class MetricsService;
+class NativeBackendKWallet;
+class ScopedAllowWaitForLegacyWebViewApi;
+class TestingAutomationProvider;
+
+namespace browser_sync {
+class NonFrontendDataTypeController;
+class UIModelWorker;
+}
+namespace cc {
+class CompletionEvent;
+}
+namespace chromeos {
+class AudioMixerAlsa;
+class BlockingMethodCaller;
+namespace system {
+class StatisticsProviderImpl;
+}
+}
+namespace chrome_browser_net {
+class Predictor;
+}
+namespace content {
+class BrowserGpuChannelHostFactory;
+class GLHelper;
+class GpuChannelHost;
+class RenderWidgetHelper;
+class TextInputClientMac;
+}
+namespace dbus {
+class Bus;
+}
+namespace disk_cache {
+class BackendImpl;
+class InFlightIO;
+}
+namespace media {
+class AudioOutputController;
+}
+namespace net {
+class FileStreamPosix;
+class FileStreamWin;
+namespace internal {
+class AddressTrackerLinux;
+}
+}
+
+namespace remoting {
+class AutoThread;
+}
+
+namespace base {
+
+class SequencedWorkerPool;
+class SimpleThread;
+class Thread;
+class ThreadTestHelper;
+
+// Certain behavior is disallowed on certain threads. ThreadRestrictions helps
+// enforce these rules. Examples of such rules:
+//
+// * Do not do blocking IO (makes the thread janky)
+// * Do not access Singleton/LazyInstance (may lead to shutdown crashes)
+//
+// Here's more about how the protection works:
+//
+// 1) If a thread should not be allowed to make IO calls, mark it:
+// base::ThreadRestrictions::SetIOAllowed(false);
+// By default, threads *are* allowed to make IO calls.
+// In Chrome browser code, IO calls should be proxied to the File thread.
+//
+// 2) If a function makes a call that will go out to disk, check whether the
+// current thread is allowed:
+// base::ThreadRestrictions::AssertIOAllowed();
+//
+//
+// Style tip: where should you put AssertIOAllowed checks? It's best
+// if you put them as close to the disk access as possible, at the
+// lowest level. This rule is simple to follow and helps catch all
+// callers. For example, if your function GoDoSomeBlockingDiskCall()
+// only calls other functions in Chrome and not fopen(), you should go
+// add the AssertIOAllowed checks in the helper functions.
+
+class BASE_EXPORT ThreadRestrictions {
+ public:
+ // Constructing a ScopedAllowIO temporarily allows IO for the current
+ // thread. Doing this is almost certainly always incorrect.
+ class BASE_EXPORT ScopedAllowIO {
+ public:
+ ScopedAllowIO() { previous_value_ = SetIOAllowed(true); }
+ ~ScopedAllowIO() { SetIOAllowed(previous_value_); }
+ private:
+ // Whether IO is allowed when the ScopedAllowIO was constructed.
+ bool previous_value_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAllowIO);
+ };
+
+ // Constructing a ScopedAllowSingleton temporarily allows accessing for the
+ // current thread. Doing this is almost always incorrect.
+ class BASE_EXPORT ScopedAllowSingleton {
+ public:
+ ScopedAllowSingleton() { previous_value_ = SetSingletonAllowed(true); }
+ ~ScopedAllowSingleton() { SetSingletonAllowed(previous_value_); }
+ private:
+ // Whether singleton use is allowed when the ScopedAllowSingleton was
+ // constructed.
+ bool previous_value_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAllowSingleton);
+ };
+
+#if ENABLE_THREAD_RESTRICTIONS
+ // Set whether the current thread to make IO calls.
+ // Threads start out in the *allowed* state.
+ // Returns the previous value.
+ static bool SetIOAllowed(bool allowed);
+
+ // Check whether the current thread is allowed to make IO calls,
+ // and DCHECK if not. See the block comment above the class for
+ // a discussion of where to add these checks.
+ static void AssertIOAllowed();
+
+#if defined(COBALT)
+ // Get whether the current thread can use singletons.
+ static bool GetSingletonAllowed();
+#endif // COBALT
+
+ // Set whether the current thread can use singletons. Returns the previous
+ // value.
+ static bool SetSingletonAllowed(bool allowed);
+
+ // Check whether the current thread is allowed to use singletons (Singleton /
+ // LazyInstance). DCHECKs if not.
+ static void AssertSingletonAllowed();
+
+ // Disable waiting on the current thread. Threads start out in the *allowed*
+ // state. Returns the previous value.
+ static void DisallowWaiting();
+
+ // Check whether the current thread is allowed to wait, and DCHECK if not.
+ static void AssertWaitAllowed();
+#else
+ // Inline the empty definitions of these functions so that they can be
+ // compiled out.
+ static bool SetIOAllowed(bool /*allowed*/) { return true; }
+ static void AssertIOAllowed() {}
+#if defined(COBALT)
+ static bool GetSingletonAllowed() { return true; }
+#endif // COBALT
+ static bool SetSingletonAllowed(bool /*allowed*/) { return true; }
+ static void AssertSingletonAllowed() {}
+ static void DisallowWaiting() {}
+ static void AssertWaitAllowed() {}
+#endif
+
+ private:
+ // DO NOT ADD ANY OTHER FRIEND STATEMENTS, talk to jam or brettw first.
+ // BEGIN ALLOWED USAGE.
+ friend class content::RenderWidgetHelper;
+ friend class ::HistogramSynchronizer;
+ friend class ::ScopedAllowWaitForLegacyWebViewApi;
+ friend class ::TestingAutomationProvider;
+ friend class cc::CompletionEvent;
+ friend class remoting::AutoThread;
+ friend class MessagePumpDefault;
+ friend class SequencedWorkerPool;
+ friend class SimpleThread;
+ friend class Thread;
+ friend class ThreadTestHelper;
+
+ // END ALLOWED USAGE.
+ // BEGIN USAGE THAT NEEDS TO BE FIXED.
+ friend class ::chromeos::AudioMixerAlsa; // http://crbug.com/125206
+ friend class ::chromeos::BlockingMethodCaller; // http://crbug.com/125360
+ friend class ::chromeos::system::StatisticsProviderImpl; // http://crbug.com/125385
+ friend class browser_sync::NonFrontendDataTypeController; // http://crbug.com/19757
+ friend class browser_sync::UIModelWorker; // http://crbug.com/19757
+ friend class chrome_browser_net::Predictor; // http://crbug.com/78451
+ friend class
+ content::BrowserGpuChannelHostFactory; // http://crbug.com/125248
+ friend class content::GLHelper; // http://crbug.com/125415
+ friend class content::GpuChannelHost; // http://crbug.com/125264
+ friend class content::TextInputClientMac; // http://crbug.com/121917
+ friend class dbus::Bus; // http://crbug.com/125222
+ friend class disk_cache::BackendImpl; // http://crbug.com/74623
+ friend class disk_cache::InFlightIO; // http://crbug.com/74623
+ friend class media::AudioOutputController; // http://crbug.com/120973
+ friend class net::FileStreamPosix; // http://crbug.com/115067
+ friend class net::FileStreamWin; // http://crbug.com/115067
+ friend class net::internal::AddressTrackerLinux; // http://crbug.com/125097
+ friend class ::AcceleratedPresenter; // http://crbug.com/125391
+ friend class ::BrowserProcessImpl; // http://crbug.com/125207
+ friend class ::MetricsService; // http://crbug.com/124954
+ friend class ::NativeBackendKWallet; // http://crbug.com/125331
+ // END USAGE THAT NEEDS TO BE FIXED.
+
+#if ENABLE_THREAD_RESTRICTIONS
+ static bool SetWaitAllowed(bool allowed);
+#else
+ static bool SetWaitAllowed(bool /*allowed*/) { return true; }
+#endif
+
+ // Constructing a ScopedAllowWait temporarily allows waiting on the current
+ // thread. Doing this is almost always incorrect, which is why we limit who
+ // can use this through friend. If you find yourself needing to use this, find
+ // another way. Talk to jam or brettw.
+ class BASE_EXPORT ScopedAllowWait {
+ public:
+ ScopedAllowWait() { previous_value_ = SetWaitAllowed(true); }
+ ~ScopedAllowWait() { SetWaitAllowed(previous_value_); }
+ private:
+ // Whether singleton use is allowed when the ScopedAllowWait was
+ // constructed.
+ bool previous_value_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAllowWait);
+ };
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ThreadRestrictions);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_RESTRICTIONS_H_
diff --git a/src/base/threading/thread_unittest.cc b/src/base/threading/thread_unittest.cc
new file mode 100644
index 0000000..28696d9
--- /dev/null
+++ b/src/base/threading/thread_unittest.cc
@@ -0,0 +1,237 @@
+// Copyright (c) 2012 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/threading/thread.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+using base::Thread;
+
+typedef PlatformTest ThreadTest;
+
+namespace {
+
+void ToggleValue(bool* value) {
+ ANNOTATE_BENIGN_RACE(value, "Test-only data race on boolean "
+ "in base/thread_unittest");
+ *value = !*value;
+}
+
+class SleepInsideInitThread : public Thread {
+ public:
+ SleepInsideInitThread() : Thread("none") {
+ init_called_ = false;
+ ANNOTATE_BENIGN_RACE(
+ this, "Benign test-only data race on vptr - http://crbug.com/98219");
+ }
+ virtual ~SleepInsideInitThread() {
+ Stop();
+ }
+
+ virtual void Init() OVERRIDE {
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(500));
+ init_called_ = true;
+ }
+ bool InitCalled() { return init_called_; }
+ private:
+ bool init_called_;
+};
+
+enum ThreadEvent {
+ // Thread::Init() was called.
+ THREAD_EVENT_INIT = 0,
+
+ // The MessageLoop for the thread was deleted.
+ THREAD_EVENT_MESSAGE_LOOP_DESTROYED,
+
+ // Thread::CleanUp() was called.
+ THREAD_EVENT_CLEANUP,
+
+ // Keep at end of list.
+ THREAD_NUM_EVENTS
+};
+
+typedef std::vector<ThreadEvent> EventList;
+
+class CaptureToEventList : public Thread {
+ public:
+ // This Thread pushes events into the vector |event_list| to show
+ // the order they occured in. |event_list| must remain valid for the
+ // lifetime of this thread.
+ explicit CaptureToEventList(EventList* event_list)
+ : Thread("none"),
+ event_list_(event_list) {
+ }
+
+ virtual ~CaptureToEventList() {
+ Stop();
+ }
+
+ virtual void Init() OVERRIDE {
+ event_list_->push_back(THREAD_EVENT_INIT);
+ }
+
+ virtual void CleanUp() OVERRIDE {
+ event_list_->push_back(THREAD_EVENT_CLEANUP);
+ }
+
+ private:
+ EventList* event_list_;
+};
+
+// Observer that writes a value into |event_list| when a message loop has been
+// destroyed.
+class CapturingDestructionObserver : public MessageLoop::DestructionObserver {
+ public:
+ // |event_list| must remain valid throughout the observer's lifetime.
+ explicit CapturingDestructionObserver(EventList* event_list)
+ : event_list_(event_list) {
+ }
+
+ // DestructionObserver implementation:
+ virtual void WillDestroyCurrentMessageLoop() OVERRIDE {
+ event_list_->push_back(THREAD_EVENT_MESSAGE_LOOP_DESTROYED);
+ event_list_ = NULL;
+ }
+
+ private:
+ EventList* event_list_;
+};
+
+// Task that adds a destruction observer to the current message loop.
+void RegisterDestructionObserver(MessageLoop::DestructionObserver* observer) {
+ MessageLoop::current()->AddDestructionObserver(observer);
+}
+
+} // namespace
+
+TEST_F(ThreadTest, Restart) {
+ Thread a("Restart");
+ a.Stop();
+ EXPECT_FALSE(a.message_loop());
+ EXPECT_FALSE(a.IsRunning());
+ EXPECT_TRUE(a.Start());
+ EXPECT_TRUE(a.message_loop());
+ EXPECT_TRUE(a.IsRunning());
+ a.Stop();
+ EXPECT_FALSE(a.message_loop());
+ EXPECT_FALSE(a.IsRunning());
+ EXPECT_TRUE(a.Start());
+ EXPECT_TRUE(a.message_loop());
+ EXPECT_TRUE(a.IsRunning());
+ a.Stop();
+ EXPECT_FALSE(a.message_loop());
+ EXPECT_FALSE(a.IsRunning());
+ a.Stop();
+ EXPECT_FALSE(a.message_loop());
+ EXPECT_FALSE(a.IsRunning());
+}
+
+TEST_F(ThreadTest, StartWithOptions_StackSize) {
+ Thread a("StartWithStackSize");
+ // Ensure that the thread can work with only 12 kb and still process a
+ // message.
+ Thread::Options options;
+ options.stack_size = 12*1024;
+ EXPECT_TRUE(a.StartWithOptions(options));
+ EXPECT_TRUE(a.message_loop());
+ EXPECT_TRUE(a.IsRunning());
+
+ bool was_invoked = false;
+ a.message_loop()->PostTask(FROM_HERE, base::Bind(&ToggleValue, &was_invoked));
+
+ // wait for the task to run (we could use a kernel event here
+ // instead to avoid busy waiting, but this is sufficient for
+ // testing purposes).
+ for (int i = 100; i >= 0 && !was_invoked; --i) {
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
+ }
+ EXPECT_TRUE(was_invoked);
+}
+
+TEST_F(ThreadTest, TwoTasks) {
+ bool was_invoked = false;
+ {
+ Thread a("TwoTasks");
+ EXPECT_TRUE(a.Start());
+ EXPECT_TRUE(a.message_loop());
+
+ // Test that all events are dispatched before the Thread object is
+ // destroyed. We do this by dispatching a sleep event before the
+ // event that will toggle our sentinel value.
+ a.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ static_cast<void (*)(base::TimeDelta)>(
+ &base::PlatformThread::Sleep),
+ base::TimeDelta::FromMilliseconds(20)));
+ a.message_loop()->PostTask(FROM_HERE, base::Bind(&ToggleValue,
+ &was_invoked));
+ }
+ EXPECT_TRUE(was_invoked);
+}
+
+TEST_F(ThreadTest, StopSoon) {
+ Thread a("StopSoon");
+ EXPECT_TRUE(a.Start());
+ EXPECT_TRUE(a.message_loop());
+ EXPECT_TRUE(a.IsRunning());
+ a.StopSoon();
+ a.StopSoon();
+ a.Stop();
+ EXPECT_FALSE(a.message_loop());
+ EXPECT_FALSE(a.IsRunning());
+}
+
+TEST_F(ThreadTest, ThreadName) {
+ Thread a("ThreadName");
+ EXPECT_TRUE(a.Start());
+ EXPECT_EQ("ThreadName", a.thread_name());
+}
+
+// Make sure we can't use a thread between Start() and Init().
+TEST_F(ThreadTest, SleepInsideInit) {
+ SleepInsideInitThread t;
+ EXPECT_FALSE(t.InitCalled());
+ t.Start();
+ EXPECT_TRUE(t.InitCalled());
+}
+
+// Make sure that the destruction sequence is:
+//
+// (1) Thread::CleanUp()
+// (2) MessageLoop::~MessageLoop()
+// MessageLoop::DestructionObservers called.
+TEST_F(ThreadTest, CleanUp) {
+ EventList captured_events;
+ CapturingDestructionObserver loop_destruction_observer(&captured_events);
+
+ {
+ // Start a thread which writes its event into |captured_events|.
+ CaptureToEventList t(&captured_events);
+ EXPECT_TRUE(t.Start());
+ EXPECT_TRUE(t.message_loop());
+ EXPECT_TRUE(t.IsRunning());
+
+ // Register an observer that writes into |captured_events| once the
+ // thread's message loop is destroyed.
+ t.message_loop()->PostTask(
+ FROM_HERE, base::Bind(&RegisterDestructionObserver,
+ base::Unretained(&loop_destruction_observer)));
+
+ // Upon leaving this scope, the thread is deleted.
+ }
+
+ // Check the order of events during shutdown.
+ ASSERT_EQ(static_cast<size_t>(THREAD_NUM_EVENTS), captured_events.size());
+ EXPECT_EQ(THREAD_EVENT_INIT, captured_events[0]);
+ EXPECT_EQ(THREAD_EVENT_CLEANUP, captured_events[1]);
+ EXPECT_EQ(THREAD_EVENT_MESSAGE_LOOP_DESTROYED, captured_events[2]);
+}
diff --git a/src/base/threading/watchdog.cc b/src/base/threading/watchdog.cc
new file mode 100644
index 0000000..d060655
--- /dev/null
+++ b/src/base/threading/watchdog.cc
@@ -0,0 +1,178 @@
+// Copyright (c) 2012 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/threading/watchdog.h"
+
+#include "base/compiler_specific.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+namespace {
+
+// When the debugger breaks (when we alarm), all the other alarms that are
+// armed will expire (also alarm). To diminish this effect, we track any
+// delay due to debugger breaks, and we *try* to adjust the effective start
+// time of other alarms to step past the debugging break.
+// Without this safety net, any alarm will typically trigger a host of follow
+// on alarms from callers that specify old times.
+
+// Lock for access of static data...
+LazyInstance<Lock>::Leaky g_static_lock = LAZY_INSTANCE_INITIALIZER;
+
+// When did we last alarm and get stuck (for a while) in a debugger?
+TimeTicks g_last_debugged_alarm_time;
+
+// How long did we sit on a break in the debugger?
+TimeDelta g_last_debugged_alarm_delay;
+
+} // namespace
+
+// Start thread running in a Disarmed state.
+Watchdog::Watchdog(const TimeDelta& duration,
+ const std::string& thread_watched_name,
+ bool enabled)
+ : enabled_(enabled),
+ lock_(),
+ condition_variable_(&lock_),
+ state_(DISARMED),
+ duration_(duration),
+ thread_watched_name_(thread_watched_name),
+ ALLOW_THIS_IN_INITIALIZER_LIST(delegate_(this)) {
+ if (!enabled_)
+ return; // Don't start thread, or doing anything really.
+ enabled_ = PlatformThread::Create(0, // Default stack size.
+ &delegate_,
+ &handle_);
+ DCHECK(enabled_);
+}
+
+// Notify watchdog thread, and wait for it to finish up.
+Watchdog::~Watchdog() {
+ if (!enabled_)
+ return;
+ if (!IsJoinable())
+ Cleanup();
+ condition_variable_.Signal();
+ PlatformThread::Join(handle_);
+}
+
+void Watchdog::Cleanup() {
+ if (!enabled_)
+ return;
+ {
+ AutoLock lock(lock_);
+ state_ = SHUTDOWN;
+ }
+ condition_variable_.Signal();
+}
+
+bool Watchdog::IsJoinable() {
+ if (!enabled_)
+ return true;
+ AutoLock lock(lock_);
+ return (state_ == JOINABLE);
+}
+
+void Watchdog::Arm() {
+ ArmAtStartTime(TimeTicks::Now());
+}
+
+void Watchdog::ArmSomeTimeDeltaAgo(const TimeDelta& time_delta) {
+ ArmAtStartTime(TimeTicks::Now() - time_delta);
+}
+
+// Start clock for watchdog.
+void Watchdog::ArmAtStartTime(const TimeTicks start_time) {
+ {
+ AutoLock lock(lock_);
+ start_time_ = start_time;
+ state_ = ARMED;
+ }
+ // Force watchdog to wake up, and go to sleep with the timer ticking with the
+ // proper duration.
+ condition_variable_.Signal();
+}
+
+// Disable watchdog so that it won't do anything when time expires.
+void Watchdog::Disarm() {
+ AutoLock lock(lock_);
+ state_ = DISARMED;
+ // We don't need to signal, as the watchdog will eventually wake up, and it
+ // will check its state and time, and act accordingly.
+}
+
+void Watchdog::Alarm() {
+ DVLOG(1) << "Watchdog alarmed for " << thread_watched_name_;
+}
+
+//------------------------------------------------------------------------------
+// Internal private methods that the watchdog thread uses.
+
+void Watchdog::ThreadDelegate::ThreadMain() {
+ SetThreadName();
+ TimeDelta remaining_duration;
+ while (1) {
+ AutoLock lock(watchdog_->lock_);
+ while (DISARMED == watchdog_->state_)
+ watchdog_->condition_variable_.Wait();
+ if (SHUTDOWN == watchdog_->state_) {
+ watchdog_->state_ = JOINABLE;
+ return;
+ }
+ DCHECK(ARMED == watchdog_->state_);
+ remaining_duration = watchdog_->duration_ -
+ (TimeTicks::Now() - watchdog_->start_time_);
+ if (remaining_duration.InMilliseconds() > 0) {
+ // Spurios wake? Timer drifts? Go back to sleep for remaining time.
+ watchdog_->condition_variable_.TimedWait(remaining_duration);
+ continue;
+ }
+ // We overslept, so this seems like a real alarm.
+ // Watch out for a user that stopped the debugger on a different alarm!
+ {
+ AutoLock static_lock(*g_static_lock.Pointer());
+ if (g_last_debugged_alarm_time > watchdog_->start_time_) {
+ // False alarm: we started our clock before the debugger break (last
+ // alarm time).
+ watchdog_->start_time_ += g_last_debugged_alarm_delay;
+ if (g_last_debugged_alarm_time > watchdog_->start_time_)
+ // Too many alarms must have taken place.
+ watchdog_->state_ = DISARMED;
+ continue;
+ }
+ }
+ watchdog_->state_ = DISARMED; // Only alarm at most once.
+ TimeTicks last_alarm_time = TimeTicks::Now();
+ {
+ AutoUnlock lock(watchdog_->lock_);
+ watchdog_->Alarm(); // Set a break point here to debug on alarms.
+ }
+ TimeDelta last_alarm_delay = TimeTicks::Now() - last_alarm_time;
+ if (last_alarm_delay <= TimeDelta::FromMilliseconds(2))
+ continue;
+ // Ignore race of two alarms/breaks going off at roughly the same time.
+ AutoLock static_lock(*g_static_lock.Pointer());
+ // This was a real debugger break.
+ g_last_debugged_alarm_time = last_alarm_time;
+ g_last_debugged_alarm_delay = last_alarm_delay;
+ }
+}
+
+void Watchdog::ThreadDelegate::SetThreadName() const {
+ std::string name = watchdog_->thread_watched_name_ + " Watchdog";
+ PlatformThread::SetName(name.c_str());
+ DVLOG(1) << "Watchdog active: " << name;
+}
+
+// static
+void Watchdog::ResetStaticData() {
+ AutoLock lock(*g_static_lock.Pointer());
+ g_last_debugged_alarm_time = TimeTicks();
+ g_last_debugged_alarm_delay = TimeDelta();
+}
+
+} // namespace base
diff --git a/src/base/threading/watchdog.h b/src/base/threading/watchdog.h
new file mode 100644
index 0000000..5b0b210
--- /dev/null
+++ b/src/base/threading/watchdog.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2012 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.
+
+// The Watchdog class creates a second thread that can Alarm if a specific
+// duration of time passes without proper attention. The duration of time is
+// specified at construction time. The Watchdog may be used many times by
+// simply calling Arm() (to start timing) and Disarm() (to reset the timer).
+// The Watchdog is typically used under a debugger, where the stack traces on
+// other threads can be examined if/when the Watchdog alarms.
+
+// Some watchdogs will be enabled or disabled via command line switches. To
+// facilitate such code, an "enabled" argument for the constuctor can be used
+// to permanently disable the watchdog. Disabled watchdogs don't even spawn
+// a second thread, and their methods call (Arm() and Disarm()) return very
+// quickly.
+
+#ifndef BASE_THREADING_WATCHDOG_H_
+#define BASE_THREADING_WATCHDOG_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"
+#include "base/time.h"
+
+namespace base {
+
+class BASE_EXPORT Watchdog {
+ public:
+ // Constructor specifies how long the Watchdog will wait before alarming.
+ Watchdog(const TimeDelta& duration,
+ const std::string& thread_watched_name,
+ bool enabled);
+ virtual ~Watchdog();
+
+ // Notify watchdog thread to finish up. Sets the state_ to SHUTDOWN.
+ void Cleanup();
+
+ // Returns true if we state_ is JOINABLE (which indicates that Watchdog has
+ // exited).
+ bool IsJoinable();
+
+ // Start timing, and alarm when time expires (unless we're disarm()ed.)
+ void Arm(); // Arm starting now.
+ void ArmSomeTimeDeltaAgo(const TimeDelta& time_delta);
+ void ArmAtStartTime(const TimeTicks start_time);
+
+ // Reset time, and do not set off the alarm.
+ void Disarm();
+
+ // Alarm is called if the time expires after an Arm() without someone calling
+ // Disarm(). This method can be overridden to create testable classes.
+ virtual void Alarm();
+
+ // Reset static data to initial state. Useful for tests, to ensure
+ // they are independent.
+ static void ResetStaticData();
+
+ private:
+ class ThreadDelegate : public PlatformThread::Delegate {
+ public:
+ explicit ThreadDelegate(Watchdog* watchdog) : watchdog_(watchdog) {
+ }
+ virtual void ThreadMain() OVERRIDE;
+ private:
+ void SetThreadName() const;
+
+ Watchdog* watchdog_;
+ };
+
+ enum State {ARMED, DISARMED, SHUTDOWN, JOINABLE };
+
+ bool enabled_;
+
+ Lock lock_; // Mutex for state_.
+ ConditionVariable condition_variable_;
+ State state_;
+ const TimeDelta duration_; // How long after start_time_ do we alarm?
+ const std::string thread_watched_name_;
+ PlatformThreadHandle handle_;
+ ThreadDelegate delegate_; // Store it, because it must outlive the thread.
+
+ TimeTicks start_time_; // Start of epoch, and alarm after duration_.
+
+ DISALLOW_COPY_AND_ASSIGN(Watchdog);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_WATCHDOG_H_
diff --git a/src/base/threading/watchdog_unittest.cc b/src/base/threading/watchdog_unittest.cc
new file mode 100644
index 0000000..92ab02c
--- /dev/null
+++ b/src/base/threading/watchdog_unittest.cc
@@ -0,0 +1,142 @@
+// Copyright (c) 2012 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/threading/watchdog.h"
+
+#include "base/logging.h"
+#include "base/synchronization/spin_wait.h"
+#include "base/threading/platform_thread.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+//------------------------------------------------------------------------------
+// Provide a derived class to facilitate testing.
+
+class WatchdogCounter : public Watchdog {
+ public:
+ WatchdogCounter(const TimeDelta& duration,
+ const std::string& thread_watched_name,
+ bool enabled)
+ : Watchdog(duration, thread_watched_name, enabled),
+ alarm_counter_(0) {
+ }
+
+ virtual ~WatchdogCounter() {}
+
+ virtual void Alarm() OVERRIDE {
+ alarm_counter_++;
+ Watchdog::Alarm();
+ }
+
+ int alarm_counter() { return alarm_counter_; }
+
+ private:
+ int alarm_counter_;
+
+ DISALLOW_COPY_AND_ASSIGN(WatchdogCounter);
+};
+
+class WatchdogTest : public testing::Test {
+ public:
+ virtual void SetUp() OVERRIDE {
+ Watchdog::ResetStaticData();
+ }
+};
+
+} // namespace
+
+//------------------------------------------------------------------------------
+// Actual tests
+
+// Minimal constructor/destructor test.
+TEST_F(WatchdogTest, StartupShutdownTest) {
+ Watchdog watchdog1(TimeDelta::FromMilliseconds(300), "Disabled", false);
+ Watchdog watchdog2(TimeDelta::FromMilliseconds(300), "Enabled", true);
+}
+
+// Test ability to call Arm and Disarm repeatedly.
+TEST_F(WatchdogTest, ArmDisarmTest) {
+ Watchdog watchdog1(TimeDelta::FromMilliseconds(300), "Disabled", false);
+ watchdog1.Arm();
+ watchdog1.Disarm();
+ watchdog1.Arm();
+ watchdog1.Disarm();
+
+ Watchdog watchdog2(TimeDelta::FromMilliseconds(300), "Enabled", true);
+ watchdog2.Arm();
+ watchdog2.Disarm();
+ watchdog2.Arm();
+ watchdog2.Disarm();
+}
+
+// Make sure a basic alarm fires when the time has expired.
+TEST_F(WatchdogTest, AlarmTest) {
+ WatchdogCounter watchdog(TimeDelta::FromMilliseconds(10), "Enabled", true);
+ watchdog.Arm();
+ SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(5),
+ watchdog.alarm_counter() > 0);
+ EXPECT_EQ(1, watchdog.alarm_counter());
+}
+
+// Make sure a basic alarm fires when the time has expired.
+TEST_F(WatchdogTest, AlarmPriorTimeTest) {
+ WatchdogCounter watchdog(TimeDelta(), "Enabled2", true);
+ // Set a time in the past.
+ watchdog.ArmSomeTimeDeltaAgo(TimeDelta::FromSeconds(2));
+ // It should instantly go off, but certainly in less than 5 minutes.
+ SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(5),
+ watchdog.alarm_counter() > 0);
+
+ EXPECT_EQ(1, watchdog.alarm_counter());
+}
+
+// Make sure a disable alarm does nothing, even if we arm it.
+TEST_F(WatchdogTest, ConstructorDisabledTest) {
+ WatchdogCounter watchdog(TimeDelta::FromMilliseconds(10), "Disabled", false);
+ watchdog.Arm();
+ // Alarm should not fire, as it was disabled.
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(500));
+ EXPECT_EQ(0, watchdog.alarm_counter());
+}
+
+// Make sure Disarming will prevent firing, even after Arming.
+TEST_F(WatchdogTest, DisarmTest) {
+ WatchdogCounter watchdog(TimeDelta::FromSeconds(1), "Enabled3", true);
+
+ TimeTicks start = TimeTicks::Now();
+ watchdog.Arm();
+ // Sleep a bit, but not past the alarm point.
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+ watchdog.Disarm();
+ TimeTicks end = TimeTicks::Now();
+
+ if (end - start > TimeDelta::FromMilliseconds(500)) {
+ LOG(WARNING) << "100ms sleep took over 500ms, making the results of this "
+ << "timing-sensitive test suspicious. Aborting now.";
+ return;
+ }
+
+ // Alarm should not have fired before it was disarmed.
+ EXPECT_EQ(0, watchdog.alarm_counter());
+
+ // Sleep past the point where it would have fired if it wasn't disarmed,
+ // and verify that it didn't fire.
+ PlatformThread::Sleep(TimeDelta::FromSeconds(1));
+ EXPECT_EQ(0, watchdog.alarm_counter());
+
+ // ...but even after disarming, we can still use the alarm...
+ // Set a time greater than the timeout into the past.
+ watchdog.ArmSomeTimeDeltaAgo(TimeDelta::FromSeconds(10));
+ // It should almost instantly go off, but certainly in less than 5 minutes.
+ SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(5),
+ watchdog.alarm_counter() > 0);
+
+ EXPECT_EQ(1, watchdog.alarm_counter());
+}
+
+} // namespace base
diff --git a/src/base/threading/worker_pool.cc b/src/base/threading/worker_pool.cc
new file mode 100644
index 0000000..16cc061
--- /dev/null
+++ b/src/base/threading/worker_pool.cc
@@ -0,0 +1,116 @@
+// Copyright (c) 2012 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/threading/worker_pool.h"
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/lazy_instance.h"
+#include "base/task_runner.h"
+#include "base/threading/post_task_and_reply_impl.h"
+#include "base/tracked_objects.h"
+
+namespace base {
+
+namespace {
+
+class PostTaskAndReplyWorkerPool : public internal::PostTaskAndReplyImpl {
+ public:
+ PostTaskAndReplyWorkerPool(bool task_is_slow) : task_is_slow_(task_is_slow) {
+ }
+
+ private:
+ virtual bool PostTask(const tracked_objects::Location& from_here,
+ const Closure& task) OVERRIDE {
+ return WorkerPool::PostTask(from_here, task, task_is_slow_);
+ }
+
+ bool task_is_slow_;
+};
+
+// WorkerPoolTaskRunner ---------------------------------------------
+// A TaskRunner which posts tasks to a WorkerPool with a
+// fixed ShutdownBehavior.
+//
+// Note that this class is RefCountedThreadSafe (inherited from TaskRunner).
+class WorkerPoolTaskRunner : public TaskRunner {
+ public:
+ WorkerPoolTaskRunner(bool tasks_are_slow);
+
+ // TaskRunner implementation
+ virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) OVERRIDE;
+ virtual bool RunsTasksOnCurrentThread() const OVERRIDE;
+
+ private:
+ virtual ~WorkerPoolTaskRunner();
+
+ // Helper function for posting a delayed task. Asserts that the delay is
+ // zero because non-zero delays are not supported.
+ bool PostDelayedTaskAssertZeroDelay(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ base::TimeDelta delay);
+
+ const bool tasks_are_slow_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkerPoolTaskRunner);
+};
+
+WorkerPoolTaskRunner::WorkerPoolTaskRunner(bool tasks_are_slow)
+ : tasks_are_slow_(tasks_are_slow) {
+}
+
+WorkerPoolTaskRunner::~WorkerPoolTaskRunner() {
+}
+
+bool WorkerPoolTaskRunner::PostDelayedTask(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ TimeDelta delay) {
+ return PostDelayedTaskAssertZeroDelay(from_here, task, delay);
+}
+
+bool WorkerPoolTaskRunner::RunsTasksOnCurrentThread() const {
+ return WorkerPool::RunsTasksOnCurrentThread();
+}
+
+bool WorkerPoolTaskRunner::PostDelayedTaskAssertZeroDelay(
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ base::TimeDelta delay) {
+ DCHECK_EQ(delay.InMillisecondsRoundedUp(), 0)
+ << "WorkerPoolTaskRunner does not support non-zero delays";
+ return WorkerPool::PostTask(from_here, task, tasks_are_slow_);
+}
+
+struct TaskRunnerHolder {
+ TaskRunnerHolder() {
+ taskrunners_[0] = new WorkerPoolTaskRunner(false);
+ taskrunners_[1] = new WorkerPoolTaskRunner(true);
+ }
+ scoped_refptr<TaskRunner> taskrunners_[2];
+};
+
+base::LazyInstance<TaskRunnerHolder>::Leaky
+ g_taskrunners = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+bool WorkerPool::PostTaskAndReply(const tracked_objects::Location& from_here,
+ const Closure& task,
+ const Closure& reply,
+ bool task_is_slow) {
+ return PostTaskAndReplyWorkerPool(task_is_slow).PostTaskAndReply(
+ from_here, task, reply);
+}
+
+// static
+const scoped_refptr<TaskRunner>&
+WorkerPool::GetTaskRunner(bool tasks_are_slow) {
+ return g_taskrunners.Get().taskrunners_[tasks_are_slow];
+}
+
+} // namespace base
diff --git a/src/base/threading/worker_pool.h b/src/base/threading/worker_pool.h
new file mode 100644
index 0000000..333b495
--- /dev/null
+++ b/src/base/threading/worker_pool.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 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_THREADING_WORKER_POOL_H_
+#define BASE_THREADING_WORKER_POOL_H_
+
+#include "base/base_export.h"
+#include "base/callback_forward.h"
+#include "base/memory/ref_counted.h"
+
+class Task;
+
+namespace tracked_objects {
+class Location;
+} // namespace tracked_objects
+
+namespace base {
+
+class TaskRunner;
+
+// This is a facility that runs tasks that don't require a specific thread or
+// a message loop.
+//
+// WARNING: This shouldn't be used unless absolutely necessary. We don't wait
+// for the worker pool threads to finish on shutdown, so the tasks running
+// inside the pool must be extremely careful about other objects they access
+// (MessageLoops, Singletons, etc). During shutdown these object may no longer
+// exist.
+class BASE_EXPORT WorkerPool {
+ public:
+ // This function posts |task| to run on a worker thread. |task_is_slow|
+ // should be used for tasks that will take a long time to execute. Returns
+ // false if |task| could not be posted to a worker thread. Regardless of
+ // return value, ownership of |task| is transferred to the worker pool.
+ static bool PostTask(const tracked_objects::Location& from_here,
+ const base::Closure& task, bool task_is_slow);
+
+ // Just like MessageLoopProxy::PostTaskAndReply, except the destination
+ // for |task| is a worker thread and you can specify |task_is_slow| just
+ // like you can for PostTask above.
+ static bool PostTaskAndReply(const tracked_objects::Location& from_here,
+ const Closure& task,
+ const Closure& reply,
+ bool task_is_slow);
+
+ // Return true if the current thread is one that this WorkerPool runs tasks
+ // on. (Note that if the Windows worker pool is used without going through
+ // this WorkerPool interface, RunsTasksOnCurrentThread would return false on
+ // those threads.)
+ static bool RunsTasksOnCurrentThread();
+
+ // Get a TaskRunner wrapper which posts to the WorkerPool using the given
+ // |task_is_slow| behavior.
+ static const scoped_refptr<TaskRunner>& GetTaskRunner(bool task_is_slow);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_WORKER_POOL_H_
diff --git a/src/base/threading/worker_pool_posix.cc b/src/base/threading/worker_pool_posix.cc
new file mode 100644
index 0000000..2ad3925
--- /dev/null
+++ b/src/base/threading/worker_pool_posix.cc
@@ -0,0 +1,204 @@
+// Copyright (c) 2012 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/threading/worker_pool_posix.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/debug/trace_event.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/stringprintf.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread_local.h"
+#include "base/threading/worker_pool.h"
+#include "base/tracked_objects.h"
+
+using tracked_objects::TrackedTime;
+
+namespace base {
+
+namespace {
+
+base::LazyInstance<ThreadLocalBoolean>::Leaky
+ g_worker_pool_running_on_this_thread = LAZY_INSTANCE_INITIALIZER;
+
+const int kIdleSecondsBeforeExit = 10 * 60;
+
+#ifdef ADDRESS_SANITIZER
+const int kWorkerThreadStackSize = 256 * 1024;
+#else
+// A stack size of 64 KB is too small for the CERT_PKIXVerifyCert
+// function of NSS because of NSS bug 439169.
+const int kWorkerThreadStackSize = 128 * 1024;
+#endif
+
+class WorkerPoolImpl {
+ public:
+ WorkerPoolImpl();
+ ~WorkerPoolImpl();
+
+ void PostTask(const tracked_objects::Location& from_here,
+ const base::Closure& task, bool task_is_slow);
+
+ private:
+ scoped_refptr<base::PosixDynamicThreadPool> pool_;
+};
+
+WorkerPoolImpl::WorkerPoolImpl()
+ : pool_(new base::PosixDynamicThreadPool("WorkerPool",
+ kIdleSecondsBeforeExit)) {
+}
+
+WorkerPoolImpl::~WorkerPoolImpl() {
+ pool_->Terminate();
+}
+
+void WorkerPoolImpl::PostTask(const tracked_objects::Location& from_here,
+ const base::Closure& task, bool task_is_slow) {
+ pool_->PostTask(from_here, task);
+}
+
+base::LazyInstance<WorkerPoolImpl> g_lazy_worker_pool =
+ LAZY_INSTANCE_INITIALIZER;
+
+class WorkerThread : public PlatformThread::Delegate {
+ public:
+ WorkerThread(const std::string& name_prefix,
+ base::PosixDynamicThreadPool* pool)
+ : name_prefix_(name_prefix),
+ pool_(pool) {}
+
+ virtual void ThreadMain() OVERRIDE;
+
+ private:
+ const std::string name_prefix_;
+ scoped_refptr<base::PosixDynamicThreadPool> pool_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkerThread);
+};
+
+void WorkerThread::ThreadMain() {
+ g_worker_pool_running_on_this_thread.Get().Set(true);
+ const std::string name = base::StringPrintf(
+ "%s/%d", name_prefix_.c_str(), PlatformThread::CurrentId());
+ // Note |name.c_str()| must remain valid for for the whole life of the thread.
+ PlatformThread::SetName(name.c_str());
+
+ for (;;) {
+ PendingTask pending_task = pool_->WaitForTask();
+ if (pending_task.task.is_null())
+ break;
+ TRACE_EVENT2("task", "WorkerThread::ThreadMain::Run",
+ "src_file", pending_task.posted_from.file_name(),
+ "src_func", pending_task.posted_from.function_name());
+
+ TrackedTime start_time =
+ tracked_objects::ThreadData::NowForStartOfRun(pending_task.birth_tally);
+
+ pending_task.task.Run();
+
+ tracked_objects::ThreadData::TallyRunOnWorkerThreadIfTracking(
+ pending_task.birth_tally, TrackedTime(pending_task.time_posted),
+ start_time, tracked_objects::ThreadData::NowForEndOfRun());
+ }
+
+ // The WorkerThread is non-joinable, so it deletes itself.
+ delete this;
+}
+
+} // namespace
+
+// static
+bool WorkerPool::PostTask(const tracked_objects::Location& from_here,
+ const base::Closure& task, bool task_is_slow) {
+ g_lazy_worker_pool.Pointer()->PostTask(from_here, task, task_is_slow);
+ return true;
+}
+
+// static
+bool WorkerPool::RunsTasksOnCurrentThread() {
+ return g_worker_pool_running_on_this_thread.Get().Get();
+}
+
+PosixDynamicThreadPool::PosixDynamicThreadPool(
+ const std::string& name_prefix,
+ int idle_seconds_before_exit)
+ : name_prefix_(name_prefix),
+ idle_seconds_before_exit_(idle_seconds_before_exit),
+ pending_tasks_available_cv_(&lock_),
+ num_idle_threads_(0),
+ terminated_(false),
+ num_idle_threads_cv_(NULL) {}
+
+PosixDynamicThreadPool::~PosixDynamicThreadPool() {
+ while (!pending_tasks_.empty())
+ pending_tasks_.pop();
+}
+
+void PosixDynamicThreadPool::Terminate() {
+ {
+ AutoLock locked(lock_);
+ DCHECK(!terminated_) << "Thread pool is already terminated.";
+ terminated_ = true;
+ }
+ pending_tasks_available_cv_.Broadcast();
+}
+
+void PosixDynamicThreadPool::PostTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ PendingTask pending_task(from_here, task);
+ AddTask(&pending_task);
+}
+
+void PosixDynamicThreadPool::AddTask(PendingTask* pending_task) {
+ AutoLock locked(lock_);
+ DCHECK(!terminated_) <<
+ "This thread pool is already terminated. Do not post new tasks.";
+
+ pending_tasks_.push(*pending_task);
+ pending_task->task.Reset();
+
+ // We have enough worker threads.
+ if (static_cast<size_t>(num_idle_threads_) >= pending_tasks_.size()) {
+ pending_tasks_available_cv_.Signal();
+ } else {
+ // The new PlatformThread will take ownership of the WorkerThread object,
+ // which will delete itself on exit.
+ WorkerThread* worker =
+ new WorkerThread(name_prefix_, this);
+ PlatformThread::CreateNonJoinable(kWorkerThreadStackSize, worker);
+ }
+}
+
+PendingTask PosixDynamicThreadPool::WaitForTask() {
+ AutoLock locked(lock_);
+
+ if (terminated_)
+ return PendingTask(FROM_HERE, base::Closure());
+
+ if (pending_tasks_.empty()) { // No work available, wait for work.
+ num_idle_threads_++;
+ if (num_idle_threads_cv_.get())
+ num_idle_threads_cv_->Signal();
+ pending_tasks_available_cv_.TimedWait(
+ TimeDelta::FromSeconds(idle_seconds_before_exit_));
+ num_idle_threads_--;
+ if (num_idle_threads_cv_.get())
+ num_idle_threads_cv_->Signal();
+ if (pending_tasks_.empty()) {
+ // We waited for work, but there's still no work. Return NULL to signal
+ // the thread to terminate.
+ return PendingTask(FROM_HERE, base::Closure());
+ }
+ }
+
+ PendingTask pending_task = pending_tasks_.front();
+ pending_tasks_.pop();
+ return pending_task;
+}
+
+} // namespace base
diff --git a/src/base/threading/worker_pool_posix.h b/src/base/threading/worker_pool_posix.h
new file mode 100644
index 0000000..dd0ffb6
--- /dev/null
+++ b/src/base/threading/worker_pool_posix.h
@@ -0,0 +1,98 @@
+// Copyright (c) 2012 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.
+//
+// The thread pool used in the POSIX implementation of WorkerPool dynamically
+// adds threads as necessary to handle all tasks. It keeps old threads around
+// for a period of time to allow them to be reused. After this waiting period,
+// the threads exit. This thread pool uses non-joinable threads, therefore
+// worker threads are not joined during process shutdown. This means that
+// potentially long running tasks (such as DNS lookup) do not block process
+// shutdown, but also means that process shutdown may "leak" objects. Note that
+// although PosixDynamicThreadPool spawns the worker threads and manages the
+// task queue, it does not own the worker threads. The worker threads ask the
+// PosixDynamicThreadPool for work and eventually clean themselves up. The
+// worker threads all maintain scoped_refptrs to the PosixDynamicThreadPool
+// instance, which prevents PosixDynamicThreadPool from disappearing before all
+// worker threads exit. The owner of PosixDynamicThreadPool should likewise
+// maintain a scoped_refptr to the PosixDynamicThreadPool instance.
+//
+// NOTE: The classes defined in this file are only meant for use by the POSIX
+// implementation of WorkerPool. No one else should be using these classes.
+// These symbols are exported in a header purely for testing purposes.
+
+#ifndef BASE_THREADING_WORKER_POOL_POSIX_H_
+#define BASE_THREADING_WORKER_POOL_POSIX_H_
+
+#include <queue>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/pending_task.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"
+#include "base/tracked_objects.h"
+
+class Task;
+
+namespace base {
+
+class BASE_EXPORT PosixDynamicThreadPool
+ : public RefCountedThreadSafe<PosixDynamicThreadPool> {
+ public:
+ class PosixDynamicThreadPoolPeer;
+
+ // All worker threads will share the same |name_prefix|. They will exit after
+ // |idle_seconds_before_exit|.
+ PosixDynamicThreadPool(const std::string& name_prefix,
+ int idle_seconds_before_exit);
+
+ // Indicates that the thread pool is going away. Stops handing out tasks to
+ // worker threads. Wakes up all the idle threads to let them exit.
+ void Terminate();
+
+ // Adds |task| to the thread pool.
+ void PostTask(const tracked_objects::Location& from_here,
+ const Closure& task);
+
+ // Worker thread method to wait for up to |idle_seconds_before_exit| for more
+ // work from the thread pool. Returns NULL if no work is available.
+ PendingTask WaitForTask();
+
+ private:
+ friend class RefCountedThreadSafe<PosixDynamicThreadPool>;
+ friend class PosixDynamicThreadPoolPeer;
+
+ ~PosixDynamicThreadPool();
+
+ // Adds pending_task to the thread pool. This function will clear
+ // |pending_task->task|.
+ void AddTask(PendingTask* pending_task);
+
+ const std::string name_prefix_;
+ const int idle_seconds_before_exit_;
+
+ Lock lock_; // Protects all the variables below.
+
+ // Signal()s worker threads to let them know more tasks are available.
+ // Also used for Broadcast()'ing to worker threads to let them know the pool
+ // is being deleted and they can exit.
+ ConditionVariable pending_tasks_available_cv_;
+ int num_idle_threads_;
+ TaskQueue pending_tasks_;
+ bool terminated_;
+ // Only used for tests to ensure correct thread ordering. It will always be
+ // NULL in non-test code.
+ scoped_ptr<ConditionVariable> num_idle_threads_cv_;
+
+ DISALLOW_COPY_AND_ASSIGN(PosixDynamicThreadPool);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_WORKER_POOL_POSIX_H_
diff --git a/src/base/threading/worker_pool_posix_unittest.cc b/src/base/threading/worker_pool_posix_unittest.cc
new file mode 100644
index 0000000..49f6570
--- /dev/null
+++ b/src/base/threading/worker_pool_posix_unittest.cc
@@ -0,0 +1,254 @@
+// Copyright (c) 2012 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/threading/worker_pool_posix.h"
+
+#include <set>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"
+#include "base/synchronization/waitable_event.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+// Peer class to provide passthrough access to PosixDynamicThreadPool internals.
+class PosixDynamicThreadPool::PosixDynamicThreadPoolPeer {
+ public:
+ explicit PosixDynamicThreadPoolPeer(PosixDynamicThreadPool* pool)
+ : pool_(pool) {}
+
+ Lock* lock() { return &pool_->lock_; }
+ ConditionVariable* pending_tasks_available_cv() {
+ return &pool_->pending_tasks_available_cv_;
+ }
+ const std::queue<PendingTask>& pending_tasks() const {
+ return pool_->pending_tasks_;
+ }
+ int num_idle_threads() const { return pool_->num_idle_threads_; }
+ ConditionVariable* num_idle_threads_cv() {
+ return pool_->num_idle_threads_cv_.get();
+ }
+ void set_num_idle_threads_cv(ConditionVariable* cv) {
+ pool_->num_idle_threads_cv_.reset(cv);
+ }
+
+ private:
+ PosixDynamicThreadPool* pool_;
+
+ DISALLOW_COPY_AND_ASSIGN(PosixDynamicThreadPoolPeer);
+};
+
+namespace {
+
+// IncrementingTask's main purpose is to increment a counter. It also updates a
+// set of unique thread ids, and signals a ConditionVariable on completion.
+// Note that since it does not block, there is no way to control the number of
+// threads used if more than one IncrementingTask is consecutively posted to the
+// thread pool, since the first one might finish executing before the subsequent
+// PostTask() calls get invoked.
+void IncrementingTask(Lock* counter_lock,
+ int* counter,
+ Lock* unique_threads_lock,
+ std::set<PlatformThreadId>* unique_threads) {
+ {
+ base::AutoLock locked(*unique_threads_lock);
+ unique_threads->insert(PlatformThread::CurrentId());
+ }
+ base::AutoLock locked(*counter_lock);
+ (*counter)++;
+}
+
+// BlockingIncrementingTask is a simple wrapper around IncrementingTask that
+// allows for waiting at the start of Run() for a WaitableEvent to be signalled.
+struct BlockingIncrementingTaskArgs {
+ Lock* counter_lock;
+ int* counter;
+ Lock* unique_threads_lock;
+ std::set<PlatformThreadId>* unique_threads;
+ Lock* num_waiting_to_start_lock;
+ int* num_waiting_to_start;
+ ConditionVariable* num_waiting_to_start_cv;
+ base::WaitableEvent* start;
+};
+
+void BlockingIncrementingTask(const BlockingIncrementingTaskArgs& args) {
+ {
+ base::AutoLock num_waiting_to_start_locked(*args.num_waiting_to_start_lock);
+ (*args.num_waiting_to_start)++;
+ }
+ args.num_waiting_to_start_cv->Signal();
+ args.start->Wait();
+ IncrementingTask(args.counter_lock, args.counter, args.unique_threads_lock,
+ args.unique_threads);
+}
+
+class PosixDynamicThreadPoolTest : public testing::Test {
+ protected:
+ PosixDynamicThreadPoolTest()
+ : pool_(new base::PosixDynamicThreadPool("dynamic_pool", 60*60)),
+ peer_(pool_.get()),
+ counter_(0),
+ num_waiting_to_start_(0),
+ num_waiting_to_start_cv_(&num_waiting_to_start_lock_),
+ start_(true, false) {}
+
+ virtual void SetUp() OVERRIDE {
+ peer_.set_num_idle_threads_cv(new ConditionVariable(peer_.lock()));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ // Wake up the idle threads so they can terminate.
+ if (pool_.get()) pool_->Terminate();
+ }
+
+ void WaitForTasksToStart(int num_tasks) {
+ base::AutoLock num_waiting_to_start_locked(num_waiting_to_start_lock_);
+ while (num_waiting_to_start_ < num_tasks) {
+ num_waiting_to_start_cv_.Wait();
+ }
+ }
+
+ void WaitForIdleThreads(int num_idle_threads) {
+ base::AutoLock pool_locked(*peer_.lock());
+ while (peer_.num_idle_threads() < num_idle_threads) {
+ peer_.num_idle_threads_cv()->Wait();
+ }
+ }
+
+ base::Closure CreateNewIncrementingTaskCallback() {
+ return base::Bind(&IncrementingTask, &counter_lock_, &counter_,
+ &unique_threads_lock_, &unique_threads_);
+ }
+
+ base::Closure CreateNewBlockingIncrementingTaskCallback() {
+ BlockingIncrementingTaskArgs args = {
+ &counter_lock_, &counter_, &unique_threads_lock_, &unique_threads_,
+ &num_waiting_to_start_lock_, &num_waiting_to_start_,
+ &num_waiting_to_start_cv_, &start_
+ };
+ return base::Bind(&BlockingIncrementingTask, args);
+ }
+
+ scoped_refptr<base::PosixDynamicThreadPool> pool_;
+ base::PosixDynamicThreadPool::PosixDynamicThreadPoolPeer peer_;
+ Lock counter_lock_;
+ int counter_;
+ Lock unique_threads_lock_;
+ std::set<PlatformThreadId> unique_threads_;
+ Lock num_waiting_to_start_lock_;
+ int num_waiting_to_start_;
+ ConditionVariable num_waiting_to_start_cv_;
+ base::WaitableEvent start_;
+};
+
+} // namespace
+
+TEST_F(PosixDynamicThreadPoolTest, Basic) {
+ EXPECT_EQ(0, peer_.num_idle_threads());
+ EXPECT_EQ(0U, unique_threads_.size());
+ EXPECT_EQ(0U, peer_.pending_tasks().size());
+
+ // Add one task and wait for it to be completed.
+ pool_->PostTask(FROM_HERE, CreateNewIncrementingTaskCallback());
+
+ WaitForIdleThreads(1);
+
+ EXPECT_EQ(1U, unique_threads_.size()) <<
+ "There should be only one thread allocated for one task.";
+ EXPECT_EQ(1, peer_.num_idle_threads());
+ EXPECT_EQ(1, counter_);
+}
+
+TEST_F(PosixDynamicThreadPoolTest, ReuseIdle) {
+ // Add one task and wait for it to be completed.
+ pool_->PostTask(FROM_HERE, CreateNewIncrementingTaskCallback());
+
+ WaitForIdleThreads(1);
+
+ // Add another 2 tasks. One should reuse the existing worker thread.
+ pool_->PostTask(FROM_HERE, CreateNewBlockingIncrementingTaskCallback());
+ pool_->PostTask(FROM_HERE, CreateNewBlockingIncrementingTaskCallback());
+
+ WaitForTasksToStart(2);
+ start_.Signal();
+ WaitForIdleThreads(2);
+
+ EXPECT_EQ(2U, unique_threads_.size());
+ EXPECT_EQ(2, peer_.num_idle_threads());
+ EXPECT_EQ(3, counter_);
+}
+
+TEST_F(PosixDynamicThreadPoolTest, TwoActiveTasks) {
+ // Add two blocking tasks.
+ pool_->PostTask(FROM_HERE, CreateNewBlockingIncrementingTaskCallback());
+ pool_->PostTask(FROM_HERE, CreateNewBlockingIncrementingTaskCallback());
+
+ EXPECT_EQ(0, counter_) << "Blocking tasks should not have started yet.";
+
+ WaitForTasksToStart(2);
+ start_.Signal();
+ WaitForIdleThreads(2);
+
+ EXPECT_EQ(2U, unique_threads_.size());
+ EXPECT_EQ(2, peer_.num_idle_threads()) << "Existing threads are now idle.";
+ EXPECT_EQ(2, counter_);
+}
+
+TEST_F(PosixDynamicThreadPoolTest, Complex) {
+ // Add two non blocking tasks and wait for them to finish.
+ pool_->PostTask(FROM_HERE, CreateNewIncrementingTaskCallback());
+
+ WaitForIdleThreads(1);
+
+ // Add two blocking tasks, start them simultaneously, and wait for them to
+ // finish.
+ pool_->PostTask(FROM_HERE, CreateNewBlockingIncrementingTaskCallback());
+ pool_->PostTask(FROM_HERE, CreateNewBlockingIncrementingTaskCallback());
+
+ WaitForTasksToStart(2);
+ start_.Signal();
+ WaitForIdleThreads(2);
+
+ EXPECT_EQ(3, counter_);
+ EXPECT_EQ(2, peer_.num_idle_threads());
+ EXPECT_EQ(2U, unique_threads_.size());
+
+ // Wake up all idle threads so they can exit.
+ {
+ base::AutoLock locked(*peer_.lock());
+ while (peer_.num_idle_threads() > 0) {
+ peer_.pending_tasks_available_cv()->Signal();
+ peer_.num_idle_threads_cv()->Wait();
+ }
+ }
+
+ // Add another non blocking task. There are no threads to reuse.
+ pool_->PostTask(FROM_HERE, CreateNewIncrementingTaskCallback());
+ WaitForIdleThreads(1);
+
+ // The POSIX implementation of PlatformThread::CurrentId() uses pthread_self()
+ // which is not guaranteed to be unique after a thread joins. The OS X
+ // implemntation of pthread_self() returns the address of the pthread_t, which
+ // is merely a malloc()ed pointer stored in the first TLS slot. When a thread
+ // joins and that structure is freed, the block of memory can be put on the
+ // OS free list, meaning the same address could be reused in a subsequent
+ // allocation. This in fact happens when allocating in a loop as this test
+ // does.
+ //
+ // Because there are two concurrent threads, there's at least the guarantee
+ // of having two unique thread IDs in the set. But after those two threads are
+ // joined, the next-created thread can get a re-used ID if the allocation of
+ // the pthread_t structure is taken from the free list. Therefore, there can
+ // be either 2 or 3 unique thread IDs in the set at this stage in the test.
+ EXPECT_TRUE(unique_threads_.size() >= 2 && unique_threads_.size() <= 3)
+ << "unique_threads_.size() = " << unique_threads_.size();
+ EXPECT_EQ(1, peer_.num_idle_threads());
+ EXPECT_EQ(4, counter_);
+}
+
+} // namespace base
diff --git a/src/base/threading/worker_pool_shell.cc b/src/base/threading/worker_pool_shell.cc
new file mode 100644
index 0000000..303675c
--- /dev/null
+++ b/src/base/threading/worker_pool_shell.cc
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2012 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "worker_pool_posix.cc"
+
diff --git a/src/base/threading/worker_pool_starboard.cc b/src/base/threading/worker_pool_starboard.cc
new file mode 100644
index 0000000..47a4e6c
--- /dev/null
+++ b/src/base/threading/worker_pool_starboard.cc
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/threading/worker_pool_posix.cc"
diff --git a/src/base/threading/worker_pool_unittest.cc b/src/base/threading/worker_pool_unittest.cc
new file mode 100644
index 0000000..107588b
--- /dev/null
+++ b/src/base/threading/worker_pool_unittest.cc
@@ -0,0 +1,111 @@
+// Copyright (c) 2012 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/threading/worker_pool.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/message_loop.h"
+#include "base/test/test_timeouts.h"
+#include "base/time.h"
+#include "base/threading/thread_checker_impl.h"
+#include "base/synchronization/waitable_event.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+typedef PlatformTest WorkerPoolTest;
+
+namespace base {
+
+namespace {
+
+class PostTaskAndReplyTester
+ : public base::RefCountedThreadSafe<PostTaskAndReplyTester> {
+ public:
+ PostTaskAndReplyTester() : finished_(false), test_event_(false, false) {}
+
+ void RunTest() {
+ ASSERT_TRUE(thread_checker_.CalledOnValidThread());
+ WorkerPool::PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&PostTaskAndReplyTester::OnWorkerThread, this),
+ base::Bind(&PostTaskAndReplyTester::OnOriginalThread, this),
+ false);
+
+ test_event_.Wait();
+ }
+
+ void OnWorkerThread() {
+ // We're not on the original thread.
+ EXPECT_FALSE(thread_checker_.CalledOnValidThread());
+
+ test_event_.Signal();
+ }
+
+ void OnOriginalThread() {
+ EXPECT_TRUE(thread_checker_.CalledOnValidThread());
+ finished_ = true;
+ }
+
+ bool finished() const {
+ return finished_;
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<PostTaskAndReplyTester>;
+ ~PostTaskAndReplyTester() {}
+
+ bool finished_;
+ WaitableEvent test_event_;
+
+ // The Impl version performs its checks even in release builds.
+ ThreadCheckerImpl thread_checker_;
+};
+
+} // namespace
+
+TEST_F(WorkerPoolTest, PostTask) {
+ WaitableEvent test_event(false, false);
+ WaitableEvent long_test_event(false, false);
+
+ WorkerPool::PostTask(FROM_HERE,
+ base::Bind(&WaitableEvent::Signal,
+ base::Unretained(&test_event)),
+ false);
+ WorkerPool::PostTask(FROM_HERE,
+ base::Bind(&WaitableEvent::Signal,
+ base::Unretained(&long_test_event)),
+ true);
+
+ test_event.Wait();
+ long_test_event.Wait();
+}
+
+#if defined(OS_WIN) || defined(OS_LINUX)
+// Flaky on Windows and Linux (http://crbug.com/130337)
+#define MAYBE_PostTaskAndReply DISABLED_PostTaskAndReply
+#else
+#define MAYBE_PostTaskAndReply PostTaskAndReply
+#endif
+
+TEST_F(WorkerPoolTest, MAYBE_PostTaskAndReply) {
+ MessageLoop message_loop;
+ scoped_refptr<PostTaskAndReplyTester> tester(new PostTaskAndReplyTester());
+ tester->RunTest();
+
+ const TimeDelta kMaxDuration = TestTimeouts::tiny_timeout();
+ TimeTicks start = TimeTicks::Now();
+ while (!tester->finished() && TimeTicks::Now() - start < kMaxDuration) {
+#if defined(OS_IOS)
+ // Ensure that the other thread has a chance to run even on a single-core
+ // device.
+ pthread_yield_np();
+#endif
+ MessageLoop::current()->RunUntilIdle();
+ }
+ EXPECT_TRUE(tester->finished());
+}
+
+} // namespace base
diff --git a/src/base/threading/worker_pool_win.cc b/src/base/threading/worker_pool_win.cc
new file mode 100644
index 0000000..1e8be26
--- /dev/null
+++ b/src/base/threading/worker_pool_win.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 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/threading/worker_pool.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "base/pending_task.h"
+#include "base/threading/thread_local.h"
+#include "base/tracked_objects.h"
+
+namespace base {
+
+namespace {
+
+base::LazyInstance<ThreadLocalBoolean>::Leaky
+ g_worker_pool_running_on_this_thread = LAZY_INSTANCE_INITIALIZER;
+
+DWORD CALLBACK WorkItemCallback(void* param) {
+ PendingTask* pending_task = static_cast<PendingTask*>(param);
+ TRACE_EVENT2("task", "WorkItemCallback::Run",
+ "src_file", pending_task->posted_from.file_name(),
+ "src_func", pending_task->posted_from.function_name());
+
+ tracked_objects::TrackedTime start_time =
+ tracked_objects::ThreadData::NowForStartOfRun(pending_task->birth_tally);
+
+ g_worker_pool_running_on_this_thread.Get().Set(true);
+ pending_task->task.Run();
+ g_worker_pool_running_on_this_thread.Get().Set(false);
+
+ tracked_objects::ThreadData::TallyRunOnWorkerThreadIfTracking(
+ pending_task->birth_tally,
+ tracked_objects::TrackedTime(pending_task->time_posted), start_time,
+ tracked_objects::ThreadData::NowForEndOfRun());
+
+ delete pending_task;
+ return 0;
+}
+
+// Takes ownership of |pending_task|
+bool PostTaskInternal(PendingTask* pending_task, bool task_is_slow) {
+ ULONG flags = 0;
+ if (task_is_slow)
+ flags |= WT_EXECUTELONGFUNCTION;
+
+ if (!QueueUserWorkItem(WorkItemCallback, pending_task, flags)) {
+ DLOG(ERROR) << "QueueUserWorkItem failed: " << GetLastError();
+ delete pending_task;
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+// static
+bool WorkerPool::PostTask(const tracked_objects::Location& from_here,
+ const base::Closure& task, bool task_is_slow) {
+ PendingTask* pending_task = new PendingTask(from_here, task);
+ return PostTaskInternal(pending_task, task_is_slow);
+}
+
+// static
+bool WorkerPool::RunsTasksOnCurrentThread() {
+ return g_worker_pool_running_on_this_thread.Get().Get();
+}
+
+} // namespace base
diff --git a/src/base/time.cc b/src/base/time.cc
new file mode 100644
index 0000000..6d20667
--- /dev/null
+++ b/src/base/time.cc
@@ -0,0 +1,237 @@
+// Copyright (c) 2012 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/time.h"
+
+#include <math.h>
+#if defined(OS_WIN)
+#include <float.h>
+#endif
+
+#include <limits>
+
+#include "base/sys_string_conversions.h"
+#include "base/third_party/nspr/prtime.h"
+
+#include "base/logging.h"
+
+namespace base {
+
+namespace {
+#if defined(OS_WIN)
+inline bool isnan(double num) { return !!_isnan(num); }
+#endif
+}
+
+// TimeDelta ------------------------------------------------------------------
+
+// static
+TimeDelta TimeDelta::Max() {
+ return TimeDelta(std::numeric_limits<int64>::max());
+}
+
+int TimeDelta::InDays() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int>::max();
+ }
+ return static_cast<int>(delta_ / Time::kMicrosecondsPerDay);
+}
+
+int TimeDelta::InHours() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int>::max();
+ }
+ return static_cast<int>(delta_ / Time::kMicrosecondsPerHour);
+}
+
+int TimeDelta::InMinutes() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int>::max();
+ }
+ return static_cast<int>(delta_ / Time::kMicrosecondsPerMinute);
+}
+
+double TimeDelta::InSecondsF() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<double>::infinity();
+ }
+ return static_cast<double>(delta_) / Time::kMicrosecondsPerSecond;
+}
+
+int64 TimeDelta::InSeconds() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int64>::max();
+ }
+ return delta_ / Time::kMicrosecondsPerSecond;
+}
+
+double TimeDelta::InMillisecondsF() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<double>::infinity();
+ }
+ return static_cast<double>(delta_) / Time::kMicrosecondsPerMillisecond;
+}
+
+int64 TimeDelta::InMilliseconds() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int64>::max();
+ }
+ return delta_ / Time::kMicrosecondsPerMillisecond;
+}
+
+int64 TimeDelta::InMillisecondsRoundedUp() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int64>::max();
+ }
+ return (delta_ + Time::kMicrosecondsPerMillisecond - 1) /
+ Time::kMicrosecondsPerMillisecond;
+}
+
+int64 TimeDelta::InMicroseconds() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int64>::max();
+ }
+ return delta_;
+}
+
+// Time -----------------------------------------------------------------------
+
+// static
+Time Time::Max() {
+ return Time(std::numeric_limits<int64>::max());
+}
+
+// static
+Time Time::FromTimeT(time_t tt) {
+ if (tt == 0)
+ return Time(); // Preserve 0 so we can tell it doesn't exist.
+ if (tt == std::numeric_limits<time_t>::max())
+ return Max();
+ return Time((tt * kMicrosecondsPerSecond) + kTimeTToMicrosecondsOffset);
+}
+
+time_t Time::ToTimeT() const {
+ if (is_null())
+ return 0; // Preserve 0 so we can tell it doesn't exist.
+ if (is_max()) {
+ // Preserve max without offset to prevent overflow.
+ return std::numeric_limits<time_t>::max();
+ }
+ if (std::numeric_limits<int64>::max() - kTimeTToMicrosecondsOffset <= us_) {
+ DLOG(WARNING) << "Overflow when converting base::Time with internal " <<
+ "value " << us_ << " to time_t.";
+ return std::numeric_limits<time_t>::max();
+ }
+ return (us_ - kTimeTToMicrosecondsOffset) / kMicrosecondsPerSecond;
+}
+
+// static
+Time Time::FromDoubleT(double dt) {
+ if (dt == 0 || isnan(dt))
+ return Time(); // Preserve 0 so we can tell it doesn't exist.
+ if (dt == std::numeric_limits<double>::max())
+ return Max();
+ return Time(static_cast<int64>((dt *
+ static_cast<double>(kMicrosecondsPerSecond)) +
+ kTimeTToMicrosecondsOffset));
+}
+
+double Time::ToDoubleT() const {
+ if (is_null())
+ return 0; // Preserve 0 so we can tell it doesn't exist.
+ if (is_max()) {
+ // Preserve max without offset to prevent overflow.
+ return std::numeric_limits<double>::max();
+ }
+ return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) /
+ static_cast<double>(kMicrosecondsPerSecond));
+}
+
+// static
+Time Time::FromJsTime(double ms_since_epoch) {
+ // The epoch is a valid time, so this constructor doesn't interpret
+ // 0 as the null time.
+ if (ms_since_epoch == std::numeric_limits<double>::max())
+ return Max();
+ return Time(static_cast<int64>(ms_since_epoch * kMicrosecondsPerMillisecond) +
+ kTimeTToMicrosecondsOffset);
+}
+
+double Time::ToJsTime() const {
+ if (is_null()) {
+ // Preserve 0 so the invalid result doesn't depend on the platform.
+ return 0;
+ }
+ if (is_max()) {
+ // Preserve max without offset to prevent overflow.
+ return std::numeric_limits<double>::max();
+ }
+ return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) /
+ kMicrosecondsPerMillisecond);
+}
+
+// static
+Time Time::UnixEpoch() {
+ Time time;
+ time.us_ = kTimeTToMicrosecondsOffset;
+ return time;
+}
+
+Time Time::LocalMidnight() const {
+ Exploded exploded;
+ LocalExplode(&exploded);
+ exploded.hour = 0;
+ exploded.minute = 0;
+ exploded.second = 0;
+ exploded.millisecond = 0;
+ return FromLocalExploded(exploded);
+}
+
+// static
+bool Time::FromStringInternal(const char* time_string,
+ bool is_local,
+ Time* parsed_time) {
+ DCHECK((time_string != NULL) && (parsed_time != NULL));
+
+ if (time_string[0] == '\0')
+ return false;
+
+ PRTime result_time = 0;
+ PRStatus result = PR_ParseTimeString(time_string,
+ is_local ? PR_FALSE : PR_TRUE,
+ &result_time);
+ if (PR_SUCCESS != result)
+ return false;
+
+ result_time += kTimeTToMicrosecondsOffset;
+ *parsed_time = Time(result_time);
+ return true;
+}
+
+// Time::Exploded -------------------------------------------------------------
+
+inline bool is_in_range(int value, int lo, int hi) {
+ return lo <= value && value <= hi;
+}
+
+bool Time::Exploded::HasValidValues() const {
+ return is_in_range(month, 1, 12) &&
+ is_in_range(day_of_week, 0, 6) &&
+ is_in_range(day_of_month, 1, 31) &&
+ is_in_range(hour, 0, 23) &&
+ is_in_range(minute, 0, 59) &&
+ is_in_range(second, 0, 60) &&
+ is_in_range(millisecond, 0, 999);
+}
+
+} // namespace base
diff --git a/src/base/time.h b/src/base/time.h
new file mode 100644
index 0000000..38bc53a
--- /dev/null
+++ b/src/base/time.h
@@ -0,0 +1,704 @@
+// Copyright (c) 2012 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.
+
+// Time represents an absolute point in time, internally represented as
+// microseconds (s/1,000,000) since the Windows epoch (1601-01-01 00:00:00 UTC)
+// (See http://crbug.com/14734). System-dependent clock interface routines are
+// defined in time_PLATFORM.cc.
+//
+// TimeDelta represents a duration of time, internally represented in
+// microseconds.
+//
+// TimeTicks represents an abstract time that is most of the time incrementing
+// for use in measuring time durations. It is internally represented in
+// microseconds. It can not be converted to a human-readable time, but is
+// guaranteed not to decrease (if the user changes the computer clock,
+// Time::Now() may actually decrease or jump). But note that TimeTicks may
+// "stand still", for example if the computer suspended.
+//
+// These classes are represented as only a 64-bit value, so they can be
+// efficiently passed by value.
+
+#ifndef BASE_TIME_H_
+#define BASE_TIME_H_
+
+#include <time.h>
+
+#include "base/atomicops.h"
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+#if defined(OS_MACOSX)
+#include <CoreFoundation/CoreFoundation.h>
+// Avoid Mac system header macro leak.
+#undef TYPE_BOOL
+#endif
+
+#if defined(OS_POSIX)
+// For struct timeval.
+#include <sys/time.h>
+#endif
+
+#if defined(OS_STARBOARD)
+#include "starboard/time.h"
+#endif
+
+#if defined(OS_WIN)
+// For FILETIME in FromFileTime, until it moves to a new converter class.
+// See TODO(iyengar) below.
+#include <windows.h>
+#endif
+
+#include <limits>
+
+namespace base {
+
+class Time;
+class TimeTicks;
+
+// TimeDelta ------------------------------------------------------------------
+
+class BASE_EXPORT TimeDelta {
+ public:
+ TimeDelta() : delta_(0) {
+ }
+
+ // Converts units of time to TimeDeltas.
+ static TimeDelta FromDays(int days);
+ static TimeDelta FromHours(int hours);
+ static TimeDelta FromMinutes(int minutes);
+ static TimeDelta FromSeconds(int64 secs);
+ static TimeDelta FromMilliseconds(int64 ms);
+ static TimeDelta FromSecondsD(double secs);
+ static TimeDelta FromMillisecondsD(double ms);
+ static TimeDelta FromMicroseconds(int64 us);
+#if defined(OS_WIN)
+ static TimeDelta FromQPCValue(LONGLONG qpc_value);
+#endif
+
+ // Converts an integer value representing TimeDelta to a class. This is used
+ // when deserializing a |TimeDelta| structure, using a value known to be
+ // compatible. It is not provided as a constructor because the integer type
+ // may be unclear from the perspective of a caller.
+ static TimeDelta FromInternalValue(int64 delta) {
+ return TimeDelta(delta);
+ }
+
+ // Returns the maximum time delta, which should be greater than any reasonable
+ // time delta we might compare it to. Adding or subtracting the maximum time
+ // delta to a time or another time delta has an undefined result.
+ static TimeDelta Max();
+
+ // Returns the internal numeric value of the TimeDelta object. Please don't
+ // use this and do arithmetic on it, as it is more error prone than using the
+ // provided operators.
+ // For serializing, use FromInternalValue to reconstitute.
+ int64 ToInternalValue() const {
+ return delta_;
+ }
+
+ // Returns true if the time delta is the maximum time delta.
+ bool is_max() const {
+ return delta_ == std::numeric_limits<int64>::max();
+ }
+
+#if defined(OS_STARBOARD)
+ SbTime ToSbTime() const;
+#endif
+
+#if defined(OS_POSIX)
+ struct timespec ToTimeSpec() const;
+#endif
+
+ // Returns the time delta in some unit. The F versions return a floating
+ // point value, the "regular" versions return a rounded-down value.
+ //
+ // InMillisecondsRoundedUp() instead returns an integer that is rounded up
+ // to the next full millisecond.
+ int InDays() const;
+ int InHours() const;
+ int InMinutes() const;
+ double InSecondsF() const;
+ int64 InSeconds() const;
+ double InMillisecondsF() const;
+ int64 InMilliseconds() const;
+ int64 InMillisecondsRoundedUp() const;
+ int64 InMicroseconds() const;
+
+ TimeDelta& operator=(TimeDelta other) {
+ delta_ = other.delta_;
+ return *this;
+ }
+
+ // Computations with other deltas.
+ TimeDelta operator+(TimeDelta other) const {
+ return TimeDelta(delta_ + other.delta_);
+ }
+ TimeDelta operator-(TimeDelta other) const {
+ return TimeDelta(delta_ - other.delta_);
+ }
+
+ TimeDelta& operator+=(TimeDelta other) {
+ delta_ += other.delta_;
+ return *this;
+ }
+ TimeDelta& operator-=(TimeDelta other) {
+ delta_ -= other.delta_;
+ return *this;
+ }
+ TimeDelta operator-() const {
+ return TimeDelta(-delta_);
+ }
+
+ // Computations with ints, note that we only allow multiplicative operations
+ // with ints, and additive operations with other deltas.
+ TimeDelta operator*(int64 a) const {
+ return TimeDelta(delta_ * a);
+ }
+ TimeDelta operator/(int64 a) const {
+ return TimeDelta(delta_ / a);
+ }
+ TimeDelta& operator*=(int64 a) {
+ delta_ *= a;
+ return *this;
+ }
+ TimeDelta& operator/=(int64 a) {
+ delta_ /= a;
+ return *this;
+ }
+ int64 operator/(TimeDelta a) const {
+ return delta_ / a.delta_;
+ }
+
+ // Defined below because it depends on the definition of the other classes.
+ Time operator+(Time t) const;
+ TimeTicks operator+(TimeTicks t) const;
+
+ // Comparison operators.
+ bool operator==(TimeDelta other) const {
+ return delta_ == other.delta_;
+ }
+ bool operator!=(TimeDelta other) const {
+ return delta_ != other.delta_;
+ }
+ bool operator<(TimeDelta other) const {
+ return delta_ < other.delta_;
+ }
+ bool operator<=(TimeDelta other) const {
+ return delta_ <= other.delta_;
+ }
+ bool operator>(TimeDelta other) const {
+ return delta_ > other.delta_;
+ }
+ bool operator>=(TimeDelta other) const {
+ return delta_ >= other.delta_;
+ }
+
+ private:
+ friend class Time;
+ friend class TimeTicks;
+ friend TimeDelta operator*(int64 a, TimeDelta td);
+
+ // Constructs a delta given the duration in microseconds. This is private
+ // to avoid confusion by callers with an integer constructor. Use
+ // FromSeconds, FromMilliseconds, etc. instead.
+ explicit TimeDelta(int64 delta_us) : delta_(delta_us) {
+ }
+
+ // Delta in microseconds.
+ int64 delta_;
+};
+
+inline TimeDelta operator*(int64 a, TimeDelta td) {
+ return TimeDelta(a * td.delta_);
+}
+
+// Time -----------------------------------------------------------------------
+
+// Represents a wall clock time.
+class BASE_EXPORT Time {
+ public:
+ static const int64 kMillisecondsPerSecond = 1000;
+ static const int64 kMicrosecondsPerMillisecond = 1000;
+ static const int64 kMicrosecondsPerSecond = kMicrosecondsPerMillisecond *
+ kMillisecondsPerSecond;
+ static const int64 kMicrosecondsPerMinute = kMicrosecondsPerSecond * 60;
+ static const int64 kMicrosecondsPerHour = kMicrosecondsPerMinute * 60;
+ static const int64 kMicrosecondsPerDay = kMicrosecondsPerHour * 24;
+ static const int64 kMicrosecondsPerWeek = kMicrosecondsPerDay * 7;
+ static const int64 kNanosecondsPerMicrosecond = 1000;
+ static const int64 kNanosecondsPerSecond = kNanosecondsPerMicrosecond *
+ kMicrosecondsPerSecond;
+
+#if !defined(OS_WIN) && !defined(OS_STARBOARD)
+ // On Mac & Linux, this value is the delta from the Windows epoch of 1601 to
+ // the Posix delta of 1970. This is used for migrating between the old
+ // 1970-based epochs to the new 1601-based ones. It should be removed from
+ // this global header and put in the platform-specific ones when we remove the
+ // migration code.
+ static const int64 kWindowsEpochDeltaMicroseconds;
+#endif
+
+ // Represents an exploded time that can be formatted nicely. This is kind of
+ // like the Win32 SYSTEMTIME structure or the Unix "struct tm" with a few
+ // additions and changes to prevent errors.
+ struct BASE_EXPORT Exploded {
+ int year; // Four digit year "2007"
+ int month; // 1-based month (values 1 = January, etc.)
+ int day_of_week; // 0-based day of week (0 = Sunday, etc.)
+ int day_of_month; // 1-based day of month (1-31)
+ int hour; // Hour within the current day (0-23)
+ int minute; // Minute within the current hour (0-59)
+ int second; // Second within the current minute (0-59 plus leap
+ // seconds which may take it up to 60).
+ int millisecond; // Milliseconds within the current second (0-999)
+
+ // A cursory test for whether the data members are within their
+ // respective ranges. A 'true' return value does not guarantee the
+ // Exploded value can be successfully converted to a Time value.
+ bool HasValidValues() const;
+ };
+
+ // Contains the NULL time. Use Time::Now() to get the current time.
+ Time() : us_(0) {
+ }
+
+ // Returns true if the time object has not been initialized.
+ bool is_null() const {
+ return us_ == 0;
+ }
+
+ // Returns true if the time object is the maximum time.
+ bool is_max() const {
+ return us_ == std::numeric_limits<int64>::max();
+ }
+
+ // Returns the time for epoch in Unix-like system (Jan 1, 1970).
+ static Time UnixEpoch();
+
+ // Returns the current time. Watch out, the system might adjust its clock
+ // in which case time will actually go backwards. We don't guarantee that
+ // times are increasing, or that two calls to Now() won't be the same.
+ static Time Now();
+
+ // Returns the maximum time, which should be greater than any reasonable time
+ // with which we might compare it.
+ static Time Max();
+
+ // Returns the current time. Same as Now() except that this function always
+ // uses system time so that there are no discrepancies between the returned
+ // time and system time even on virtual environments including our test bot.
+ // For timing sensitive unittests, this function should be used.
+ static Time NowFromSystemTime();
+
+ // Converts to/from time_t in UTC and a Time class.
+ // TODO(brettw) this should be removed once everybody starts using the |Time|
+ // class.
+ static Time FromTimeT(time_t tt);
+ time_t ToTimeT() const;
+
+ // Converts time to/from a double which is the number of seconds since epoch
+ // (Jan 1, 1970). Webkit uses this format to represent time.
+ // Because WebKit initializes double time value to 0 to indicate "not
+ // initialized", we map it to empty Time object that also means "not
+ // initialized".
+ static Time FromDoubleT(double dt);
+ double ToDoubleT() const;
+
+ // Converts to/from the Javascript convention for times, a number of
+ // milliseconds since the epoch:
+ // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/getTime.
+ static Time FromJsTime(double ms_since_epoch);
+ double ToJsTime() const;
+
+#if defined(OS_STARBOARD)
+ static Time FromSbTime(SbTime t);
+ SbTime ToSbTime() const;
+#endif
+
+#if defined(OS_POSIX)
+ static Time FromTimeVal(struct timeval t);
+ struct timeval ToTimeVal() const;
+#endif
+
+#if defined(OS_MACOSX)
+ static Time FromCFAbsoluteTime(CFAbsoluteTime t);
+ CFAbsoluteTime ToCFAbsoluteTime() const;
+#endif
+
+#if defined(OS_WIN)
+ static Time FromFileTime(FILETIME ft);
+ FILETIME ToFileTime() const;
+
+ // The minimum time of a low resolution timer. This is basically a windows
+ // constant of ~15.6ms. While it does vary on some older OS versions, we'll
+ // treat it as static across all windows versions.
+ static const int kMinLowResolutionThresholdMs = 16;
+
+ // Enable or disable Windows high resolution timer. If the high resolution
+ // timer is not enabled, calls to ActivateHighResolutionTimer will fail.
+ // When disabling the high resolution timer, this function will not cause
+ // the high resolution timer to be deactivated, but will prevent future
+ // activations.
+ // Must be called from the main thread.
+ // For more details see comments in time_win.cc.
+ static void EnableHighResolutionTimer(bool enable);
+
+ // Activates or deactivates the high resolution timer based on the |activate|
+ // flag. If the HighResolutionTimer is not Enabled (see
+ // EnableHighResolutionTimer), this function will return false. Otherwise
+ // returns true. Each successful activate call must be paired with a
+ // subsequent deactivate call.
+ // All callers to activate the high resolution timer must eventually call
+ // this function to deactivate the high resolution timer.
+ static bool ActivateHighResolutionTimer(bool activate);
+
+ // Returns true if the high resolution timer is both enabled and activated.
+ // This is provided for testing only, and is not tracked in a thread-safe
+ // way.
+ static bool IsHighResolutionTimerInUse();
+#endif
+
+ // Converts an exploded structure representing either the local time or UTC
+ // into a Time class.
+ static Time FromUTCExploded(const Exploded& exploded) {
+ return FromExploded(false, exploded);
+ }
+ static Time FromLocalExploded(const Exploded& exploded) {
+ return FromExploded(true, exploded);
+ }
+
+ // Converts an integer value representing Time to a class. This is used
+ // when deserializing a |Time| structure, using a value known to be
+ // compatible. It is not provided as a constructor because the integer type
+ // may be unclear from the perspective of a caller.
+ static Time FromInternalValue(int64 us) {
+ return Time(us);
+ }
+
+ // Converts a string representation of time to a Time object.
+ // An example of a time string which is converted is as below:-
+ // "Tue, 15 Nov 1994 12:45:26 GMT". If the timezone is not specified
+ // in the input string, FromString assumes local time and FromUTCString
+ // assumes UTC. A timezone that cannot be parsed (e.g. "UTC" which is not
+ // specified in RFC822) is treated as if the timezone is not specified.
+ // TODO(iyengar) Move the FromString/FromTimeT/ToTimeT/FromFileTime to
+ // a new time converter class.
+ static bool FromString(const char* time_string, Time* parsed_time) {
+ return FromStringInternal(time_string, true, parsed_time);
+ }
+ static bool FromUTCString(const char* time_string, Time* parsed_time) {
+ return FromStringInternal(time_string, false, parsed_time);
+ }
+
+ // For serializing, use FromInternalValue to reconstitute. Please don't use
+ // this and do arithmetic on it, as it is more error prone than using the
+ // provided operators.
+ int64 ToInternalValue() const {
+ return us_;
+ }
+
+ // Fills the given exploded structure with either the local time or UTC from
+ // this time structure (containing UTC).
+ void UTCExplode(Exploded* exploded) const {
+ return Explode(false, exploded);
+ }
+ void LocalExplode(Exploded* exploded) const {
+ return Explode(true, exploded);
+ }
+
+ // Rounds this time down to the nearest day in local time. It will represent
+ // midnight on that day.
+ Time LocalMidnight() const;
+
+ Time& operator=(Time other) {
+ us_ = other.us_;
+ return *this;
+ }
+
+ // Compute the difference between two times.
+ TimeDelta operator-(Time other) const {
+ return TimeDelta(us_ - other.us_);
+ }
+
+ // Modify by some time delta.
+ Time& operator+=(TimeDelta delta) {
+ us_ += delta.delta_;
+ return *this;
+ }
+ Time& operator-=(TimeDelta delta) {
+ us_ -= delta.delta_;
+ return *this;
+ }
+
+ // Return a new time modified by some delta.
+ Time operator+(TimeDelta delta) const {
+ return Time(us_ + delta.delta_);
+ }
+ Time operator-(TimeDelta delta) const {
+ return Time(us_ - delta.delta_);
+ }
+
+ // Comparison operators
+ bool operator==(Time other) const {
+ return us_ == other.us_;
+ }
+ bool operator!=(Time other) const {
+ return us_ != other.us_;
+ }
+ bool operator<(Time other) const {
+ return us_ < other.us_;
+ }
+ bool operator<=(Time other) const {
+ return us_ <= other.us_;
+ }
+ bool operator>(Time other) const {
+ return us_ > other.us_;
+ }
+ bool operator>=(Time other) const {
+ return us_ >= other.us_;
+ }
+
+ private:
+ friend class TimeDelta;
+
+ explicit Time(int64 us) : us_(us) {
+ }
+
+ // Explodes the given time to either local time |is_local = true| or UTC
+ // |is_local = false|.
+ void Explode(bool is_local, Exploded* exploded) const;
+
+ // Unexplodes a given time assuming the source is either local time
+ // |is_local = true| or UTC |is_local = false|.
+ static Time FromExploded(bool is_local, const Exploded& exploded);
+
+ // Converts a string representation of time to a Time object.
+ // An example of a time string which is converted is as below:-
+ // "Tue, 15 Nov 1994 12:45:26 GMT". If the timezone is not specified
+ // in the input string, local time |is_local = true| or
+ // UTC |is_local = false| is assumed. A timezone that cannot be parsed
+ // (e.g. "UTC" which is not specified in RFC822) is treated as if the
+ // timezone is not specified.
+ static bool FromStringInternal(const char* time_string,
+ bool is_local,
+ Time* parsed_time);
+
+ // The representation of Jan 1, 1970 UTC in microseconds since the
+ // platform-dependent epoch.
+ static const int64 kTimeTToMicrosecondsOffset;
+
+#if defined(OS_WIN)
+ // Indicates whether fast timers are usable right now. For instance,
+ // when using battery power, we might elect to prevent high speed timers
+ // which would draw more power.
+ static bool high_resolution_timer_enabled_;
+ // Count of activations on the high resolution timer. Only use in tests
+ // which are single threaded.
+ static int high_resolution_timer_activated_;
+#endif
+
+ // Time in microseconds in UTC.
+ int64 us_;
+};
+
+// Inline the TimeDelta factory methods, for fast TimeDelta construction.
+
+// static
+inline TimeDelta TimeDelta::FromDays(int days) {
+ // Preserve max to prevent overflow.
+ if (days == std::numeric_limits<int>::max())
+ return Max();
+ return TimeDelta(days * Time::kMicrosecondsPerDay);
+}
+
+// static
+inline TimeDelta TimeDelta::FromHours(int hours) {
+ // Preserve max to prevent overflow.
+ if (hours == std::numeric_limits<int>::max())
+ return Max();
+ return TimeDelta(hours * Time::kMicrosecondsPerHour);
+}
+
+// static
+inline TimeDelta TimeDelta::FromMinutes(int minutes) {
+ // Preserve max to prevent overflow.
+ if (minutes == std::numeric_limits<int>::max())
+ return Max();
+ return TimeDelta(minutes * Time::kMicrosecondsPerMinute);
+}
+
+// static
+inline TimeDelta TimeDelta::FromSeconds(int64 secs) {
+ // Preserve max to prevent overflow.
+ if (secs == std::numeric_limits<int64>::max())
+ return Max();
+ return TimeDelta(secs * Time::kMicrosecondsPerSecond);
+}
+
+// static
+inline TimeDelta TimeDelta::FromMilliseconds(int64 ms) {
+ // Preserve max to prevent overflow.
+ if (ms == std::numeric_limits<int64>::max())
+ return Max();
+ return TimeDelta(ms * Time::kMicrosecondsPerMillisecond);
+}
+
+// static
+inline TimeDelta TimeDelta::FromSecondsD(double secs) {
+ // Preserve max to prevent overflow.
+ if (secs == std::numeric_limits<double>::infinity())
+ return Max();
+ return TimeDelta(
+ static_cast<int64>(secs * Time::kMicrosecondsPerSecond));
+}
+
+// static
+inline TimeDelta TimeDelta::FromMillisecondsD(double ms) {
+ // Preserve max to prevent overflow.
+ if (ms == std::numeric_limits<double>::infinity())
+ return Max();
+ return TimeDelta(
+ static_cast<int64>(ms * Time::kMicrosecondsPerMillisecond));
+}
+
+// static
+inline TimeDelta TimeDelta::FromMicroseconds(int64 us) {
+ // Preserve max to prevent overflow.
+ if (us == std::numeric_limits<int64>::max())
+ return Max();
+ return TimeDelta(us);
+}
+
+inline Time TimeDelta::operator+(Time t) const {
+ return Time(t.us_ + delta_);
+}
+
+// TimeTicks ------------------------------------------------------------------
+
+class BASE_EXPORT TimeTicks {
+ public:
+ TimeTicks() : ticks_(0) {
+ }
+
+ // Platform-dependent tick count representing "right now."
+ // The resolution of this clock is ~1-15ms. Resolution varies depending
+ // on hardware/operating system configuration.
+ static TimeTicks Now();
+
+ // Returns a platform-dependent high-resolution tick count. Implementation
+ // is hardware dependent and may or may not return sub-millisecond
+ // resolution. THIS CALL IS GENERALLY MUCH MORE EXPENSIVE THAN Now() AND
+ // SHOULD ONLY BE USED WHEN IT IS REALLY NEEDED.
+ static TimeTicks HighResNow();
+
+ // Returns the current system trace time or, if none is defined, the current
+ // high-res time (i.e. HighResNow()). On systems where a global trace clock
+ // is defined, timestamping TraceEvents's with this value guarantees
+ // synchronization between events collected inside chrome and events
+ // collected outside (e.g. kernel, X server).
+ static TimeTicks NowFromSystemTraceTime();
+
+#if defined(OS_WIN)
+ // Get the absolute value of QPC time drift. For testing.
+ static int64 GetQPCDriftMicroseconds();
+
+ static TimeTicks FromQPCValue(LONGLONG qpc_value);
+
+ // Returns true if the high resolution clock is working on this system.
+ // This is only for testing.
+ static bool IsHighResClockWorking();
+#endif
+
+ // Returns true if this object has not been initialized.
+ bool is_null() const {
+ return ticks_ == 0;
+ }
+
+ // Converts an integer value representing TimeTicks to a class. This is used
+ // when deserializing a |TimeTicks| structure, using a value known to be
+ // compatible. It is not provided as a constructor because the integer type
+ // may be unclear from the perspective of a caller.
+ static TimeTicks FromInternalValue(int64 ticks) {
+ return TimeTicks(ticks);
+ }
+
+ // Returns the internal numeric value of the TimeTicks object.
+ // For serializing, use FromInternalValue to reconstitute.
+ int64 ToInternalValue() const {
+ return ticks_;
+ }
+
+ TimeTicks& operator=(TimeTicks other) {
+ ticks_ = other.ticks_;
+ return *this;
+ }
+
+ // Compute the difference between two times.
+ TimeDelta operator-(TimeTicks other) const {
+ return TimeDelta(ticks_ - other.ticks_);
+ }
+
+ // Modify by some time delta.
+ TimeTicks& operator+=(TimeDelta delta) {
+ ticks_ += delta.delta_;
+ return *this;
+ }
+ TimeTicks& operator-=(TimeDelta delta) {
+ ticks_ -= delta.delta_;
+ return *this;
+ }
+
+ // Return a new TimeTicks modified by some delta.
+ TimeTicks operator+(TimeDelta delta) const {
+ return TimeTicks(ticks_ + delta.delta_);
+ }
+ TimeTicks operator-(TimeDelta delta) const {
+ return TimeTicks(ticks_ - delta.delta_);
+ }
+
+ // Comparison operators
+ bool operator==(TimeTicks other) const {
+ return ticks_ == other.ticks_;
+ }
+ bool operator!=(TimeTicks other) const {
+ return ticks_ != other.ticks_;
+ }
+ bool operator<(TimeTicks other) const {
+ return ticks_ < other.ticks_;
+ }
+ bool operator<=(TimeTicks other) const {
+ return ticks_ <= other.ticks_;
+ }
+ bool operator>(TimeTicks other) const {
+ return ticks_ > other.ticks_;
+ }
+ bool operator>=(TimeTicks other) const {
+ return ticks_ >= other.ticks_;
+ }
+
+ protected:
+ friend class TimeDelta;
+
+ // Please use Now() to create a new object. This is for internal use
+ // and testing. Ticks is in microseconds.
+ explicit TimeTicks(int64 ticks) : ticks_(ticks) {
+ }
+
+ // Tick count in microseconds.
+ int64 ticks_;
+
+#if defined(OS_WIN)
+ typedef DWORD (*TickFunctionType)(void);
+ static TickFunctionType SetMockTickFunction(TickFunctionType ticker);
+#endif
+};
+
+inline TimeTicks TimeDelta::operator+(TimeTicks t) const {
+ return TimeTicks(t.ticks_ + delta_);
+}
+
+} // namespace base
+
+#endif // BASE_TIME_H_
diff --git a/src/base/time_mac.cc b/src/base/time_mac.cc
new file mode 100644
index 0000000..883a35b
--- /dev/null
+++ b/src/base/time_mac.cc
@@ -0,0 +1,199 @@
+// Copyright (c) 2012 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/time.h"
+
+#include <CoreFoundation/CFDate.h>
+#include <CoreFoundation/CFTimeZone.h>
+#include <mach/mach_time.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/mac/scoped_cftyperef.h"
+
+namespace {
+
+uint64_t ComputeCurrentTicks() {
+#if defined(OS_IOS)
+ // On iOS mach_absolute_time stops while the device is sleeping. Instead use
+ // now - KERN_BOOTTIME to get a time difference that is not impacted by clock
+ // changes. KERN_BOOTTIME will be updated by the system whenever the system
+ // clock change.
+ struct timeval boottime;
+ int mib[2] = {CTL_KERN, KERN_BOOTTIME};
+ size_t size = sizeof(boottime);
+ int kr = sysctl(mib, arraysize(mib), &boottime, &size, NULL, 0);
+ DCHECK_EQ(KERN_SUCCESS, kr);
+ base::TimeDelta time_difference = base::Time::Now() -
+ (base::Time::FromTimeT(boottime.tv_sec) +
+ base::TimeDelta::FromMicroseconds(boottime.tv_usec));
+ return time_difference.InMicroseconds();
+#else
+ uint64_t absolute_micro;
+
+ static mach_timebase_info_data_t timebase_info;
+ if (timebase_info.denom == 0) {
+ // Zero-initialization of statics guarantees that denom will be 0 before
+ // calling mach_timebase_info. mach_timebase_info will never set denom to
+ // 0 as that would be invalid, so the zero-check can be used to determine
+ // whether mach_timebase_info has already been called. This is
+ // recommended by Apple's QA1398.
+ kern_return_t kr = mach_timebase_info(&timebase_info);
+ DCHECK_EQ(KERN_SUCCESS, kr);
+ }
+
+ // mach_absolute_time is it when it comes to ticks on the Mac. Other calls
+ // with less precision (such as TickCount) just call through to
+ // mach_absolute_time.
+
+ // timebase_info converts absolute time tick units into nanoseconds. Convert
+ // to microseconds up front to stave off overflows.
+ absolute_micro =
+ mach_absolute_time() / base::Time::kNanosecondsPerMicrosecond *
+ timebase_info.numer / timebase_info.denom;
+
+ // Don't bother with the rollover handling that the Windows version does.
+ // With numer and denom = 1 (the expected case), the 64-bit absolute time
+ // reported in nanoseconds is enough to last nearly 585 years.
+ return absolute_micro;
+#endif // defined(OS_IOS)
+}
+
+} // namespace
+
+namespace base {
+
+// The Time routines in this file use Mach and CoreFoundation APIs, since the
+// POSIX definition of time_t in Mac OS X wraps around after 2038--and
+// there are already cookie expiration dates, etc., past that time out in
+// the field. Using CFDate prevents that problem, and using mach_absolute_time
+// for TimeTicks gives us nice high-resolution interval timing.
+
+// Time -----------------------------------------------------------------------
+
+// Core Foundation uses a double second count since 2001-01-01 00:00:00 UTC.
+// The UNIX epoch is 1970-01-01 00:00:00 UTC.
+// Windows uses a Gregorian epoch of 1601. We need to match this internally
+// so that our time representations match across all platforms. See bug 14734.
+// irb(main):010:0> Time.at(0).getutc()
+// => Thu Jan 01 00:00:00 UTC 1970
+// irb(main):011:0> Time.at(-11644473600).getutc()
+// => Mon Jan 01 00:00:00 UTC 1601
+static const int64 kWindowsEpochDeltaSeconds = GG_INT64_C(11644473600);
+static const int64 kWindowsEpochDeltaMilliseconds =
+ kWindowsEpochDeltaSeconds * Time::kMillisecondsPerSecond;
+
+// static
+const int64 Time::kWindowsEpochDeltaMicroseconds =
+ kWindowsEpochDeltaSeconds * Time::kMicrosecondsPerSecond;
+
+// Some functions in time.cc use time_t directly, so we provide an offset
+// to convert from time_t (Unix epoch) and internal (Windows epoch).
+// static
+const int64 Time::kTimeTToMicrosecondsOffset = kWindowsEpochDeltaMicroseconds;
+
+// static
+Time Time::Now() {
+ return FromCFAbsoluteTime(CFAbsoluteTimeGetCurrent());
+}
+
+// static
+Time Time::FromCFAbsoluteTime(CFAbsoluteTime t) {
+ if (t == 0)
+ return Time(); // Consider 0 as a null Time.
+ if (t == std::numeric_limits<CFAbsoluteTime>::max())
+ return Max();
+ return Time(static_cast<int64>(
+ (t + kCFAbsoluteTimeIntervalSince1970) * kMicrosecondsPerSecond) +
+ kWindowsEpochDeltaMicroseconds);
+}
+
+CFAbsoluteTime Time::ToCFAbsoluteTime() const {
+ if (is_null())
+ return 0; // Consider 0 as a null Time.
+ if (is_max())
+ return std::numeric_limits<CFAbsoluteTime>::max();
+ return (static_cast<CFAbsoluteTime>(us_ - kWindowsEpochDeltaMicroseconds) /
+ kMicrosecondsPerSecond) - kCFAbsoluteTimeIntervalSince1970;
+}
+
+// static
+Time Time::NowFromSystemTime() {
+ // Just use Now() because Now() returns the system time.
+ return Now();
+}
+
+// static
+Time Time::FromExploded(bool is_local, const Exploded& exploded) {
+ CFGregorianDate date;
+ date.second = exploded.second +
+ exploded.millisecond / static_cast<double>(kMillisecondsPerSecond);
+ date.minute = exploded.minute;
+ date.hour = exploded.hour;
+ date.day = exploded.day_of_month;
+ date.month = exploded.month;
+ date.year = exploded.year;
+
+ base::mac::ScopedCFTypeRef<CFTimeZoneRef>
+ time_zone(is_local ? CFTimeZoneCopySystem() : NULL);
+ CFAbsoluteTime seconds = CFGregorianDateGetAbsoluteTime(date, time_zone) +
+ kCFAbsoluteTimeIntervalSince1970;
+ return Time(static_cast<int64>(seconds * kMicrosecondsPerSecond) +
+ kWindowsEpochDeltaMicroseconds);
+}
+
+void Time::Explode(bool is_local, Exploded* exploded) const {
+ // Avoid rounding issues, by only putting the integral number of seconds
+ // (rounded towards -infinity) into a |CFAbsoluteTime| (which is a |double|).
+ int64 microsecond = us_ % kMicrosecondsPerSecond;
+ if (microsecond < 0)
+ microsecond += kMicrosecondsPerSecond;
+ CFAbsoluteTime seconds = ((us_ - microsecond) / kMicrosecondsPerSecond) -
+ kWindowsEpochDeltaSeconds -
+ kCFAbsoluteTimeIntervalSince1970;
+
+ base::mac::ScopedCFTypeRef<CFTimeZoneRef>
+ time_zone(is_local ? CFTimeZoneCopySystem() : NULL);
+ CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(seconds, time_zone);
+ // 1 = Monday, ..., 7 = Sunday.
+ int cf_day_of_week = CFAbsoluteTimeGetDayOfWeek(seconds, time_zone);
+
+ exploded->year = date.year;
+ exploded->month = date.month;
+ exploded->day_of_week = cf_day_of_week % 7;
+ exploded->day_of_month = date.day;
+ exploded->hour = date.hour;
+ exploded->minute = date.minute;
+ // Make sure seconds are rounded down towards -infinity.
+ exploded->second = floor(date.second);
+ // Calculate milliseconds ourselves, since we rounded the |seconds|, making
+ // sure to round towards -infinity.
+ exploded->millisecond =
+ (microsecond >= 0) ? microsecond / kMicrosecondsPerMillisecond :
+ (microsecond - kMicrosecondsPerMillisecond + 1) /
+ kMicrosecondsPerMillisecond;
+}
+
+// TimeTicks ------------------------------------------------------------------
+
+// static
+TimeTicks TimeTicks::Now() {
+ return TimeTicks(ComputeCurrentTicks());
+}
+
+// static
+TimeTicks TimeTicks::HighResNow() {
+ return Now();
+}
+
+// static
+TimeTicks TimeTicks::NowFromSystemTraceTime() {
+ return HighResNow();
+}
+
+} // namespace base
diff --git a/src/base/time_posix.cc b/src/base/time_posix.cc
new file mode 100644
index 0000000..80b1c4e
--- /dev/null
+++ b/src/base/time_posix.cc
@@ -0,0 +1,337 @@
+// Copyright (c) 2012 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/time.h"
+
+#include <sys/time.h>
+#include <time.h>
+#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
+#include <time64.h>
+#endif
+#include <unistd.h>
+
+#include <limits>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
+#include "base/os_compat_android.h"
+#elif defined(OS_NACL)
+#include "base/os_compat_nacl.h"
+#endif
+
+namespace {
+
+// Define a system-specific SysTime that wraps either to a time_t or
+// a time64_t depending on the host system, and associated convertion.
+// See crbug.com/162007
+#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
+typedef time64_t SysTime;
+
+SysTime SysTimeFromTimeStruct(struct tm* timestruct, bool is_local) {
+ if (is_local)
+ return mktime64(timestruct);
+ else
+ return timegm64(timestruct);
+}
+
+void SysTimeToTimeStruct(SysTime t, struct tm* timestruct, bool is_local) {
+ if (is_local)
+ localtime64_r(&t, timestruct);
+ else
+ gmtime64_r(&t, timestruct);
+}
+
+#else // OS_ANDROID
+typedef time_t SysTime;
+
+SysTime SysTimeFromTimeStruct(struct tm* timestruct, bool is_local) {
+ if (is_local)
+ return mktime(timestruct);
+ else
+ return timegm(timestruct);
+}
+
+void SysTimeToTimeStruct(SysTime t, struct tm* timestruct, bool is_local) {
+ if (is_local)
+ localtime_r(&t, timestruct);
+ else
+ gmtime_r(&t, timestruct);
+}
+#endif // OS_ANDROID
+
+} // namespace
+
+namespace base {
+
+#if defined(OS_ANDROID)
+#define _POSIX_MONOTONIC_CLOCK 1
+#endif
+
+struct timespec TimeDelta::ToTimeSpec() const {
+ int64 microseconds = InMicroseconds();
+ time_t seconds = 0;
+ if (microseconds >= Time::kMicrosecondsPerSecond) {
+ seconds = InSeconds();
+ microseconds -= seconds * Time::kMicrosecondsPerSecond;
+ }
+ struct timespec result =
+ {seconds,
+ static_cast<long>(microseconds * Time::kNanosecondsPerMicrosecond)};
+ return result;
+}
+
+#if !defined(OS_MACOSX)
+// The Time routines in this file use standard POSIX routines, or almost-
+// standard routines in the case of timegm. We need to use a Mach-specific
+// function for TimeTicks::Now() on Mac OS X.
+
+// Time -----------------------------------------------------------------------
+
+// Windows uses a Gregorian epoch of 1601. We need to match this internally
+// so that our time representations match across all platforms. See bug 14734.
+// irb(main):010:0> Time.at(0).getutc()
+// => Thu Jan 01 00:00:00 UTC 1970
+// irb(main):011:0> Time.at(-11644473600).getutc()
+// => Mon Jan 01 00:00:00 UTC 1601
+static const int64 kWindowsEpochDeltaSeconds = GG_INT64_C(11644473600);
+static const int64 kWindowsEpochDeltaMilliseconds =
+ kWindowsEpochDeltaSeconds * Time::kMillisecondsPerSecond;
+
+// static
+const int64 Time::kWindowsEpochDeltaMicroseconds =
+ kWindowsEpochDeltaSeconds * Time::kMicrosecondsPerSecond;
+
+// Some functions in time.cc use time_t directly, so we provide an offset
+// to convert from time_t (Unix epoch) and internal (Windows epoch).
+// static
+const int64 Time::kTimeTToMicrosecondsOffset = kWindowsEpochDeltaMicroseconds;
+
+// static
+Time Time::Now() {
+ struct timeval tv;
+ struct timezone tz = { 0, 0 }; // UTC
+ if (gettimeofday(&tv, &tz) != 0) {
+ DCHECK(0) << "Could not determine time of day";
+ LOG_ERRNO(ERROR) << "Call to gettimeofday failed.";
+ // Return null instead of uninitialized |tv| value, which contains random
+ // garbage data. This may result in the crash seen in crbug.com/147570.
+ return Time();
+ }
+ // Combine seconds and microseconds in a 64-bit field containing microseconds
+ // since the epoch. That's enough for nearly 600 centuries. Adjust from
+ // Unix (1970) to Windows (1601) epoch.
+ return Time((tv.tv_sec * kMicrosecondsPerSecond + tv.tv_usec) +
+ kWindowsEpochDeltaMicroseconds);
+}
+
+// static
+Time Time::NowFromSystemTime() {
+ // Just use Now() because Now() returns the system time.
+ return Now();
+}
+
+void Time::Explode(bool is_local, Exploded* exploded) const {
+ // Time stores times with microsecond resolution, but Exploded only carries
+ // millisecond resolution, so begin by being lossy. Adjust from Windows
+ // epoch (1601) to Unix epoch (1970);
+ int64 microseconds = us_ - kWindowsEpochDeltaMicroseconds;
+ // The following values are all rounded towards -infinity.
+ int64 milliseconds; // Milliseconds since epoch.
+ SysTime seconds; // Seconds since epoch.
+ int millisecond; // Exploded millisecond value (0-999).
+ if (microseconds >= 0) {
+ // Rounding towards -infinity <=> rounding towards 0, in this case.
+ milliseconds = microseconds / kMicrosecondsPerMillisecond;
+ seconds = milliseconds / kMillisecondsPerSecond;
+ millisecond = milliseconds % kMillisecondsPerSecond;
+ } else {
+ // Round these *down* (towards -infinity).
+ milliseconds = (microseconds - kMicrosecondsPerMillisecond + 1) /
+ kMicrosecondsPerMillisecond;
+ seconds = (milliseconds - kMillisecondsPerSecond + 1) /
+ kMillisecondsPerSecond;
+ // Make this nonnegative (and between 0 and 999 inclusive).
+ millisecond = milliseconds % kMillisecondsPerSecond;
+ if (millisecond < 0)
+ millisecond += kMillisecondsPerSecond;
+ }
+
+ struct tm timestruct;
+ SysTimeToTimeStruct(seconds, ×truct, is_local);
+
+ exploded->year = timestruct.tm_year + 1900;
+ exploded->month = timestruct.tm_mon + 1;
+ exploded->day_of_week = timestruct.tm_wday;
+ exploded->day_of_month = timestruct.tm_mday;
+ exploded->hour = timestruct.tm_hour;
+ exploded->minute = timestruct.tm_min;
+ exploded->second = timestruct.tm_sec;
+ exploded->millisecond = millisecond;
+}
+
+// static
+Time Time::FromExploded(bool is_local, const Exploded& exploded) {
+ struct tm timestruct;
+ timestruct.tm_sec = exploded.second;
+ timestruct.tm_min = exploded.minute;
+ timestruct.tm_hour = exploded.hour;
+ timestruct.tm_mday = exploded.day_of_month;
+ timestruct.tm_mon = exploded.month - 1;
+ timestruct.tm_year = exploded.year - 1900;
+ timestruct.tm_wday = exploded.day_of_week; // mktime/timegm ignore this
+ timestruct.tm_yday = 0; // mktime/timegm ignore this
+ timestruct.tm_isdst = -1; // attempt to figure it out
+#if !defined(OS_NACL) && !defined(OS_SOLARIS) && !defined(__LB_WIIU__) && \
+ !defined(__LB_PS4__)
+ timestruct.tm_gmtoff = 0; // not a POSIX field, so mktime/timegm ignore
+ timestruct.tm_zone = NULL; // not a POSIX field, so mktime/timegm ignore
+#endif
+
+ SysTime seconds = SysTimeFromTimeStruct(×truct, is_local);
+
+ int64 milliseconds;
+ // Handle overflow. Clamping the range to what mktime and timegm might
+ // return is the best that can be done here. It's not ideal, but it's better
+ // than failing here or ignoring the overflow case and treating each time
+ // overflow as one second prior to the epoch.
+ if (seconds == -1 &&
+ (exploded.year < 1969 || exploded.year > 1970)) {
+ // If exploded.year is 1969 or 1970, take -1 as correct, with the
+ // time indicating 1 second prior to the epoch. (1970 is allowed to handle
+ // time zone and DST offsets.) Otherwise, return the most future or past
+ // time representable. Assumes the time_t epoch is 1970-01-01 00:00:00 UTC.
+ //
+ // The minimum and maximum representible times that mktime and timegm could
+ // return are used here instead of values outside that range to allow for
+ // proper round-tripping between exploded and counter-type time
+ // representations in the presence of possible truncation to time_t by
+ // division and use with other functions that accept time_t.
+ //
+ // When representing the most distant time in the future, add in an extra
+ // 999ms to avoid the time being less than any other possible value that
+ // this function can return.
+
+ // numeric_limits<int32_t>::min() and max() represent times well outside
+ // the range of results we expect from mktime().
+
+ if (exploded.year < 1969) {
+ milliseconds = std::numeric_limits<int32_t>::min() *
+ kMillisecondsPerSecond;
+ } else {
+ milliseconds = std::numeric_limits<int32_t>::max() *
+ kMillisecondsPerSecond;
+ milliseconds += (kMillisecondsPerSecond - 1);
+ }
+ } else {
+ milliseconds = seconds * kMillisecondsPerSecond + exploded.millisecond;
+ }
+
+ // Adjust from Unix (1970) to Windows (1601) epoch.
+ return Time((milliseconds * kMicrosecondsPerMillisecond) +
+ kWindowsEpochDeltaMicroseconds);
+}
+
+// TimeTicks ------------------------------------------------------------------
+// FreeBSD 6 has CLOCK_MONOLITHIC but defines _POSIX_MONOTONIC_CLOCK to -1.
+#if (defined(OS_POSIX) && \
+ defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0) || \
+ defined(OS_BSD) || defined(OS_ANDROID)
+
+// static
+TimeTicks TimeTicks::Now() {
+ uint64_t absolute_micro;
+
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
+ NOTREACHED() << "clock_gettime(CLOCK_MONOTONIC) failed.";
+ return TimeTicks();
+ }
+
+ absolute_micro =
+ (static_cast<int64>(ts.tv_sec) * Time::kMicrosecondsPerSecond) +
+ (static_cast<int64>(ts.tv_nsec) / Time::kNanosecondsPerMicrosecond);
+
+ return TimeTicks(absolute_micro);
+}
+#elif defined(__LB_WIIU__)
+ // Defined in platform-specific code
+#else // _POSIX_MONOTONIC_CLOCK
+#error No usable tick clock function on this platform.
+#endif // _POSIX_MONOTONIC_CLOCK
+
+// static
+TimeTicks TimeTicks::HighResNow() {
+ return Now();
+}
+
+#if defined(OS_CHROMEOS)
+// Force definition of the system trace clock; it is a chromeos-only api
+// at the moment and surfacing it in the right place requires mucking
+// with glibc et al.
+#define CLOCK_SYSTEM_TRACE 11
+
+// static
+TimeTicks TimeTicks::NowFromSystemTraceTime() {
+ uint64_t absolute_micro;
+
+ struct timespec ts;
+ if (clock_gettime(CLOCK_SYSTEM_TRACE, &ts) != 0) {
+ // NB: fall-back for a chrome os build running on linux
+ return HighResNow();
+ }
+
+ absolute_micro =
+ (static_cast<int64>(ts.tv_sec) * Time::kMicrosecondsPerSecond) +
+ (static_cast<int64>(ts.tv_nsec) / Time::kNanosecondsPerMicrosecond);
+
+ return TimeTicks(absolute_micro);
+}
+
+#else // !defined(OS_CHROMEOS)
+
+// static
+TimeTicks TimeTicks::NowFromSystemTraceTime() {
+ return HighResNow();
+}
+
+#endif // defined(OS_CHROMEOS)
+
+#endif // !OS_MACOSX
+
+// static
+Time Time::FromTimeVal(struct timeval t) {
+ DCHECK_LT(t.tv_usec, static_cast<int>(Time::kMicrosecondsPerSecond));
+ DCHECK_GE(t.tv_usec, 0);
+ if (t.tv_usec == 0 && t.tv_sec == 0)
+ return Time();
+ if (t.tv_usec == static_cast<suseconds_t>(Time::kMicrosecondsPerSecond) - 1 &&
+ t.tv_sec == std::numeric_limits<time_t>::max())
+ return Max();
+ return Time(
+ (static_cast<int64>(t.tv_sec) * Time::kMicrosecondsPerSecond) +
+ t.tv_usec +
+ kTimeTToMicrosecondsOffset);
+}
+
+struct timeval Time::ToTimeVal() const {
+ struct timeval result;
+ if (is_null()) {
+ result.tv_sec = 0;
+ result.tv_usec = 0;
+ return result;
+ }
+ if (is_max()) {
+ result.tv_sec = std::numeric_limits<time_t>::max();
+ result.tv_usec = static_cast<suseconds_t>(Time::kMicrosecondsPerSecond) - 1;
+ return result;
+ }
+ int64 us = us_ - kTimeTToMicrosecondsOffset;
+ result.tv_sec = us / Time::kMicrosecondsPerSecond;
+ result.tv_usec = us % Time::kMicrosecondsPerSecond;
+ return result;
+}
+
+} // namespace base
diff --git a/src/base/time_starboard.cc b/src/base/time_starboard.cc
new file mode 100644
index 0000000..2e7785e
--- /dev/null
+++ b/src/base/time_starboard.cc
@@ -0,0 +1,104 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "base/time.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "starboard/client_porting/eztime/eztime.h"
+#include "starboard/time.h"
+
+namespace base {
+
+namespace {
+EzTimeZone GetTz(bool is_local) {
+ return is_local ? kEzTimeZoneLocal : kEzTimeZoneUTC;
+}
+} // namespace
+
+SbTime TimeDelta::ToSbTime() const {
+ return InMicroseconds();
+}
+
+// static
+const int64 Time::kTimeTToMicrosecondsOffset = -kSbTimeToPosixDelta;
+
+// static
+Time Time::Now() {
+ return Time(SbTimeGetNow());
+}
+
+// static
+Time Time::NowFromSystemTime() {
+ return Now();
+}
+
+void Time::Explode(bool is_local, Exploded *exploded) const {
+ EzTimeValue value = EzTimeValueFromSbTime(ToSbTime());
+ EzTimeExploded ez_exploded;
+ int millisecond;
+ bool result = EzTimeValueExplode(&value, GetTz(is_local), &ez_exploded,
+ &millisecond);
+ DCHECK(result);
+
+ exploded->year = ez_exploded.tm_year + 1900;
+ exploded->month = ez_exploded.tm_mon + 1;
+ exploded->day_of_week = ez_exploded.tm_wday;
+ exploded->day_of_month = ez_exploded.tm_mday;
+ exploded->hour = ez_exploded.tm_hour;
+ exploded->minute = ez_exploded.tm_min;
+ exploded->second = ez_exploded.tm_sec;
+ exploded->millisecond = millisecond;
+}
+
+// static
+Time Time::FromExploded(bool is_local, const Exploded &exploded) {
+ EzTimeExploded ez_exploded;
+ ez_exploded.tm_sec = exploded.second;
+ ez_exploded.tm_min = exploded.minute;
+ ez_exploded.tm_hour = exploded.hour;
+ ez_exploded.tm_mday = exploded.day_of_month;
+ ez_exploded.tm_mon = exploded.month - 1;
+ ez_exploded.tm_year = exploded.year - 1900;
+ ez_exploded.tm_isdst = -1;
+ EzTimeValue value = EzTimeValueImplode(&ez_exploded, exploded.millisecond,
+ GetTz(is_local));
+ return Time::FromSbTime(EzTimeValueToSbTime(&value));
+}
+
+// static
+Time Time::FromSbTime(SbTime t) {
+ return Time(t);
+}
+
+SbTime Time::ToSbTime() const {
+ return us_;
+}
+
+// static
+TimeTicks TimeTicks::Now() {
+ return TimeTicks(SbTimeGetMonotonicNow());
+}
+
+// static
+TimeTicks TimeTicks::HighResNow() {
+ return Now();
+}
+
+// static
+TimeTicks TimeTicks::NowFromSystemTraceTime() {
+ return HighResNow();
+}
+
+} // namespace base
diff --git a/src/base/time_unittest.cc b/src/base/time_unittest.cc
new file mode 100644
index 0000000..f1c0d54
--- /dev/null
+++ b/src/base/time_unittest.cc
@@ -0,0 +1,778 @@
+// Copyright (c) 2012 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/time.h"
+
+#if !defined(OS_STARBOARD)
+#include <time.h>
+#endif
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/test/time_helpers.h"
+#include "base/threading/platform_thread.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::Time;
+using base::TimeDelta;
+using base::TimeTicks;
+
+// Specialized test fixture allowing time strings without timezones to be
+// tested by comparing them to a known time in the local zone.
+// See also pr_time_unittests.cc
+class TimeTest : public testing::Test {
+ protected:
+ virtual void SetUp() OVERRIDE {
+#if defined(OS_STARBOARD)
+ // Since we don't have access to mktime, let's use time_helpers to do the
+ // same thing in a portable way.
+ comparison_time_local_ = base::test::time_helpers::TestDateToTime(
+ base::test::time_helpers::kTimeZoneLocal);
+ comparison_time_pdt_ = base::test::time_helpers::TestDateToTime(
+ base::test::time_helpers::kTimeZonePacific);
+#else // defined(OS_STARBOARD)
+ // Use mktime to get a time_t, and turn it into a PRTime by converting
+ // seconds to microseconds. Use 15th Oct 2007 12:45:00 local. This
+ // must be a time guaranteed to be outside of a DST fallback hour in
+ // any timezone.
+ struct tm local_comparison_tm = {
+ 0, // second
+ 45, // minute
+ 12, // hour
+ 15, // day of month
+ 10 - 1, // month
+ 2007 - 1900, // year
+ 0, // day of week (ignored, output only)
+ 0, // day of year (ignored, output only)
+ -1 // DST in effect, -1 tells mktime to figure it out
+ };
+
+ time_t converted_time = mktime(&local_comparison_tm);
+ ASSERT_GT(converted_time, 0);
+ comparison_time_local_ = Time::FromTimeT(converted_time);
+ // time_t representation of 15th Oct 2007 12:45:00 PDT
+ comparison_time_pdt_ = Time::FromTimeT(1192477500);
+#endif // defined(OS_STARBOARD)
+ }
+
+ Time comparison_time_local_;
+ Time comparison_time_pdt_;
+};
+
+#if !defined(OS_STARBOARD)
+// Test conversions to/from time_t and exploding/unexploding.
+TEST_F(TimeTest, TimeT) {
+ // C library time and exploded time.
+ time_t now_t_1 = time(NULL);
+ struct tm tms;
+#if defined(OS_WIN)
+ localtime_s(&tms, &now_t_1);
+#elif defined(OS_POSIX)
+ localtime_r(&now_t_1, &tms);
+#endif
+
+ // Convert to ours.
+ Time our_time_1 = Time::FromTimeT(now_t_1);
+ Time::Exploded exploded;
+ our_time_1.LocalExplode(&exploded);
+
+ // This will test both our exploding and our time_t -> Time conversion.
+ EXPECT_EQ(tms.tm_year + 1900, exploded.year);
+ EXPECT_EQ(tms.tm_mon + 1, exploded.month);
+ EXPECT_EQ(tms.tm_mday, exploded.day_of_month);
+ EXPECT_EQ(tms.tm_hour, exploded.hour);
+ EXPECT_EQ(tms.tm_min, exploded.minute);
+ EXPECT_EQ(tms.tm_sec, exploded.second);
+
+ // Convert exploded back to the time struct.
+ Time our_time_2 = Time::FromLocalExploded(exploded);
+ EXPECT_TRUE(our_time_1 == our_time_2);
+
+ time_t now_t_2 = our_time_2.ToTimeT();
+ EXPECT_EQ(now_t_1, now_t_2);
+
+ EXPECT_EQ(10, Time().FromTimeT(10).ToTimeT());
+ EXPECT_EQ(10.0, Time().FromTimeT(10).ToDoubleT());
+
+ // Conversions of 0 should stay 0.
+ EXPECT_EQ(0, Time().ToTimeT());
+ EXPECT_EQ(0, Time::FromTimeT(0).ToInternalValue());
+}
+#endif // !defined(OS_STARBOARD)
+
+// Test conversions to/from javascript time.
+TEST_F(TimeTest, JsTime) {
+ Time epoch = Time::FromJsTime(0.0);
+ EXPECT_EQ(epoch, Time::UnixEpoch());
+ Time t = Time::FromJsTime(700000.3);
+ EXPECT_EQ(700.0003, t.ToDoubleT());
+ t = Time::FromDoubleT(800.73);
+ EXPECT_EQ(800730.0, t.ToJsTime());
+}
+
+#if defined(OS_POSIX) && !defined(__LB_SHELL__)
+// FromTimeVal is only used on Macs, and ToTimeVal is only used in conjunction
+// with TouchPlatformFile(), which is not well supported in lb_shell.
+
+TEST_F(TimeTest, FromTimeVal) {
+ Time now = Time::Now();
+ Time also_now = Time::FromTimeVal(now.ToTimeVal());
+ EXPECT_EQ(now, also_now);
+}
+#endif // OS_POSIX
+
+TEST_F(TimeTest, FromExplodedWithMilliseconds) {
+ // Some platform implementations of FromExploded are liable to drop
+ // milliseconds if we aren't careful.
+ Time now = Time::NowFromSystemTime();
+ Time::Exploded exploded1 = {0};
+ now.UTCExplode(&exploded1);
+ exploded1.millisecond = 500;
+ Time time = Time::FromUTCExploded(exploded1);
+ Time::Exploded exploded2 = {0};
+ time.UTCExplode(&exploded2);
+ EXPECT_EQ(exploded1.millisecond, exploded2.millisecond);
+}
+
+TEST_F(TimeTest, ZeroIsSymmetric) {
+ Time zero_time(Time::FromTimeT(0));
+ EXPECT_EQ(0, zero_time.ToTimeT());
+
+ EXPECT_EQ(0.0, zero_time.ToDoubleT());
+}
+
+TEST_F(TimeTest, LocalExplode) {
+ Time a = Time::Now();
+ Time::Exploded exploded;
+ a.LocalExplode(&exploded);
+
+ Time b = Time::FromLocalExploded(exploded);
+
+ // The exploded structure doesn't have microseconds, and on Mac & Linux, the
+ // internal OS conversion uses seconds, which will cause truncation. So we
+ // can only make sure that the delta is within one second.
+ EXPECT_TRUE((a - b) < TimeDelta::FromSeconds(1));
+}
+
+TEST_F(TimeTest, UTCExplode) {
+ Time a = Time::Now();
+ Time::Exploded exploded;
+ a.UTCExplode(&exploded);
+
+ Time b = Time::FromUTCExploded(exploded);
+ EXPECT_TRUE((a - b) < TimeDelta::FromSeconds(1));
+}
+
+TEST_F(TimeTest, LocalMidnight) {
+ Time::Exploded exploded;
+ Time::Now().LocalMidnight().LocalExplode(&exploded);
+ EXPECT_EQ(0, exploded.hour);
+ EXPECT_EQ(0, exploded.minute);
+ EXPECT_EQ(0, exploded.second);
+ EXPECT_EQ(0, exploded.millisecond);
+}
+
+#if defined(OS_STARBOARD)
+TEST_F(TimeTest, ParseTimeTest1) {
+ Time now = Time::Now();
+
+ Time parsed_time;
+ std::string formatted = base::test::time_helpers::TimeFormatUTC(now);
+ EXPECT_TRUE(Time::FromUTCString(formatted.c_str(), &parsed_time));
+ EXPECT_GE(1, (now - parsed_time).InSecondsF());
+ EXPECT_GE(1, (parsed_time - now).InSecondsF());
+
+ formatted = base::test::time_helpers::TimeFormatLocal(now);
+ EXPECT_TRUE(Time::FromString(formatted.c_str(), &parsed_time));
+ EXPECT_GE(1, (now - parsed_time).InSecondsF());
+ EXPECT_GE(1, (parsed_time - now).InSecondsF());
+}
+#else // !defined(OS_STARBOARD)
+TEST_F(TimeTest, ParseTimeTest1) {
+ time_t current_time = 0;
+ time(¤t_time);
+
+ const int BUFFER_SIZE = 64;
+ struct tm local_time = {0};
+ char time_buf[BUFFER_SIZE] = {0};
+#if defined(OS_WIN)
+ localtime_s(&local_time, ¤t_time);
+ asctime_s(time_buf, arraysize(time_buf), &local_time);
+#elif defined(__STDC_LIB_EXT1__)
+ localtime_s(¤t_time, &local_time);
+ asctime_s(time_buf, arraysize(time_buf), &local_time);
+#elif defined(OS_POSIX)
+ localtime_r(¤t_time, &local_time);
+ asctime_r(&local_time, time_buf);
+#endif
+
+ Time parsed_time;
+ EXPECT_TRUE(Time::FromString(time_buf, &parsed_time));
+ EXPECT_EQ(current_time, parsed_time.ToTimeT());
+}
+#endif // !defined(OS_STARBOARD)
+
+TEST_F(TimeTest, DayOfWeekSunday) {
+ Time time;
+ EXPECT_TRUE(Time::FromString("Sun, 06 May 2012 12:00:00 GMT", &time));
+ Time::Exploded exploded;
+ time.UTCExplode(&exploded);
+ EXPECT_EQ(0, exploded.day_of_week);
+}
+
+TEST_F(TimeTest, DayOfWeekWednesday) {
+ Time time;
+ EXPECT_TRUE(Time::FromString("Wed, 09 May 2012 12:00:00 GMT", &time));
+ Time::Exploded exploded;
+ time.UTCExplode(&exploded);
+ EXPECT_EQ(3, exploded.day_of_week);
+}
+
+TEST_F(TimeTest, DayOfWeekSaturday) {
+ Time time;
+ EXPECT_TRUE(Time::FromString("Sat, 12 May 2012 12:00:00 GMT", &time));
+ Time::Exploded exploded;
+ time.UTCExplode(&exploded);
+ EXPECT_EQ(6, exploded.day_of_week);
+}
+
+TEST_F(TimeTest, ParseTimeTest2) {
+ Time parsed_time;
+ EXPECT_TRUE(Time::FromString("Mon, 15 Oct 2007 19:45:00 GMT", &parsed_time));
+ EXPECT_EQ(comparison_time_pdt_, parsed_time);
+}
+
+TEST_F(TimeTest, ParseTimeTest3) {
+ Time parsed_time;
+ EXPECT_TRUE(Time::FromString("15 Oct 07 12:45:00", &parsed_time));
+ EXPECT_EQ(comparison_time_local_, parsed_time);
+}
+
+TEST_F(TimeTest, ParseTimeTest4) {
+ Time parsed_time;
+ EXPECT_TRUE(Time::FromString("15 Oct 07 19:45 GMT", &parsed_time));
+ EXPECT_EQ(comparison_time_pdt_, parsed_time);
+}
+
+TEST_F(TimeTest, ParseTimeTest5) {
+ Time parsed_time;
+ EXPECT_TRUE(Time::FromString("Mon Oct 15 12:45 PDT 2007", &parsed_time));
+ EXPECT_EQ(comparison_time_pdt_, parsed_time);
+}
+
+TEST_F(TimeTest, ParseTimeTest6) {
+ Time parsed_time;
+ EXPECT_TRUE(Time::FromString("Monday, Oct 15, 2007 12:45 PM", &parsed_time));
+ EXPECT_EQ(comparison_time_local_, parsed_time);
+}
+
+TEST_F(TimeTest, ParseTimeTest7) {
+ Time parsed_time;
+ EXPECT_TRUE(Time::FromString("10/15/07 12:45:00 PM", &parsed_time));
+ EXPECT_EQ(comparison_time_local_, parsed_time);
+}
+
+TEST_F(TimeTest, ParseTimeTest8) {
+ Time parsed_time;
+ EXPECT_TRUE(Time::FromString("15-OCT-2007 12:45pm", &parsed_time));
+ EXPECT_EQ(comparison_time_local_, parsed_time);
+}
+
+TEST_F(TimeTest, ParseTimeTest9) {
+ Time parsed_time;
+ EXPECT_TRUE(Time::FromString("16 Oct 2007 4:45-JST (Tuesday)", &parsed_time));
+ EXPECT_EQ(comparison_time_pdt_, parsed_time);
+}
+
+TEST_F(TimeTest, ParseTimeTest10) {
+ Time parsed_time;
+ EXPECT_TRUE(Time::FromString("15/10/07 12:45", &parsed_time));
+ EXPECT_EQ(parsed_time, comparison_time_local_);
+}
+
+// Test some of edge cases around epoch, etc.
+TEST_F(TimeTest, ParseTimeTestEpoch0) {
+ Time parsed_time;
+
+ // time_t == epoch == 0
+ EXPECT_TRUE(Time::FromString("Thu Jan 01 01:00:00 +0100 1970",
+ &parsed_time));
+ EXPECT_EQ(0, parsed_time.ToTimeT());
+ EXPECT_TRUE(Time::FromString("Thu Jan 01 00:00:00 GMT 1970",
+ &parsed_time));
+ EXPECT_EQ(0, parsed_time.ToTimeT());
+}
+
+TEST_F(TimeTest, ParseTimeTestEpoch1) {
+ Time parsed_time;
+
+ // time_t == 1 second after epoch == 1
+ EXPECT_TRUE(Time::FromString("Thu Jan 01 01:00:01 +0100 1970",
+ &parsed_time));
+ EXPECT_EQ(1, parsed_time.ToTimeT());
+ EXPECT_TRUE(Time::FromString("Thu Jan 01 00:00:01 GMT 1970",
+ &parsed_time));
+ EXPECT_EQ(1, parsed_time.ToTimeT());
+}
+
+TEST_F(TimeTest, ParseTimeTestEpoch2) {
+ Time parsed_time;
+
+ // time_t == 2 seconds after epoch == 2
+ EXPECT_TRUE(Time::FromString("Thu Jan 01 01:00:02 +0100 1970",
+ &parsed_time));
+ EXPECT_EQ(2, parsed_time.ToTimeT());
+ EXPECT_TRUE(Time::FromString("Thu Jan 01 00:00:02 GMT 1970",
+ &parsed_time));
+ EXPECT_EQ(2, parsed_time.ToTimeT());
+}
+
+TEST_F(TimeTest, ParseTimeTestEpochNeg1) {
+ Time parsed_time;
+
+ // time_t == 1 second before epoch == -1
+ EXPECT_TRUE(Time::FromString("Thu Jan 01 00:59:59 +0100 1970",
+ &parsed_time));
+ EXPECT_EQ(-1, parsed_time.ToTimeT());
+ EXPECT_TRUE(Time::FromString("Wed Dec 31 23:59:59 GMT 1969",
+ &parsed_time));
+ EXPECT_EQ(-1, parsed_time.ToTimeT());
+}
+
+// If time_t is 32 bits, a date after year 2038 will overflow time_t and
+// cause timegm() to return -1. The parsed time should not be 1 second
+// before epoch.
+TEST_F(TimeTest, ParseTimeTestEpochNotNeg1) {
+ Time parsed_time;
+
+ EXPECT_TRUE(Time::FromString("Wed Dec 31 23:59:59 GMT 2100",
+ &parsed_time));
+ EXPECT_NE(-1, parsed_time.ToTimeT());
+}
+
+TEST_F(TimeTest, ParseTimeTestEpochNeg2) {
+ Time parsed_time;
+
+ // time_t == 2 seconds before epoch == -2
+ EXPECT_TRUE(Time::FromString("Thu Jan 01 00:59:58 +0100 1970",
+ &parsed_time));
+ EXPECT_EQ(-2, parsed_time.ToTimeT());
+ EXPECT_TRUE(Time::FromString("Wed Dec 31 23:59:58 GMT 1969",
+ &parsed_time));
+ EXPECT_EQ(-2, parsed_time.ToTimeT());
+}
+
+TEST_F(TimeTest, ParseTimeTestEpoch1960) {
+ Time parsed_time;
+
+ // time_t before Epoch, in 1960
+ EXPECT_TRUE(Time::FromString("Wed Jun 29 19:40:01 +0100 1960",
+ &parsed_time));
+ EXPECT_EQ(-299999999, parsed_time.ToTimeT());
+ EXPECT_TRUE(Time::FromString("Wed Jun 29 18:40:01 GMT 1960",
+ &parsed_time));
+ EXPECT_EQ(-299999999, parsed_time.ToTimeT());
+ EXPECT_TRUE(Time::FromString("Wed Jun 29 17:40:01 GMT 1960",
+ &parsed_time));
+ EXPECT_EQ(-300003599, parsed_time.ToTimeT());
+}
+
+TEST_F(TimeTest, ParseTimeTestEmpty) {
+ Time parsed_time;
+ EXPECT_FALSE(Time::FromString("", &parsed_time));
+}
+
+TEST_F(TimeTest, ParseTimeTestInvalidString) {
+ Time parsed_time;
+ EXPECT_FALSE(Time::FromString("Monday morning 2000", &parsed_time));
+}
+
+TEST_F(TimeTest, ExplodeBeforeUnixEpoch) {
+ static const int kUnixEpochYear = 1970; // In case this changes (ha!).
+ Time t;
+ Time::Exploded exploded;
+
+ t = Time::UnixEpoch() - TimeDelta::FromMicroseconds(1);
+ t.UTCExplode(&exploded);
+ EXPECT_TRUE(exploded.HasValidValues());
+ // Should be 1969-12-31 23:59:59 999 milliseconds (and 999 microseconds).
+ EXPECT_EQ(kUnixEpochYear - 1, exploded.year);
+ EXPECT_EQ(12, exploded.month);
+ EXPECT_EQ(31, exploded.day_of_month);
+ EXPECT_EQ(23, exploded.hour);
+ EXPECT_EQ(59, exploded.minute);
+ EXPECT_EQ(59, exploded.second);
+ EXPECT_EQ(999, exploded.millisecond);
+
+ t = Time::UnixEpoch() - TimeDelta::FromMicroseconds(1000);
+ t.UTCExplode(&exploded);
+ EXPECT_TRUE(exploded.HasValidValues());
+ // Should be 1969-12-31 23:59:59 999 milliseconds.
+ EXPECT_EQ(kUnixEpochYear - 1, exploded.year);
+ EXPECT_EQ(12, exploded.month);
+ EXPECT_EQ(31, exploded.day_of_month);
+ EXPECT_EQ(23, exploded.hour);
+ EXPECT_EQ(59, exploded.minute);
+ EXPECT_EQ(59, exploded.second);
+ EXPECT_EQ(999, exploded.millisecond);
+
+ t = Time::UnixEpoch() - TimeDelta::FromMicroseconds(1001);
+ t.UTCExplode(&exploded);
+ EXPECT_TRUE(exploded.HasValidValues());
+ // Should be 1969-12-31 23:59:59 998 milliseconds (and 999 microseconds).
+ EXPECT_EQ(kUnixEpochYear - 1, exploded.year);
+ EXPECT_EQ(12, exploded.month);
+ EXPECT_EQ(31, exploded.day_of_month);
+ EXPECT_EQ(23, exploded.hour);
+ EXPECT_EQ(59, exploded.minute);
+ EXPECT_EQ(59, exploded.second);
+ EXPECT_EQ(998, exploded.millisecond);
+
+ t = Time::UnixEpoch() - TimeDelta::FromMilliseconds(1000);
+ t.UTCExplode(&exploded);
+ EXPECT_TRUE(exploded.HasValidValues());
+ // Should be 1969-12-31 23:59:59.
+ EXPECT_EQ(kUnixEpochYear - 1, exploded.year);
+ EXPECT_EQ(12, exploded.month);
+ EXPECT_EQ(31, exploded.day_of_month);
+ EXPECT_EQ(23, exploded.hour);
+ EXPECT_EQ(59, exploded.minute);
+ EXPECT_EQ(59, exploded.second);
+ EXPECT_EQ(0, exploded.millisecond);
+
+ t = Time::UnixEpoch() - TimeDelta::FromMilliseconds(1001);
+ t.UTCExplode(&exploded);
+ EXPECT_TRUE(exploded.HasValidValues());
+ // Should be 1969-12-31 23:59:58 999 milliseconds.
+ EXPECT_EQ(kUnixEpochYear - 1, exploded.year);
+ EXPECT_EQ(12, exploded.month);
+ EXPECT_EQ(31, exploded.day_of_month);
+ EXPECT_EQ(23, exploded.hour);
+ EXPECT_EQ(59, exploded.minute);
+ EXPECT_EQ(58, exploded.second);
+ EXPECT_EQ(999, exploded.millisecond);
+
+ // Make sure we still handle at/after Unix epoch correctly.
+ t = Time::UnixEpoch();
+ t.UTCExplode(&exploded);
+ EXPECT_TRUE(exploded.HasValidValues());
+ // Should be 1970-12-31 00:00:00 0 milliseconds.
+ EXPECT_EQ(kUnixEpochYear, exploded.year);
+ EXPECT_EQ(1, exploded.month);
+ EXPECT_EQ(1, exploded.day_of_month);
+ EXPECT_EQ(0, exploded.hour);
+ EXPECT_EQ(0, exploded.minute);
+ EXPECT_EQ(0, exploded.second);
+ EXPECT_EQ(0, exploded.millisecond);
+
+ t = Time::UnixEpoch() + TimeDelta::FromMicroseconds(1);
+ t.UTCExplode(&exploded);
+ EXPECT_TRUE(exploded.HasValidValues());
+ // Should be 1970-01-01 00:00:00 0 milliseconds (and 1 microsecond).
+ EXPECT_EQ(kUnixEpochYear, exploded.year);
+ EXPECT_EQ(1, exploded.month);
+ EXPECT_EQ(1, exploded.day_of_month);
+ EXPECT_EQ(0, exploded.hour);
+ EXPECT_EQ(0, exploded.minute);
+ EXPECT_EQ(0, exploded.second);
+ EXPECT_EQ(0, exploded.millisecond);
+
+ t = Time::UnixEpoch() + TimeDelta::FromMicroseconds(1000);
+ t.UTCExplode(&exploded);
+ EXPECT_TRUE(exploded.HasValidValues());
+ // Should be 1970-01-01 00:00:00 1 millisecond.
+ EXPECT_EQ(kUnixEpochYear, exploded.year);
+ EXPECT_EQ(1, exploded.month);
+ EXPECT_EQ(1, exploded.day_of_month);
+ EXPECT_EQ(0, exploded.hour);
+ EXPECT_EQ(0, exploded.minute);
+ EXPECT_EQ(0, exploded.second);
+ EXPECT_EQ(1, exploded.millisecond);
+
+ t = Time::UnixEpoch() + TimeDelta::FromMilliseconds(1000);
+ t.UTCExplode(&exploded);
+ EXPECT_TRUE(exploded.HasValidValues());
+ // Should be 1970-01-01 00:00:01.
+ EXPECT_EQ(kUnixEpochYear, exploded.year);
+ EXPECT_EQ(1, exploded.month);
+ EXPECT_EQ(1, exploded.day_of_month);
+ EXPECT_EQ(0, exploded.hour);
+ EXPECT_EQ(0, exploded.minute);
+ EXPECT_EQ(1, exploded.second);
+ EXPECT_EQ(0, exploded.millisecond);
+
+ t = Time::UnixEpoch() + TimeDelta::FromMilliseconds(1001);
+ t.UTCExplode(&exploded);
+ EXPECT_TRUE(exploded.HasValidValues());
+ // Should be 1970-01-01 00:00:01 1 millisecond.
+ EXPECT_EQ(kUnixEpochYear, exploded.year);
+ EXPECT_EQ(1, exploded.month);
+ EXPECT_EQ(1, exploded.day_of_month);
+ EXPECT_EQ(0, exploded.hour);
+ EXPECT_EQ(0, exploded.minute);
+ EXPECT_EQ(1, exploded.second);
+ EXPECT_EQ(1, exploded.millisecond);
+}
+
+TEST_F(TimeTest, TimeDeltaMax) {
+ TimeDelta max = TimeDelta::Max();
+ EXPECT_TRUE(max.is_max());
+ EXPECT_EQ(max, TimeDelta::Max());
+ EXPECT_GT(max, TimeDelta::FromDays(100 * 365));
+ EXPECT_GT(max, TimeDelta());
+}
+
+TEST_F(TimeTest, TimeDeltaMaxConversions) {
+ TimeDelta t = TimeDelta::Max();
+ EXPECT_EQ(std::numeric_limits<int64>::max(), t.ToInternalValue());
+
+ EXPECT_EQ(std::numeric_limits<int>::max(), t.InDays());
+ EXPECT_EQ(std::numeric_limits<int>::max(), t.InHours());
+ EXPECT_EQ(std::numeric_limits<int>::max(), t.InMinutes());
+ EXPECT_EQ(std::numeric_limits<double>::infinity(), t.InSecondsF());
+ EXPECT_EQ(std::numeric_limits<int64>::max(), t.InSeconds());
+ EXPECT_EQ(std::numeric_limits<double>::infinity(), t.InMillisecondsF());
+ EXPECT_EQ(std::numeric_limits<int64>::max(), t.InMilliseconds());
+ EXPECT_EQ(std::numeric_limits<int64>::max(), t.InMillisecondsRoundedUp());
+
+ t = TimeDelta::FromDays(std::numeric_limits<int>::max());
+ EXPECT_TRUE(t.is_max());
+
+ t = TimeDelta::FromHours(std::numeric_limits<int>::max());
+ EXPECT_TRUE(t.is_max());
+
+ t = TimeDelta::FromMinutes(std::numeric_limits<int>::max());
+ EXPECT_TRUE(t.is_max());
+
+ t = TimeDelta::FromSeconds(std::numeric_limits<int64>::max());
+ EXPECT_TRUE(t.is_max());
+
+ t = TimeDelta::FromMilliseconds(std::numeric_limits<int64>::max());
+ EXPECT_TRUE(t.is_max());
+
+ t = TimeDelta::FromSecondsD(std::numeric_limits<double>::infinity());
+ EXPECT_TRUE(t.is_max());
+
+ t = TimeDelta::FromMillisecondsD(std::numeric_limits<double>::infinity());
+ EXPECT_TRUE(t.is_max());
+
+ t = TimeDelta::FromMicroseconds(std::numeric_limits<int64>::max());
+ EXPECT_TRUE(t.is_max());
+}
+
+TEST_F(TimeTest, Max) {
+ Time max = Time::Max();
+ EXPECT_TRUE(max.is_max());
+ EXPECT_EQ(max, Time::Max());
+ EXPECT_GT(max, Time::Now());
+ EXPECT_GT(max, Time());
+}
+
+TEST_F(TimeTest, MaxConversions) {
+ Time t = Time::Max();
+ EXPECT_EQ(std::numeric_limits<int64>::max(), t.ToInternalValue());
+
+ t = Time::FromDoubleT(std::numeric_limits<double>::max());
+ EXPECT_TRUE(t.is_max());
+ EXPECT_EQ(std::numeric_limits<double>::max(), t.ToDoubleT());
+
+ t = Time::FromJsTime(std::numeric_limits<double>::max());
+ EXPECT_TRUE(t.is_max());
+ EXPECT_EQ(std::numeric_limits<double>::max(), t.ToJsTime());
+
+ t = Time::FromTimeT(std::numeric_limits<time_t>::max());
+ EXPECT_TRUE(t.is_max());
+ EXPECT_EQ(std::numeric_limits<time_t>::max(), t.ToTimeT());
+
+#if defined(OS_POSIX) && !defined(__LB_SHELL__)
+ struct timeval tval;
+ tval.tv_sec = std::numeric_limits<time_t>::max();
+ tval.tv_usec = static_cast<suseconds_t>(Time::kMicrosecondsPerSecond) - 1;
+ t = Time::FromTimeVal(tval);
+ EXPECT_TRUE(t.is_max());
+ tval = t.ToTimeVal();
+ EXPECT_EQ(std::numeric_limits<time_t>::max(), tval.tv_sec);
+ EXPECT_EQ(static_cast<suseconds_t>(Time::kMicrosecondsPerSecond) - 1,
+ tval.tv_usec);
+#endif
+
+#if defined(OS_MACOSX)
+ t = Time::FromCFAbsoluteTime(std::numeric_limits<CFAbsoluteTime>::max());
+ EXPECT_TRUE(t.is_max());
+ EXPECT_EQ(std::numeric_limits<CFAbsoluteTime>::max(), t.ToCFAbsoluteTime());
+#endif
+
+#if defined(OS_WIN)
+ FILETIME ftime;
+ ftime.dwHighDateTime = std::numeric_limits<DWORD>::max();
+ ftime.dwLowDateTime = std::numeric_limits<DWORD>::max();
+ t = Time::FromFileTime(ftime);
+ EXPECT_TRUE(t.is_max());
+ ftime = t.ToFileTime();
+ EXPECT_EQ(std::numeric_limits<DWORD>::max(), ftime.dwHighDateTime);
+ EXPECT_EQ(std::numeric_limits<DWORD>::max(), ftime.dwLowDateTime);
+#endif
+}
+
+#if defined(OS_MACOSX)
+TEST_F(TimeTest, TimeTOverflow) {
+ Time t = Time::FromInternalValue(std::numeric_limits<int64>::max() - 1);
+ EXPECT_FALSE(t.is_max());
+ EXPECT_EQ(std::numeric_limits<time_t>::max(), t.ToTimeT());
+}
+#endif
+
+TEST(TimeTicks, Deltas) {
+ for (int index = 0; index < 50; index++) {
+ TimeTicks ticks_start = TimeTicks::Now();
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
+ TimeTicks ticks_stop = TimeTicks::Now();
+ TimeDelta delta = ticks_stop - ticks_start;
+ // Note: Although we asked for a 10ms sleep, if the
+ // time clock has a finer granularity than the Sleep()
+ // clock, it is quite possible to wakeup early. Here
+ // is how that works:
+ // Time(ms timer) Time(us timer)
+ // 5 5010
+ // 6 6010
+ // 7 7010
+ // 8 8010
+ // 9 9000
+ // Elapsed 4ms 3990us
+ //
+ // Unfortunately, our InMilliseconds() function truncates
+ // rather than rounds. We should consider fixing this
+ // so that our averages come out better.
+ EXPECT_GE(delta.InMilliseconds(), 9);
+ EXPECT_GE(delta.InMicroseconds(), 9000);
+ EXPECT_EQ(delta.InSeconds(), 0);
+ }
+}
+
+static void HighResClockTest(TimeTicks (*GetTicks)()) {
+#if defined(OS_WIN)
+ // HighResNow doesn't work on some systems. Since the product still works
+ // even if it doesn't work, it makes this entire test questionable.
+ if (!TimeTicks::IsHighResClockWorking())
+ return;
+#endif
+
+ // Why do we loop here?
+ // We're trying to measure that intervals increment in a VERY small amount
+ // of time -- less than 15ms. Unfortunately, if we happen to have a
+ // context switch in the middle of our test, the context switch could easily
+ // exceed our limit. So, we iterate on this several times. As long as we're
+ // able to detect the fine-granularity timers at least once, then the test
+ // has succeeded.
+
+ const int kTargetGranularityUs = 15000; // 15ms
+
+ bool success = false;
+ int retries = 100; // Arbitrary.
+ TimeDelta delta;
+ while (!success && retries--) {
+ TimeTicks ticks_start = GetTicks();
+ // Loop until we can detect that the clock has changed. Non-HighRes timers
+ // will increment in chunks, e.g. 15ms. By spinning until we see a clock
+ // change, we detect the minimum time between measurements.
+ do {
+ delta = GetTicks() - ticks_start;
+ } while (delta.InMilliseconds() == 0);
+
+ if (delta.InMicroseconds() <= kTargetGranularityUs)
+ success = true;
+ }
+
+ // In high resolution mode, we expect to see the clock increment
+ // in intervals less than 15ms.
+ EXPECT_TRUE(success);
+}
+
+TEST(TimeTicks, HighResNow) {
+ HighResClockTest(&TimeTicks::HighResNow);
+}
+
+TEST(TimeTicks, NowFromSystemTraceTime) {
+ // Re-use HighResNow test for now since clock properties are identical.
+ HighResClockTest(&TimeTicks::NowFromSystemTraceTime);
+}
+
+TEST(TimeDelta, FromAndIn) {
+ EXPECT_TRUE(TimeDelta::FromDays(2) == TimeDelta::FromHours(48));
+ EXPECT_TRUE(TimeDelta::FromHours(3) == TimeDelta::FromMinutes(180));
+ EXPECT_TRUE(TimeDelta::FromMinutes(2) == TimeDelta::FromSeconds(120));
+ EXPECT_TRUE(TimeDelta::FromSeconds(2) == TimeDelta::FromMilliseconds(2000));
+ EXPECT_TRUE(TimeDelta::FromMilliseconds(2) ==
+ TimeDelta::FromMicroseconds(2000));
+ EXPECT_TRUE(TimeDelta::FromSecondsD(2.3) ==
+ TimeDelta::FromMilliseconds(2300));
+ EXPECT_TRUE(TimeDelta::FromMillisecondsD(2.5) ==
+ TimeDelta::FromMicroseconds(2500));
+ EXPECT_EQ(13, TimeDelta::FromDays(13).InDays());
+ EXPECT_EQ(13, TimeDelta::FromHours(13).InHours());
+ EXPECT_EQ(13, TimeDelta::FromMinutes(13).InMinutes());
+ EXPECT_EQ(13, TimeDelta::FromSeconds(13).InSeconds());
+ EXPECT_EQ(13.0, TimeDelta::FromSeconds(13).InSecondsF());
+ EXPECT_EQ(13, TimeDelta::FromMilliseconds(13).InMilliseconds());
+ EXPECT_EQ(13.0, TimeDelta::FromMilliseconds(13).InMillisecondsF());
+ EXPECT_EQ(13, TimeDelta::FromSecondsD(13.1).InSeconds());
+ EXPECT_EQ(13.1, TimeDelta::FromSecondsD(13.1).InSecondsF());
+ EXPECT_EQ(13, TimeDelta::FromMillisecondsD(13.3).InMilliseconds());
+ EXPECT_EQ(13.3, TimeDelta::FromMillisecondsD(13.3).InMillisecondsF());
+ EXPECT_EQ(13, TimeDelta::FromMicroseconds(13).InMicroseconds());
+}
+
+#if defined(OS_POSIX) && !defined(__LB_SHELL__)
+// ToTimeSpec() only ever called for Macs (see process_util_posix.cc)
+
+TEST(TimeDelta, TimeSpecConversion) {
+ struct timespec result = TimeDelta::FromSeconds(0).ToTimeSpec();
+ EXPECT_EQ(result.tv_sec, 0);
+ EXPECT_EQ(result.tv_nsec, 0);
+
+ result = TimeDelta::FromSeconds(1).ToTimeSpec();
+ EXPECT_EQ(result.tv_sec, 1);
+ EXPECT_EQ(result.tv_nsec, 0);
+
+ result = TimeDelta::FromMicroseconds(1).ToTimeSpec();
+ EXPECT_EQ(result.tv_sec, 0);
+ EXPECT_EQ(result.tv_nsec, 1000);
+
+ result = TimeDelta::FromMicroseconds(
+ Time::kMicrosecondsPerSecond + 1).ToTimeSpec();
+ EXPECT_EQ(result.tv_sec, 1);
+ EXPECT_EQ(result.tv_nsec, 1000);
+}
+#endif // OS_POSIX
+
+// Our internal time format is serialized in things like databases, so it's
+// important that it's consistent across all our platforms. We use the 1601
+// Windows epoch as the internal format across all platforms.
+TEST(TimeDelta, WindowsEpoch) {
+ Time::Exploded exploded;
+ exploded.year = 1970;
+ exploded.month = 1;
+ exploded.day_of_week = 0; // Should be unusued.
+ exploded.day_of_month = 1;
+ exploded.hour = 0;
+ exploded.minute = 0;
+ exploded.second = 0;
+ exploded.millisecond = 0;
+ Time t = Time::FromUTCExploded(exploded);
+ // Unix 1970 epoch.
+ EXPECT_EQ(GG_INT64_C(11644473600000000), t.ToInternalValue());
+
+ // We can't test 1601 epoch, since the system time functions on Linux
+ // only compute years starting from 1900.
+
+#if defined(OS_STARBOARD)
+ // But we can test this in Starboard, because we've implemented it in terms of
+ // ICU instead of possibly-flawed system APIs.
+ exploded.year = 1601;
+ // Unix 1970 epoch.
+ EXPECT_EQ(GG_INT64_C(0), Time::FromUTCExploded(exploded).ToInternalValue());
+#endif
+}
diff --git a/src/base/time_win.cc b/src/base/time_win.cc
new file mode 100644
index 0000000..6d8e432
--- /dev/null
+++ b/src/base/time_win.cc
@@ -0,0 +1,489 @@
+// Copyright (c) 2012 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.
+
+
+// Windows Timer Primer
+//
+// A good article: http://www.ddj.com/windows/184416651
+// A good mozilla bug: http://bugzilla.mozilla.org/show_bug.cgi?id=363258
+//
+// The default windows timer, GetSystemTimeAsFileTime is not very precise.
+// It is only good to ~15.5ms.
+//
+// QueryPerformanceCounter is the logical choice for a high-precision timer.
+// However, it is known to be buggy on some hardware. Specifically, it can
+// sometimes "jump". On laptops, QPC can also be very expensive to call.
+// It's 3-4x slower than timeGetTime() on desktops, but can be 10x slower
+// on laptops. A unittest exists which will show the relative cost of various
+// timers on any system.
+//
+// The next logical choice is timeGetTime(). timeGetTime has a precision of
+// 1ms, but only if you call APIs (timeBeginPeriod()) which affect all other
+// applications on the system. By default, precision is only 15.5ms.
+// Unfortunately, we don't want to call timeBeginPeriod because we don't
+// want to affect other applications. Further, on mobile platforms, use of
+// faster multimedia timers can hurt battery life. See the intel
+// article about this here:
+// http://softwarecommunity.intel.com/articles/eng/1086.htm
+//
+// To work around all this, we're going to generally use timeGetTime(). We
+// will only increase the system-wide timer if we're not running on battery
+// power. Using timeBeginPeriod(1) is a requirement in order to make our
+// message loop waits have the same resolution that our time measurements
+// do. Otherwise, WaitForSingleObject(..., 1) will no less than 15ms when
+// there is nothing else to waken the Wait.
+
+#include "base/time.h"
+
+#pragma comment(lib, "winmm.lib")
+#include <windows.h>
+#include <mmsystem.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/cpu.h"
+#include "base/memory/singleton.h"
+#include "base/synchronization/lock.h"
+
+using base::Time;
+using base::TimeDelta;
+using base::TimeTicks;
+
+namespace {
+
+// From MSDN, FILETIME "Contains a 64-bit value representing the number of
+// 100-nanosecond intervals since January 1, 1601 (UTC)."
+int64 FileTimeToMicroseconds(const FILETIME& ft) {
+ // Need to bit_cast to fix alignment, then divide by 10 to convert
+ // 100-nanoseconds to milliseconds. This only works on little-endian
+ // machines.
+ return bit_cast<int64, FILETIME>(ft) / 10;
+}
+
+void MicrosecondsToFileTime(int64 us, FILETIME* ft) {
+ DCHECK_GE(us, 0LL) << "Time is less than 0, negative values are not "
+ "representable in FILETIME";
+
+ // Multiply by 10 to convert milliseconds to 100-nanoseconds. Bit_cast will
+ // handle alignment problems. This only works on little-endian machines.
+ *ft = bit_cast<FILETIME, int64>(us * 10);
+}
+
+int64 CurrentWallclockMicroseconds() {
+ FILETIME ft;
+ ::GetSystemTimeAsFileTime(&ft);
+ return FileTimeToMicroseconds(ft);
+}
+
+// Time between resampling the un-granular clock for this API. 60 seconds.
+const int kMaxMillisecondsToAvoidDrift = 60 * Time::kMillisecondsPerSecond;
+
+int64 initial_time = 0;
+TimeTicks initial_ticks;
+
+void InitializeClock() {
+ initial_ticks = TimeTicks::Now();
+ initial_time = CurrentWallclockMicroseconds();
+}
+
+} // namespace
+
+// Time -----------------------------------------------------------------------
+
+// The internal representation of Time uses FILETIME, whose epoch is 1601-01-01
+// 00:00:00 UTC. ((1970-1601)*365+89)*24*60*60*1000*1000, where 89 is the
+// number of leap year days between 1601 and 1970: (1970-1601)/4 excluding
+// 1700, 1800, and 1900.
+// static
+const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(11644473600000000);
+
+bool Time::high_resolution_timer_enabled_ = false;
+int Time::high_resolution_timer_activated_ = 0;
+
+// static
+Time Time::Now() {
+ if (initial_time == 0)
+ InitializeClock();
+
+ // We implement time using the high-resolution timers so that we can get
+ // timeouts which are smaller than 10-15ms. If we just used
+ // CurrentWallclockMicroseconds(), we'd have the less-granular timer.
+ //
+ // To make this work, we initialize the clock (initial_time) and the
+ // counter (initial_ctr). To compute the initial time, we can check
+ // the number of ticks that have elapsed, and compute the delta.
+ //
+ // To avoid any drift, we periodically resync the counters to the system
+ // clock.
+ while (true) {
+ TimeTicks ticks = TimeTicks::Now();
+
+ // Calculate the time elapsed since we started our timer
+ TimeDelta elapsed = ticks - initial_ticks;
+
+ // Check if enough time has elapsed that we need to resync the clock.
+ if (elapsed.InMilliseconds() > kMaxMillisecondsToAvoidDrift) {
+ InitializeClock();
+ continue;
+ }
+
+ return Time(elapsed + Time(initial_time));
+ }
+}
+
+// static
+Time Time::NowFromSystemTime() {
+ // Force resync.
+ InitializeClock();
+ return Time(initial_time);
+}
+
+// static
+Time Time::FromFileTime(FILETIME ft) {
+ if (bit_cast<int64, FILETIME>(ft) == 0)
+ return Time();
+ if (ft.dwHighDateTime == std::numeric_limits<DWORD>::max() &&
+ ft.dwLowDateTime == std::numeric_limits<DWORD>::max())
+ return Max();
+ return Time(FileTimeToMicroseconds(ft));
+}
+
+FILETIME Time::ToFileTime() const {
+ if (is_null())
+ return bit_cast<FILETIME, int64>(0);
+ if (is_max()) {
+ FILETIME result;
+ result.dwHighDateTime = std::numeric_limits<DWORD>::max();
+ result.dwLowDateTime = std::numeric_limits<DWORD>::max();
+ return result;
+ }
+ FILETIME utc_ft;
+ MicrosecondsToFileTime(us_, &utc_ft);
+ return utc_ft;
+}
+
+// static
+void Time::EnableHighResolutionTimer(bool enable) {
+ // Test for single-threaded access.
+ static PlatformThreadId my_thread = PlatformThread::CurrentId();
+ DCHECK(PlatformThread::CurrentId() == my_thread);
+
+ if (high_resolution_timer_enabled_ == enable)
+ return;
+
+ high_resolution_timer_enabled_ = enable;
+}
+
+// static
+bool Time::ActivateHighResolutionTimer(bool activating) {
+ if (!high_resolution_timer_enabled_ && activating)
+ return false;
+
+ // Using anything other than 1ms makes timers granular
+ // to that interval.
+ const int kMinTimerIntervalMs = 1;
+ MMRESULT result;
+ if (activating) {
+ result = timeBeginPeriod(kMinTimerIntervalMs);
+ high_resolution_timer_activated_++;
+ } else {
+ result = timeEndPeriod(kMinTimerIntervalMs);
+ high_resolution_timer_activated_--;
+ }
+ return result == TIMERR_NOERROR;
+}
+
+// static
+bool Time::IsHighResolutionTimerInUse() {
+ // Note: we should track the high_resolution_timer_activated_ value
+ // under a lock if we want it to be accurate in a system with multiple
+ // message loops. We don't do that - because we don't want to take the
+ // expense of a lock for this. We *only* track this value so that unit
+ // tests can see if the high resolution timer is on or off.
+ return high_resolution_timer_enabled_ &&
+ high_resolution_timer_activated_ > 0;
+}
+
+// static
+Time Time::FromExploded(bool is_local, const Exploded& exploded) {
+ // Create the system struct representing our exploded time. It will either be
+ // in local time or UTC.
+ SYSTEMTIME st;
+ st.wYear = exploded.year;
+ st.wMonth = exploded.month;
+ st.wDayOfWeek = exploded.day_of_week;
+ st.wDay = exploded.day_of_month;
+ st.wHour = exploded.hour;
+ st.wMinute = exploded.minute;
+ st.wSecond = exploded.second;
+ st.wMilliseconds = exploded.millisecond;
+
+ FILETIME ft;
+ bool success = true;
+ // Ensure that it's in UTC.
+ if (is_local) {
+ SYSTEMTIME utc_st;
+ success = TzSpecificLocalTimeToSystemTime(NULL, &st, &utc_st) &&
+ SystemTimeToFileTime(&utc_st, &ft);
+ } else {
+ success = !!SystemTimeToFileTime(&st, &ft);
+ }
+
+ if (!success) {
+ NOTREACHED() << "Unable to convert time";
+ return Time(0);
+ }
+ return Time(FileTimeToMicroseconds(ft));
+}
+
+void Time::Explode(bool is_local, Exploded* exploded) const {
+ if (us_ < 0LL) {
+ // We are not able to convert it to FILETIME.
+ ZeroMemory(exploded, sizeof(*exploded));
+ return;
+ }
+
+ // FILETIME in UTC.
+ FILETIME utc_ft;
+ MicrosecondsToFileTime(us_, &utc_ft);
+
+ // FILETIME in local time if necessary.
+ bool success = true;
+ // FILETIME in SYSTEMTIME (exploded).
+ SYSTEMTIME st;
+ if (is_local) {
+ SYSTEMTIME utc_st;
+ // We don't use FileTimeToLocalFileTime here, since it uses the current
+ // settings for the time zone and daylight saving time. Therefore, if it is
+ // daylight saving time, it will take daylight saving time into account,
+ // even if the time you are converting is in standard time.
+ success = FileTimeToSystemTime(&utc_ft, &utc_st) &&
+ SystemTimeToTzSpecificLocalTime(NULL, &utc_st, &st);
+ } else {
+ success = !!FileTimeToSystemTime(&utc_ft, &st);
+ }
+
+ if (!success) {
+ NOTREACHED() << "Unable to convert time, don't know why";
+ ZeroMemory(exploded, sizeof(*exploded));
+ return;
+ }
+
+ exploded->year = st.wYear;
+ exploded->month = st.wMonth;
+ exploded->day_of_week = st.wDayOfWeek;
+ exploded->day_of_month = st.wDay;
+ exploded->hour = st.wHour;
+ exploded->minute = st.wMinute;
+ exploded->second = st.wSecond;
+ exploded->millisecond = st.wMilliseconds;
+}
+
+// TimeTicks ------------------------------------------------------------------
+namespace {
+
+// We define a wrapper to adapt between the __stdcall and __cdecl call of the
+// mock function, and to avoid a static constructor. Assigning an import to a
+// function pointer directly would require setup code to fetch from the IAT.
+DWORD timeGetTimeWrapper() {
+ return timeGetTime();
+}
+
+DWORD (*tick_function)(void) = &timeGetTimeWrapper;
+
+// Accumulation of time lost due to rollover (in milliseconds).
+int64 rollover_ms = 0;
+
+// The last timeGetTime value we saw, to detect rollover.
+DWORD last_seen_now = 0;
+
+// Lock protecting rollover_ms and last_seen_now.
+// Note: this is a global object, and we usually avoid these. However, the time
+// code is low-level, and we don't want to use Singletons here (it would be too
+// easy to use a Singleton without even knowing it, and that may lead to many
+// gotchas). Its impact on startup time should be negligible due to low-level
+// nature of time code.
+base::Lock rollover_lock;
+
+// We use timeGetTime() to implement TimeTicks::Now(). This can be problematic
+// because it returns the number of milliseconds since Windows has started,
+// which will roll over the 32-bit value every ~49 days. We try to track
+// rollover ourselves, which works if TimeTicks::Now() is called at least every
+// 49 days.
+TimeDelta RolloverProtectedNow() {
+ base::AutoLock locked(rollover_lock);
+ // We should hold the lock while calling tick_function to make sure that
+ // we keep last_seen_now stay correctly in sync.
+ DWORD now = tick_function();
+ if (now < last_seen_now)
+ rollover_ms += 0x100000000I64; // ~49.7 days.
+ last_seen_now = now;
+ return TimeDelta::FromMilliseconds(now + rollover_ms);
+}
+
+// Overview of time counters:
+// (1) CPU cycle counter. (Retrieved via RDTSC)
+// The CPU counter provides the highest resolution time stamp and is the least
+// expensive to retrieve. However, the CPU counter is unreliable and should not
+// be used in production. Its biggest issue is that it is per processor and it
+// is not synchronized between processors. Also, on some computers, the counters
+// will change frequency due to thermal and power changes, and stop in some
+// states.
+//
+// (2) QueryPerformanceCounter (QPC). The QPC counter provides a high-
+// resolution (100 nanoseconds) time stamp but is comparatively more expensive
+// to retrieve. What QueryPerformanceCounter actually does is up to the HAL.
+// (with some help from ACPI).
+// According to http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx
+// in the worst case, it gets the counter from the rollover interrupt on the
+// programmable interrupt timer. In best cases, the HAL may conclude that the
+// RDTSC counter runs at a constant frequency, then it uses that instead. On
+// multiprocessor machines, it will try to verify the values returned from
+// RDTSC on each processor are consistent with each other, and apply a handful
+// of workarounds for known buggy hardware. In other words, QPC is supposed to
+// give consistent result on a multiprocessor computer, but it is unreliable in
+// reality due to bugs in BIOS or HAL on some, especially old computers.
+// With recent updates on HAL and newer BIOS, QPC is getting more reliable but
+// it should be used with caution.
+//
+// (3) System time. The system time provides a low-resolution (typically 10ms
+// to 55 milliseconds) time stamp but is comparatively less expensive to
+// retrieve and more reliable.
+class HighResNowSingleton {
+ public:
+ static HighResNowSingleton* GetInstance() {
+ return Singleton<HighResNowSingleton>::get();
+ }
+
+ bool IsUsingHighResClock() {
+ return ticks_per_second_ != 0.0;
+ }
+
+ void DisableHighResClock() {
+ ticks_per_second_ = 0.0;
+ }
+
+ TimeDelta Now() {
+ if (IsUsingHighResClock())
+ return TimeDelta::FromMicroseconds(UnreliableNow());
+
+ // Just fallback to the slower clock.
+ return RolloverProtectedNow();
+ }
+
+ int64 GetQPCDriftMicroseconds() {
+ if (!IsUsingHighResClock())
+ return 0;
+
+ // The static_cast<long> is needed as a hint to VS 2008 to tell it
+ // which version of abs() to use. Other compilers don't seem to
+ // need it, including VS 2010, but to keep code identical we use it
+ // everywhere.
+ // TODO(joi): Remove the hint if/when we no longer support VS 2008.
+ return abs(static_cast<long>((UnreliableNow() - ReliableNow()) - skew_));
+ }
+
+ int64 QPCValueToMicroseconds(LONGLONG qpc_value) {
+ if (!ticks_per_second_)
+ return 0;
+
+ // Intentionally calculate microseconds in a round about manner to avoid
+ // overflow and precision issues. Think twice before simplifying!
+ int64 whole_seconds = qpc_value / ticks_per_second_;
+ int64 leftover_ticks = qpc_value % ticks_per_second_;
+ int64 microseconds = (whole_seconds * Time::kMicrosecondsPerSecond) +
+ ((leftover_ticks * Time::kMicrosecondsPerSecond) /
+ ticks_per_second_);
+ return microseconds;
+ }
+
+ private:
+ HighResNowSingleton()
+ : ticks_per_second_(0),
+ skew_(0) {
+ InitializeClock();
+
+ // On Athlon X2 CPUs (e.g. model 15) QueryPerformanceCounter is
+ // unreliable. Fallback to low-res clock.
+ base::CPU cpu;
+ if (cpu.vendor_name() == "AuthenticAMD" && cpu.family() == 15)
+ DisableHighResClock();
+ }
+
+ // Synchronize the QPC clock with GetSystemTimeAsFileTime.
+ void InitializeClock() {
+ LARGE_INTEGER ticks_per_sec = {0};
+ if (!QueryPerformanceFrequency(&ticks_per_sec))
+ return; // Broken, we don't guarantee this function works.
+ ticks_per_second_ = ticks_per_sec.QuadPart;
+
+ skew_ = UnreliableNow() - ReliableNow();
+ }
+
+ // Get the number of microseconds since boot in an unreliable fashion.
+ int64 UnreliableNow() {
+ LARGE_INTEGER now;
+ QueryPerformanceCounter(&now);
+ return QPCValueToMicroseconds(now.QuadPart);
+ }
+
+ // Get the number of microseconds since boot in a reliable fashion.
+ int64 ReliableNow() {
+ return RolloverProtectedNow().InMicroseconds();
+ }
+
+ int64 ticks_per_second_; // 0 indicates QPF failed and we're broken.
+ int64 skew_; // Skew between lo-res and hi-res clocks (for debugging).
+
+ friend struct DefaultSingletonTraits<HighResNowSingleton>;
+};
+
+} // namespace
+
+// static
+TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction(
+ TickFunctionType ticker) {
+ TickFunctionType old = tick_function;
+ tick_function = ticker;
+ return old;
+}
+
+// static
+TimeTicks TimeTicks::Now() {
+ return TimeTicks() + RolloverProtectedNow();
+}
+
+// static
+TimeTicks TimeTicks::HighResNow() {
+ return TimeTicks() + HighResNowSingleton::GetInstance()->Now();
+}
+
+// static
+TimeTicks TimeTicks::NowFromSystemTraceTime() {
+ return HighResNow();
+}
+
+// static
+int64 TimeTicks::GetQPCDriftMicroseconds() {
+ return HighResNowSingleton::GetInstance()->GetQPCDriftMicroseconds();
+}
+
+// static
+TimeTicks TimeTicks::FromQPCValue(LONGLONG qpc_value) {
+ return TimeTicks(
+ HighResNowSingleton::GetInstance()->QPCValueToMicroseconds(qpc_value));
+}
+
+// static
+bool TimeTicks::IsHighResClockWorking() {
+ return HighResNowSingleton::GetInstance()->IsUsingHighResClock();
+}
+
+// TimeDelta ------------------------------------------------------------------
+
+// static
+TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) {
+ return TimeDelta(
+ HighResNowSingleton::GetInstance()->QPCValueToMicroseconds(qpc_value));
+}
diff --git a/src/base/time_win_unittest.cc b/src/base/time_win_unittest.cc
new file mode 100644
index 0000000..7ac3662
--- /dev/null
+++ b/src/base/time_win_unittest.cc
@@ -0,0 +1,239 @@
+// Copyright (c) 2012 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 <mmsystem.h>
+#include <process.h>
+
+#include "base/threading/platform_thread.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::Time;
+using base::TimeDelta;
+using base::TimeTicks;
+
+namespace {
+
+class MockTimeTicks : public TimeTicks {
+ public:
+ static DWORD Ticker() {
+ return static_cast<int>(InterlockedIncrement(&ticker_));
+ }
+
+ static void InstallTicker() {
+ old_tick_function_ = SetMockTickFunction(&Ticker);
+ ticker_ = -5;
+ }
+
+ static void UninstallTicker() {
+ SetMockTickFunction(old_tick_function_);
+ }
+
+ private:
+ static volatile LONG ticker_;
+ static TickFunctionType old_tick_function_;
+};
+
+volatile LONG MockTimeTicks::ticker_;
+MockTimeTicks::TickFunctionType MockTimeTicks::old_tick_function_;
+
+HANDLE g_rollover_test_start;
+
+unsigned __stdcall RolloverTestThreadMain(void* param) {
+ int64 counter = reinterpret_cast<int64>(param);
+ DWORD rv = WaitForSingleObject(g_rollover_test_start, INFINITE);
+ EXPECT_EQ(rv, WAIT_OBJECT_0);
+
+ TimeTicks last = TimeTicks::Now();
+ for (int index = 0; index < counter; index++) {
+ TimeTicks now = TimeTicks::Now();
+ int64 milliseconds = (now - last).InMilliseconds();
+ // This is a tight loop; we could have looped faster than our
+ // measurements, so the time might be 0 millis.
+ EXPECT_GE(milliseconds, 0);
+ EXPECT_LT(milliseconds, 250);
+ last = now;
+ }
+ return 0;
+}
+
+} // namespace
+
+TEST(TimeTicks, WinRollover) {
+ // The internal counter rolls over at ~49days. We'll use a mock
+ // timer to test this case.
+ // Basic test algorithm:
+ // 1) Set clock to rollover - N
+ // 2) Create N threads
+ // 3) Start the threads
+ // 4) Each thread loops through TimeTicks() N times
+ // 5) Each thread verifies integrity of result.
+
+ const int kThreads = 8;
+ // Use int64 so we can cast into a void* without a compiler warning.
+ const int64 kChecks = 10;
+
+ // It takes a lot of iterations to reproduce the bug!
+ // (See bug 1081395)
+ for (int loop = 0; loop < 4096; loop++) {
+ // Setup
+ MockTimeTicks::InstallTicker();
+ g_rollover_test_start = CreateEvent(0, TRUE, FALSE, 0);
+ HANDLE threads[kThreads];
+
+ for (int index = 0; index < kThreads; index++) {
+ void* argument = reinterpret_cast<void*>(kChecks);
+ unsigned thread_id;
+ threads[index] = reinterpret_cast<HANDLE>(
+ _beginthreadex(NULL, 0, RolloverTestThreadMain, argument, 0,
+ &thread_id));
+ EXPECT_NE((HANDLE)NULL, threads[index]);
+ }
+
+ // Start!
+ SetEvent(g_rollover_test_start);
+
+ // Wait for threads to finish
+ for (int index = 0; index < kThreads; index++) {
+ DWORD rv = WaitForSingleObject(threads[index], INFINITE);
+ EXPECT_EQ(rv, WAIT_OBJECT_0);
+ }
+
+ CloseHandle(g_rollover_test_start);
+
+ // Teardown
+ MockTimeTicks::UninstallTicker();
+ }
+}
+
+TEST(TimeTicks, SubMillisecondTimers) {
+ // HighResNow doesn't work on some systems. Since the product still works
+ // even if it doesn't work, it makes this entire test questionable.
+ if (!TimeTicks::IsHighResClockWorking())
+ return;
+
+ const int kRetries = 1000;
+ bool saw_submillisecond_timer = false;
+
+ // Run kRetries attempts to see a sub-millisecond timer.
+ for (int index = 0; index < 1000; index++) {
+ TimeTicks last_time = TimeTicks::HighResNow();
+ TimeDelta delta;
+ // Spin until the clock has detected a change.
+ do {
+ delta = TimeTicks::HighResNow() - last_time;
+ } while (delta.InMicroseconds() == 0);
+ if (delta.InMicroseconds() < 1000) {
+ saw_submillisecond_timer = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(saw_submillisecond_timer);
+}
+
+TEST(TimeTicks, TimeGetTimeCaps) {
+ // Test some basic assumptions that we expect about how timeGetDevCaps works.
+
+ TIMECAPS caps;
+ MMRESULT status = timeGetDevCaps(&caps, sizeof(caps));
+ EXPECT_EQ(TIMERR_NOERROR, status);
+ if (status != TIMERR_NOERROR) {
+ printf("Could not get timeGetDevCaps\n");
+ return;
+ }
+
+ EXPECT_GE(static_cast<int>(caps.wPeriodMin), 1);
+ EXPECT_GT(static_cast<int>(caps.wPeriodMax), 1);
+ EXPECT_GE(static_cast<int>(caps.wPeriodMin), 1);
+ EXPECT_GT(static_cast<int>(caps.wPeriodMax), 1);
+ printf("timeGetTime range is %d to %dms\n", caps.wPeriodMin,
+ caps.wPeriodMax);
+}
+
+TEST(TimeTicks, QueryPerformanceFrequency) {
+ // Test some basic assumptions that we expect about QPC.
+
+ LARGE_INTEGER frequency;
+ BOOL rv = QueryPerformanceFrequency(&frequency);
+ EXPECT_EQ(TRUE, rv);
+ EXPECT_GT(frequency.QuadPart, 1000000); // Expect at least 1MHz
+ printf("QueryPerformanceFrequency is %5.2fMHz\n",
+ frequency.QuadPart / 1000000.0);
+}
+
+TEST(TimeTicks, TimerPerformance) {
+ // Verify that various timer mechanisms can always complete quickly.
+ // Note: This is a somewhat arbitrary test.
+ const int kLoops = 10000;
+ // Due to the fact that these run on bbots, which are horribly slow,
+ // we can't really make any guarantees about minimum runtime.
+ // Really, we want these to finish in ~10ms, and that is generous.
+ const int kMaxTime = 35; // Maximum acceptible milliseconds for test.
+
+ typedef TimeTicks (*TestFunc)();
+ struct TestCase {
+ TestFunc func;
+ char *description;
+ };
+ // Cheating a bit here: assumes sizeof(TimeTicks) == sizeof(Time)
+ // in order to create a single test case list.
+ COMPILE_ASSERT(sizeof(TimeTicks) == sizeof(Time),
+ test_only_works_with_same_sizes);
+ TestCase cases[] = {
+ { reinterpret_cast<TestFunc>(Time::Now), "Time::Now" },
+ { TimeTicks::Now, "TimeTicks::Now" },
+ { TimeTicks::HighResNow, "TimeTicks::HighResNow" },
+ { NULL, "" }
+ };
+
+ int test_case = 0;
+ while (cases[test_case].func) {
+ TimeTicks start = TimeTicks::HighResNow();
+ for (int index = 0; index < kLoops; index++)
+ cases[test_case].func();
+ TimeTicks stop = TimeTicks::HighResNow();
+ // Turning off the check for acceptible delays. Without this check,
+ // the test really doesn't do much other than measure. But the
+ // measurements are still useful for testing timers on various platforms.
+ // The reason to remove the check is because the tests run on many
+ // buildbots, some of which are VMs. These machines can run horribly
+ // slow, and there is really no value for checking against a max timer.
+ //EXPECT_LT((stop - start).InMilliseconds(), kMaxTime);
+ printf("%s: %1.2fus per call\n", cases[test_case].description,
+ (stop - start).InMillisecondsF() * 1000 / kLoops);
+ test_case++;
+ }
+}
+
+TEST(TimeTicks, Drift) {
+ // If QPC is disabled, this isn't measuring anything.
+ if (!TimeTicks::IsHighResClockWorking())
+ return;
+
+ const int kIterations = 100;
+ int64 total_drift = 0;
+
+ for (int i = 0; i < kIterations; ++i) {
+ int64 drift_microseconds = TimeTicks::GetQPCDriftMicroseconds();
+
+ // Make sure the drift never exceeds our limit.
+ EXPECT_LT(drift_microseconds, 50000);
+
+ // Sleep for a few milliseconds (note that it means 1000 microseconds).
+ // If we check the drift too frequently, it's going to increase
+ // monotonically, making our measurement less realistic.
+ base::PlatformThread::Sleep(
+ base::TimeDelta::FromMilliseconds((i % 2 == 0) ? 1 : 2));
+
+ total_drift += drift_microseconds;
+ }
+
+ // Sanity check. We expect some time drift to occur, especially across
+ // the number of iterations we do.
+ EXPECT_LT(0, total_drift);
+
+ printf("average time drift in microseconds: %lld\n",
+ total_drift / kIterations);
+}
diff --git a/src/base/timer.cc b/src/base/timer.cc
new file mode 100644
index 0000000..952ce37
--- /dev/null
+++ b/src/base/timer.cc
@@ -0,0 +1,247 @@
+// Copyright (c) 2012 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/timer.h"
+
+#include "base/logging.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+// BaseTimerTaskInternal is a simple delegate for scheduling a callback to
+// Timer in the thread's default task runner. It also handles the following
+// edge cases:
+// - deleted by the task runner.
+// - abandoned (orphaned) by Timer.
+class BaseTimerTaskInternal {
+ public:
+ BaseTimerTaskInternal(Timer* timer)
+ : timer_(timer) {
+ }
+
+ ~BaseTimerTaskInternal() {
+ // This task may be getting cleared because the task runner has been
+ // destructed. If so, don't leave Timer with a dangling pointer
+ // to this.
+ if (timer_)
+ timer_->StopAndAbandon();
+ }
+
+ void Run() {
+ // timer_ is NULL if we were abandoned.
+ if (!timer_)
+ return;
+
+ // *this will be deleted by the task runner, so Timer needs to
+ // forget us:
+ timer_->scheduled_task_ = NULL;
+
+ // Although Timer should not call back into *this, let's clear
+ // the timer_ member first to be pedantic.
+ Timer* timer = timer_;
+ timer_ = NULL;
+ timer->RunScheduledTask();
+ }
+
+ // The task remains in the MessageLoop queue, but nothing will happen when it
+ // runs.
+ void Abandon() {
+ timer_ = NULL;
+ }
+
+ private:
+ Timer* timer_;
+};
+
+Timer::Timer(bool retain_user_task,
+ bool is_repeating,
+ bool is_task_run_before_scheduling_next)
+ : scheduled_task_(NULL),
+ thread_id_(0),
+ is_repeating_(is_repeating),
+ is_task_run_before_scheduling_next_(is_task_run_before_scheduling_next),
+ retain_user_task_(retain_user_task),
+ is_running_(false) {
+ if (is_task_run_before_scheduling_next_) {
+ DCHECK(is_repeating_);
+ }
+}
+
+Timer::Timer(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ const base::Closure& user_task,
+ bool is_repeating,
+ bool is_task_run_before_scheduling_next)
+ : scheduled_task_(NULL),
+ posted_from_(posted_from),
+ delay_(delay),
+ user_task_(user_task),
+ thread_id_(0),
+ is_repeating_(is_repeating),
+ is_task_run_before_scheduling_next_(is_task_run_before_scheduling_next),
+ retain_user_task_(true),
+ is_running_(false) {
+ if (is_task_run_before_scheduling_next_) {
+ DCHECK(is_repeating_);
+ }
+}
+
+Timer::~Timer() {
+ StopAndAbandon();
+}
+
+void Timer::Start(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ const base::Closure& user_task) {
+ SetTaskInfo(posted_from, delay, user_task);
+ Reset();
+}
+
+void Timer::Stop() {
+ is_running_ = false;
+ if (!retain_user_task_)
+ user_task_.Reset();
+}
+
+void Timer::Reset() {
+ DCHECK(!user_task_.is_null());
+
+ // If there's no pending task, start one up and return.
+ if (!scheduled_task_) {
+ PostNewScheduledTask(delay_);
+ return;
+ }
+
+ // Set the new desired_run_time_.
+ desired_run_time_ = TimeTicks::Now() + delay_;
+
+ // We can use the existing scheduled task if it arrives before the new
+ // desired_run_time_.
+ if (desired_run_time_ > scheduled_run_time_) {
+ is_running_ = true;
+ return;
+ }
+
+ // We can't reuse the scheduled_task_, so abandon it and post a new one.
+ AbandonScheduledTask();
+ PostNewScheduledTask(delay_);
+}
+
+void Timer::SetTaskInfo(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ const base::Closure& user_task) {
+ posted_from_ = posted_from;
+ delay_ = delay;
+ user_task_ = user_task;
+}
+
+// This function is not re-implemented using SetupNewScheduledTask() and
+// PostNewScheduledTask() to ensure that the default behavior of Timer is
+// exactly the same as before.
+void Timer::PostNewScheduledTask(TimeDelta delay) {
+ DCHECK(scheduled_task_ == NULL);
+ is_running_ = true;
+ scheduled_task_ = new BaseTimerTaskInternal(this);
+ ThreadTaskRunnerHandle::Get()->PostDelayedTask(posted_from_,
+ base::Bind(&BaseTimerTaskInternal::Run, base::Owned(scheduled_task_)),
+ delay);
+ scheduled_run_time_ = desired_run_time_ = TimeTicks::Now() + delay;
+ // Remember the thread ID that posts the first task -- this will be verified
+ // later when the task is abandoned to detect misuse from multiple threads.
+ if (!thread_id_)
+ thread_id_ = static_cast<int>(PlatformThread::CurrentId());
+}
+
+Timer::NewScheduledTaskInfo Timer::SetupNewScheduledTask(
+ TimeDelta expected_delay) {
+ DCHECK(scheduled_task_ == NULL);
+ DCHECK(is_task_run_before_scheduling_next_);
+ DCHECK(thread_id_);
+
+ is_running_ = true;
+ scheduled_task_ = new BaseTimerTaskInternal(this);
+
+ NewScheduledTaskInfo task_info;
+ task_info.posted_from = posted_from_;
+ task_info.task =
+ base::Bind(&BaseTimerTaskInternal::Run, base::Owned(scheduled_task_));
+
+ scheduled_run_time_ = desired_run_time_ = TimeTicks::Now() + expected_delay;
+
+ return task_info;
+}
+
+void Timer::PostNewScheduledTask(NewScheduledTaskInfo task_info,
+ TimeDelta delay) {
+ // Some task runners expect a non-zero delay for PostDelayedTask.
+ if (delay.ToInternalValue() == 0) {
+ ThreadTaskRunnerHandle::Get()->PostTask(task_info.posted_from,
+ task_info.task);
+ } else {
+ ThreadTaskRunnerHandle::Get()->PostDelayedTask(task_info.posted_from,
+ task_info.task, delay);
+ }
+}
+
+void Timer::AbandonScheduledTask() {
+ DCHECK(thread_id_ == 0 ||
+ thread_id_ == static_cast<int>(PlatformThread::CurrentId()));
+ if (scheduled_task_) {
+ scheduled_task_->Abandon();
+ scheduled_task_ = NULL;
+ }
+}
+
+void Timer::RunScheduledTask() {
+ // Task may have been disabled.
+ if (!is_running_)
+ return;
+
+ // First check if we need to delay the task because of a new target time.
+ if (desired_run_time_ > scheduled_run_time_) {
+ // TimeTicks::Now() can be expensive, so only call it if we know the user
+ // has changed the desired_run_time_.
+ TimeTicks now = TimeTicks::Now();
+ // Task runner may have called us late anyway, so only post a continuation
+ // task if the desired_run_time_ is in the future.
+ if (desired_run_time_ > now) {
+ // Post a new task to span the remaining time.
+ PostNewScheduledTask(desired_run_time_ - now);
+ return;
+ }
+ }
+
+ // Make a local copy of the task to run. The Stop method will reset the
+ // user_task_ member if retain_user_task_ is false.
+ base::Closure task = user_task_;
+
+ if (!is_repeating_) {
+ Stop();
+ task.Run();
+ return;
+ }
+
+ if (is_task_run_before_scheduling_next_) {
+ // Setup member variables and the next tasks before the current one runs as
+ // we cannot access any member variables after calling task.Run().
+ NewScheduledTaskInfo task_info = SetupNewScheduledTask(delay_);
+ base::Time task_start_time = base::Time::Now();
+ task.Run();
+ base::TimeDelta task_duration = base::Time::Now() - task_start_time;
+ if (task_duration >= delay_) {
+ PostNewScheduledTask(task_info, base::TimeDelta::FromInternalValue(0));
+ } else {
+ PostNewScheduledTask(task_info, delay_ - task_duration);
+ }
+ } else {
+ PostNewScheduledTask(delay_);
+ task.Run();
+ }
+
+ // No more member accesses here: *this could be deleted at this point.
+}
+
+} // namespace base
diff --git a/src/base/timer.h b/src/base/timer.h
new file mode 100644
index 0000000..e1ea113
--- /dev/null
+++ b/src/base/timer.h
@@ -0,0 +1,283 @@
+// Copyright (c) 2012 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.
+
+// OneShotTimer and RepeatingTimer provide a simple timer API. As the names
+// suggest, OneShotTimer calls you back once after a time delay expires.
+// RepeatingTimer on the other hand calls you back periodically with the
+// prescribed time interval.
+//
+// OneShotTimer and RepeatingTimer both cancel the timer when they go out of
+// scope, which makes it easy to ensure that you do not get called when your
+// object has gone out of scope. Just instantiate a OneShotTimer or
+// RepeatingTimer as a member variable of the class for which you wish to
+// receive timer events.
+//
+// Sample RepeatingTimer usage:
+//
+// class MyClass {
+// public:
+// void StartDoingStuff() {
+// timer_.Start(FROM_HERE, TimeDelta::FromSeconds(1),
+// this, &MyClass::DoStuff);
+// }
+// void StopDoingStuff() {
+// timer_.Stop();
+// }
+// private:
+// void DoStuff() {
+// // This method is called every second to do stuff.
+// ...
+// }
+// base::RepeatingTimer<MyClass> timer_;
+// };
+//
+// Both OneShotTimer and RepeatingTimer also support a Reset method, which
+// allows you to easily defer the timer event until the timer delay passes once
+// again. So, in the above example, if 0.5 seconds have already passed,
+// calling Reset on timer_ would postpone DoStuff by another 1 second. In
+// other words, Reset is shorthand for calling Stop and then Start again with
+// the same arguments.
+//
+// NOTE: These APIs are not thread safe. Always call from the same thread.
+
+#ifndef BASE_TIMER_H_
+#define BASE_TIMER_H_
+
+// IMPORTANT: If you change timer code, make sure that all tests (including
+// disabled ones) from timer_unittests.cc pass locally. Some are disabled
+// because they're flaky on the buildbot, but when you run them locally you
+// should be able to tell the difference.
+
+#include "base/base_export.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/time.h"
+
+#if defined(__LB_SHELL_DEBUG_TASKS__)
+#include <stdio.h>
+#include <string>
+#endif
+
+class MessageLoop;
+
+namespace base {
+
+class BaseTimerTaskInternal;
+
+//-----------------------------------------------------------------------------
+// This class wraps MessageLoop::PostDelayedTask to manage delayed and repeating
+// tasks. It must be destructed on the same thread that starts tasks. There are
+// DCHECKs in place to verify this.
+// For a Timer with repeating task, the user can choose whether the Timer
+// should post the next task T(n+1) before or after running the current task
+// T(n). As tasks are run in sequence, in the first case T(n+1) is likely to
+// run before other tasks scheduled while the T(n) is running. In the second
+// case T(n+1) will be run after other tasks scheduled while T(n) is running.
+// By default Timer uses the behavior but the user should use the second
+// behavior if the repeating task can be lengthy and is intended to be run at a
+// slightly lower priority.
+class BASE_EXPORT Timer {
+ public:
+ // Construct a timer in repeating or one-shot mode. Start or SetTaskInfo must
+ // be called later to set task info. |retain_user_task| determines whether the
+ // user_task is retained or reset when it runs or stops. When
+ // |is_task_run_before_scheduling_next| is true, the next task will be posted
+ // after the current one is finished.
+ Timer(bool retain_user_task,
+ bool is_repeating,
+ bool is_task_run_before_scheduling_next = false);
+
+ // Construct a timer with retained task info. When
+ // |is_task_run_before_scheduling_next| is true, the next task will be posted
+ // after the current one is finished.
+ Timer(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ const base::Closure& user_task,
+ bool is_repeating,
+ bool is_task_run_before_scheduling_next = false);
+
+ virtual ~Timer();
+
+ // Returns true if the timer is running (i.e., not stopped).
+ bool IsRunning() const {
+ return is_running_;
+ }
+
+ // Returns the current delay for this timer.
+ TimeDelta GetCurrentDelay() const {
+ return delay_;
+ }
+
+ // Start the timer to run at the given |delay| from now. If the timer is
+ // already running, it will be replaced to call the given |user_task|.
+ void Start(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ const base::Closure& user_task);
+
+ // Call this method to stop and cancel the timer. It is a no-op if the timer
+ // is not running.
+ void Stop();
+
+ // Call this method to reset the timer delay. The user_task_ must be set. If
+ // the timer is not running, this will start it by posting a task.
+ void Reset();
+
+ const base::Closure& user_task() const { return user_task_; }
+ const TimeTicks& desired_run_time() const { return desired_run_time_; }
+
+ protected:
+ // Used to initiate a new delayed task. This has the side-effect of disabling
+ // scheduled_task_ if it is non-null.
+ void SetTaskInfo(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ const base::Closure& user_task);
+
+ private:
+ struct NewScheduledTaskInfo {
+ tracked_objects::Location posted_from;
+ base::Closure task;
+ };
+
+ friend class BaseTimerTaskInternal;
+
+ // Allocates a new scheduled_task_ and posts it on the current MessageLoop
+ // with the given |delay|. scheduled_task_ must be NULL. scheduled_run_time_
+ // and desired_run_time_ are reset to Now() + delay.
+ void PostNewScheduledTask(TimeDelta delay);
+ // Allocates a new scheduled_task_ so it can be posted on the current
+ // MessageLoop later. scheduled_task_ must be NULL. scheduled_run_time_ and
+ // desired_run_time_ are reset to Now() + delay.
+ NewScheduledTaskInfo SetupNewScheduledTask(TimeDelta expected_delay);
+ // Post the task on the current MessageLoop. Note that the delay isn't the
+ // same as the expected_delay in the above function. It has been adjusted
+ // according to the duration of the current task.
+ void PostNewScheduledTask(NewScheduledTaskInfo task_info, TimeDelta delay);
+
+ // Disable scheduled_task_ and abandon it so that it no longer refers back to
+ // this object.
+ void AbandonScheduledTask();
+
+ // Called by BaseTimerTaskInternal when the MessageLoop runs it.
+ void RunScheduledTask();
+
+ // Stop running task (if any) and abandon scheduled task (if any).
+ void StopAndAbandon() {
+ Stop();
+ AbandonScheduledTask();
+ }
+
+ // When non-NULL, the scheduled_task_ is waiting in the MessageLoop to call
+ // RunScheduledTask() at scheduled_run_time_.
+ BaseTimerTaskInternal* scheduled_task_;
+
+ // Location in user code.
+ tracked_objects::Location posted_from_;
+ // Delay requested by user.
+ TimeDelta delay_;
+ // user_task_ is what the user wants to be run at desired_run_time_.
+ base::Closure user_task_;
+
+ // The estimated time that the MessageLoop will run the scheduled_task_ that
+ // will call RunScheduledTask().
+ TimeTicks scheduled_run_time_;
+
+ // The desired run time of user_task_. The user may update this at any time,
+ // even if their previous request has not run yet. If desired_run_time_ is
+ // greater than scheduled_run_time_, a continuation task will be posted to
+ // wait for the remaining time. This allows us to reuse the pending task so as
+ // not to flood the MessageLoop with orphaned tasks when the user code
+ // excessively Stops and Starts the timer.
+ TimeTicks desired_run_time_;
+
+ // Thread ID of current MessageLoop for verifying single-threaded usage.
+ int thread_id_;
+
+ // Repeating timers automatically post the task again before calling the task
+ // callback.
+ const bool is_repeating_;
+
+ // The next task will be scheduled after the current one is finished. Can
+ // only be true when is_repeating_ is true.
+ const bool is_task_run_before_scheduling_next_;
+
+ // If true, hold on to the user_task_ closure object for reuse.
+ const bool retain_user_task_;
+
+ // If true, user_task_ is scheduled to run sometime in the future.
+ bool is_running_;
+
+ DISALLOW_COPY_AND_ASSIGN(Timer);
+};
+
+//-----------------------------------------------------------------------------
+// This class is an implementation detail of OneShotTimer and RepeatingTimer.
+// Please do not use this class directly.
+template <class Receiver, bool kIsRepeating>
+class BaseTimerMethodPointer : public Timer {
+ public:
+ typedef void (Receiver::*ReceiverMethod)();
+
+ // This is here to work around the fact that Timer::Start is "hidden" by the
+ // Start definition below, rather than being overloaded.
+ // TODO(tim): We should remove uses of BaseTimerMethodPointer::Start below
+ // and convert callers to use the base::Closure version in Timer::Start,
+ // see bug 148832.
+ using Timer::Start;
+
+ BaseTimerMethodPointer() : Timer(kIsRepeating, kIsRepeating) {}
+
+ // Start the timer to run at the given |delay| from now. If the timer is
+ // already running, it will be replaced to call a task formed from
+ // |reviewer->*method|.
+ void Start(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ Receiver* receiver,
+ ReceiverMethod method) {
+ Timer::Start(posted_from, delay,
+ base::Bind(method, base::Unretained(receiver)));
+ }
+};
+
+//-----------------------------------------------------------------------------
+// A simple, one-shot timer. See usage notes at the top of the file.
+template <class Receiver>
+class OneShotTimer : public BaseTimerMethodPointer<Receiver, false> {};
+
+//-----------------------------------------------------------------------------
+// A simple, repeating timer. See usage notes at the top of the file.
+template <class Receiver>
+class RepeatingTimer : public BaseTimerMethodPointer<Receiver, true> {};
+
+//-----------------------------------------------------------------------------
+// A Delay timer is like The Button from Lost. Once started, you have to keep
+// calling Reset otherwise it will call the given method in the MessageLoop
+// thread.
+//
+// Once created, it is inactive until Reset is called. Once |delay| seconds have
+// passed since the last call to Reset, the callback is made. Once the callback
+// has been made, it's inactive until Reset is called again.
+//
+// If destroyed, the timeout is canceled and will not occur even if already
+// inflight.
+template <class Receiver>
+class DelayTimer : protected Timer {
+ public:
+ typedef void (Receiver::*ReceiverMethod)();
+
+ DelayTimer(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ Receiver* receiver,
+ ReceiverMethod method)
+ : Timer(posted_from, delay,
+ base::Bind(method, base::Unretained(receiver)),
+ false) {}
+
+ void Reset() { Timer::Reset(); }
+};
+
+} // namespace base
+
+#endif // BASE_TIMER_H_
diff --git a/src/base/timer_unittest.cc b/src/base/timer_unittest.cc
new file mode 100644
index 0000000..9c31264
--- /dev/null
+++ b/src/base/timer_unittest.cc
@@ -0,0 +1,488 @@
+// Copyright (c) 2012 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/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/timer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::TimeDelta;
+
+namespace {
+
+// The message loops on which each timer should be tested.
+const MessageLoop::Type testing_message_loops[] = {
+ MessageLoop::TYPE_DEFAULT, MessageLoop::TYPE_IO,
+#if !defined(OS_IOS) && !defined(OS_STARBOARD)
+ // iOS and Starboard do not allow direct running of the UI loop.
+ MessageLoop::TYPE_UI,
+#endif
+};
+
+const int kNumTestingMessageLoops = arraysize(testing_message_loops);
+
+class OneShotTimerTester {
+ public:
+ OneShotTimerTester(bool* did_run, unsigned milliseconds = 10)
+ : did_run_(did_run),
+ delay_ms_(milliseconds) {
+ }
+ void Start() {
+ timer_.Start(FROM_HERE, TimeDelta::FromMilliseconds(delay_ms_), this,
+ &OneShotTimerTester::Run);
+ }
+ private:
+ void Run() {
+ *did_run_ = true;
+ MessageLoop::current()->Quit();
+ }
+ bool* did_run_;
+ base::OneShotTimer<OneShotTimerTester> timer_;
+ const unsigned delay_ms_;
+};
+
+class OneShotSelfDeletingTimerTester {
+ public:
+ explicit OneShotSelfDeletingTimerTester(bool* did_run) :
+ did_run_(did_run),
+ timer_(new base::OneShotTimer<OneShotSelfDeletingTimerTester>()) {
+ }
+ void Start() {
+ timer_->Start(FROM_HERE, TimeDelta::FromMilliseconds(10), this,
+ &OneShotSelfDeletingTimerTester::Run);
+ }
+ private:
+ void Run() {
+ *did_run_ = true;
+ timer_.reset();
+ MessageLoop::current()->Quit();
+ }
+ bool* did_run_;
+ scoped_ptr<base::OneShotTimer<OneShotSelfDeletingTimerTester> > timer_;
+};
+
+class RepeatingTimerTester {
+ public:
+ explicit RepeatingTimerTester(bool* did_run)
+ : did_run_(did_run), counter_(10) {
+ }
+
+ void Start() {
+ timer_.Start(FROM_HERE, TimeDelta::FromMilliseconds(10), this,
+ &RepeatingTimerTester::Run);
+ }
+ private:
+ void Run() {
+ if (--counter_ == 0) {
+ *did_run_ = true;
+ MessageLoop::current()->Quit();
+ }
+ }
+ bool* did_run_;
+ int counter_;
+ base::RepeatingTimer<RepeatingTimerTester> timer_;
+};
+
+void RunTest_OneShotTimer(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ bool did_run = false;
+ OneShotTimerTester f(&did_run);
+ f.Start();
+
+ MessageLoop::current()->Run();
+
+ EXPECT_TRUE(did_run);
+}
+
+void RunTest_OneShotTimer_Cancel(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ bool did_run_a = false;
+ OneShotTimerTester* a = new OneShotTimerTester(&did_run_a);
+
+ // This should run before the timer expires.
+ MessageLoop::current()->DeleteSoon(FROM_HERE, a);
+
+ // Now start the timer.
+ a->Start();
+
+ bool did_run_b = false;
+ OneShotTimerTester b(&did_run_b);
+ b.Start();
+
+ MessageLoop::current()->Run();
+
+ EXPECT_FALSE(did_run_a);
+ EXPECT_TRUE(did_run_b);
+}
+
+void RunTest_OneShotSelfDeletingTimer(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ bool did_run = false;
+ OneShotSelfDeletingTimerTester f(&did_run);
+ f.Start();
+
+ MessageLoop::current()->Run();
+
+ EXPECT_TRUE(did_run);
+}
+
+void RunTest_RepeatingTimer(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ bool did_run = false;
+ RepeatingTimerTester f(&did_run);
+ f.Start();
+
+ MessageLoop::current()->Run();
+
+ EXPECT_TRUE(did_run);
+}
+
+void RunTest_RepeatingTimer_Cancel(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ bool did_run_a = false;
+ RepeatingTimerTester* a = new RepeatingTimerTester(&did_run_a);
+
+ // This should run before the timer expires.
+ MessageLoop::current()->DeleteSoon(FROM_HERE, a);
+
+ // Now start the timer.
+ a->Start();
+
+ bool did_run_b = false;
+ RepeatingTimerTester b(&did_run_b);
+ b.Start();
+
+ MessageLoop::current()->Run();
+
+ EXPECT_FALSE(did_run_a);
+ EXPECT_TRUE(did_run_b);
+}
+
+class DelayTimerTarget {
+ public:
+ DelayTimerTarget()
+ : signaled_(false) {
+ }
+
+ bool signaled() const { return signaled_; }
+
+ void Signal() {
+ ASSERT_FALSE(signaled_);
+ signaled_ = true;
+ }
+
+ private:
+ bool signaled_;
+};
+
+void RunTest_DelayTimer_NoCall(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ // If Delay is never called, the timer shouldn't go off.
+ DelayTimerTarget target;
+ base::DelayTimer<DelayTimerTarget> timer(FROM_HERE,
+ TimeDelta::FromMilliseconds(1), &target, &DelayTimerTarget::Signal);
+
+ bool did_run = false;
+ OneShotTimerTester tester(&did_run);
+ tester.Start();
+ MessageLoop::current()->Run();
+
+ ASSERT_FALSE(target.signaled());
+}
+
+void RunTest_DelayTimer_OneCall(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ DelayTimerTarget target;
+ base::DelayTimer<DelayTimerTarget> timer(FROM_HERE,
+ TimeDelta::FromMilliseconds(1), &target, &DelayTimerTarget::Signal);
+ timer.Reset();
+
+ bool did_run = false;
+ OneShotTimerTester tester(&did_run, 100 /* milliseconds */);
+ tester.Start();
+ MessageLoop::current()->Run();
+
+ ASSERT_TRUE(target.signaled());
+}
+
+struct ResetHelper {
+ ResetHelper(base::DelayTimer<DelayTimerTarget>* timer,
+ DelayTimerTarget* target)
+ : timer_(timer),
+ target_(target) {
+ }
+
+ void Reset() {
+ ASSERT_FALSE(target_->signaled());
+ timer_->Reset();
+ }
+
+ private:
+ base::DelayTimer<DelayTimerTarget> *const timer_;
+ DelayTimerTarget *const target_;
+};
+
+void RunTest_DelayTimer_Reset(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ // If Delay is never called, the timer shouldn't go off.
+ DelayTimerTarget target;
+ base::DelayTimer<DelayTimerTarget> timer(FROM_HERE,
+ TimeDelta::FromMilliseconds(50), &target, &DelayTimerTarget::Signal);
+ timer.Reset();
+
+ ResetHelper reset_helper(&timer, &target);
+
+ base::OneShotTimer<ResetHelper> timers[20];
+ for (size_t i = 0; i < arraysize(timers); ++i) {
+ timers[i].Start(FROM_HERE, TimeDelta::FromMilliseconds(i * 10),
+ &reset_helper, &ResetHelper::Reset);
+ }
+
+ bool did_run = false;
+ OneShotTimerTester tester(&did_run, 300);
+ tester.Start();
+ MessageLoop::current()->Run();
+
+ ASSERT_TRUE(target.signaled());
+}
+
+class DelayTimerFatalTarget {
+ public:
+ void Signal() {
+ ASSERT_TRUE(false);
+ }
+};
+
+
+void RunTest_DelayTimer_Deleted(MessageLoop::Type message_loop_type) {
+ MessageLoop loop(message_loop_type);
+
+ DelayTimerFatalTarget target;
+
+ {
+ base::DelayTimer<DelayTimerFatalTarget> timer(
+ FROM_HERE, TimeDelta::FromMilliseconds(50), &target,
+ &DelayTimerFatalTarget::Signal);
+ timer.Reset();
+ }
+
+ // When the timer is deleted, the DelayTimerFatalTarget should never be
+ // called.
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
+}
+
+} // namespace
+
+//-----------------------------------------------------------------------------
+// Each test is run against each type of MessageLoop. That way we are sure
+// that timers work properly in all configurations.
+
+TEST(TimerTest, OneShotTimer) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_OneShotTimer(testing_message_loops[i]);
+ }
+}
+
+TEST(TimerTest, OneShotTimer_Cancel) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_OneShotTimer_Cancel(testing_message_loops[i]);
+ }
+}
+
+// If underline timer does not handle properly, we will crash or fail
+// in full page heap environment.
+TEST(TimerTest, OneShotSelfDeletingTimer) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_OneShotSelfDeletingTimer(testing_message_loops[i]);
+ }
+}
+
+TEST(TimerTest, RepeatingTimer) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_RepeatingTimer(testing_message_loops[i]);
+ }
+}
+
+TEST(TimerTest, RepeatingTimer_Cancel) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_RepeatingTimer_Cancel(testing_message_loops[i]);
+ }
+}
+
+TEST(TimerTest, DelayTimer_NoCall) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_DelayTimer_NoCall(testing_message_loops[i]);
+ }
+}
+
+TEST(TimerTest, DelayTimer_OneCall) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_DelayTimer_OneCall(testing_message_loops[i]);
+ }
+}
+
+// It's flaky on the buildbot, http://crbug.com/25038.
+TEST(TimerTest, DISABLED_DelayTimer_Reset) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_DelayTimer_Reset(testing_message_loops[i]);
+ }
+}
+
+TEST(TimerTest, DelayTimer_Deleted) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_DelayTimer_Deleted(testing_message_loops[i]);
+ }
+}
+
+TEST(TimerTest, MessageLoopShutdown) {
+ // This test is designed to verify that shutdown of the
+ // message loop does not cause crashes if there were pending
+ // timers not yet fired. It may only trigger exceptions
+ // if debug heap checking is enabled.
+ bool did_run = false;
+ {
+ OneShotTimerTester a(&did_run);
+ OneShotTimerTester b(&did_run);
+ OneShotTimerTester c(&did_run);
+ OneShotTimerTester d(&did_run);
+ {
+ MessageLoop loop(MessageLoop::TYPE_DEFAULT);
+ a.Start();
+ b.Start();
+ } // MessageLoop destructs by falling out of scope.
+ } // OneShotTimers destruct. SHOULD NOT CRASH, of course.
+
+ EXPECT_FALSE(did_run);
+}
+
+void TimerTestCallback() {
+}
+
+TEST(TimerTest, NonRepeatIsRunning) {
+ {
+ MessageLoop loop(MessageLoop::TYPE_DEFAULT);
+ base::Timer timer(false, false);
+ EXPECT_FALSE(timer.IsRunning());
+ timer.Start(FROM_HERE, TimeDelta::FromDays(1),
+ base::Bind(&TimerTestCallback));
+ EXPECT_TRUE(timer.IsRunning());
+ timer.Stop();
+ EXPECT_FALSE(timer.IsRunning());
+ EXPECT_TRUE(timer.user_task().is_null());
+ }
+
+ {
+ base::Timer timer(true, false);
+ MessageLoop loop(MessageLoop::TYPE_DEFAULT);
+ EXPECT_FALSE(timer.IsRunning());
+ timer.Start(FROM_HERE, TimeDelta::FromDays(1),
+ base::Bind(&TimerTestCallback));
+ EXPECT_TRUE(timer.IsRunning());
+ timer.Stop();
+ EXPECT_FALSE(timer.IsRunning());
+ ASSERT_FALSE(timer.user_task().is_null());
+ timer.Reset();
+ EXPECT_TRUE(timer.IsRunning());
+ }
+}
+
+TEST(TimerTest, NonRepeatMessageLoopDeath) {
+ base::Timer timer(false, false);
+ {
+ MessageLoop loop(MessageLoop::TYPE_DEFAULT);
+ EXPECT_FALSE(timer.IsRunning());
+ timer.Start(FROM_HERE, TimeDelta::FromDays(1),
+ base::Bind(&TimerTestCallback));
+ EXPECT_TRUE(timer.IsRunning());
+ }
+ EXPECT_FALSE(timer.IsRunning());
+ EXPECT_TRUE(timer.user_task().is_null());
+}
+
+TEST(TimerTest, RetainRepeatIsRunning) {
+ MessageLoop loop(MessageLoop::TYPE_DEFAULT);
+ base::Timer timer(FROM_HERE, TimeDelta::FromDays(1),
+ base::Bind(&TimerTestCallback), true);
+ EXPECT_FALSE(timer.IsRunning());
+ timer.Reset();
+ EXPECT_TRUE(timer.IsRunning());
+ timer.Stop();
+ EXPECT_FALSE(timer.IsRunning());
+ timer.Reset();
+ EXPECT_TRUE(timer.IsRunning());
+}
+
+TEST(TimerTest, RetainNonRepeatIsRunning) {
+ MessageLoop loop(MessageLoop::TYPE_DEFAULT);
+ base::Timer timer(FROM_HERE, TimeDelta::FromDays(1),
+ base::Bind(&TimerTestCallback), false);
+ EXPECT_FALSE(timer.IsRunning());
+ timer.Reset();
+ EXPECT_TRUE(timer.IsRunning());
+ timer.Stop();
+ EXPECT_FALSE(timer.IsRunning());
+ timer.Reset();
+ EXPECT_TRUE(timer.IsRunning());
+}
+
+namespace {
+
+bool g_callback_happened1 = false;
+bool g_callback_happened2 = false;
+
+void ClearAllCallbackHappened() {
+ g_callback_happened1 = false;
+ g_callback_happened2 = false;
+}
+
+void SetCallbackHappened1() {
+ g_callback_happened1 = true;
+ MessageLoop::current()->Quit();
+}
+
+void SetCallbackHappened2() {
+ g_callback_happened2 = true;
+ MessageLoop::current()->Quit();
+}
+
+TEST(TimerTest, ContinuationStopStart) {
+ {
+ ClearAllCallbackHappened();
+ MessageLoop loop(MessageLoop::TYPE_DEFAULT);
+ base::Timer timer(false, false);
+ timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(10),
+ base::Bind(&SetCallbackHappened1));
+ timer.Stop();
+ timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(40),
+ base::Bind(&SetCallbackHappened2));
+ MessageLoop::current()->Run();
+ EXPECT_FALSE(g_callback_happened1);
+ EXPECT_TRUE(g_callback_happened2);
+ }
+}
+
+TEST(TimerTest, ContinuationReset) {
+ {
+ ClearAllCallbackHappened();
+ MessageLoop loop(MessageLoop::TYPE_DEFAULT);
+ base::Timer timer(false, false);
+ timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(10),
+ base::Bind(&SetCallbackHappened1));
+ timer.Reset();
+ // Since Reset happened before task ran, the user_task must not be cleared:
+ ASSERT_FALSE(timer.user_task().is_null());
+ MessageLoop::current()->Run();
+ EXPECT_TRUE(g_callback_happened1);
+ }
+}
+
+} // namespace
diff --git a/src/base/tools_sanity_unittest.cc b/src/base/tools_sanity_unittest.cc
new file mode 100644
index 0000000..a901f59
--- /dev/null
+++ b/src/base/tools_sanity_unittest.cc
@@ -0,0 +1,287 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file contains intentional memory errors, some of which may lead to
+// crashes if the test is ran without special memory testing tools. We use these
+// errors to verify the sanity of the tools.
+
+#include "base/atomicops.h"
+#include "base/message_loop.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/threading/thread.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_STARBOARD)
+#include "starboard/memory.h"
+#define free SbMemoryFree
+#define malloc SbMemoryAllocate
+#endif
+
+namespace base {
+
+namespace {
+
+const base::subtle::Atomic32 kMagicValue = 42;
+
+// Helper for memory accesses that can potentially corrupt memory or cause a
+// crash during a native run.
+#if defined(ADDRESS_SANITIZER)
+#if defined(OS_IOS) || defined(__LB_SHELL__) || defined(OS_STARBOARD)
+// EXPECT_DEATH is not supported on IOS.
+// TODO: We have EXPECT_DEATH disabled for all Steel platforms,
+// but we could potentially support it on Linux.
+#define HARMFUL_ACCESS(action,error_regexp) do { action; } while (0)
+#else
+#define HARMFUL_ACCESS(action,error_regexp) EXPECT_DEATH(action,error_regexp)
+#endif // !OS_IOS
+#else
+#define HARMFUL_ACCESS(action,error_regexp) \
+do { if (RunningOnValgrind()) { action; } } while (0)
+#endif
+
+void ReadUninitializedValue(char *ptr) {
+ // Comparison with 64 is to prevent clang from optimizing away the
+ // jump -- valgrind only catches jumps and conditional moves, but clang uses
+ // the borrow flag if the condition is just `*ptr == '\0'`.
+ if (*ptr == 64) {
+ (*ptr)++;
+ } else {
+ (*ptr)--;
+ }
+}
+
+void ReadValueOutOfArrayBoundsLeft(char *ptr) {
+ char c = ptr[-2];
+ VLOG(1) << "Reading a byte out of bounds: " << c;
+}
+
+void ReadValueOutOfArrayBoundsRight(char *ptr, size_t size) {
+ char c = ptr[size + 1];
+ VLOG(1) << "Reading a byte out of bounds: " << c;
+}
+
+// This is harmless if you run it under Valgrind thanks to redzones.
+void WriteValueOutOfArrayBoundsLeft(char *ptr) {
+ ptr[-1] = kMagicValue;
+}
+
+// This is harmless if you run it under Valgrind thanks to redzones.
+void WriteValueOutOfArrayBoundsRight(char *ptr, size_t size) {
+ ptr[size] = kMagicValue;
+}
+
+void MakeSomeErrors(char *ptr, size_t size) {
+ ReadUninitializedValue(ptr);
+ HARMFUL_ACCESS(ReadValueOutOfArrayBoundsLeft(ptr),
+ "heap-buffer-overflow.*2 bytes to the left");
+ HARMFUL_ACCESS(ReadValueOutOfArrayBoundsRight(ptr, size),
+ "heap-buffer-overflow.*1 bytes to the right");
+ HARMFUL_ACCESS(WriteValueOutOfArrayBoundsLeft(ptr),
+ "heap-buffer-overflow.*1 bytes to the left");
+ HARMFUL_ACCESS(WriteValueOutOfArrayBoundsRight(ptr, size),
+ "heap-buffer-overflow.*0 bytes to the right");
+}
+
+} // namespace
+
+#if defined(ADDRESS_SANITIZER) && \
+ (defined(OS_IOS) || defined(__LB_SHELL__) || defined(OS_STARBOARD))
+// Because iOS doesn't support death tests, each of the following tests will
+// crash the whole program under ASan.
+#define MAYBE_AccessesToNewMemory DISABLED_AccessesToNewMemory
+#define MAYBE_AccessesToMallocMemory DISABLED_AccessesToMallocMemory
+#define MAYBE_ArrayDeletedWithoutBraces DISABLED_ArrayDeletedWithoutBraces
+#define MAYBE_MemoryLeak DISABLED_MemoryLeak
+#define MAYBE_SingleElementDeletedWithBraces \
+ DISABLED_SingleElementDeletedWithBraces
+#else
+#define MAYBE_AccessesToNewMemory AccessesToNewMemory
+#define MAYBE_AccessesToMallocMemory AccessesToMallocMemory
+#define MAYBE_ArrayDeletedWithoutBraces ArrayDeletedWithoutBraces
+#define MAYBE_MemoryLeak MemoryLeak
+#define MAYBE_SingleElementDeletedWithBraces SingleElementDeletedWithBraces
+#endif
+// A memory leak detector should report an error in this test.
+TEST(ToolsSanityTest, MAYBE_MemoryLeak) {
+ // Without the |volatile|, clang optimizes away the next two lines.
+ int* volatile leak = new int[256]; // Leak some memory intentionally.
+ leak[4] = 1; // Make sure the allocated memory is used.
+}
+
+TEST(ToolsSanityTest, MAYBE_AccessesToNewMemory) {
+ char *foo = new char[10];
+ MakeSomeErrors(foo, 10);
+ delete [] foo;
+ // Use after delete.
+ HARMFUL_ACCESS(foo[5] = 0, "heap-use-after-free");
+}
+
+TEST(ToolsSanityTest, MAYBE_AccessesToMallocMemory) {
+ char *foo = reinterpret_cast<char*>(malloc(10));
+ MakeSomeErrors(foo, 10);
+ free(foo);
+ // Use after free.
+ HARMFUL_ACCESS(foo[5] = 0, "heap-use-after-free");
+}
+
+
+static int* allocateArray() {
+ // Clang warns about the mismatched new[]/delete if they occur in the same
+ // function.
+ return new int[10];
+}
+
+TEST(ToolsSanityTest, MAYBE_ArrayDeletedWithoutBraces) {
+#if !defined(ADDRESS_SANITIZER)
+ // This test may corrupt memory if not run under Valgrind or compiled with
+ // AddressSanitizer.
+ if (!RunningOnValgrind())
+ return;
+#endif
+
+ // Without the |volatile|, clang optimizes away the next two lines.
+ int* volatile foo = allocateArray();
+ delete foo;
+}
+
+static int* allocateScalar() {
+ // Clang warns about the mismatched new/delete[] if they occur in the same
+ // function.
+ return new int;
+}
+
+TEST(ToolsSanityTest, MAYBE_SingleElementDeletedWithBraces) {
+#if !defined(ADDRESS_SANITIZER)
+ // This test may corrupt memory if not run under Valgrind or compiled with
+ // AddressSanitizer.
+ if (!RunningOnValgrind())
+ return;
+#endif
+
+ // Without the |volatile|, clang optimizes away the next two lines.
+ int* volatile foo = allocateScalar();
+ (void) foo;
+ delete [] foo;
+}
+
+#if defined(ADDRESS_SANITIZER)
+TEST(ToolsSanityTest, DISABLED_AddressSanitizerNullDerefCrashTest) {
+ // Intentionally crash to make sure AddressSanitizer is running.
+ // This test should not be ran on bots.
+ int* volatile zero = NULL;
+ *zero = 0;
+}
+
+TEST(ToolsSanityTest, DISABLED_AddressSanitizerLocalOOBCrashTest) {
+ // Intentionally crash to make sure AddressSanitizer is instrumenting
+ // the local variables.
+ // This test should not be ran on bots.
+ int array[5];
+ // Work around the OOB warning reported by Clang.
+ int* volatile access = &array[5];
+ *access = 43;
+}
+
+namespace {
+int g_asan_test_global_array[10];
+} // namespace
+
+TEST(ToolsSanityTest, DISABLED_AddressSanitizerGlobalOOBCrashTest) {
+ // Intentionally crash to make sure AddressSanitizer is instrumenting
+ // the global variables.
+ // This test should not be ran on bots.
+
+ // Work around the OOB warning reported by Clang.
+ int* volatile access = g_asan_test_global_array - 1;
+ *access = 43;
+}
+
+#endif
+
+namespace {
+
+// We use caps here just to ensure that the method name doesn't interfere with
+// the wildcarded suppressions.
+class TOOLS_SANITY_TEST_CONCURRENT_THREAD : public PlatformThread::Delegate {
+ public:
+ explicit TOOLS_SANITY_TEST_CONCURRENT_THREAD(bool *value) : value_(value) {}
+ virtual ~TOOLS_SANITY_TEST_CONCURRENT_THREAD() {}
+ virtual void ThreadMain() OVERRIDE {
+ *value_ = true;
+
+ // Sleep for a few milliseconds so the two threads are more likely to live
+ // simultaneously. Otherwise we may miss the report due to mutex
+ // lock/unlock's inside thread creation code in pure-happens-before mode...
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+ }
+ private:
+ bool *value_;
+};
+
+class ReleaseStoreThread : public PlatformThread::Delegate {
+ public:
+ explicit ReleaseStoreThread(base::subtle::Atomic32 *value) : value_(value) {}
+ virtual ~ReleaseStoreThread() {}
+ virtual void ThreadMain() OVERRIDE {
+ base::subtle::Release_Store(value_, kMagicValue);
+
+ // Sleep for a few milliseconds so the two threads are more likely to live
+ // simultaneously. Otherwise we may miss the report due to mutex
+ // lock/unlock's inside thread creation code in pure-happens-before mode...
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+ }
+ private:
+ base::subtle::Atomic32 *value_;
+};
+
+class AcquireLoadThread : public PlatformThread::Delegate {
+ public:
+ explicit AcquireLoadThread(base::subtle::Atomic32 *value) : value_(value) {}
+ virtual ~AcquireLoadThread() {}
+ virtual void ThreadMain() OVERRIDE {
+ // Wait for the other thread to make Release_Store
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+ base::subtle::Acquire_Load(value_);
+ }
+ private:
+ base::subtle::Atomic32 *value_;
+};
+
+void RunInParallel(PlatformThread::Delegate *d1, PlatformThread::Delegate *d2) {
+ PlatformThreadHandle a;
+ PlatformThreadHandle b;
+ PlatformThread::Create(0, d1, &a);
+ PlatformThread::Create(0, d2, &b);
+ PlatformThread::Join(a);
+ PlatformThread::Join(b);
+}
+
+} // namespace
+
+// A data race detector should report an error in this test.
+TEST(ToolsSanityTest, DataRace) {
+ bool shared = false;
+ TOOLS_SANITY_TEST_CONCURRENT_THREAD thread1(&shared), thread2(&shared);
+ RunInParallel(&thread1, &thread2);
+ EXPECT_TRUE(shared);
+}
+
+TEST(ToolsSanityTest, AnnotateBenignRace) {
+ bool shared = false;
+ ANNOTATE_BENIGN_RACE(&shared, "Intentional race - make sure doesn't show up");
+ TOOLS_SANITY_TEST_CONCURRENT_THREAD thread1(&shared), thread2(&shared);
+ RunInParallel(&thread1, &thread2);
+ EXPECT_TRUE(shared);
+}
+
+TEST(ToolsSanityTest, AtomicsAreIgnored) {
+ base::subtle::Atomic32 shared = 0;
+ ReleaseStoreThread thread1(&shared);
+ AcquireLoadThread thread2(&shared);
+ RunInParallel(&thread1, &thread2);
+ EXPECT_EQ(kMagicValue, shared);
+}
+
+} // namespace base
diff --git a/src/base/tracked_objects.cc b/src/base/tracked_objects.cc
new file mode 100644
index 0000000..9165169
--- /dev/null
+++ b/src/base/tracked_objects.cc
@@ -0,0 +1,885 @@
+// Copyright (c) 2012 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/tracked_objects.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "base/format_macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/process_util.h"
+#include "base/profiler/alternate_timer.h"
+#include "base/stringprintf.h"
+#include "base/third_party/valgrind/memcheck.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/port.h"
+
+using base::TimeDelta;
+
+namespace tracked_objects {
+
+namespace {
+// Flag to compile out almost all of the task tracking code.
+#if defined(__LB_SHELL__FOR_RELEASE__)
+const bool kTrackAllTaskObjects = false;
+#else
+const bool kTrackAllTaskObjects = true;
+#endif
+
+// TODO(jar): Evaluate the perf impact of enabling this. If the perf impact is
+// negligible, enable by default.
+// Flag to compile out parent-child link recording.
+const bool kTrackParentChildLinks = false;
+
+// When ThreadData is first initialized, should we start in an ACTIVE state to
+// record all of the startup-time tasks, or should we start up DEACTIVATED, so
+// that we only record after parsing the command line flag --enable-tracking.
+// Note that the flag may force either state, so this really controls only the
+// period of time up until that flag is parsed. If there is no flag seen, then
+// this state may prevail for much or all of the process lifetime.
+const ThreadData::Status kInitialStartupState =
+ ThreadData::PROFILING_CHILDREN_ACTIVE;
+
+// Control whether an alternate time source (Now() function) is supported by
+// the ThreadData class. This compile time flag should be set to true if we
+// want other modules (such as a memory allocator, or a thread-specific CPU time
+// clock) to be able to provide a thread-specific Now() function. Without this
+// compile-time flag, the code will only support the wall-clock time. This flag
+// can be flipped to efficiently disable this path (if there is a performance
+// problem with its presence).
+static const bool kAllowAlternateTimeSourceHandling = true;
+
+} // namespace
+
+//------------------------------------------------------------------------------
+// DeathData tallies durations when a death takes place.
+
+DeathData::DeathData() {
+ Clear();
+}
+
+DeathData::DeathData(int count) {
+ Clear();
+ count_ = count;
+}
+
+// TODO(jar): I need to see if this macro to optimize branching is worth using.
+//
+// This macro has no branching, so it is surely fast, and is equivalent to:
+// if (assign_it)
+// target = source;
+// We use a macro rather than a template to force this to inline.
+// Related code for calculating max is discussed on the web.
+#define CONDITIONAL_ASSIGN(assign_it, target, source) \
+ ((target) ^= ((target) ^ (source)) & -static_cast<int32>(assign_it))
+
+void DeathData::RecordDeath(const int32 queue_duration,
+ const int32 run_duration,
+ int32 random_number) {
+ ++count_;
+ queue_duration_sum_ += queue_duration;
+ run_duration_sum_ += run_duration;
+
+ if (queue_duration_max_ < queue_duration)
+ queue_duration_max_ = queue_duration;
+ if (run_duration_max_ < run_duration)
+ run_duration_max_ = run_duration;
+
+ // Take a uniformly distributed sample over all durations ever supplied.
+ // The probability that we (instead) use this new sample is 1/count_. This
+ // results in a completely uniform selection of the sample (at least when we
+ // don't clamp count_... but that should be inconsequentially likely).
+ // We ignore the fact that we correlated our selection of a sample to the run
+ // and queue times (i.e., we used them to generate random_number).
+ if (count_ <= 0) { // Handle wrapping of count_, such as in bug 138961.
+ CHECK_GE(count_ - 1, 0); // Detect memory corruption.
+ // We'll just clamp at INT_MAX, but we should note this in the UI as such.
+ count_ = INT_MAX;
+ }
+ if (0 == (random_number % count_)) {
+ queue_duration_sample_ = queue_duration;
+ run_duration_sample_ = run_duration;
+ }
+}
+
+int DeathData::count() const { return count_; }
+
+int32 DeathData::run_duration_sum() const { return run_duration_sum_; }
+
+int32 DeathData::run_duration_max() const { return run_duration_max_; }
+
+int32 DeathData::run_duration_sample() const {
+ return run_duration_sample_;
+}
+
+int32 DeathData::queue_duration_sum() const {
+ return queue_duration_sum_;
+}
+
+int32 DeathData::queue_duration_max() const {
+ return queue_duration_max_;
+}
+
+int32 DeathData::queue_duration_sample() const {
+ return queue_duration_sample_;
+}
+
+void DeathData::ResetMax() {
+ run_duration_max_ = 0;
+ queue_duration_max_ = 0;
+}
+
+void DeathData::Clear() {
+ count_ = 0;
+ run_duration_sum_ = 0;
+ run_duration_max_ = 0;
+ run_duration_sample_ = 0;
+ queue_duration_sum_ = 0;
+ queue_duration_max_ = 0;
+ queue_duration_sample_ = 0;
+}
+
+//------------------------------------------------------------------------------
+DeathDataSnapshot::DeathDataSnapshot()
+ : count(-1),
+ run_duration_sum(-1),
+ run_duration_max(-1),
+ run_duration_sample(-1),
+ queue_duration_sum(-1),
+ queue_duration_max(-1),
+ queue_duration_sample(-1) {
+}
+
+DeathDataSnapshot::DeathDataSnapshot(
+ const tracked_objects::DeathData& death_data)
+ : count(death_data.count()),
+ run_duration_sum(death_data.run_duration_sum()),
+ run_duration_max(death_data.run_duration_max()),
+ run_duration_sample(death_data.run_duration_sample()),
+ queue_duration_sum(death_data.queue_duration_sum()),
+ queue_duration_max(death_data.queue_duration_max()),
+ queue_duration_sample(death_data.queue_duration_sample()) {
+}
+
+DeathDataSnapshot::~DeathDataSnapshot() {
+}
+
+//------------------------------------------------------------------------------
+BirthOnThread::BirthOnThread(const Location& location,
+ const ThreadData& current)
+ : location_(location),
+ birth_thread_(¤t) {
+}
+
+//------------------------------------------------------------------------------
+BirthOnThreadSnapshot::BirthOnThreadSnapshot() {
+}
+
+BirthOnThreadSnapshot::BirthOnThreadSnapshot(
+ const tracked_objects::BirthOnThread& birth)
+ : location(birth.location()),
+ thread_name(birth.birth_thread()->thread_name()) {
+}
+
+BirthOnThreadSnapshot::~BirthOnThreadSnapshot() {
+}
+
+//------------------------------------------------------------------------------
+Births::Births(const Location& location, const ThreadData& current)
+ : BirthOnThread(location, current),
+ birth_count_(1) { }
+
+int Births::birth_count() const { return birth_count_; }
+
+void Births::RecordBirth() { ++birth_count_; }
+
+void Births::ForgetBirth() { --birth_count_; }
+
+void Births::Clear() { birth_count_ = 0; }
+
+//------------------------------------------------------------------------------
+// ThreadData maintains the central data for all births and deaths on a single
+// thread.
+
+// TODO(jar): We should pull all these static vars together, into a struct, and
+// optimize layout so that we benefit from locality of reference during accesses
+// to them.
+
+// static
+NowFunction* ThreadData::now_function_ = NULL;
+
+// A TLS slot which points to the ThreadData instance for the current thread. We
+// do a fake initialization here (zeroing out data), and then the real in-place
+// construction happens when we call tls_index_.Initialize().
+// static
+base::ThreadLocalStorage::StaticSlot ThreadData::tls_index_ = TLS_INITIALIZER;
+
+// static
+int ThreadData::worker_thread_data_creation_count_ = 0;
+
+// static
+int ThreadData::cleanup_count_ = 0;
+
+// static
+int ThreadData::incarnation_counter_ = 0;
+
+// static
+ThreadData* ThreadData::all_thread_data_list_head_ = NULL;
+
+// static
+ThreadData* ThreadData::first_retired_worker_ = NULL;
+
+// static
+base::LazyInstance<base::Lock>::Leaky
+ ThreadData::list_lock_ = LAZY_INSTANCE_INITIALIZER;
+
+// static
+ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED;
+
+ThreadData::ThreadData(const std::string& suggested_name)
+ : next_(NULL),
+ next_retired_worker_(NULL),
+ worker_thread_number_(0),
+ incarnation_count_for_pool_(-1) {
+ DCHECK_GE(suggested_name.size(), 0u);
+ thread_name_ = suggested_name;
+ PushToHeadOfList(); // Which sets real incarnation_count_for_pool_.
+}
+
+ThreadData::ThreadData(int thread_number)
+ : next_(NULL),
+ next_retired_worker_(NULL),
+ worker_thread_number_(thread_number),
+ incarnation_count_for_pool_(-1) {
+ CHECK_GT(thread_number, 0);
+ base::StringAppendF(&thread_name_, "WorkerThread-%d", thread_number);
+ PushToHeadOfList(); // Which sets real incarnation_count_for_pool_.
+}
+
+ThreadData::~ThreadData() {}
+
+void ThreadData::PushToHeadOfList() {
+ // Toss in a hint of randomness (atop the uniniitalized value).
+ (void)VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(&random_number_,
+ sizeof(random_number_));
+ random_number_ += static_cast<int32>(this - static_cast<ThreadData*>(0));
+ random_number_ ^= (Now() - TrackedTime()).InMilliseconds();
+
+ DCHECK(!next_);
+ base::AutoLock lock(*list_lock_.Pointer());
+ incarnation_count_for_pool_ = incarnation_counter_;
+ next_ = all_thread_data_list_head_;
+ all_thread_data_list_head_ = this;
+}
+
+// static
+ThreadData* ThreadData::first() {
+ base::AutoLock lock(*list_lock_.Pointer());
+ return all_thread_data_list_head_;
+}
+
+ThreadData* ThreadData::next() const { return next_; }
+
+// static
+void ThreadData::InitializeThreadContext(const std::string& suggested_name) {
+ if (!Initialize()) // Always initialize if needed.
+ return;
+ ThreadData* current_thread_data =
+ reinterpret_cast<ThreadData*>(tls_index_.Get());
+ if (current_thread_data)
+ return; // Browser tests instigate this.
+ current_thread_data = new ThreadData(suggested_name);
+ tls_index_.Set(current_thread_data);
+}
+
+// static
+ThreadData* ThreadData::Get() {
+ if (!tls_index_.initialized())
+ return NULL; // For unittests only.
+ ThreadData* registered = reinterpret_cast<ThreadData*>(tls_index_.Get());
+ if (registered)
+ return registered;
+
+ // We must be a worker thread, since we didn't pre-register.
+ ThreadData* worker_thread_data = NULL;
+ int worker_thread_number = 0;
+ {
+ base::AutoLock lock(*list_lock_.Pointer());
+ if (first_retired_worker_) {
+ worker_thread_data = first_retired_worker_;
+ first_retired_worker_ = first_retired_worker_->next_retired_worker_;
+ worker_thread_data->next_retired_worker_ = NULL;
+ } else {
+ worker_thread_number = ++worker_thread_data_creation_count_;
+ }
+ }
+
+ // If we can't find a previously used instance, then we have to create one.
+ if (!worker_thread_data) {
+ DCHECK_GT(worker_thread_number, 0);
+ worker_thread_data = new ThreadData(worker_thread_number);
+ }
+ DCHECK_GT(worker_thread_data->worker_thread_number_, 0);
+
+ tls_index_.Set(worker_thread_data);
+ return worker_thread_data;
+}
+
+// static
+void ThreadData::OnThreadTermination(void* thread_data) {
+ DCHECK(thread_data); // TLS should *never* call us with a NULL.
+ // We must NOT do any allocations during this callback. There is a chance
+ // that the allocator is no longer active on this thread.
+ if (!kTrackAllTaskObjects)
+ return; // Not compiled in.
+ reinterpret_cast<ThreadData*>(thread_data)->OnThreadTerminationCleanup();
+}
+
+void ThreadData::OnThreadTerminationCleanup() {
+#if defined(__LB_SHELL__) || defined(OS_STARBOARD)
+ // The Chromium code avoids doing allocations here and puts ThreadDatas
+ // into a linked list for reuse.
+ // However, every call to InitializeThreadContext() will create
+ // a new ThreadData without any possibility of reuse.
+ // We prefer to delete the ThreadData to avoid leaking mutexes and memory.
+ delete this;
+#else
+ // The list_lock_ was created when we registered the callback, so it won't be
+ // allocated here despite the lazy reference.
+ base::AutoLock lock(*list_lock_.Pointer());
+ if (incarnation_counter_ != incarnation_count_for_pool_)
+ return; // ThreadData was constructed in an earlier unit test.
+ ++cleanup_count_;
+ // Only worker threads need to be retired and reused.
+ if (!worker_thread_number_) {
+ return;
+ }
+ // We must NOT do any allocations during this callback.
+ // Using the simple linked lists avoids all allocations.
+ DCHECK_EQ(this->next_retired_worker_, reinterpret_cast<ThreadData*>(NULL));
+ this->next_retired_worker_ = first_retired_worker_;
+ first_retired_worker_ = this;
+#endif
+}
+
+// static
+void ThreadData::Snapshot(bool reset_max, ProcessDataSnapshot* process_data) {
+ // Add births that have run to completion to |collected_data|.
+ // |birth_counts| tracks the total number of births recorded at each location
+ // for which we have not seen a death count.
+ BirthCountMap birth_counts;
+ ThreadData::SnapshotAllExecutedTasks(reset_max, process_data, &birth_counts);
+
+ // Add births that are still active -- i.e. objects that have tallied a birth,
+ // but have not yet tallied a matching death, and hence must be either
+ // running, queued up, or being held in limbo for future posting.
+ for (BirthCountMap::const_iterator it = birth_counts.begin();
+ it != birth_counts.end(); ++it) {
+ if (it->second > 0) {
+ process_data->tasks.push_back(
+ TaskSnapshot(*it->first, DeathData(it->second), "Still_Alive"));
+ }
+ }
+}
+
+Births* ThreadData::TallyABirth(const Location& location) {
+ BirthMap::iterator it = birth_map_.find(location);
+ Births* child;
+ if (it != birth_map_.end()) {
+ child = it->second;
+ child->RecordBirth();
+ } else {
+ child = new Births(location, *this); // Leak this.
+ // Lock since the map may get relocated now, and other threads sometimes
+ // snapshot it (but they lock before copying it).
+ base::AutoLock lock(map_lock_);
+ birth_map_[location] = child;
+ }
+
+ if (kTrackParentChildLinks && status_ > PROFILING_ACTIVE &&
+ !parent_stack_.empty()) {
+ const Births* parent = parent_stack_.top();
+ ParentChildPair pair(parent, child);
+ if (parent_child_set_.find(pair) == parent_child_set_.end()) {
+ // Lock since the map may get relocated now, and other threads sometimes
+ // snapshot it (but they lock before copying it).
+ base::AutoLock lock(map_lock_);
+ parent_child_set_.insert(pair);
+ }
+ }
+
+ return child;
+}
+
+void ThreadData::TallyADeath(const Births& birth,
+ int32 queue_duration,
+ int32 run_duration) {
+ // Stir in some randomness, plus add constant in case durations are zero.
+ const int32 kSomePrimeNumber = 2147483647;
+ random_number_ += queue_duration + run_duration + kSomePrimeNumber;
+ // An address is going to have some randomness to it as well ;-).
+ random_number_ ^= static_cast<int32>(&birth - reinterpret_cast<Births*>(0));
+
+ // We don't have queue durations without OS timer. OS timer is automatically
+ // used for task-post-timing, so the use of an alternate timer implies all
+ // queue times are invalid.
+ if (kAllowAlternateTimeSourceHandling && now_function_)
+ queue_duration = 0;
+
+ DeathMap::iterator it = death_map_.find(&birth);
+ DeathData* death_data;
+ if (it != death_map_.end()) {
+ death_data = &it->second;
+ } else {
+ base::AutoLock lock(map_lock_); // Lock as the map may get relocated now.
+ death_data = &death_map_[&birth];
+ } // Release lock ASAP.
+ death_data->RecordDeath(queue_duration, run_duration, random_number_);
+
+ if (!kTrackParentChildLinks)
+ return;
+ if (!parent_stack_.empty()) { // We might get turned off.
+ DCHECK_EQ(parent_stack_.top(), &birth);
+ parent_stack_.pop();
+ }
+}
+
+// static
+Births* ThreadData::TallyABirthIfActive(const Location& location) {
+ if (!kTrackAllTaskObjects)
+ return NULL; // Not compiled in.
+
+ if (!TrackingStatus())
+ return NULL;
+ ThreadData* current_thread_data = Get();
+ if (!current_thread_data)
+ return NULL;
+ return current_thread_data->TallyABirth(location);
+}
+
+// static
+void ThreadData::TallyRunOnNamedThreadIfTracking(
+ const base::TrackingInfo& completed_task,
+ const TrackedTime& start_of_run,
+ const TrackedTime& end_of_run) {
+ if (!kTrackAllTaskObjects)
+ return; // Not compiled in.
+
+ // Even if we have been DEACTIVATED, we will process any pending births so
+ // that our data structures (which counted the outstanding births) remain
+ // consistent.
+ const Births* birth = completed_task.birth_tally;
+ if (!birth)
+ return;
+ ThreadData* current_thread_data = Get();
+ if (!current_thread_data)
+ return;
+
+ // To avoid conflating our stats with the delay duration in a PostDelayedTask,
+ // we identify such tasks, and replace their post_time with the time they
+ // were scheduled (requested?) to emerge from the delayed task queue. This
+ // means that queueing delay for such tasks will show how long they went
+ // unserviced, after they *could* be serviced. This is the same stat as we
+ // have for non-delayed tasks, and we consistently call it queueing delay.
+ TrackedTime effective_post_time = completed_task.delayed_run_time.is_null()
+ ? tracked_objects::TrackedTime(completed_task.time_posted)
+ : tracked_objects::TrackedTime(completed_task.delayed_run_time);
+
+ // Watch out for a race where status_ is changing, and hence one or both
+ // of start_of_run or end_of_run is zero. In that case, we didn't bother to
+ // get a time value since we "weren't tracking" and we were trying to be
+ // efficient by not calling for a genuine time value. For simplicity, we'll
+ // use a default zero duration when we can't calculate a true value.
+ int32 queue_duration = 0;
+ int32 run_duration = 0;
+ if (!start_of_run.is_null()) {
+ queue_duration = (start_of_run - effective_post_time).InMilliseconds();
+ if (!end_of_run.is_null())
+ run_duration = (end_of_run - start_of_run).InMilliseconds();
+ }
+ current_thread_data->TallyADeath(*birth, queue_duration, run_duration);
+}
+
+// static
+void ThreadData::TallyRunOnWorkerThreadIfTracking(
+ const Births* birth,
+ const TrackedTime& time_posted,
+ const TrackedTime& start_of_run,
+ const TrackedTime& end_of_run) {
+ if (!kTrackAllTaskObjects)
+ return; // Not compiled in.
+
+ // Even if we have been DEACTIVATED, we will process any pending births so
+ // that our data structures (which counted the outstanding births) remain
+ // consistent.
+ if (!birth)
+ return;
+
+ // TODO(jar): Support the option to coalesce all worker-thread activity under
+ // one ThreadData instance that uses locks to protect *all* access. This will
+ // reduce memory (making it provably bounded), but run incrementally slower
+ // (since we'll use locks on TallyBirth and TallyDeath). The good news is
+ // that the locks on TallyDeath will be *after* the worker thread has run, and
+ // hence nothing will be waiting for the completion (... besides some other
+ // thread that might like to run). Also, the worker threads tasks are
+ // generally longer, and hence the cost of the lock may perchance be amortized
+ // over the long task's lifetime.
+ ThreadData* current_thread_data = Get();
+ if (!current_thread_data)
+ return;
+
+ int32 queue_duration = 0;
+ int32 run_duration = 0;
+ if (!start_of_run.is_null()) {
+ queue_duration = (start_of_run - time_posted).InMilliseconds();
+ if (!end_of_run.is_null())
+ run_duration = (end_of_run - start_of_run).InMilliseconds();
+ }
+ current_thread_data->TallyADeath(*birth, queue_duration, run_duration);
+}
+
+// static
+void ThreadData::TallyRunInAScopedRegionIfTracking(
+ const Births* birth,
+ const TrackedTime& start_of_run,
+ const TrackedTime& end_of_run) {
+ if (!kTrackAllTaskObjects)
+ return; // Not compiled in.
+
+ // Even if we have been DEACTIVATED, we will process any pending births so
+ // that our data structures (which counted the outstanding births) remain
+ // consistent.
+ if (!birth)
+ return;
+
+ ThreadData* current_thread_data = Get();
+ if (!current_thread_data)
+ return;
+
+ int32 queue_duration = 0;
+ int32 run_duration = 0;
+ if (!start_of_run.is_null() && !end_of_run.is_null())
+ run_duration = (end_of_run - start_of_run).InMilliseconds();
+ current_thread_data->TallyADeath(*birth, queue_duration, run_duration);
+}
+
+// static
+void ThreadData::SnapshotAllExecutedTasks(bool reset_max,
+ ProcessDataSnapshot* process_data,
+ BirthCountMap* birth_counts) {
+ if (!kTrackAllTaskObjects)
+ return; // Not compiled in.
+
+ // Get an unchanging copy of a ThreadData list.
+ ThreadData* my_list = ThreadData::first();
+
+ // Gather data serially.
+ // This hackish approach *can* get some slighly corrupt tallies, as we are
+ // grabbing values without the protection of a lock, but it has the advantage
+ // of working even with threads that don't have message loops. If a user
+ // sees any strangeness, they can always just run their stats gathering a
+ // second time.
+ for (ThreadData* thread_data = my_list;
+ thread_data;
+ thread_data = thread_data->next()) {
+ thread_data->SnapshotExecutedTasks(reset_max, process_data, birth_counts);
+ }
+}
+
+void ThreadData::SnapshotExecutedTasks(bool reset_max,
+ ProcessDataSnapshot* process_data,
+ BirthCountMap* birth_counts) {
+ // Get copy of data, so that the data will not change during the iterations
+ // and processing.
+ ThreadData::BirthMap birth_map;
+ ThreadData::DeathMap death_map;
+ ThreadData::ParentChildSet parent_child_set;
+ SnapshotMaps(reset_max, &birth_map, &death_map, &parent_child_set);
+
+ for (ThreadData::DeathMap::const_iterator it = death_map.begin();
+ it != death_map.end(); ++it) {
+ process_data->tasks.push_back(
+ TaskSnapshot(*it->first, it->second, thread_name()));
+ (*birth_counts)[it->first] -= it->first->birth_count();
+ }
+
+ for (ThreadData::BirthMap::const_iterator it = birth_map.begin();
+ it != birth_map.end(); ++it) {
+ (*birth_counts)[it->second] += it->second->birth_count();
+ }
+
+ if (!kTrackParentChildLinks)
+ return;
+
+ for (ThreadData::ParentChildSet::const_iterator it = parent_child_set.begin();
+ it != parent_child_set.end(); ++it) {
+ process_data->descendants.push_back(ParentChildPairSnapshot(*it));
+ }
+}
+
+// This may be called from another thread.
+void ThreadData::SnapshotMaps(bool reset_max,
+ BirthMap* birth_map,
+ DeathMap* death_map,
+ ParentChildSet* parent_child_set) {
+ base::AutoLock lock(map_lock_);
+ for (BirthMap::const_iterator it = birth_map_.begin();
+ it != birth_map_.end(); ++it)
+ (*birth_map)[it->first] = it->second;
+ for (DeathMap::iterator it = death_map_.begin();
+ it != death_map_.end(); ++it) {
+ (*death_map)[it->first] = it->second;
+ if (reset_max)
+ it->second.ResetMax();
+ }
+
+ if (!kTrackParentChildLinks)
+ return;
+
+ for (ParentChildSet::iterator it = parent_child_set_.begin();
+ it != parent_child_set_.end(); ++it)
+ parent_child_set->insert(*it);
+}
+
+// static
+void ThreadData::ResetAllThreadData() {
+ ThreadData* my_list = first();
+
+ for (ThreadData* thread_data = my_list;
+ thread_data;
+ thread_data = thread_data->next())
+ thread_data->Reset();
+}
+
+void ThreadData::Reset() {
+ base::AutoLock lock(map_lock_);
+ for (DeathMap::iterator it = death_map_.begin();
+ it != death_map_.end(); ++it)
+ it->second.Clear();
+ for (BirthMap::iterator it = birth_map_.begin();
+ it != birth_map_.end(); ++it)
+ it->second->Clear();
+}
+
+static void OptionallyInitializeAlternateTimer() {
+ NowFunction* alternate_time_source = GetAlternateTimeSource();
+ if (alternate_time_source)
+ ThreadData::SetAlternateTimeSource(alternate_time_source);
+}
+
+bool ThreadData::Initialize() {
+ if (!kTrackAllTaskObjects)
+ return false; // Not compiled in.
+ if (status_ >= DEACTIVATED)
+ return true; // Someone else did the initialization.
+ // Due to racy lazy initialization in tests, we'll need to recheck status_
+ // after we acquire the lock.
+
+ // Ensure that we don't double initialize tls. We are called when single
+ // threaded in the product, but some tests may be racy and lazy about our
+ // initialization.
+ base::AutoLock lock(*list_lock_.Pointer());
+ if (status_ >= DEACTIVATED)
+ return true; // Someone raced in here and beat us.
+
+ // Put an alternate timer in place if the environment calls for it, such as
+ // for tracking TCMalloc allocations. This insertion is idempotent, so we
+ // don't mind if there is a race, and we'd prefer not to be in a lock while
+ // doing this work.
+ if (kAllowAlternateTimeSourceHandling)
+ OptionallyInitializeAlternateTimer();
+
+ // Perform the "real" TLS initialization now, and leave it intact through
+ // process termination.
+ if (!tls_index_.initialized()) { // Testing may have initialized this.
+ DCHECK_EQ(status_, UNINITIALIZED);
+ tls_index_.Initialize(&ThreadData::OnThreadTermination);
+ if (!tls_index_.initialized())
+ return false;
+ } else {
+ // TLS was initialzed for us earlier.
+ DCHECK_EQ(status_, DORMANT_DURING_TESTS);
+ }
+
+ // Incarnation counter is only significant to testing, as it otherwise will
+ // never again change in this process.
+ ++incarnation_counter_;
+
+ // The lock is not critical for setting status_, but it doesn't hurt. It also
+ // ensures that if we have a racy initialization, that we'll bail as soon as
+ // we get the lock earlier in this method.
+ status_ = kInitialStartupState;
+ if (!kTrackParentChildLinks &&
+ kInitialStartupState == PROFILING_CHILDREN_ACTIVE)
+ status_ = PROFILING_ACTIVE;
+ DCHECK(status_ != UNINITIALIZED);
+ return true;
+}
+
+// static
+bool ThreadData::InitializeAndSetTrackingStatus(Status status) {
+ DCHECK_GE(status, DEACTIVATED);
+ DCHECK_LE(status, PROFILING_CHILDREN_ACTIVE);
+
+ if (!Initialize()) // No-op if already initialized.
+ return false; // Not compiled in.
+
+ if (!kTrackParentChildLinks && status > DEACTIVATED)
+ status = PROFILING_ACTIVE;
+ status_ = status;
+ return true;
+}
+
+// static
+ThreadData::Status ThreadData::status() {
+ return status_;
+}
+
+// static
+bool ThreadData::TrackingStatus() {
+ return status_ > DEACTIVATED;
+}
+
+// static
+bool ThreadData::TrackingParentChildStatus() {
+ return status_ >= PROFILING_CHILDREN_ACTIVE;
+}
+
+// static
+TrackedTime ThreadData::NowForStartOfRun(const Births* parent) {
+ if (kTrackParentChildLinks && parent && status_ > PROFILING_ACTIVE) {
+ ThreadData* current_thread_data = Get();
+ if (current_thread_data)
+ current_thread_data->parent_stack_.push(parent);
+ }
+ return Now();
+}
+
+// static
+TrackedTime ThreadData::NowForEndOfRun() {
+ return Now();
+}
+
+// static
+void ThreadData::SetAlternateTimeSource(NowFunction* now_function) {
+ DCHECK(now_function);
+ if (kAllowAlternateTimeSourceHandling)
+ now_function_ = now_function;
+}
+
+// static
+TrackedTime ThreadData::Now() {
+ if (kAllowAlternateTimeSourceHandling && now_function_)
+ return TrackedTime::FromMilliseconds((*now_function_)());
+ if (kTrackAllTaskObjects && TrackingStatus())
+ return TrackedTime::Now();
+ return TrackedTime(); // Super fast when disabled, or not compiled.
+}
+
+// static
+void ThreadData::EnsureCleanupWasCalled(int major_threads_shutdown_count) {
+ base::AutoLock lock(*list_lock_.Pointer());
+ if (worker_thread_data_creation_count_ == 0)
+ return; // We haven't really run much, and couldn't have leaked.
+ // Verify that we've at least shutdown/cleanup the major namesd threads. The
+ // caller should tell us how many thread shutdowns should have taken place by
+ // now.
+ return; // TODO(jar): until this is working on XP, don't run the real test.
+ CHECK_GT(cleanup_count_, major_threads_shutdown_count);
+}
+
+// static
+void ThreadData::ShutdownSingleThreadedCleanup(bool leak) {
+ // This is only called from test code, where we need to cleanup so that
+ // additional tests can be run.
+ // We must be single threaded... but be careful anyway.
+ if (!InitializeAndSetTrackingStatus(DEACTIVATED))
+ return;
+ ThreadData* thread_data_list;
+ {
+ base::AutoLock lock(*list_lock_.Pointer());
+ thread_data_list = all_thread_data_list_head_;
+ all_thread_data_list_head_ = NULL;
+ ++incarnation_counter_;
+ // To be clean, break apart the retired worker list (though we leak them).
+ while (first_retired_worker_) {
+ ThreadData* worker = first_retired_worker_;
+ CHECK_GT(worker->worker_thread_number_, 0);
+ first_retired_worker_ = worker->next_retired_worker_;
+ worker->next_retired_worker_ = NULL;
+ }
+ }
+
+ // Put most global static back in pristine shape.
+ worker_thread_data_creation_count_ = 0;
+ cleanup_count_ = 0;
+ tls_index_.Set(NULL);
+ status_ = DORMANT_DURING_TESTS; // Almost UNINITIALIZED.
+
+ // To avoid any chance of racing in unit tests, which is the only place we
+ // call this function, we may sometimes leak all the data structures we
+ // recovered, as they may still be in use on threads from prior tests!
+ if (leak)
+ return;
+
+ // When we want to cleanup (on a single thread), here is what we do.
+
+ // Do actual recursive delete in all ThreadData instances.
+ while (thread_data_list) {
+ ThreadData* next_thread_data = thread_data_list;
+ thread_data_list = thread_data_list->next();
+
+ for (BirthMap::iterator it = next_thread_data->birth_map_.begin();
+ next_thread_data->birth_map_.end() != it; ++it)
+ delete it->second; // Delete the Birth Records.
+ delete next_thread_data; // Includes all Death Records.
+ }
+}
+
+//------------------------------------------------------------------------------
+TaskSnapshot::TaskSnapshot() {
+}
+
+TaskSnapshot::TaskSnapshot(const BirthOnThread& birth,
+ const DeathData& death_data,
+ const std::string& death_thread_name)
+ : birth(birth),
+ death_data(death_data),
+ death_thread_name(death_thread_name) {
+}
+
+TaskSnapshot::~TaskSnapshot() {
+}
+
+//------------------------------------------------------------------------------
+// ParentChildPairSnapshot
+
+ParentChildPairSnapshot::ParentChildPairSnapshot() {
+}
+
+ParentChildPairSnapshot::ParentChildPairSnapshot(
+ const ThreadData::ParentChildPair& parent_child)
+ : parent(*parent_child.first),
+ child(*parent_child.second) {
+}
+
+ParentChildPairSnapshot::~ParentChildPairSnapshot() {
+}
+
+//------------------------------------------------------------------------------
+// ProcessDataSnapshot
+
+ProcessDataSnapshot::ProcessDataSnapshot()
+#if !defined(OS_NACL)
+ : process_id(base::GetCurrentProcId()) {
+#else
+ : process_id(0) {
+#endif
+}
+
+ProcessDataSnapshot::~ProcessDataSnapshot() {
+}
+
+} // namespace tracked_objects
diff --git a/src/base/tracked_objects.h b/src/base/tracked_objects.h
new file mode 100644
index 0000000..2bcb94a
--- /dev/null
+++ b/src/base/tracked_objects.h
@@ -0,0 +1,718 @@
+// Copyright (c) 2012 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_TRACKED_OBJECTS_H_
+#define BASE_TRACKED_OBJECTS_H_
+
+#include <map>
+#include <set>
+#include <stack>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/gtest_prod_util.h"
+#include "base/lazy_instance.h"
+#include "base/location.h"
+#include "base/profiler/alternate_timer.h"
+#include "base/profiler/tracked_time.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_local_storage.h"
+#include "base/tracking_info.h"
+
+// TrackedObjects provides a database of stats about objects (generally Tasks)
+// that are tracked. Tracking means their birth, death, duration, birth thread,
+// death thread, and birth place are recorded. This data is carefully spread
+// across a series of objects so that the counts and times can be rapidly
+// updated without (usually) having to lock the data, and hence there is usually
+// very little contention caused by the tracking. The data can be viewed via
+// the about:profiler URL, with a variety of sorting and filtering choices.
+//
+// These classes serve as the basis of a profiler of sorts for the Tasks system.
+// As a result, design decisions were made to maximize speed, by minimizing
+// recurring allocation/deallocation, lock contention and data copying. In the
+// "stable" state, which is reached relatively quickly, there is no separate
+// marginal allocation cost associated with construction or destruction of
+// tracked objects, no locks are generally employed, and probably the largest
+// computational cost is associated with obtaining start and stop times for
+// instances as they are created and destroyed.
+//
+// The following describes the lifecycle of tracking an instance.
+//
+// First off, when the instance is created, the FROM_HERE macro is expanded
+// to specify the birth place (file, line, function) where the instance was
+// created. That data is used to create a transient Location instance
+// encapsulating the above triple of information. The strings (like __FILE__)
+// are passed around by reference, with the assumption that they are static, and
+// will never go away. This ensures that the strings can be dealt with as atoms
+// with great efficiency (i.e., copying of strings is never needed, and
+// comparisons for equality can be based on pointer comparisons).
+//
+// Next, a Births instance is created for use ONLY on the thread where this
+// instance was created. That Births instance records (in a base class
+// BirthOnThread) references to the static data provided in a Location instance,
+// as well as a pointer specifying the thread on which the birth takes place.
+// Hence there is at most one Births instance for each Location on each thread.
+// The derived Births class contains slots for recording statistics about all
+// instances born at the same location. Statistics currently include only the
+// count of instances constructed.
+//
+// Since the base class BirthOnThread contains only constant data, it can be
+// freely accessed by any thread at any time (i.e., only the statistic needs to
+// be handled carefully, and stats are updated exclusively on the birth thread).
+//
+// For Tasks, having now either constructed or found the Births instance
+// described above, a pointer to the Births instance is then recorded into the
+// PendingTask structure in MessageLoop. This fact alone is very useful in
+// debugging, when there is a question of where an instance came from. In
+// addition, the birth time is also recorded and used to later evaluate the
+// lifetime duration of the whole Task. As a result of the above embedding, we
+// can find out a Task's location of birth, and thread of birth, without using
+// any locks, as all that data is constant across the life of the process.
+//
+// The above work *could* also be done for any other object as well by calling
+// TallyABirthIfActive() and TallyRunOnNamedThreadIfTracking() as appropriate.
+//
+// The amount of memory used in the above data structures depends on how many
+// threads there are, and how many Locations of construction there are.
+// Fortunately, we don't use memory that is the product of those two counts, but
+// rather we only need one Births instance for each thread that constructs an
+// instance at a Location. In many cases, instances are only created on one
+// thread, so the memory utilization is actually fairly restrained.
+//
+// Lastly, when an instance is deleted, the final tallies of statistics are
+// carefully accumulated. That tallying writes into slots (members) in a
+// collection of DeathData instances. For each birth place Location that is
+// destroyed on a thread, there is a DeathData instance to record the additional
+// death count, as well as accumulate the run-time and queue-time durations for
+// the instance as it is destroyed (dies). By maintaining a single place to
+// aggregate this running sum *only* for the given thread, we avoid the need to
+// lock such DeathData instances. (i.e., these accumulated stats in a DeathData
+// instance are exclusively updated by the singular owning thread).
+//
+// With the above lifecycle description complete, the major remaining detail is
+// explaining how each thread maintains a list of DeathData instances, and of
+// Births instances, and is able to avoid additional (redundant/unnecessary)
+// allocations.
+//
+// Each thread maintains a list of data items specific to that thread in a
+// ThreadData instance (for that specific thread only). The two critical items
+// are lists of DeathData and Births instances. These lists are maintained in
+// STL maps, which are indexed by Location. As noted earlier, we can compare
+// locations very efficiently as we consider the underlying data (file,
+// function, line) to be atoms, and hence pointer comparison is used rather than
+// (slow) string comparisons.
+//
+// To provide a mechanism for iterating over all "known threads," which means
+// threads that have recorded a birth or a death, we create a singly linked list
+// of ThreadData instances. Each such instance maintains a pointer to the next
+// one. A static member of ThreadData provides a pointer to the first item on
+// this global list, and access via that all_thread_data_list_head_ item
+// requires the use of the list_lock_.
+// When new ThreadData instances is added to the global list, it is pre-pended,
+// which ensures that any prior acquisition of the list is valid (i.e., the
+// holder can iterate over it without fear of it changing, or the necessity of
+// using an additional lock. Iterations are actually pretty rare (used
+// primarilly for cleanup, or snapshotting data for display), so this lock has
+// very little global performance impact.
+//
+// The above description tries to define the high performance (run time)
+// portions of these classes. After gathering statistics, calls instigated
+// by visiting about:profiler will assemble and aggregate data for display. The
+// following data structures are used for producing such displays. They are
+// not performance critical, and their only major constraint is that they should
+// be able to run concurrently with ongoing augmentation of the birth and death
+// data.
+//
+// This header also exports collection of classes that provide "snapshotted"
+// representations of the core tracked_objects:: classes. These snapshotted
+// representations are designed for safe transmission of the tracked_objects::
+// data across process boundaries. Each consists of:
+// (1) a default constructor, to support the IPC serialization macros,
+// (2) a constructor that extracts data from the type being snapshotted, and
+// (3) the snapshotted data.
+//
+// For a given birth location, information about births is spread across data
+// structures that are asynchronously changing on various threads. For
+// serialization and display purposes, we need to construct TaskSnapshot
+// instances for each combination of birth thread, death thread, and location,
+// along with the count of such lifetimes. We gather such data into a
+// TaskSnapshot instances, so that such instances can be sorted and
+// aggregated (and remain frozen during our processing).
+//
+// The ProcessDataSnapshot struct is a serialized representation of the list
+// of ThreadData objects for a process. It holds a set of TaskSnapshots
+// and tracks parent/child relationships for the executed tasks. The statistics
+// in a snapshot are gathered asynhcronously relative to their ongoing updates.
+// It is possible, though highly unlikely, that stats could be incorrectly
+// recorded by this process (all data is held in 32 bit ints, but we are not
+// atomically collecting all data, so we could have count that does not, for
+// example, match with the number of durations we accumulated). The advantage
+// to having fast (non-atomic) updates of the data outweighs the minimal risk of
+// a singular corrupt statistic snapshot (only the snapshot could be corrupt,
+// not the underlying and ongoing statistic). In constrast, pointer data that
+// is accessed during snapshotting is completely invariant, and hence is
+// perfectly acquired (i.e., no potential corruption, and no risk of a bad
+// memory reference).
+//
+// TODO(jar): We can implement a Snapshot system that *tries* to grab the
+// snapshots on the source threads *when* they have MessageLoops available
+// (worker threads don't have message loops generally, and hence gathering from
+// them will continue to be asynchronous). We had an implementation of this in
+// the past, but the difficulty is dealing with message loops being terminated.
+// We can *try* to spam the available threads via some message loop proxy to
+// achieve this feat, and it *might* be valuable when we are colecting data for
+// upload via UMA (where correctness of data may be more significant than for a
+// single screen of about:profiler).
+//
+// TODO(jar): We should support (optionally) the recording of parent-child
+// relationships for tasks. This should be done by detecting what tasks are
+// Born during the running of a parent task. The resulting data can be used by
+// a smarter profiler to aggregate the cost of a series of child tasks into
+// the ancestor task. It can also be used to illuminate what child or parent is
+// related to each task.
+//
+// TODO(jar): We need to store DataCollections, and provide facilities for
+// taking the difference between two gathered DataCollections. For now, we're
+// just adding a hack that Reset()s to zero all counts and stats. This is also
+// done in a slighly thread-unsafe fashion, as the resetting is done
+// asynchronously relative to ongoing updates (but all data is 32 bit in size).
+// For basic profiling, this will work "most of the time," and should be
+// sufficient... but storing away DataCollections is the "right way" to do this.
+// We'll accomplish this via JavaScript storage of snapshots, and then we'll
+// remove the Reset() methods. We may also need a short-term-max value in
+// DeathData that is reset (as synchronously as possible) during each snapshot.
+// This will facilitate displaying a max value for each snapshot period.
+
+namespace tracked_objects {
+
+//------------------------------------------------------------------------------
+// For a specific thread, and a specific birth place, the collection of all
+// death info (with tallies for each death thread, to prevent access conflicts).
+class ThreadData;
+class BASE_EXPORT BirthOnThread {
+ public:
+ BirthOnThread(const Location& location, const ThreadData& current);
+
+ const Location location() const { return location_; }
+ const ThreadData* birth_thread() const { return birth_thread_; }
+
+ private:
+ // File/lineno of birth. This defines the essence of the task, as the context
+ // of the birth (construction) often tell what the item is for. This field
+ // is const, and hence safe to access from any thread.
+ const Location location_;
+
+ // The thread that records births into this object. Only this thread is
+ // allowed to update birth_count_ (which changes over time).
+ const ThreadData* const birth_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(BirthOnThread);
+};
+
+//------------------------------------------------------------------------------
+// A "snapshotted" representation of the BirthOnThread class.
+
+struct BASE_EXPORT BirthOnThreadSnapshot {
+ BirthOnThreadSnapshot();
+ explicit BirthOnThreadSnapshot(const BirthOnThread& birth);
+ ~BirthOnThreadSnapshot();
+
+ LocationSnapshot location;
+ std::string thread_name;
+};
+
+//------------------------------------------------------------------------------
+// A class for accumulating counts of births (without bothering with a map<>).
+
+class BASE_EXPORT Births: public BirthOnThread {
+ public:
+ Births(const Location& location, const ThreadData& current);
+
+ int birth_count() const;
+
+ // When we have a birth we update the count for this birthplace.
+ void RecordBirth();
+
+ // When a birthplace is changed (updated), we need to decrement the counter
+ // for the old instance.
+ void ForgetBirth();
+
+ // Hack to quickly reset all counts to zero.
+ void Clear();
+
+ private:
+ // The number of births on this thread for our location_.
+ int birth_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(Births);
+};
+
+//------------------------------------------------------------------------------
+// Basic info summarizing multiple destructions of a tracked object with a
+// single birthplace (fixed Location). Used both on specific threads, and also
+// in snapshots when integrating assembled data.
+
+class BASE_EXPORT DeathData {
+ public:
+ // Default initializer.
+ DeathData();
+
+ // When deaths have not yet taken place, and we gather data from all the
+ // threads, we create DeathData stats that tally the number of births without
+ // a corresponding death.
+ explicit DeathData(int count);
+
+ // Update stats for a task destruction (death) that had a Run() time of
+ // |duration|, and has had a queueing delay of |queue_duration|.
+ void RecordDeath(const int32 queue_duration,
+ const int32 run_duration,
+ int random_number);
+
+ // Metrics accessors, used only for serialization and in tests.
+ int count() const;
+ int32 run_duration_sum() const;
+ int32 run_duration_max() const;
+ int32 run_duration_sample() const;
+ int32 queue_duration_sum() const;
+ int32 queue_duration_max() const;
+ int32 queue_duration_sample() const;
+
+ // Reset the max values to zero.
+ void ResetMax();
+
+ // Reset all tallies to zero. This is used as a hack on realtime data.
+ void Clear();
+
+ private:
+ // Members are ordered from most regularly read and updated, to least
+ // frequently used. This might help a bit with cache lines.
+ // Number of runs seen (divisor for calculating averages).
+ int count_;
+ // Basic tallies, used to compute averages.
+ int32 run_duration_sum_;
+ int32 queue_duration_sum_;
+ // Max values, used by local visualization routines. These are often read,
+ // but rarely updated.
+ int32 run_duration_max_;
+ int32 queue_duration_max_;
+ // Samples, used by crowd sourcing gatherers. These are almost never read,
+ // and rarely updated.
+ int32 run_duration_sample_;
+ int32 queue_duration_sample_;
+};
+
+//------------------------------------------------------------------------------
+// A "snapshotted" representation of the DeathData class.
+
+struct BASE_EXPORT DeathDataSnapshot {
+ DeathDataSnapshot();
+ explicit DeathDataSnapshot(const DeathData& death_data);
+ ~DeathDataSnapshot();
+
+ int count;
+ int32 run_duration_sum;
+ int32 run_duration_max;
+ int32 run_duration_sample;
+ int32 queue_duration_sum;
+ int32 queue_duration_max;
+ int32 queue_duration_sample;
+};
+
+//------------------------------------------------------------------------------
+// A temporary collection of data that can be sorted and summarized. It is
+// gathered (carefully) from many threads. Instances are held in arrays and
+// processed, filtered, and rendered.
+// The source of this data was collected on many threads, and is asynchronously
+// changing. The data in this instance is not asynchronously changing.
+
+struct BASE_EXPORT TaskSnapshot {
+ TaskSnapshot();
+ TaskSnapshot(const BirthOnThread& birth,
+ const DeathData& death_data,
+ const std::string& death_thread_name);
+ ~TaskSnapshot();
+
+ BirthOnThreadSnapshot birth;
+ DeathDataSnapshot death_data;
+ std::string death_thread_name;
+};
+
+//------------------------------------------------------------------------------
+// For each thread, we have a ThreadData that stores all tracking info generated
+// on this thread. This prevents the need for locking as data accumulates.
+// We use ThreadLocalStorage to quickly identfy the current ThreadData context.
+// We also have a linked list of ThreadData instances, and that list is used to
+// harvest data from all existing instances.
+
+struct ProcessDataSnapshot;
+class BASE_EXPORT ThreadData {
+ public:
+ // Current allowable states of the tracking system. The states can vary
+ // between ACTIVE and DEACTIVATED, but can never go back to UNINITIALIZED.
+ enum Status {
+ UNINITIALIZED, // PRistine, link-time state before running.
+ DORMANT_DURING_TESTS, // Only used during testing.
+ DEACTIVATED, // No longer recording profling.
+ PROFILING_ACTIVE, // Recording profiles (no parent-child links).
+ PROFILING_CHILDREN_ACTIVE, // Fully active, recording parent-child links.
+ };
+
+ typedef std::map<Location, Births*> BirthMap;
+ typedef std::map<const Births*, DeathData> DeathMap;
+ typedef std::pair<const Births*, const Births*> ParentChildPair;
+ typedef std::set<ParentChildPair> ParentChildSet;
+ typedef std::stack<const Births*> ParentStack;
+
+ // Initialize the current thread context with a new instance of ThreadData.
+ // This is used by all threads that have names, and should be explicitly
+ // set *before* any births on the threads have taken place. It is generally
+ // only used by the message loop, which has a well defined thread name.
+ static void InitializeThreadContext(const std::string& suggested_name);
+
+ // Using Thread Local Store, find the current instance for collecting data.
+ // If an instance does not exist, construct one (and remember it for use on
+ // this thread.
+ // This may return NULL if the system is disabled for any reason.
+ static ThreadData* Get();
+
+ // Fills |process_data| with all the recursive results in our process.
+ // During the scavenging, if |reset_max| is true, then the DeathData instances
+ // max-values are reset to zero during this scan.
+ static void Snapshot(bool reset_max, ProcessDataSnapshot* process_data);
+
+ // Finds (or creates) a place to count births from the given location in this
+ // thread, and increment that tally.
+ // TallyABirthIfActive will returns NULL if the birth cannot be tallied.
+ static Births* TallyABirthIfActive(const Location& location);
+
+ // Records the end of a timed run of an object. The |completed_task| contains
+ // a pointer to a Births, the time_posted, and a delayed_start_time if any.
+ // The |start_of_run| indicates when we started to perform the run of the
+ // task. The delayed_start_time is non-null for tasks that were posted as
+ // delayed tasks, and it indicates when the task should have run (i.e., when
+ // it should have posted out of the timer queue, and into the work queue.
+ // The |end_of_run| was just obtained by a call to Now() (just after the task
+ // finished). It is provided as an argument to help with testing.
+ static void TallyRunOnNamedThreadIfTracking(
+ const base::TrackingInfo& completed_task,
+ const TrackedTime& start_of_run,
+ const TrackedTime& end_of_run);
+
+ // Record the end of a timed run of an object. The |birth| is the record for
+ // the instance, the |time_posted| records that instant, which is presumed to
+ // be when the task was posted into a queue to run on a worker thread.
+ // The |start_of_run| is when the worker thread started to perform the run of
+ // the task.
+ // The |end_of_run| was just obtained by a call to Now() (just after the task
+ // finished).
+ static void TallyRunOnWorkerThreadIfTracking(
+ const Births* birth,
+ const TrackedTime& time_posted,
+ const TrackedTime& start_of_run,
+ const TrackedTime& end_of_run);
+
+ // Record the end of execution in region, generally corresponding to a scope
+ // being exited.
+ static void TallyRunInAScopedRegionIfTracking(
+ const Births* birth,
+ const TrackedTime& start_of_run,
+ const TrackedTime& end_of_run);
+
+ const std::string& thread_name() const { return thread_name_; }
+
+ // Hack: asynchronously clear all birth counts and death tallies data values
+ // in all ThreadData instances. The numerical (zeroing) part is done without
+ // use of a locks or atomics exchanges, and may (for int64 values) produce
+ // bogus counts VERY rarely.
+ static void ResetAllThreadData();
+
+ // Initializes all statics if needed (this initialization call should be made
+ // while we are single threaded). Returns false if unable to initialize.
+ static bool Initialize();
+
+ // Sets internal status_.
+ // If |status| is false, then status_ is set to DEACTIVATED.
+ // If |status| is true, then status_ is set to, PROFILING_ACTIVE, or
+ // PROFILING_CHILDREN_ACTIVE.
+ // If tracking is not compiled in, this function will return false.
+ // If parent-child tracking is not compiled in, then an attempt to set the
+ // status to PROFILING_CHILDREN_ACTIVE will only result in a status of
+ // PROFILING_ACTIVE (i.e., it can't be set to a higher level than what is
+ // compiled into the binary, and parent-child tracking at the
+ // PROFILING_CHILDREN_ACTIVE level might not be compiled in).
+ static bool InitializeAndSetTrackingStatus(Status status);
+
+ static Status status();
+
+ // Indicate if any sort of profiling is being done (i.e., we are more than
+ // DEACTIVATED).
+ static bool TrackingStatus();
+
+ // For testing only, indicate if the status of parent-child tracking is turned
+ // on. This is currently a compiled option, atop TrackingStatus().
+ static bool TrackingParentChildStatus();
+
+ // Special versions of Now() for getting times at start and end of a tracked
+ // run. They are super fast when tracking is disabled, and have some internal
+ // side effects when we are tracking, so that we can deduce the amount of time
+ // accumulated outside of execution of tracked runs.
+ // The task that will be tracked is passed in as |parent| so that parent-child
+ // relationships can be (optionally) calculated.
+ static TrackedTime NowForStartOfRun(const Births* parent);
+ static TrackedTime NowForEndOfRun();
+
+ // Provide a time function that does nothing (runs fast) when we don't have
+ // the profiler enabled. It will generally be optimized away when it is
+ // ifdef'ed to be small enough (allowing the profiler to be "compiled out" of
+ // the code).
+ static TrackedTime Now();
+
+ // Use the function |now| to provide current times, instead of calling the
+ // TrackedTime::Now() function. Since this alternate function is being used,
+ // the other time arguments (used for calculating queueing delay) will be
+ // ignored.
+ static void SetAlternateTimeSource(NowFunction* now);
+
+ // This function can be called at process termination to validate that thread
+ // cleanup routines have been called for at least some number of named
+ // threads.
+ static void EnsureCleanupWasCalled(int major_threads_shutdown_count);
+
+ private:
+ // Allow only tests to call ShutdownSingleThreadedCleanup. We NEVER call it
+ // in production code.
+ // TODO(jar): Make this a friend in DEBUG only, so that the optimizer has a
+ // better change of optimizing (inlining? etc.) private methods (knowing that
+ // there will be no need for an external entry point).
+ friend class TrackedObjectsTest;
+ FRIEND_TEST_ALL_PREFIXES(TrackedObjectsTest, MinimalStartupShutdown);
+ FRIEND_TEST_ALL_PREFIXES(TrackedObjectsTest, TinyStartupShutdown);
+ FRIEND_TEST_ALL_PREFIXES(TrackedObjectsTest, ParentChildTest);
+ friend class TrackedTimeTest;
+
+ typedef std::map<const BirthOnThread*, int> BirthCountMap;
+
+ // Worker thread construction creates a name since there is none.
+ explicit ThreadData(int thread_number);
+
+ // Message loop based construction should provide a name.
+ explicit ThreadData(const std::string& suggested_name);
+
+ ~ThreadData();
+
+ // Push this instance to the head of all_thread_data_list_head_, linking it to
+ // the previous head. This is performed after each construction, and leaves
+ // the instance permanently on that list.
+ void PushToHeadOfList();
+
+ // (Thread safe) Get start of list of all ThreadData instances using the lock.
+ static ThreadData* first();
+
+ // Iterate through the null terminated list of ThreadData instances.
+ ThreadData* next() const;
+
+
+ // In this thread's data, record a new birth.
+ Births* TallyABirth(const Location& location);
+
+ // Find a place to record a death on this thread.
+ void TallyADeath(const Births& birth, int32 queue_duration, int32 duration);
+
+ // Snapshot (under a lock) the profiled data for the tasks in each ThreadData
+ // instance. Also updates the |birth_counts| tally for each task to keep
+ // track of the number of living instances of the task. If |reset_max| is
+ // true, then the max values in each DeathData instance are reset during the
+ // scan.
+ static void SnapshotAllExecutedTasks(bool reset_max,
+ ProcessDataSnapshot* process_data,
+ BirthCountMap* birth_counts);
+
+ // Snapshots (under a lock) the profiled data for the tasks for this thread
+ // and writes all of the executed tasks' data -- i.e. the data for the tasks
+ // with with entries in the death_map_ -- into |process_data|. Also updates
+ // the |birth_counts| tally for each task to keep track of the number of
+ // living instances of the task -- that is, each task maps to the number of
+ // births for the task that have not yet been balanced by a death. If
+ // |reset_max| is true, then the max values in each DeathData instance are
+ // reset during the scan.
+ void SnapshotExecutedTasks(bool reset_max,
+ ProcessDataSnapshot* process_data,
+ BirthCountMap* birth_counts);
+
+ // Using our lock, make a copy of the specified maps. This call may be made
+ // on non-local threads, which necessitate the use of the lock to prevent
+ // the map(s) from being reallocaed while they are copied. If |reset_max| is
+ // true, then, just after we copy the DeathMap, we will set the max values to
+ // zero in the active DeathMap (not the snapshot).
+ void SnapshotMaps(bool reset_max,
+ BirthMap* birth_map,
+ DeathMap* death_map,
+ ParentChildSet* parent_child_set);
+
+ // Using our lock to protect the iteration, Clear all birth and death data.
+ void Reset();
+
+ // This method is called by the TLS system when a thread terminates.
+ // The argument may be NULL if this thread has never tracked a birth or death.
+ static void OnThreadTermination(void* thread_data);
+
+ // This method should be called when a worker thread terminates, so that we
+ // can save all the thread data into a cache of reusable ThreadData instances.
+ void OnThreadTerminationCleanup();
+
+ // Cleans up data structures, and returns statics to near pristine (mostly
+ // uninitialized) state. If there is any chance that other threads are still
+ // using the data structures, then the |leak| argument should be passed in as
+ // true, and the data structures (birth maps, death maps, ThreadData
+ // insntances, etc.) will be leaked and not deleted. If you have joined all
+ // threads since the time that InitializeAndSetTrackingStatus() was called,
+ // then you can pass in a |leak| value of false, and this function will
+ // delete recursively all data structures, starting with the list of
+ // ThreadData instances.
+ static void ShutdownSingleThreadedCleanup(bool leak);
+
+ // When non-null, this specifies an external function that supplies monotone
+ // increasing time functcion.
+ static NowFunction* now_function_;
+
+ // We use thread local store to identify which ThreadData to interact with.
+ static base::ThreadLocalStorage::StaticSlot tls_index_;
+
+ // List of ThreadData instances for use with worker threads. When a worker
+ // thread is done (terminated), we push it onto this llist. When a new worker
+ // thread is created, we first try to re-use a ThreadData instance from the
+ // list, and if none are available, construct a new one.
+ // This is only accessed while list_lock_ is held.
+ static ThreadData* first_retired_worker_;
+
+ // Link to the most recently created instance (starts a null terminated list).
+ // The list is traversed by about:profiler when it needs to snapshot data.
+ // This is only accessed while list_lock_ is held.
+ static ThreadData* all_thread_data_list_head_;
+
+ // The next available worker thread number. This should only be accessed when
+ // the list_lock_ is held.
+ static int worker_thread_data_creation_count_;
+
+ // The number of times TLS has called us back to cleanup a ThreadData
+ // instance. This is only accessed while list_lock_ is held.
+ static int cleanup_count_;
+
+ // Incarnation sequence number, indicating how many times (during unittests)
+ // we've either transitioned out of UNINITIALIZED, or into that state. This
+ // value is only accessed while the list_lock_ is held.
+ static int incarnation_counter_;
+
+ // Protection for access to all_thread_data_list_head_, and to
+ // unregistered_thread_data_pool_. This lock is leaked at shutdown.
+ // The lock is very infrequently used, so we can afford to just make a lazy
+ // instance and be safe.
+ static base::LazyInstance<base::Lock>::Leaky list_lock_;
+
+ // We set status_ to SHUTDOWN when we shut down the tracking service.
+ static Status status_;
+
+ // Link to next instance (null terminated list). Used to globally track all
+ // registered instances (corresponds to all registered threads where we keep
+ // data).
+ ThreadData* next_;
+
+ // Pointer to another ThreadData instance for a Worker-Thread that has been
+ // retired (its thread was terminated). This value is non-NULL only for a
+ // retired ThreadData associated with a Worker-Thread.
+ ThreadData* next_retired_worker_;
+
+ // The name of the thread that is being recorded. If this thread has no
+ // message_loop, then this is a worker thread, with a sequence number postfix.
+ std::string thread_name_;
+
+ // Indicate if this is a worker thread, and the ThreadData contexts should be
+ // stored in the unregistered_thread_data_pool_ when not in use.
+ // Value is zero when it is not a worker thread. Value is a positive integer
+ // corresponding to the created thread name if it is a worker thread.
+ int worker_thread_number_;
+
+ // A map used on each thread to keep track of Births on this thread.
+ // This map should only be accessed on the thread it was constructed on.
+ // When a snapshot is needed, this structure can be locked in place for the
+ // duration of the snapshotting activity.
+ BirthMap birth_map_;
+
+ // Similar to birth_map_, this records informations about death of tracked
+ // instances (i.e., when a tracked instance was destroyed on this thread).
+ // It is locked before changing, and hence other threads may access it by
+ // locking before reading it.
+ DeathMap death_map_;
+
+ // A set of parents that created children tasks on this thread. Each pair
+ // corresponds to potentially non-local Births (location and thread), and a
+ // local Births (that took place on this thread).
+ ParentChildSet parent_child_set_;
+
+ // Lock to protect *some* access to BirthMap and DeathMap. The maps are
+ // regularly read and written on this thread, but may only be read from other
+ // threads. To support this, we acquire this lock if we are writing from this
+ // thread, or reading from another thread. For reading from this thread we
+ // don't need a lock, as there is no potential for a conflict since the
+ // writing is only done from this thread.
+ mutable base::Lock map_lock_;
+
+ // The stack of parents that are currently being profiled. This includes only
+ // tasks that have started a timer recently via NowForStartOfRun(), but not
+ // yet concluded with a NowForEndOfRun(). Usually this stack is one deep, but
+ // if a scoped region is profiled, or <sigh> a task runs a nested-message
+ // loop, then the stack can grow larger. Note that we don't try to deduct
+ // time in nested porfiles, as our current timer is based on wall-clock time,
+ // and not CPU time (and we're hopeful that nested timing won't be a
+ // significant additional cost).
+ ParentStack parent_stack_;
+
+ // A random number that we used to select decide which sample to keep as a
+ // representative sample in each DeathData instance. We can't start off with
+ // much randomness (because we can't call RandInt() on all our threads), so
+ // we stir in more and more as we go.
+ int32 random_number_;
+
+ // Record of what the incarnation_counter_ was when this instance was created.
+ // If the incarnation_counter_ has changed, then we avoid pushing into the
+ // pool (this is only critical in tests which go through multiple
+ // incarnations).
+ int incarnation_count_for_pool_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadData);
+};
+
+//------------------------------------------------------------------------------
+// A snapshotted representation of a (parent, child) task pair, for tracking
+// hierarchical profiles.
+
+struct BASE_EXPORT ParentChildPairSnapshot {
+ public:
+ ParentChildPairSnapshot();
+ explicit ParentChildPairSnapshot(
+ const ThreadData::ParentChildPair& parent_child);
+ ~ParentChildPairSnapshot();
+
+ BirthOnThreadSnapshot parent;
+ BirthOnThreadSnapshot child;
+};
+
+//------------------------------------------------------------------------------
+// A snapshotted representation of the list of ThreadData objects for a process.
+
+struct BASE_EXPORT ProcessDataSnapshot {
+ public:
+ ProcessDataSnapshot();
+ ~ProcessDataSnapshot();
+
+ std::vector<TaskSnapshot> tasks;
+ std::vector<ParentChildPairSnapshot> descendants;
+ int process_id;
+};
+
+} // namespace tracked_objects
+
+#endif // BASE_TRACKED_OBJECTS_H_
diff --git a/src/base/tracked_objects_unittest.cc b/src/base/tracked_objects_unittest.cc
new file mode 100644
index 0000000..1e64508
--- /dev/null
+++ b/src/base/tracked_objects_unittest.cc
@@ -0,0 +1,581 @@
+// Copyright (c) 2012 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.
+
+// Test of classes in the tracked_objects.h classes.
+
+#include "base/tracked_objects.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/process_util.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+const int kLineNumber = 1776;
+const char kFile[] = "FixedUnitTestFileName";
+const char kWorkerThreadName[] = "WorkerThread-1";
+const char kMainThreadName[] = "SomeMainThreadName";
+const char kStillAlive[] = "Still_Alive";
+
+namespace tracked_objects {
+
+class TrackedObjectsTest : public testing::Test {
+ protected:
+ TrackedObjectsTest() {
+ // On entry, leak any database structures in case they are still in use by
+ // prior threads.
+ ThreadData::ShutdownSingleThreadedCleanup(true);
+ }
+
+ virtual ~TrackedObjectsTest() {
+ // We should not need to leak any structures we create, since we are
+ // single threaded, and carefully accounting for items.
+ ThreadData::ShutdownSingleThreadedCleanup(false);
+ }
+
+ // Reset the profiler state.
+ void Reset() {
+ ThreadData::ShutdownSingleThreadedCleanup(false);
+ }
+
+ // Simulate a birth on the thread named |thread_name|, at the given
+ // |location|.
+ void TallyABirth(const Location& location, const std::string& thread_name) {
+ // If the |thread_name| is empty, we don't initialize system with a thread
+ // name, so we're viewed as a worker thread.
+ if (!thread_name.empty())
+ ThreadData::InitializeThreadContext(kMainThreadName);
+
+ // Do not delete |birth|. We don't own it.
+ Births* birth = ThreadData::TallyABirthIfActive(location);
+
+ if (ThreadData::status() == ThreadData::DEACTIVATED)
+ EXPECT_EQ(reinterpret_cast<Births*>(NULL), birth);
+ else
+ EXPECT_NE(reinterpret_cast<Births*>(NULL), birth);
+ }
+
+ // Helper function to verify the most common test expectations.
+ void ExpectSimpleProcessData(const ProcessDataSnapshot& process_data,
+ const std::string& function_name,
+ const std::string& birth_thread,
+ const std::string& death_thread,
+ int count,
+ int run_ms,
+ int queue_ms) {
+ ASSERT_EQ(1u, process_data.tasks.size());
+
+ EXPECT_EQ(kFile, process_data.tasks[0].birth.location.file_name);
+ EXPECT_EQ(function_name,
+ process_data.tasks[0].birth.location.function_name);
+ EXPECT_EQ(kLineNumber, process_data.tasks[0].birth.location.line_number);
+
+ EXPECT_EQ(birth_thread, process_data.tasks[0].birth.thread_name);
+
+ EXPECT_EQ(count, process_data.tasks[0].death_data.count);
+ EXPECT_EQ(count * run_ms,
+ process_data.tasks[0].death_data.run_duration_sum);
+ EXPECT_EQ(run_ms, process_data.tasks[0].death_data.run_duration_max);
+ EXPECT_EQ(run_ms, process_data.tasks[0].death_data.run_duration_sample);
+ EXPECT_EQ(count * queue_ms,
+ process_data.tasks[0].death_data.queue_duration_sum);
+ EXPECT_EQ(queue_ms, process_data.tasks[0].death_data.queue_duration_max);
+ EXPECT_EQ(queue_ms, process_data.tasks[0].death_data.queue_duration_sample);
+
+ EXPECT_EQ(death_thread, process_data.tasks[0].death_thread_name);
+
+ EXPECT_EQ(0u, process_data.descendants.size());
+
+ EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id);
+ }
+};
+
+TEST_F(TrackedObjectsTest, MinimalStartupShutdown) {
+ // Minimal test doesn't even create any tasks.
+ if (!ThreadData::InitializeAndSetTrackingStatus(
+ ThreadData::PROFILING_CHILDREN_ACTIVE))
+ return;
+
+ EXPECT_FALSE(ThreadData::first()); // No activity even on this thread.
+ ThreadData* data = ThreadData::Get();
+ EXPECT_TRUE(ThreadData::first()); // Now class was constructed.
+ ASSERT_TRUE(data);
+ EXPECT_FALSE(data->next());
+ EXPECT_EQ(data, ThreadData::Get());
+ ThreadData::BirthMap birth_map;
+ ThreadData::DeathMap death_map;
+ ThreadData::ParentChildSet parent_child_set;
+ data->SnapshotMaps(false, &birth_map, &death_map, &parent_child_set);
+ EXPECT_EQ(0u, birth_map.size());
+ EXPECT_EQ(0u, death_map.size());
+ EXPECT_EQ(0u, parent_child_set.size());
+
+ // Clean up with no leaking.
+ Reset();
+
+ // Do it again, just to be sure we reset state completely.
+ EXPECT_TRUE(ThreadData::InitializeAndSetTrackingStatus(
+ ThreadData::PROFILING_CHILDREN_ACTIVE));
+ EXPECT_FALSE(ThreadData::first()); // No activity even on this thread.
+ data = ThreadData::Get();
+ EXPECT_TRUE(ThreadData::first()); // Now class was constructed.
+ ASSERT_TRUE(data);
+ EXPECT_FALSE(data->next());
+ EXPECT_EQ(data, ThreadData::Get());
+ birth_map.clear();
+ death_map.clear();
+ parent_child_set.clear();
+ data->SnapshotMaps(false, &birth_map, &death_map, &parent_child_set);
+ EXPECT_EQ(0u, birth_map.size());
+ EXPECT_EQ(0u, death_map.size());
+ EXPECT_EQ(0u, parent_child_set.size());
+}
+
+TEST_F(TrackedObjectsTest, TinyStartupShutdown) {
+ if (!ThreadData::InitializeAndSetTrackingStatus(
+ ThreadData::PROFILING_CHILDREN_ACTIVE))
+ return;
+
+ // Instigate tracking on a single tracked object, on our thread.
+ const char kFunction[] = "TinyStartupShutdown";
+ Location location(kFunction, kFile, kLineNumber, NULL);
+ Births* first_birth = ThreadData::TallyABirthIfActive(location);
+
+ ThreadData* data = ThreadData::first();
+ ASSERT_TRUE(data);
+ EXPECT_FALSE(data->next());
+ EXPECT_EQ(data, ThreadData::Get());
+ ThreadData::BirthMap birth_map;
+ ThreadData::DeathMap death_map;
+ ThreadData::ParentChildSet parent_child_set;
+ data->SnapshotMaps(false, &birth_map, &death_map, &parent_child_set);
+ EXPECT_EQ(1u, birth_map.size()); // 1 birth location.
+ EXPECT_EQ(1, birth_map.begin()->second->birth_count()); // 1 birth.
+ EXPECT_EQ(0u, death_map.size()); // No deaths.
+ EXPECT_EQ(0u, parent_child_set.size()); // No children.
+
+
+ // Now instigate another birth, while we are timing the run of the first
+ // execution.
+ ThreadData::NowForStartOfRun(first_birth);
+ // Create a child (using the same birth location).
+ // TrackingInfo will call TallyABirth() during construction.
+ base::TimeTicks kBogusBirthTime;
+ base::TrackingInfo pending_task(location, kBogusBirthTime);
+ TrackedTime start_time(pending_task.time_posted);
+ // Finally conclude the outer run.
+ TrackedTime end_time = ThreadData::NowForEndOfRun();
+ ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, start_time,
+ end_time);
+
+ birth_map.clear();
+ death_map.clear();
+ parent_child_set.clear();
+ data->SnapshotMaps(false, &birth_map, &death_map, &parent_child_set);
+ EXPECT_EQ(1u, birth_map.size()); // 1 birth location.
+ EXPECT_EQ(2, birth_map.begin()->second->birth_count()); // 2 births.
+ EXPECT_EQ(1u, death_map.size()); // 1 location.
+ EXPECT_EQ(1, death_map.begin()->second.count()); // 1 death.
+ if (ThreadData::TrackingParentChildStatus()) {
+ EXPECT_EQ(1u, parent_child_set.size()); // 1 child.
+ EXPECT_EQ(parent_child_set.begin()->first,
+ parent_child_set.begin()->second);
+ } else {
+ EXPECT_EQ(0u, parent_child_set.size()); // no stats.
+ }
+
+ // The births were at the same location as the one known death.
+ EXPECT_EQ(birth_map.begin()->second, death_map.begin()->first);
+
+ ProcessDataSnapshot process_data;
+ ThreadData::Snapshot(false, &process_data);
+
+ const int32 time_elapsed = (end_time - start_time).InMilliseconds();
+ ASSERT_EQ(1u, process_data.tasks.size());
+ EXPECT_EQ(kFile, process_data.tasks[0].birth.location.file_name);
+ EXPECT_EQ(kFunction, process_data.tasks[0].birth.location.function_name);
+ EXPECT_EQ(kLineNumber, process_data.tasks[0].birth.location.line_number);
+ EXPECT_EQ(kWorkerThreadName, process_data.tasks[0].birth.thread_name);
+ EXPECT_EQ(1, process_data.tasks[0].death_data.count);
+ EXPECT_EQ(time_elapsed, process_data.tasks[0].death_data.run_duration_sum);
+ EXPECT_EQ(time_elapsed, process_data.tasks[0].death_data.run_duration_max);
+ EXPECT_EQ(time_elapsed, process_data.tasks[0].death_data.run_duration_sample);
+ EXPECT_EQ(0, process_data.tasks[0].death_data.queue_duration_sum);
+ EXPECT_EQ(0, process_data.tasks[0].death_data.queue_duration_max);
+ EXPECT_EQ(0, process_data.tasks[0].death_data.queue_duration_sample);
+ EXPECT_EQ(kWorkerThreadName, process_data.tasks[0].death_thread_name);
+
+ if (ThreadData::TrackingParentChildStatus()) {
+ ASSERT_EQ(1u, process_data.descendants.size());
+ EXPECT_EQ(kFile, process_data.descendants[0].parent.location.file_name);
+ EXPECT_EQ(kFunction,
+ process_data.descendants[0].parent.location.function_name);
+ EXPECT_EQ(kLineNumber,
+ process_data.descendants[0].parent.location.line_number);
+ EXPECT_EQ(kWorkerThreadName,
+ process_data.descendants[0].parent.thread_name);
+ EXPECT_EQ(kFile, process_data.descendants[0].child.location.file_name);
+ EXPECT_EQ(kFunction,
+ process_data.descendants[0].child.location.function_name);
+ EXPECT_EQ(kLineNumber,
+ process_data.descendants[0].child.location.line_number);
+ EXPECT_EQ(kWorkerThreadName, process_data.descendants[0].child.thread_name);
+ } else {
+ EXPECT_EQ(0u, process_data.descendants.size());
+ }
+}
+
+TEST_F(TrackedObjectsTest, DeathDataTest) {
+ if (!ThreadData::InitializeAndSetTrackingStatus(
+ ThreadData::PROFILING_CHILDREN_ACTIVE))
+ return;
+
+ scoped_ptr<DeathData> data(new DeathData());
+ ASSERT_NE(data, reinterpret_cast<DeathData*>(NULL));
+ EXPECT_EQ(data->run_duration_sum(), 0);
+ EXPECT_EQ(data->run_duration_sample(), 0);
+ EXPECT_EQ(data->queue_duration_sum(), 0);
+ EXPECT_EQ(data->queue_duration_sample(), 0);
+ EXPECT_EQ(data->count(), 0);
+
+ int32 run_ms = 42;
+ int32 queue_ms = 8;
+
+ const int kUnrandomInt = 0; // Fake random int that ensure we sample data.
+ data->RecordDeath(queue_ms, run_ms, kUnrandomInt);
+ EXPECT_EQ(data->run_duration_sum(), run_ms);
+ EXPECT_EQ(data->run_duration_sample(), run_ms);
+ EXPECT_EQ(data->queue_duration_sum(), queue_ms);
+ EXPECT_EQ(data->queue_duration_sample(), queue_ms);
+ EXPECT_EQ(data->count(), 1);
+
+ data->RecordDeath(queue_ms, run_ms, kUnrandomInt);
+ EXPECT_EQ(data->run_duration_sum(), run_ms + run_ms);
+ EXPECT_EQ(data->run_duration_sample(), run_ms);
+ EXPECT_EQ(data->queue_duration_sum(), queue_ms + queue_ms);
+ EXPECT_EQ(data->queue_duration_sample(), queue_ms);
+ EXPECT_EQ(data->count(), 2);
+
+ DeathDataSnapshot snapshot(*data);
+ EXPECT_EQ(2, snapshot.count);
+ EXPECT_EQ(2 * run_ms, snapshot.run_duration_sum);
+ EXPECT_EQ(run_ms, snapshot.run_duration_max);
+ EXPECT_EQ(run_ms, snapshot.run_duration_sample);
+ EXPECT_EQ(2 * queue_ms, snapshot.queue_duration_sum);
+ EXPECT_EQ(queue_ms, snapshot.queue_duration_max);
+ EXPECT_EQ(queue_ms, snapshot.queue_duration_sample);
+}
+
+TEST_F(TrackedObjectsTest, DeactivatedBirthOnlyToSnapshotWorkerThread) {
+ // Start in the deactivated state.
+ if (!ThreadData::InitializeAndSetTrackingStatus(ThreadData::DEACTIVATED))
+ return;
+
+ const char kFunction[] = "DeactivatedBirthOnlyToSnapshotWorkerThread";
+ Location location(kFunction, kFile, kLineNumber, NULL);
+ TallyABirth(location, std::string());
+
+ ProcessDataSnapshot process_data;
+ ThreadData::Snapshot(false, &process_data);
+ EXPECT_EQ(0u, process_data.tasks.size());
+ EXPECT_EQ(0u, process_data.descendants.size());
+ EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id);
+}
+
+TEST_F(TrackedObjectsTest, DeactivatedBirthOnlyToSnapshotMainThread) {
+ // Start in the deactivated state.
+ if (!ThreadData::InitializeAndSetTrackingStatus(ThreadData::DEACTIVATED))
+ return;
+
+ const char kFunction[] = "DeactivatedBirthOnlyToSnapshotMainThread";
+ Location location(kFunction, kFile, kLineNumber, NULL);
+ TallyABirth(location, kMainThreadName);
+
+ ProcessDataSnapshot process_data;
+ ThreadData::Snapshot(false, &process_data);
+ EXPECT_EQ(0u, process_data.tasks.size());
+ EXPECT_EQ(0u, process_data.descendants.size());
+ EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id);
+}
+
+TEST_F(TrackedObjectsTest, BirthOnlyToSnapshotWorkerThread) {
+ if (!ThreadData::InitializeAndSetTrackingStatus(
+ ThreadData::PROFILING_CHILDREN_ACTIVE))
+ return;
+
+ const char kFunction[] = "BirthOnlyToSnapshotWorkerThread";
+ Location location(kFunction, kFile, kLineNumber, NULL);
+ TallyABirth(location, std::string());
+
+ ProcessDataSnapshot process_data;
+ ThreadData::Snapshot(false, &process_data);
+ ExpectSimpleProcessData(process_data, kFunction, kWorkerThreadName,
+ kStillAlive, 1, 0, 0);
+}
+
+TEST_F(TrackedObjectsTest, BirthOnlyToSnapshotMainThread) {
+ if (!ThreadData::InitializeAndSetTrackingStatus(
+ ThreadData::PROFILING_CHILDREN_ACTIVE))
+ return;
+
+ const char kFunction[] = "BirthOnlyToSnapshotMainThread";
+ Location location(kFunction, kFile, kLineNumber, NULL);
+ TallyABirth(location, kMainThreadName);
+
+ ProcessDataSnapshot process_data;
+ ThreadData::Snapshot(false, &process_data);
+ ExpectSimpleProcessData(process_data, kFunction, kMainThreadName, kStillAlive,
+ 1, 0, 0);
+}
+
+TEST_F(TrackedObjectsTest, LifeCycleToSnapshotMainThread) {
+ if (!ThreadData::InitializeAndSetTrackingStatus(
+ ThreadData::PROFILING_CHILDREN_ACTIVE))
+ return;
+
+ const char kFunction[] = "LifeCycleToSnapshotMainThread";
+ Location location(kFunction, kFile, kLineNumber, NULL);
+ TallyABirth(location, kMainThreadName);
+
+ const base::TimeTicks kTimePosted = base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(1);
+ const base::TimeTicks kDelayedStartTime = base::TimeTicks();
+ // TrackingInfo will call TallyABirth() during construction.
+ base::TrackingInfo pending_task(location, kDelayedStartTime);
+ pending_task.time_posted = kTimePosted; // Overwrite implied Now().
+
+ const TrackedTime kStartOfRun = TrackedTime() +
+ Duration::FromMilliseconds(5);
+ const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7);
+ ThreadData::TallyRunOnNamedThreadIfTracking(pending_task,
+ kStartOfRun, kEndOfRun);
+
+ ProcessDataSnapshot process_data;
+ ThreadData::Snapshot(false, &process_data);
+ ExpectSimpleProcessData(process_data, kFunction, kMainThreadName,
+ kMainThreadName, 1, 2, 4);
+}
+
+// We will deactivate tracking after the birth, and before the death, and
+// demonstrate that the lifecycle is completely tallied. This ensures that
+// our tallied births are matched by tallied deaths (except for when the
+// task is still running, or is queued).
+TEST_F(TrackedObjectsTest, LifeCycleMidDeactivatedToSnapshotMainThread) {
+ if (!ThreadData::InitializeAndSetTrackingStatus(
+ ThreadData::PROFILING_CHILDREN_ACTIVE))
+ return;
+
+ const char kFunction[] = "LifeCycleMidDeactivatedToSnapshotMainThread";
+ Location location(kFunction, kFile, kLineNumber, NULL);
+ TallyABirth(location, kMainThreadName);
+
+ const base::TimeTicks kTimePosted = base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(1);
+ const base::TimeTicks kDelayedStartTime = base::TimeTicks();
+ // TrackingInfo will call TallyABirth() during construction.
+ base::TrackingInfo pending_task(location, kDelayedStartTime);
+ pending_task.time_posted = kTimePosted; // Overwrite implied Now().
+
+ // Turn off tracking now that we have births.
+ EXPECT_TRUE(ThreadData::InitializeAndSetTrackingStatus(
+ ThreadData::DEACTIVATED));
+
+ const TrackedTime kStartOfRun = TrackedTime() +
+ Duration::FromMilliseconds(5);
+ const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7);
+ ThreadData::TallyRunOnNamedThreadIfTracking(pending_task,
+ kStartOfRun, kEndOfRun);
+
+ ProcessDataSnapshot process_data;
+ ThreadData::Snapshot(false, &process_data);
+ ExpectSimpleProcessData(process_data, kFunction, kMainThreadName,
+ kMainThreadName, 1, 2, 4);
+}
+
+// We will deactivate tracking before starting a life cycle, and neither
+// the birth nor the death will be recorded.
+TEST_F(TrackedObjectsTest, LifeCyclePreDeactivatedToSnapshotMainThread) {
+ // Start in the deactivated state.
+ if (!ThreadData::InitializeAndSetTrackingStatus(ThreadData::DEACTIVATED))
+ return;
+
+ const char kFunction[] = "LifeCyclePreDeactivatedToSnapshotMainThread";
+ Location location(kFunction, kFile, kLineNumber, NULL);
+ TallyABirth(location, kMainThreadName);
+
+ const base::TimeTicks kTimePosted = base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(1);
+ const base::TimeTicks kDelayedStartTime = base::TimeTicks();
+ // TrackingInfo will call TallyABirth() during construction.
+ base::TrackingInfo pending_task(location, kDelayedStartTime);
+ pending_task.time_posted = kTimePosted; // Overwrite implied Now().
+
+ const TrackedTime kStartOfRun = TrackedTime() +
+ Duration::FromMilliseconds(5);
+ const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7);
+ ThreadData::TallyRunOnNamedThreadIfTracking(pending_task,
+ kStartOfRun, kEndOfRun);
+
+ ProcessDataSnapshot process_data;
+ ThreadData::Snapshot(false, &process_data);
+ EXPECT_EQ(0u, process_data.tasks.size());
+ EXPECT_EQ(0u, process_data.descendants.size());
+ EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id);
+}
+
+TEST_F(TrackedObjectsTest, LifeCycleToSnapshotWorkerThread) {
+ if (!ThreadData::InitializeAndSetTrackingStatus(
+ ThreadData::PROFILING_CHILDREN_ACTIVE))
+ return;
+
+ const char kFunction[] = "LifeCycleToSnapshotWorkerThread";
+ Location location(kFunction, kFile, kLineNumber, NULL);
+ // Do not delete |birth|. We don't own it.
+ Births* birth = ThreadData::TallyABirthIfActive(location);
+ EXPECT_NE(reinterpret_cast<Births*>(NULL), birth);
+
+ const TrackedTime kTimePosted = TrackedTime() + Duration::FromMilliseconds(1);
+ const TrackedTime kStartOfRun = TrackedTime() +
+ Duration::FromMilliseconds(5);
+ const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7);
+ ThreadData::TallyRunOnWorkerThreadIfTracking(birth, kTimePosted,
+ kStartOfRun, kEndOfRun);
+
+ // Call for the ToSnapshot, but tell it to not reset the maxes after scanning.
+ ProcessDataSnapshot process_data;
+ ThreadData::Snapshot(false, &process_data);
+ ExpectSimpleProcessData(process_data, kFunction, kWorkerThreadName,
+ kWorkerThreadName, 1, 2, 4);
+
+ // Call for the ToSnapshot, but tell it to reset the maxes after scanning.
+ // We'll still get the same values, but the data will be reset (which we'll
+ // see in a moment).
+ ProcessDataSnapshot process_data_pre_reset;
+ ThreadData::Snapshot(true, &process_data_pre_reset);
+ ExpectSimpleProcessData(process_data, kFunction, kWorkerThreadName,
+ kWorkerThreadName, 1, 2, 4);
+
+ // Call for the ToSnapshot, and now we'll see the result of the last
+ // translation, as the max will have been pushed back to zero.
+ ProcessDataSnapshot process_data_post_reset;
+ ThreadData::Snapshot(true, &process_data_post_reset);
+ ASSERT_EQ(1u, process_data_post_reset.tasks.size());
+ EXPECT_EQ(kFile, process_data_post_reset.tasks[0].birth.location.file_name);
+ EXPECT_EQ(kFunction,
+ process_data_post_reset.tasks[0].birth.location.function_name);
+ EXPECT_EQ(kLineNumber,
+ process_data_post_reset.tasks[0].birth.location.line_number);
+ EXPECT_EQ(kWorkerThreadName,
+ process_data_post_reset.tasks[0].birth.thread_name);
+ EXPECT_EQ(1, process_data_post_reset.tasks[0].death_data.count);
+ EXPECT_EQ(2, process_data_post_reset.tasks[0].death_data.run_duration_sum);
+ EXPECT_EQ(0, process_data_post_reset.tasks[0].death_data.run_duration_max);
+ EXPECT_EQ(2, process_data_post_reset.tasks[0].death_data.run_duration_sample);
+ EXPECT_EQ(4, process_data_post_reset.tasks[0].death_data.queue_duration_sum);
+ EXPECT_EQ(0, process_data_post_reset.tasks[0].death_data.queue_duration_max);
+ EXPECT_EQ(4,
+ process_data_post_reset.tasks[0].death_data.queue_duration_sample);
+ EXPECT_EQ(kWorkerThreadName,
+ process_data_post_reset.tasks[0].death_thread_name);
+ EXPECT_EQ(0u, process_data_post_reset.descendants.size());
+ EXPECT_EQ(base::GetCurrentProcId(), process_data_post_reset.process_id);
+}
+
+TEST_F(TrackedObjectsTest, TwoLives) {
+ if (!ThreadData::InitializeAndSetTrackingStatus(
+ ThreadData::PROFILING_CHILDREN_ACTIVE))
+ return;
+
+ const char kFunction[] = "TwoLives";
+ Location location(kFunction, kFile, kLineNumber, NULL);
+ TallyABirth(location, kMainThreadName);
+
+ const base::TimeTicks kTimePosted = base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(1);
+ const base::TimeTicks kDelayedStartTime = base::TimeTicks();
+ // TrackingInfo will call TallyABirth() during construction.
+ base::TrackingInfo pending_task(location, kDelayedStartTime);
+ pending_task.time_posted = kTimePosted; // Overwrite implied Now().
+
+ const TrackedTime kStartOfRun = TrackedTime() +
+ Duration::FromMilliseconds(5);
+ const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7);
+ ThreadData::TallyRunOnNamedThreadIfTracking(pending_task,
+ kStartOfRun, kEndOfRun);
+
+ // TrackingInfo will call TallyABirth() during construction.
+ base::TrackingInfo pending_task2(location, kDelayedStartTime);
+ pending_task2.time_posted = kTimePosted; // Overwrite implied Now().
+
+ ThreadData::TallyRunOnNamedThreadIfTracking(pending_task2,
+ kStartOfRun, kEndOfRun);
+
+ ProcessDataSnapshot process_data;
+ ThreadData::Snapshot(false, &process_data);
+ ExpectSimpleProcessData(process_data, kFunction, kMainThreadName,
+ kMainThreadName, 2, 2, 4);
+}
+
+TEST_F(TrackedObjectsTest, DifferentLives) {
+ if (!ThreadData::InitializeAndSetTrackingStatus(
+ ThreadData::PROFILING_CHILDREN_ACTIVE))
+ return;
+
+ // Use a well named thread.
+ ThreadData::InitializeThreadContext(kMainThreadName);
+ const char kFunction[] = "DifferentLives";
+ Location location(kFunction, kFile, kLineNumber, NULL);
+
+ const base::TimeTicks kTimePosted = base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(1);
+ const base::TimeTicks kDelayedStartTime = base::TimeTicks();
+ // TrackingInfo will call TallyABirth() during construction.
+ base::TrackingInfo pending_task(location, kDelayedStartTime);
+ pending_task.time_posted = kTimePosted; // Overwrite implied Now().
+
+ const TrackedTime kStartOfRun = TrackedTime() +
+ Duration::FromMilliseconds(5);
+ const TrackedTime kEndOfRun = TrackedTime() + Duration::FromMilliseconds(7);
+ ThreadData::TallyRunOnNamedThreadIfTracking(pending_task,
+ kStartOfRun, kEndOfRun);
+
+ const int kSecondFakeLineNumber = 999;
+ Location second_location(kFunction, kFile, kSecondFakeLineNumber, NULL);
+
+ // TrackingInfo will call TallyABirth() during construction.
+ base::TrackingInfo pending_task2(second_location, kDelayedStartTime);
+ pending_task2.time_posted = kTimePosted; // Overwrite implied Now().
+
+ ProcessDataSnapshot process_data;
+ ThreadData::Snapshot(false, &process_data);
+ ASSERT_EQ(2u, process_data.tasks.size());
+ EXPECT_EQ(kFile, process_data.tasks[0].birth.location.file_name);
+ EXPECT_EQ(kFunction, process_data.tasks[0].birth.location.function_name);
+ EXPECT_EQ(kLineNumber, process_data.tasks[0].birth.location.line_number);
+ EXPECT_EQ(kMainThreadName, process_data.tasks[0].birth.thread_name);
+ EXPECT_EQ(1, process_data.tasks[0].death_data.count);
+ EXPECT_EQ(2, process_data.tasks[0].death_data.run_duration_sum);
+ EXPECT_EQ(2, process_data.tasks[0].death_data.run_duration_max);
+ EXPECT_EQ(2, process_data.tasks[0].death_data.run_duration_sample);
+ EXPECT_EQ(4, process_data.tasks[0].death_data.queue_duration_sum);
+ EXPECT_EQ(4, process_data.tasks[0].death_data.queue_duration_max);
+ EXPECT_EQ(4, process_data.tasks[0].death_data.queue_duration_sample);
+ EXPECT_EQ(kMainThreadName, process_data.tasks[0].death_thread_name);
+ EXPECT_EQ(kFile, process_data.tasks[1].birth.location.file_name);
+ EXPECT_EQ(kFunction, process_data.tasks[1].birth.location.function_name);
+ EXPECT_EQ(kSecondFakeLineNumber,
+ process_data.tasks[1].birth.location.line_number);
+ EXPECT_EQ(kMainThreadName, process_data.tasks[1].birth.thread_name);
+ EXPECT_EQ(1, process_data.tasks[1].death_data.count);
+ EXPECT_EQ(0, process_data.tasks[1].death_data.run_duration_sum);
+ EXPECT_EQ(0, process_data.tasks[1].death_data.run_duration_max);
+ EXPECT_EQ(0, process_data.tasks[1].death_data.run_duration_sample);
+ EXPECT_EQ(0, process_data.tasks[1].death_data.queue_duration_sum);
+ EXPECT_EQ(0, process_data.tasks[1].death_data.queue_duration_max);
+ EXPECT_EQ(0, process_data.tasks[1].death_data.queue_duration_sample);
+ EXPECT_EQ(kStillAlive, process_data.tasks[1].death_thread_name);
+ EXPECT_EQ(0u, process_data.descendants.size());
+ EXPECT_EQ(base::GetCurrentProcId(), process_data.process_id);
+}
+
+} // namespace tracked_objects
diff --git a/src/base/tracking_info.cc b/src/base/tracking_info.cc
new file mode 100644
index 0000000..0b091f8
--- /dev/null
+++ b/src/base/tracking_info.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 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/tracking_info.h"
+
+#include <stddef.h>
+#include "base/tracked_objects.h"
+
+namespace base {
+
+TrackingInfo::TrackingInfo()
+ : birth_tally(NULL) {
+}
+
+TrackingInfo::TrackingInfo(
+ const tracked_objects::Location& posted_from,
+ base::TimeTicks delayed_run_time)
+ : birth_tally(
+ tracked_objects::ThreadData::TallyABirthIfActive(posted_from)),
+ time_posted(TimeTicks::Now()),
+ delayed_run_time(delayed_run_time) {
+}
+
+TrackingInfo::~TrackingInfo() {}
+
+} // namespace base
+
diff --git a/src/base/tracking_info.h b/src/base/tracking_info.h
new file mode 100644
index 0000000..738bdee
--- /dev/null
+++ b/src/base/tracking_info.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2012 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 simple struct with tracking information that is stored
+// with a PendingTask (when message_loop is handling the task).
+// Only the information that is shared with the profiler in tracked_objects
+// are included in this structure.
+
+
+#ifndef BASE_TRACKING_INFO_H_
+#define BASE_TRACKING_INFO_H_
+
+#include "base/time.h"
+
+namespace tracked_objects {
+class Location;
+class Births;
+}
+
+namespace base {
+
+// This structure is copied around by value.
+struct BASE_EXPORT TrackingInfo {
+ TrackingInfo();
+ TrackingInfo(const tracked_objects::Location& posted_from,
+ base::TimeTicks delayed_run_time);
+ ~TrackingInfo();
+
+ // Record of location and thread that the task came from.
+ tracked_objects::Births* birth_tally;
+
+ // Time when the related task was posted.
+ base::TimeTicks time_posted;
+
+ // The time when the task should be run.
+ base::TimeTicks delayed_run_time;
+};
+
+} // namespace base
+
+#endif // BASE_TRACKING_INFO_H_
diff --git a/src/base/tuple.h b/src/base/tuple.h
new file mode 100644
index 0000000..01bda74
--- /dev/null
+++ b/src/base/tuple.h
@@ -0,0 +1,1065 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A Tuple is a generic templatized container, similar in concept to std::pair.
+// There are classes Tuple0 to Tuple6, cooresponding to the number of elements
+// it contains. The convenient MakeTuple() function takes 0 to 6 arguments,
+// and will construct and return the appropriate Tuple object. The functions
+// DispatchToMethod and DispatchToFunction take a function pointer or instance
+// and method pointer, and unpack a tuple into arguments to the call.
+//
+// Tuple elements are copied by value, and stored in the tuple. See the unit
+// tests for more details of how/when the values are copied.
+//
+// Example usage:
+// // These two methods of creating a Tuple are identical.
+// Tuple2<int, const char*> tuple_a(1, "wee");
+// Tuple2<int, const char*> tuple_b = MakeTuple(1, "wee");
+//
+// void SomeFunc(int a, const char* b) { }
+// DispatchToFunction(&SomeFunc, tuple_a); // SomeFunc(1, "wee")
+// DispatchToFunction(
+// &SomeFunc, MakeTuple(10, "foo")); // SomeFunc(10, "foo")
+//
+// struct { void SomeMeth(int a, int b, int c) { } } foo;
+// DispatchToMethod(&foo, &Foo::SomeMeth, MakeTuple(1, 2, 3));
+// // foo->SomeMeth(1, 2, 3);
+
+#ifndef BASE_TUPLE_H__
+#define BASE_TUPLE_H__
+
+// Traits ----------------------------------------------------------------------
+//
+// A simple traits class for tuple arguments.
+//
+// ValueType: the bare, nonref version of a type (same as the type for nonrefs).
+// RefType: the ref version of a type (same as the type for refs).
+// ParamType: what type to pass to functions (refs should not be constified).
+
+template <class P>
+struct TupleTraits {
+ typedef P ValueType;
+ typedef P& RefType;
+ typedef const P& ParamType;
+};
+
+template <class P>
+struct TupleTraits<P&> {
+ typedef P ValueType;
+ typedef P& RefType;
+ typedef P& ParamType;
+};
+
+template <class P>
+struct TupleTypes { };
+
+// Tuple -----------------------------------------------------------------------
+//
+// This set of classes is useful for bundling 0 or more heterogeneous data types
+// into a single variable. The advantage of this is that it greatly simplifies
+// function objects that need to take an arbitrary number of parameters; see
+// RunnableMethod and IPC::MessageWithTuple.
+//
+// Tuple0 is supplied to act as a 'void' type. It can be used, for example,
+// when dispatching to a function that accepts no arguments (see the
+// Dispatchers below).
+// Tuple1<A> is rarely useful. One such use is when A is non-const ref that you
+// want filled by the dispatchee, and the tuple is merely a container for that
+// output (a "tier"). See MakeRefTuple and its usages.
+
+struct Tuple0 {
+ typedef Tuple0 ValueTuple;
+ typedef Tuple0 RefTuple;
+ typedef Tuple0 ParamTuple;
+};
+
+template <class A>
+struct Tuple1 {
+ public:
+ typedef A TypeA;
+
+ Tuple1() {}
+ explicit Tuple1(typename TupleTraits<A>::ParamType a) : a(a) {}
+
+ A a;
+};
+
+template <class A, class B>
+struct Tuple2 {
+ public:
+ typedef A TypeA;
+ typedef B TypeB;
+
+ Tuple2() {}
+ Tuple2(typename TupleTraits<A>::ParamType a,
+ typename TupleTraits<B>::ParamType b)
+ : a(a), b(b) {
+ }
+
+ A a;
+ B b;
+};
+
+template <class A, class B, class C>
+struct Tuple3 {
+ public:
+ typedef A TypeA;
+ typedef B TypeB;
+ typedef C TypeC;
+
+ Tuple3() {}
+ Tuple3(typename TupleTraits<A>::ParamType a,
+ typename TupleTraits<B>::ParamType b,
+ typename TupleTraits<C>::ParamType c)
+ : a(a), b(b), c(c){
+ }
+
+ A a;
+ B b;
+ C c;
+};
+
+template <class A, class B, class C, class D>
+struct Tuple4 {
+ public:
+ typedef A TypeA;
+ typedef B TypeB;
+ typedef C TypeC;
+ typedef D TypeD;
+
+ Tuple4() {}
+ Tuple4(typename TupleTraits<A>::ParamType a,
+ typename TupleTraits<B>::ParamType b,
+ typename TupleTraits<C>::ParamType c,
+ typename TupleTraits<D>::ParamType d)
+ : a(a), b(b), c(c), d(d) {
+ }
+
+ A a;
+ B b;
+ C c;
+ D d;
+};
+
+template <class A, class B, class C, class D, class E>
+struct Tuple5 {
+ public:
+ typedef A TypeA;
+ typedef B TypeB;
+ typedef C TypeC;
+ typedef D TypeD;
+ typedef E TypeE;
+
+ Tuple5() {}
+ Tuple5(typename TupleTraits<A>::ParamType a,
+ typename TupleTraits<B>::ParamType b,
+ typename TupleTraits<C>::ParamType c,
+ typename TupleTraits<D>::ParamType d,
+ typename TupleTraits<E>::ParamType e)
+ : a(a), b(b), c(c), d(d), e(e) {
+ }
+
+ A a;
+ B b;
+ C c;
+ D d;
+ E e;
+};
+
+template <class A, class B, class C, class D, class E, class F>
+struct Tuple6 {
+ public:
+ typedef A TypeA;
+ typedef B TypeB;
+ typedef C TypeC;
+ typedef D TypeD;
+ typedef E TypeE;
+ typedef F TypeF;
+
+ Tuple6() {}
+ Tuple6(typename TupleTraits<A>::ParamType a,
+ typename TupleTraits<B>::ParamType b,
+ typename TupleTraits<C>::ParamType c,
+ typename TupleTraits<D>::ParamType d,
+ typename TupleTraits<E>::ParamType e,
+ typename TupleTraits<F>::ParamType f)
+ : a(a), b(b), c(c), d(d), e(e), f(f) {
+ }
+
+ A a;
+ B b;
+ C c;
+ D d;
+ E e;
+ F f;
+};
+
+template <class A, class B, class C, class D, class E, class F, class G>
+struct Tuple7 {
+ public:
+ typedef A TypeA;
+ typedef B TypeB;
+ typedef C TypeC;
+ typedef D TypeD;
+ typedef E TypeE;
+ typedef F TypeF;
+ typedef G TypeG;
+
+ Tuple7() {}
+ Tuple7(typename TupleTraits<A>::ParamType a,
+ typename TupleTraits<B>::ParamType b,
+ typename TupleTraits<C>::ParamType c,
+ typename TupleTraits<D>::ParamType d,
+ typename TupleTraits<E>::ParamType e,
+ typename TupleTraits<F>::ParamType f,
+ typename TupleTraits<G>::ParamType g)
+ : a(a), b(b), c(c), d(d), e(e), f(f), g(g) {
+ }
+
+ A a;
+ B b;
+ C c;
+ D d;
+ E e;
+ F f;
+ G g;
+};
+
+template <class A, class B, class C, class D, class E, class F, class G,
+ class H>
+struct Tuple8 {
+ public:
+ typedef A TypeA;
+ typedef B TypeB;
+ typedef C TypeC;
+ typedef D TypeD;
+ typedef E TypeE;
+ typedef F TypeF;
+ typedef G TypeG;
+ typedef H TypeH;
+
+ Tuple8() {}
+ Tuple8(typename TupleTraits<A>::ParamType a,
+ typename TupleTraits<B>::ParamType b,
+ typename TupleTraits<C>::ParamType c,
+ typename TupleTraits<D>::ParamType d,
+ typename TupleTraits<E>::ParamType e,
+ typename TupleTraits<F>::ParamType f,
+ typename TupleTraits<G>::ParamType g,
+ typename TupleTraits<H>::ParamType h)
+ : a(a), b(b), c(c), d(d), e(e), f(f), g(g), h(h) {
+ }
+
+ A a;
+ B b;
+ C c;
+ D d;
+ E e;
+ F f;
+ G g;
+ H h;
+};
+
+// Tuple types ----------------------------------------------------------------
+//
+// Allows for selection of ValueTuple/RefTuple/ParamTuple without needing the
+// definitions of class types the tuple takes as parameters.
+
+template <>
+struct TupleTypes< Tuple0 > {
+ typedef Tuple0 ValueTuple;
+ typedef Tuple0 RefTuple;
+ typedef Tuple0 ParamTuple;
+};
+
+template <class A>
+struct TupleTypes< Tuple1<A> > {
+ typedef Tuple1<typename TupleTraits<A>::ValueType> ValueTuple;
+ typedef Tuple1<typename TupleTraits<A>::RefType> RefTuple;
+ typedef Tuple1<typename TupleTraits<A>::ParamType> ParamTuple;
+};
+
+template <class A, class B>
+struct TupleTypes< Tuple2<A, B> > {
+ typedef Tuple2<typename TupleTraits<A>::ValueType,
+ typename TupleTraits<B>::ValueType> ValueTuple;
+typedef Tuple2<typename TupleTraits<A>::RefType,
+ typename TupleTraits<B>::RefType> RefTuple;
+ typedef Tuple2<typename TupleTraits<A>::ParamType,
+ typename TupleTraits<B>::ParamType> ParamTuple;
+};
+
+template <class A, class B, class C>
+struct TupleTypes< Tuple3<A, B, C> > {
+ typedef Tuple3<typename TupleTraits<A>::ValueType,
+ typename TupleTraits<B>::ValueType,
+ typename TupleTraits<C>::ValueType> ValueTuple;
+typedef Tuple3<typename TupleTraits<A>::RefType,
+ typename TupleTraits<B>::RefType,
+ typename TupleTraits<C>::RefType> RefTuple;
+ typedef Tuple3<typename TupleTraits<A>::ParamType,
+ typename TupleTraits<B>::ParamType,
+ typename TupleTraits<C>::ParamType> ParamTuple;
+};
+
+template <class A, class B, class C, class D>
+struct TupleTypes< Tuple4<A, B, C, D> > {
+ typedef Tuple4<typename TupleTraits<A>::ValueType,
+ typename TupleTraits<B>::ValueType,
+ typename TupleTraits<C>::ValueType,
+ typename TupleTraits<D>::ValueType> ValueTuple;
+typedef Tuple4<typename TupleTraits<A>::RefType,
+ typename TupleTraits<B>::RefType,
+ typename TupleTraits<C>::RefType,
+ typename TupleTraits<D>::RefType> RefTuple;
+ typedef Tuple4<typename TupleTraits<A>::ParamType,
+ typename TupleTraits<B>::ParamType,
+ typename TupleTraits<C>::ParamType,
+ typename TupleTraits<D>::ParamType> ParamTuple;
+};
+
+template <class A, class B, class C, class D, class E>
+struct TupleTypes< Tuple5<A, B, C, D, E> > {
+ typedef Tuple5<typename TupleTraits<A>::ValueType,
+ typename TupleTraits<B>::ValueType,
+ typename TupleTraits<C>::ValueType,
+ typename TupleTraits<D>::ValueType,
+ typename TupleTraits<E>::ValueType> ValueTuple;
+typedef Tuple5<typename TupleTraits<A>::RefType,
+ typename TupleTraits<B>::RefType,
+ typename TupleTraits<C>::RefType,
+ typename TupleTraits<D>::RefType,
+ typename TupleTraits<E>::RefType> RefTuple;
+ typedef Tuple5<typename TupleTraits<A>::ParamType,
+ typename TupleTraits<B>::ParamType,
+ typename TupleTraits<C>::ParamType,
+ typename TupleTraits<D>::ParamType,
+ typename TupleTraits<E>::ParamType> ParamTuple;
+};
+
+template <class A, class B, class C, class D, class E, class F>
+struct TupleTypes< Tuple6<A, B, C, D, E, F> > {
+ typedef Tuple6<typename TupleTraits<A>::ValueType,
+ typename TupleTraits<B>::ValueType,
+ typename TupleTraits<C>::ValueType,
+ typename TupleTraits<D>::ValueType,
+ typename TupleTraits<E>::ValueType,
+ typename TupleTraits<F>::ValueType> ValueTuple;
+typedef Tuple6<typename TupleTraits<A>::RefType,
+ typename TupleTraits<B>::RefType,
+ typename TupleTraits<C>::RefType,
+ typename TupleTraits<D>::RefType,
+ typename TupleTraits<E>::RefType,
+ typename TupleTraits<F>::RefType> RefTuple;
+ typedef Tuple6<typename TupleTraits<A>::ParamType,
+ typename TupleTraits<B>::ParamType,
+ typename TupleTraits<C>::ParamType,
+ typename TupleTraits<D>::ParamType,
+ typename TupleTraits<E>::ParamType,
+ typename TupleTraits<F>::ParamType> ParamTuple;
+};
+
+template <class A, class B, class C, class D, class E, class F, class G>
+struct TupleTypes< Tuple7<A, B, C, D, E, F, G> > {
+ typedef Tuple7<typename TupleTraits<A>::ValueType,
+ typename TupleTraits<B>::ValueType,
+ typename TupleTraits<C>::ValueType,
+ typename TupleTraits<D>::ValueType,
+ typename TupleTraits<E>::ValueType,
+ typename TupleTraits<F>::ValueType,
+ typename TupleTraits<G>::ValueType> ValueTuple;
+typedef Tuple7<typename TupleTraits<A>::RefType,
+ typename TupleTraits<B>::RefType,
+ typename TupleTraits<C>::RefType,
+ typename TupleTraits<D>::RefType,
+ typename TupleTraits<E>::RefType,
+ typename TupleTraits<F>::RefType,
+ typename TupleTraits<G>::RefType> RefTuple;
+ typedef Tuple7<typename TupleTraits<A>::ParamType,
+ typename TupleTraits<B>::ParamType,
+ typename TupleTraits<C>::ParamType,
+ typename TupleTraits<D>::ParamType,
+ typename TupleTraits<E>::ParamType,
+ typename TupleTraits<F>::ParamType,
+ typename TupleTraits<G>::ParamType> ParamTuple;
+};
+
+template <class A, class B, class C, class D, class E, class F, class G,
+ class H>
+struct TupleTypes< Tuple8<A, B, C, D, E, F, G, H> > {
+ typedef Tuple8<typename TupleTraits<A>::ValueType,
+ typename TupleTraits<B>::ValueType,
+ typename TupleTraits<C>::ValueType,
+ typename TupleTraits<D>::ValueType,
+ typename TupleTraits<E>::ValueType,
+ typename TupleTraits<F>::ValueType,
+ typename TupleTraits<G>::ValueType,
+ typename TupleTraits<H>::ValueType> ValueTuple;
+typedef Tuple8<typename TupleTraits<A>::RefType,
+ typename TupleTraits<B>::RefType,
+ typename TupleTraits<C>::RefType,
+ typename TupleTraits<D>::RefType,
+ typename TupleTraits<E>::RefType,
+ typename TupleTraits<F>::RefType,
+ typename TupleTraits<G>::RefType,
+ typename TupleTraits<H>::RefType> RefTuple;
+ typedef Tuple8<typename TupleTraits<A>::ParamType,
+ typename TupleTraits<B>::ParamType,
+ typename TupleTraits<C>::ParamType,
+ typename TupleTraits<D>::ParamType,
+ typename TupleTraits<E>::ParamType,
+ typename TupleTraits<F>::ParamType,
+ typename TupleTraits<G>::ParamType,
+ typename TupleTraits<H>::ParamType> ParamTuple;
+};
+
+// Tuple creators -------------------------------------------------------------
+//
+// Helper functions for constructing tuples while inferring the template
+// argument types.
+
+inline Tuple0 MakeTuple() {
+ return Tuple0();
+}
+
+template <class A>
+inline Tuple1<A> MakeTuple(const A& a) {
+ return Tuple1<A>(a);
+}
+
+template <class A, class B>
+inline Tuple2<A, B> MakeTuple(const A& a, const B& b) {
+ return Tuple2<A, B>(a, b);
+}
+
+template <class A, class B, class C>
+inline Tuple3<A, B, C> MakeTuple(const A& a, const B& b, const C& c) {
+ return Tuple3<A, B, C>(a, b, c);
+}
+
+template <class A, class B, class C, class D>
+inline Tuple4<A, B, C, D> MakeTuple(const A& a, const B& b, const C& c,
+ const D& d) {
+ return Tuple4<A, B, C, D>(a, b, c, d);
+}
+
+template <class A, class B, class C, class D, class E>
+inline Tuple5<A, B, C, D, E> MakeTuple(const A& a, const B& b, const C& c,
+ const D& d, const E& e) {
+ return Tuple5<A, B, C, D, E>(a, b, c, d, e);
+}
+
+template <class A, class B, class C, class D, class E, class F>
+inline Tuple6<A, B, C, D, E, F> MakeTuple(const A& a, const B& b, const C& c,
+ const D& d, const E& e, const F& f) {
+ return Tuple6<A, B, C, D, E, F>(a, b, c, d, e, f);
+}
+
+template <class A, class B, class C, class D, class E, class F, class G>
+inline Tuple7<A, B, C, D, E, F, G> MakeTuple(const A& a, const B& b, const C& c,
+ const D& d, const E& e, const F& f,
+ const G& g) {
+ return Tuple7<A, B, C, D, E, F, G>(a, b, c, d, e, f, g);
+}
+
+template <class A, class B, class C, class D, class E, class F, class G,
+ class H>
+inline Tuple8<A, B, C, D, E, F, G, H> MakeTuple(const A& a, const B& b,
+ const C& c, const D& d,
+ const E& e, const F& f,
+ const G& g, const H& h) {
+ return Tuple8<A, B, C, D, E, F, G, H>(a, b, c, d, e, f, g, h);
+}
+
+// The following set of helpers make what Boost refers to as "Tiers" - a tuple
+// of references.
+
+template <class A>
+inline Tuple1<A&> MakeRefTuple(A& a) {
+ return Tuple1<A&>(a);
+}
+
+template <class A, class B>
+inline Tuple2<A&, B&> MakeRefTuple(A& a, B& b) {
+ return Tuple2<A&, B&>(a, b);
+}
+
+template <class A, class B, class C>
+inline Tuple3<A&, B&, C&> MakeRefTuple(A& a, B& b, C& c) {
+ return Tuple3<A&, B&, C&>(a, b, c);
+}
+
+template <class A, class B, class C, class D>
+inline Tuple4<A&, B&, C&, D&> MakeRefTuple(A& a, B& b, C& c, D& d) {
+ return Tuple4<A&, B&, C&, D&>(a, b, c, d);
+}
+
+template <class A, class B, class C, class D, class E>
+inline Tuple5<A&, B&, C&, D&, E&> MakeRefTuple(A& a, B& b, C& c, D& d, E& e) {
+ return Tuple5<A&, B&, C&, D&, E&>(a, b, c, d, e);
+}
+
+template <class A, class B, class C, class D, class E, class F>
+inline Tuple6<A&, B&, C&, D&, E&, F&> MakeRefTuple(A& a, B& b, C& c, D& d, E& e,
+ F& f) {
+ return Tuple6<A&, B&, C&, D&, E&, F&>(a, b, c, d, e, f);
+}
+
+template <class A, class B, class C, class D, class E, class F, class G>
+inline Tuple7<A&, B&, C&, D&, E&, F&, G&> MakeRefTuple(A& a, B& b, C& c, D& d,
+ E& e, F& f, G& g) {
+ return Tuple7<A&, B&, C&, D&, E&, F&, G&>(a, b, c, d, e, f, g);
+}
+
+template <class A, class B, class C, class D, class E, class F, class G,
+ class H>
+inline Tuple8<A&, B&, C&, D&, E&, F&, G&, H&> MakeRefTuple(A& a, B& b, C& c,
+ D& d, E& e, F& f,
+ G& g, H& h) {
+ return Tuple8<A&, B&, C&, D&, E&, F&, G&, H&>(a, b, c, d, e, f, g, h);
+}
+
+// Dispatchers ----------------------------------------------------------------
+//
+// Helper functions that call the given method on an object, with the unpacked
+// tuple arguments. Notice that they all have the same number of arguments,
+// so you need only write:
+// DispatchToMethod(object, &Object::method, args);
+// This is very useful for templated dispatchers, since they don't need to know
+// what type |args| is.
+
+// Non-Static Dispatchers with no out params.
+
+template <class ObjT, class Method>
+inline void DispatchToMethod(ObjT* obj,
+ Method method,
+ const Tuple0& /* arg */) {
+ (obj->*method)();
+}
+
+template <class ObjT, class Method, class A>
+inline void DispatchToMethod(ObjT* obj, Method method, const A& arg) {
+ (obj->*method)(arg);
+}
+
+template <class ObjT, class Method, class A>
+inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1<A>& arg) {
+ (obj->*method)(arg.a);
+}
+
+template<class ObjT, class Method, class A, class B>
+inline void DispatchToMethod(ObjT* obj,
+ Method method,
+ const Tuple2<A, B>& arg) {
+ (obj->*method)(arg.a, arg.b);
+}
+
+template<class ObjT, class Method, class A, class B, class C>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple3<A, B, C>& arg) {
+ (obj->*method)(arg.a, arg.b, arg.c);
+}
+
+template<class ObjT, class Method, class A, class B, class C, class D>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple4<A, B, C, D>& arg) {
+ (obj->*method)(arg.a, arg.b, arg.c, arg.d);
+}
+
+template<class ObjT, class Method, class A, class B, class C, class D, class E>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple5<A, B, C, D, E>& arg) {
+ (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e);
+}
+
+template<class ObjT, class Method, class A, class B, class C, class D, class E,
+ class F>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple6<A, B, C, D, E, F>& arg) {
+ (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f);
+}
+
+template<class ObjT, class Method, class A, class B, class C, class D, class E,
+ class F, class G>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple7<A, B, C, D, E, F, G>& arg) {
+ (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f, arg.g);
+}
+
+template<class ObjT, class Method, class A, class B, class C, class D, class E,
+ class F, class G, class H>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple8<A, B, C, D, E, F, G, H>& arg) {
+ (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f, arg.g, arg.h);
+}
+
+// Static Dispatchers with no out params.
+
+template <class Function>
+inline void DispatchToFunction(Function function, const Tuple0& /* arg */) {
+ (*function)();
+}
+
+template <class Function, class A>
+inline void DispatchToFunction(Function function, const A& arg) {
+ (*function)(arg);
+}
+
+template <class Function, class A>
+inline void DispatchToFunction(Function function, const Tuple1<A>& arg) {
+ (*function)(arg.a);
+}
+
+template<class Function, class A, class B>
+inline void DispatchToFunction(Function function, const Tuple2<A, B>& arg) {
+ (*function)(arg.a, arg.b);
+}
+
+template<class Function, class A, class B, class C>
+inline void DispatchToFunction(Function function, const Tuple3<A, B, C>& arg) {
+ (*function)(arg.a, arg.b, arg.c);
+}
+
+template<class Function, class A, class B, class C, class D>
+inline void DispatchToFunction(Function function,
+ const Tuple4<A, B, C, D>& arg) {
+ (*function)(arg.a, arg.b, arg.c, arg.d);
+}
+
+template<class Function, class A, class B, class C, class D, class E>
+inline void DispatchToFunction(Function function,
+ const Tuple5<A, B, C, D, E>& arg) {
+ (*function)(arg.a, arg.b, arg.c, arg.d, arg.e);
+}
+
+template<class Function, class A, class B, class C, class D, class E, class F>
+inline void DispatchToFunction(Function function,
+ const Tuple6<A, B, C, D, E, F>& arg) {
+ (*function)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f);
+}
+
+template<class Function, class A, class B, class C, class D, class E, class F,
+ class G>
+inline void DispatchToFunction(Function function,
+ const Tuple7<A, B, C, D, E, F, G>& arg) {
+ (*function)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f, arg.g);
+}
+
+template<class Function, class A, class B, class C, class D, class E, class F,
+ class G, class H>
+inline void DispatchToFunction(Function function,
+ const Tuple8<A, B, C, D, E, F, G, H>& arg) {
+ (*function)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f, arg.g, arg.h);
+}
+
+// Dispatchers with 0 out param (as a Tuple0).
+
+template <class ObjT, class Method>
+inline void DispatchToMethod(ObjT* obj,
+ Method method,
+ const Tuple0& /* arg */,
+ Tuple0*) {
+ (obj->*method)();
+}
+
+template <class ObjT, class Method, class A>
+inline void DispatchToMethod(ObjT* obj, Method method, const A& arg, Tuple0*) {
+ (obj->*method)(arg);
+}
+
+template <class ObjT, class Method, class A>
+inline void DispatchToMethod(ObjT* obj,
+ Method method,
+ const Tuple1<A>& arg, Tuple0*) {
+ (obj->*method)(arg.a);
+}
+
+template<class ObjT, class Method, class A, class B>
+inline void DispatchToMethod(ObjT* obj,
+ Method method,
+ const Tuple2<A, B>& arg, Tuple0*) {
+ (obj->*method)(arg.a, arg.b);
+}
+
+template<class ObjT, class Method, class A, class B, class C>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple3<A, B, C>& arg, Tuple0*) {
+ (obj->*method)(arg.a, arg.b, arg.c);
+}
+
+template<class ObjT, class Method, class A, class B, class C, class D>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple4<A, B, C, D>& arg, Tuple0*) {
+ (obj->*method)(arg.a, arg.b, arg.c, arg.d);
+}
+
+template<class ObjT, class Method, class A, class B, class C, class D, class E>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple5<A, B, C, D, E>& arg, Tuple0*) {
+ (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e);
+}
+
+template<class ObjT, class Method, class A, class B, class C, class D, class E,
+ class F>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple6<A, B, C, D, E, F>& arg, Tuple0*) {
+ (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f);
+}
+
+// Dispatchers with 1 out param.
+
+template <class ObjT, class Method, class OutA>
+inline void DispatchToMethod(ObjT* obj,
+ Method method,
+ const Tuple0& /* in */,
+ Tuple1<OutA>* out) {
+ (obj->*method)(&out->a);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const InA& in,
+ Tuple1<OutA>* out) {
+ (obj->*method)(in, &out->a);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple1<InA>& in,
+ Tuple1<OutA>* out) {
+ (obj->*method)(in.a, &out->a);
+}
+
+template<class ObjT, class Method, class InA, class InB,
+ class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple2<InA, InB>& in,
+ Tuple1<OutA>* out) {
+ (obj->*method)(in.a, in.b, &out->a);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC,
+ class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple3<InA, InB, InC>& in,
+ Tuple1<OutA>* out) {
+ (obj->*method)(in.a, in.b, in.c, &out->a);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC, class InD,
+ class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple4<InA, InB, InC, InD>& in,
+ Tuple1<OutA>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, &out->a);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC, class InD,
+ class InE, class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple5<InA, InB, InC, InD, InE>& in,
+ Tuple1<OutA>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a);
+}
+
+template<class ObjT, class Method,
+ class InA, class InB, class InC, class InD, class InE, class InF,
+ class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple6<InA, InB, InC, InD, InE, InF>& in,
+ Tuple1<OutA>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f, &out->a);
+}
+
+// Dispatchers with 2 out params.
+
+template <class ObjT, class Method, class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj,
+ Method method,
+ const Tuple0& /* in */,
+ Tuple2<OutA, OutB>* out) {
+ (obj->*method)(&out->a, &out->b);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const InA& in,
+ Tuple2<OutA, OutB>* out) {
+ (obj->*method)(in, &out->a, &out->b);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple1<InA>& in,
+ Tuple2<OutA, OutB>* out) {
+ (obj->*method)(in.a, &out->a, &out->b);
+}
+
+template<class ObjT, class Method, class InA, class InB,
+ class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple2<InA, InB>& in,
+ Tuple2<OutA, OutB>* out) {
+ (obj->*method)(in.a, in.b, &out->a, &out->b);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC,
+ class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple3<InA, InB, InC>& in,
+ Tuple2<OutA, OutB>* out) {
+ (obj->*method)(in.a, in.b, in.c, &out->a, &out->b);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC, class InD,
+ class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple4<InA, InB, InC, InD>& in,
+ Tuple2<OutA, OutB>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b);
+}
+
+template<class ObjT, class Method,
+ class InA, class InB, class InC, class InD, class InE,
+ class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple5<InA, InB, InC, InD, InE>& in,
+ Tuple2<OutA, OutB>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a, &out->b);
+}
+
+template<class ObjT, class Method,
+ class InA, class InB, class InC, class InD, class InE, class InF,
+ class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple6<InA, InB, InC, InD, InE, InF>& in,
+ Tuple2<OutA, OutB>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f, &out->a, &out->b);
+}
+
+// Dispatchers with 3 out params.
+
+template <class ObjT, class Method, class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj,
+ Method method,
+ const Tuple0& /* in */,
+ Tuple3<OutA, OutB, OutC>* out) {
+ (obj->*method)(&out->a, &out->b, &out->c);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const InA& in,
+ Tuple3<OutA, OutB, OutC>* out) {
+ (obj->*method)(in, &out->a, &out->b, &out->c);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple1<InA>& in,
+ Tuple3<OutA, OutB, OutC>* out) {
+ (obj->*method)(in.a, &out->a, &out->b, &out->c);
+}
+
+template<class ObjT, class Method, class InA, class InB,
+ class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple2<InA, InB>& in,
+ Tuple3<OutA, OutB, OutC>* out) {
+ (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC,
+ class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple3<InA, InB, InC>& in,
+ Tuple3<OutA, OutB, OutC>* out) {
+ (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC, class InD,
+ class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple4<InA, InB, InC, InD>& in,
+ Tuple3<OutA, OutB, OutC>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c);
+}
+
+template<class ObjT, class Method,
+ class InA, class InB, class InC, class InD, class InE,
+ class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple5<InA, InB, InC, InD, InE>& in,
+ Tuple3<OutA, OutB, OutC>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a, &out->b, &out->c);
+}
+
+template<class ObjT, class Method,
+ class InA, class InB, class InC, class InD, class InE, class InF,
+ class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple6<InA, InB, InC, InD, InE, InF>& in,
+ Tuple3<OutA, OutB, OutC>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f, &out->a, &out->b, &out->c);
+}
+
+// Dispatchers with 4 out params.
+
+template <class ObjT,
+ class Method,
+ class OutA,
+ class OutB,
+ class OutC,
+ class OutD>
+inline void DispatchToMethod(ObjT* obj,
+ Method method,
+ const Tuple0& /* in */,
+ Tuple4<OutA, OutB, OutC, OutD>* out) {
+ (obj->*method)(&out->a, &out->b, &out->c, &out->d);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const InA& in,
+ Tuple4<OutA, OutB, OutC, OutD>* out) {
+ (obj->*method)(in, &out->a, &out->b, &out->c, &out->d);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple1<InA>& in,
+ Tuple4<OutA, OutB, OutC, OutD>* out) {
+ (obj->*method)(in.a, &out->a, &out->b, &out->c, &out->d);
+}
+
+template<class ObjT, class Method, class InA, class InB,
+ class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple2<InA, InB>& in,
+ Tuple4<OutA, OutB, OutC, OutD>* out) {
+ (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c, &out->d);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC,
+ class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple3<InA, InB, InC>& in,
+ Tuple4<OutA, OutB, OutC, OutD>* out) {
+ (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c, &out->d);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC, class InD,
+ class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple4<InA, InB, InC, InD>& in,
+ Tuple4<OutA, OutB, OutC, OutD>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c, &out->d);
+}
+
+template<class ObjT, class Method,
+ class InA, class InB, class InC, class InD, class InE,
+ class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple5<InA, InB, InC, InD, InE>& in,
+ Tuple4<OutA, OutB, OutC, OutD>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, in.e,
+ &out->a, &out->b, &out->c, &out->d);
+}
+
+template<class ObjT, class Method,
+ class InA, class InB, class InC, class InD, class InE, class InF,
+ class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple6<InA, InB, InC, InD, InE, InF>& in,
+ Tuple4<OutA, OutB, OutC, OutD>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f,
+ &out->a, &out->b, &out->c, &out->d);
+}
+
+// Dispatchers with 5 out params.
+
+template <class ObjT,
+ class Method,
+ class OutA,
+ class OutB,
+ class OutC,
+ class OutD,
+ class OutE>
+inline void DispatchToMethod(ObjT* obj,
+ Method method,
+ const Tuple0& /* in */,
+ Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+ (obj->*method)(&out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const InA& in,
+ Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+ (obj->*method)(in, &out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple1<InA>& in,
+ Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+ (obj->*method)(in.a, &out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+template<class ObjT, class Method, class InA, class InB,
+ class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple2<InA, InB>& in,
+ Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+ (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC,
+ class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple3<InA, InB, InC>& in,
+ Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+ (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC, class InD,
+ class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple4<InA, InB, InC, InD>& in,
+ Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c, &out->d,
+ &out->e);
+}
+
+template<class ObjT, class Method,
+ class InA, class InB, class InC, class InD, class InE,
+ class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple5<InA, InB, InC, InD, InE>& in,
+ Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, in.e,
+ &out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+template<class ObjT, class Method,
+ class InA, class InB, class InC, class InD, class InE, class InF,
+ class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple6<InA, InB, InC, InD, InE, InF>& in,
+ Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f,
+ &out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+#endif // BASE_TUPLE_H__
diff --git a/src/base/tuple_unittest.cc b/src/base/tuple_unittest.cc
new file mode 100644
index 0000000..402394c
--- /dev/null
+++ b/src/base/tuple_unittest.cc
@@ -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 "base/tuple.h"
+
+#include "base/compiler_specific.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void DoAdd(int a, int b, int c, int* res) {
+ *res = a + b + c;
+}
+
+struct Addy {
+ Addy() { }
+ void DoAdd(int a, int b, int c, int d, int* res) {
+ *res = a + b + c + d;
+ }
+};
+
+struct Addz {
+ Addz() { }
+ void DoAdd(int a, int b, int c, int d, int e, int* res) {
+ *res = a + b + c + d + e;
+ }
+};
+
+} // namespace
+
+TEST(TupleTest, Basic) {
+ Tuple0 t0 ALLOW_UNUSED = MakeTuple();
+ Tuple1<int> t1(1);
+ Tuple2<int, const char*> t2 = MakeTuple(1, static_cast<const char*>("wee"));
+ Tuple3<int, int, int> t3(1, 2, 3);
+ Tuple4<int, int, int, int*> t4(1, 2, 3, &t1.a);
+ Tuple5<int, int, int, int, int*> t5(1, 2, 3, 4, &t4.a);
+ Tuple6<int, int, int, int, int, int*> t6(1, 2, 3, 4, 5, &t4.a);
+
+ EXPECT_EQ(1, t1.a);
+ EXPECT_EQ(1, t2.a);
+ EXPECT_EQ(1, t3.a);
+ EXPECT_EQ(2, t3.b);
+ EXPECT_EQ(3, t3.c);
+ EXPECT_EQ(1, t4.a);
+ EXPECT_EQ(2, t4.b);
+ EXPECT_EQ(3, t4.c);
+ EXPECT_EQ(1, t5.a);
+ EXPECT_EQ(2, t5.b);
+ EXPECT_EQ(3, t5.c);
+ EXPECT_EQ(4, t5.d);
+ EXPECT_EQ(1, t6.a);
+ EXPECT_EQ(2, t6.b);
+ EXPECT_EQ(3, t6.c);
+ EXPECT_EQ(4, t6.d);
+ EXPECT_EQ(5, t6.e);
+
+ EXPECT_EQ(1, t1.a);
+ DispatchToFunction(&DoAdd, t4);
+ EXPECT_EQ(6, t1.a);
+
+ int res = 0;
+ DispatchToFunction(&DoAdd, MakeTuple(9, 8, 7, &res));
+ EXPECT_EQ(24, res);
+
+ Addy addy;
+ EXPECT_EQ(1, t4.a);
+ DispatchToMethod(&addy, &Addy::DoAdd, t5);
+ EXPECT_EQ(10, t4.a);
+
+ Addz addz;
+ EXPECT_EQ(10, t4.a);
+ DispatchToMethod(&addz, &Addz::DoAdd, t6);
+ EXPECT_EQ(15, t4.a);
+}
+
+namespace {
+
+struct CopyLogger {
+ CopyLogger() { ++TimesConstructed; }
+ CopyLogger(const CopyLogger& tocopy) { ++TimesConstructed; ++TimesCopied; }
+ ~CopyLogger() { }
+
+ static int TimesCopied;
+ static int TimesConstructed;
+};
+
+void SomeLoggerMethRef(const CopyLogger& logy, const CopyLogger* ptr, bool* b) {
+ *b = &logy == ptr;
+}
+
+void SomeLoggerMethCopy(CopyLogger logy, const CopyLogger* ptr, bool* b) {
+ *b = &logy == ptr;
+}
+
+int CopyLogger::TimesCopied = 0;
+int CopyLogger::TimesConstructed = 0;
+
+} // namespace
+
+TEST(TupleTest, Copying) {
+ CopyLogger logger;
+ EXPECT_EQ(0, CopyLogger::TimesCopied);
+ EXPECT_EQ(1, CopyLogger::TimesConstructed);
+
+ bool res = false;
+
+ // Creating the tuple should copy the class to store internally in the tuple.
+ Tuple3<CopyLogger, CopyLogger*, bool*> tuple(logger, &logger, &res);
+ tuple.b = &tuple.a;
+ EXPECT_EQ(2, CopyLogger::TimesConstructed);
+ EXPECT_EQ(1, CopyLogger::TimesCopied);
+
+ // Our internal Logger and the one passed to the function should be the same.
+ res = false;
+ DispatchToFunction(&SomeLoggerMethRef, tuple);
+ EXPECT_TRUE(res);
+ EXPECT_EQ(2, CopyLogger::TimesConstructed);
+ EXPECT_EQ(1, CopyLogger::TimesCopied);
+
+ // Now they should be different, since the function call will make a copy.
+ res = false;
+ DispatchToFunction(&SomeLoggerMethCopy, tuple);
+ EXPECT_FALSE(res);
+ EXPECT_EQ(3, CopyLogger::TimesConstructed);
+ EXPECT_EQ(2, CopyLogger::TimesCopied);
+}
diff --git a/src/base/utf_offset_string_conversions.cc b/src/base/utf_offset_string_conversions.cc
new file mode 100644
index 0000000..8405303
--- /dev/null
+++ b/src/base/utf_offset_string_conversions.cc
@@ -0,0 +1,167 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/utf_offset_string_conversions.h"
+
+#include <algorithm>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/string_piece.h"
+#include "base/utf_string_conversion_utils.h"
+
+using base::PrepareForUTF16Or32Output;
+using base::PrepareForUTF8Output;
+using base::ReadUnicodeCharacter;
+using base::WriteUnicodeCharacter;
+
+// Converts the given source Unicode character type to the given destination
+// Unicode character type as a STL string. The given input buffer and size
+// determine the source, and the given output STL string will be replaced by
+// the result.
+template<typename SrcChar, typename DestStdString>
+bool ConvertUnicode(const SrcChar* src,
+ size_t src_len,
+ DestStdString* output,
+ std::vector<size_t>* offsets_for_adjustment) {
+ if (offsets_for_adjustment) {
+ std::for_each(offsets_for_adjustment->begin(),
+ offsets_for_adjustment->end(),
+ LimitOffset<DestStdString>(src_len));
+ }
+
+ // ICU requires 32-bit numbers.
+ bool success = true;
+ OffsetAdjuster offset_adjuster(offsets_for_adjustment);
+ int32 src_len32 = static_cast<int32>(src_len);
+ for (int32 i = 0; i < src_len32; i++) {
+ uint32 code_point;
+ size_t original_i = i;
+ size_t chars_written = 0;
+ if (ReadUnicodeCharacter(src, src_len32, &i, &code_point)) {
+ chars_written = WriteUnicodeCharacter(code_point, output);
+ } else {
+ chars_written = WriteUnicodeCharacter(0xFFFD, output);
+ success = false;
+ }
+ if (offsets_for_adjustment) {
+ // NOTE: ReadUnicodeCharacter() adjusts |i| to point _at_ the last
+ // character read, not after it (so that incrementing it in the loop
+ // increment will place it at the right location), so we need to account
+ // for that in determining the amount that was read.
+ offset_adjuster.Add(OffsetAdjuster::Adjustment(original_i,
+ i - original_i + 1, chars_written));
+ }
+ }
+ return success;
+}
+
+bool UTF8ToUTF16AndAdjustOffset(const char* src,
+ size_t src_len,
+ string16* output,
+ size_t* offset_for_adjustment) {
+ std::vector<size_t> offsets;
+ if (offset_for_adjustment)
+ offsets.push_back(*offset_for_adjustment);
+ PrepareForUTF16Or32Output(src, src_len, output);
+ bool ret = ConvertUnicode(src, src_len, output, &offsets);
+ if (offset_for_adjustment)
+ *offset_for_adjustment = offsets[0];
+ return ret;
+}
+
+bool UTF8ToUTF16AndAdjustOffsets(const char* src,
+ size_t src_len,
+ string16* output,
+ std::vector<size_t>* offsets_for_adjustment) {
+ PrepareForUTF16Or32Output(src, src_len, output);
+ return ConvertUnicode(src, src_len, output, offsets_for_adjustment);
+}
+
+string16 UTF8ToUTF16AndAdjustOffset(const base::StringPiece& utf8,
+ size_t* offset_for_adjustment) {
+ std::vector<size_t> offsets;
+ if (offset_for_adjustment)
+ offsets.push_back(*offset_for_adjustment);
+ string16 result;
+ UTF8ToUTF16AndAdjustOffsets(utf8.data(), utf8.length(), &result,
+ &offsets);
+ if (offset_for_adjustment)
+ *offset_for_adjustment = offsets[0];
+ return result;
+}
+
+string16 UTF8ToUTF16AndAdjustOffsets(
+ const base::StringPiece& utf8,
+ std::vector<size_t>* offsets_for_adjustment) {
+ string16 result;
+ UTF8ToUTF16AndAdjustOffsets(utf8.data(), utf8.length(), &result,
+ offsets_for_adjustment);
+ return result;
+}
+
+std::string UTF16ToUTF8AndAdjustOffset(
+ const base::StringPiece16& utf16,
+ size_t* offset_for_adjustment) {
+ std::vector<size_t> offsets;
+ if (offset_for_adjustment)
+ offsets.push_back(*offset_for_adjustment);
+ std::string result = UTF16ToUTF8AndAdjustOffsets(utf16, &offsets);
+ if (offset_for_adjustment)
+ *offset_for_adjustment = offsets[0];
+ return result;
+}
+
+std::string UTF16ToUTF8AndAdjustOffsets(
+ const base::StringPiece16& utf16,
+ std::vector<size_t>* offsets_for_adjustment) {
+ std::string result;
+ PrepareForUTF8Output(utf16.data(), utf16.length(), &result);
+ ConvertUnicode(utf16.data(), utf16.length(), &result, offsets_for_adjustment);
+ return result;
+}
+
+OffsetAdjuster::Adjustment::Adjustment(size_t original_offset,
+ size_t original_length,
+ size_t output_length)
+ : original_offset(original_offset),
+ original_length(original_length),
+ output_length(output_length) {
+}
+
+OffsetAdjuster::OffsetAdjuster(std::vector<size_t>* offsets_for_adjustment)
+ : offsets_for_adjustment_(offsets_for_adjustment) {
+}
+
+OffsetAdjuster::~OffsetAdjuster() {
+ if (!offsets_for_adjustment_ || adjustments_.empty())
+ return;
+ for (std::vector<size_t>::iterator i(offsets_for_adjustment_->begin());
+ i != offsets_for_adjustment_->end(); ++i)
+ AdjustOffset(i);
+}
+
+void OffsetAdjuster::Add(const Adjustment& adjustment) {
+ adjustments_.push_back(adjustment);
+}
+
+void OffsetAdjuster::AdjustOffset(std::vector<size_t>::iterator offset) {
+ if (*offset == string16::npos)
+ return;
+ size_t adjustment = 0;
+ for (std::vector<Adjustment>::const_iterator i = adjustments_.begin();
+ i != adjustments_.end(); ++i) {
+ if (*offset == i->original_offset && i->output_length == 0) {
+ *offset = string16::npos;
+ return;
+ }
+ if (*offset <= i->original_offset)
+ break;
+ if (*offset < (i->original_offset + i->original_length)) {
+ *offset = string16::npos;
+ return;
+ }
+ adjustment += (i->original_length - i->output_length);
+ }
+ *offset -= adjustment;
+}
diff --git a/src/base/utf_offset_string_conversions.h b/src/base/utf_offset_string_conversions.h
new file mode 100644
index 0000000..e9cb558
--- /dev/null
+++ b/src/base/utf_offset_string_conversions.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_UTF_OFFSET_STRING_CONVERSIONS_H_
+#define BASE_UTF_OFFSET_STRING_CONVERSIONS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/string16.h"
+#include "base/string_piece.h"
+
+// Like the conversions in utf_string_conversions.h, but also takes one or more
+// offsets (|offset[s]_for_adjustment|) into the source strings, each offset
+// will be adjusted to point at the same logical place in the result strings.
+// If this isn't possible because an offset points past the end of the source
+// strings or into the middle of a multibyte sequence, the offending offset will
+// be set to string16::npos. |offset[s]_for_adjustment| may be NULL.
+BASE_EXPORT bool UTF8ToUTF16AndAdjustOffset(const char* src,
+ size_t src_len,
+ string16* output,
+ size_t* offset_for_adjustment);
+BASE_EXPORT bool UTF8ToUTF16AndAdjustOffsets(
+ const char* src,
+ size_t src_len,
+ string16* output,
+ std::vector<size_t>* offsets_for_adjustment);
+
+BASE_EXPORT string16 UTF8ToUTF16AndAdjustOffset(const base::StringPiece& utf8,
+ size_t* offset_for_adjustment);
+BASE_EXPORT string16 UTF8ToUTF16AndAdjustOffsets(
+ const base::StringPiece& utf8,
+ std::vector<size_t>* offsets_for_adjustment);
+
+BASE_EXPORT std::string UTF16ToUTF8AndAdjustOffset(
+ const base::StringPiece16& utf16,
+ size_t* offset_for_adjustment);
+BASE_EXPORT std::string UTF16ToUTF8AndAdjustOffsets(
+ const base::StringPiece16& utf16,
+ std::vector<size_t>* offsets_for_adjustment);
+
+// Limiting function callable by std::for_each which will replace any value
+// which is equal to or greater than |limit| with npos.
+template <typename T>
+struct LimitOffset {
+ explicit LimitOffset(size_t limit)
+ : limit_(limit) {}
+
+ void operator()(size_t& offset) {
+ if (offset >= limit_)
+ offset = T::npos;
+ }
+
+ size_t limit_;
+};
+
+// Stack object which, on destruction, will update a vector of offsets based on
+// any supplied adjustments. To use, declare one of these, providing the
+// address of the offset vector to adjust. Then Add() any number of Adjustments
+// (each Adjustment gives the |original_offset| of a substring and the lengths
+// of the substring before and after transforming). When the OffsetAdjuster
+// goes out of scope, all the offsets in the provided vector will be updated.
+class BASE_EXPORT OffsetAdjuster {
+ public:
+ struct BASE_EXPORT Adjustment {
+ Adjustment(size_t original_offset,
+ size_t original_length,
+ size_t output_length);
+
+ size_t original_offset;
+ size_t original_length;
+ size_t output_length;
+ };
+
+ explicit OffsetAdjuster(std::vector<size_t>* offsets_for_adjustment);
+ ~OffsetAdjuster();
+
+ void Add(const Adjustment& adjustment);
+
+ private:
+ void AdjustOffset(std::vector<size_t>::iterator offset);
+
+ std::vector<size_t>* offsets_for_adjustment_;
+ std::vector<Adjustment> adjustments_;
+};
+
+#endif // BASE_UTF_OFFSET_STRING_CONVERSIONS_H_
diff --git a/src/base/utf_offset_string_conversions_unittest.cc b/src/base/utf_offset_string_conversions_unittest.cc
new file mode 100644
index 0000000..ff03a75
--- /dev/null
+++ b/src/base/utf_offset_string_conversions_unittest.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/string_piece.h"
+#include "base/utf_offset_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+static const size_t kNpos = string16::npos;
+
+} // namespace
+
+TEST(UTFOffsetStringConversionsTest, AdjustOffset) {
+ struct UTF8ToUTF16Case {
+ const char* utf8;
+ size_t input_offset;
+ size_t output_offset;
+ } utf8_to_utf16_cases[] = {
+ {"", 0, kNpos},
+ {"\xe4\xbd\xa0\xe5\xa5\xbd", 1, kNpos},
+ {"\xe4\xbd\xa0\xe5\xa5\xbd", 3, 1},
+ {"\xed\xb0\x80z", 3, 1},
+ {"A\xF0\x90\x8C\x80z", 1, 1},
+ {"A\xF0\x90\x8C\x80z", 2, kNpos},
+ {"A\xF0\x90\x8C\x80z", 5, 3},
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(utf8_to_utf16_cases); ++i) {
+ size_t offset = utf8_to_utf16_cases[i].input_offset;
+ UTF8ToUTF16AndAdjustOffset(utf8_to_utf16_cases[i].utf8, &offset);
+ EXPECT_EQ(utf8_to_utf16_cases[i].output_offset, offset);
+ }
+
+ struct UTF16ToUTF8Case {
+ char16 utf16[10];
+ size_t input_offset;
+ size_t output_offset;
+ } utf16_to_utf8_cases[] = {
+ {{}, 0, kNpos},
+ // Converted to 3-byte utf-8 sequences
+ {{0x5909, 0x63DB}, 2, kNpos},
+ {{0x5909, 0x63DB}, 1, 3},
+ // Converted to 2-byte utf-8 sequences
+ {{'A', 0x00bc, 0x00be, 'z'}, 1, 1},
+ {{'A', 0x00bc, 0x00be, 'z'}, 2, 3},
+ {{'A', 0x00bc, 0x00be, 'z'}, 3, 5},
+ // Surrogate pair
+ {{'A', 0xd800, 0xdf00, 'z'}, 1, 1},
+ {{'A', 0xd800, 0xdf00, 'z'}, 2, kNpos},
+ {{'A', 0xd800, 0xdf00, 'z'}, 3, 5},
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(utf16_to_utf8_cases); ++i) {
+ size_t offset = utf16_to_utf8_cases[i].input_offset;
+ UTF16ToUTF8AndAdjustOffset(utf16_to_utf8_cases[i].utf16, &offset);
+ EXPECT_EQ(utf16_to_utf8_cases[i].output_offset, offset);
+ }
+}
+
+TEST(UTFOffsetStringConversionsTest, LimitOffsets) {
+ const size_t kLimit = 10;
+ const size_t kItems = 20;
+ std::vector<size_t> size_ts;
+ for (size_t t = 0; t < kItems; ++t)
+ size_ts.push_back(t);
+ std::for_each(size_ts.begin(), size_ts.end(),
+ LimitOffset<string16>(kLimit));
+ size_t unlimited_count = 0;
+ for (std::vector<size_t>::iterator ti = size_ts.begin(); ti != size_ts.end();
+ ++ti) {
+ if (*ti < kLimit && *ti != kNpos)
+ ++unlimited_count;
+ }
+ EXPECT_EQ(10U, unlimited_count);
+
+ // Reverse the values in the vector and try again.
+ size_ts.clear();
+ for (size_t t = kItems; t > 0; --t)
+ size_ts.push_back(t - 1);
+ std::for_each(size_ts.begin(), size_ts.end(),
+ LimitOffset<string16>(kLimit));
+ unlimited_count = 0;
+ for (std::vector<size_t>::iterator ti = size_ts.begin(); ti != size_ts.end();
+ ++ti) {
+ if (*ti < kLimit && *ti != kNpos)
+ ++unlimited_count;
+ }
+ EXPECT_EQ(10U, unlimited_count);
+}
+
+TEST(UTFOffsetStringConversionsTest, AdjustOffsets) {
+ // Imagine we have strings as shown in the following cases where the
+ // X's represent encoded characters.
+ // 1: abcXXXdef ==> abcXdef
+ {
+ std::vector<size_t> offsets;
+ for (size_t t = 0; t < 9; ++t)
+ offsets.push_back(t);
+ {
+ OffsetAdjuster offset_adjuster(&offsets);
+ offset_adjuster.Add(OffsetAdjuster::Adjustment(3, 3, 1));
+ }
+ size_t expected_1[] = {0, 1, 2, 3, kNpos, kNpos, 4, 5, 6};
+ EXPECT_EQ(offsets.size(), arraysize(expected_1));
+ for (size_t i = 0; i < arraysize(expected_1); ++i)
+ EXPECT_EQ(expected_1[i], offsets[i]);
+ }
+
+ // 2: XXXaXXXXbcXXXXXXXdefXXX ==> XaXXbcXXXXdefX
+ {
+ std::vector<size_t> offsets;
+ for (size_t t = 0; t < 23; ++t)
+ offsets.push_back(t);
+ {
+ OffsetAdjuster offset_adjuster(&offsets);
+ offset_adjuster.Add(OffsetAdjuster::Adjustment(0, 3, 1));
+ offset_adjuster.Add(OffsetAdjuster::Adjustment(4, 4, 2));
+ offset_adjuster.Add(OffsetAdjuster::Adjustment(10, 7, 4));
+ offset_adjuster.Add(OffsetAdjuster::Adjustment(20, 3, 1));
+ }
+ size_t expected_2[] = {0, kNpos, kNpos, 1, 2, kNpos, kNpos, kNpos, 4, 5, 6,
+ kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 10, 11, 12,
+ 13, kNpos, kNpos};
+ EXPECT_EQ(offsets.size(), arraysize(expected_2));
+ for (size_t i = 0; i < arraysize(expected_2); ++i)
+ EXPECT_EQ(expected_2[i], offsets[i]);
+ }
+
+ // 3: XXXaXXXXbcdXXXeXX ==> aXXXXbcdXXXe
+ {
+ std::vector<size_t> offsets;
+ for (size_t t = 0; t < 17; ++t)
+ offsets.push_back(t);
+ {
+ OffsetAdjuster offset_adjuster(&offsets);
+ offset_adjuster.Add(OffsetAdjuster::Adjustment(0, 3, 0));
+ offset_adjuster.Add(OffsetAdjuster::Adjustment(4, 4, 4));
+ offset_adjuster.Add(OffsetAdjuster::Adjustment(11, 3, 3));
+ offset_adjuster.Add(OffsetAdjuster::Adjustment(15, 2, 0));
+ }
+ size_t expected_3[] = {kNpos, kNpos, kNpos, 0, 1, kNpos, kNpos, kNpos, 5, 6,
+ 7, 8, kNpos, kNpos, 11, kNpos, kNpos};
+ EXPECT_EQ(offsets.size(), arraysize(expected_3));
+ for (size_t i = 0; i < arraysize(expected_3); ++i)
+ EXPECT_EQ(expected_3[i], offsets[i]);
+ }
+}
+
+} // namaspace base
diff --git a/src/base/utf_string_conversion_utils.cc b/src/base/utf_string_conversion_utils.cc
new file mode 100644
index 0000000..019df50
--- /dev/null
+++ b/src/base/utf_string_conversion_utils.cc
@@ -0,0 +1,152 @@
+// 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/utf_string_conversion_utils.h"
+
+#include "base/third_party/icu/icu_utf.h"
+
+namespace base {
+
+// ReadUnicodeCharacter --------------------------------------------------------
+
+bool ReadUnicodeCharacter(const char* src,
+ int32 src_len,
+ int32* char_index,
+ uint32* code_point_out) {
+ // U8_NEXT expects to be able to use -1 to signal an error, so we must
+ // use a signed type for code_point. But this function returns false
+ // on error anyway, so code_point_out is unsigned.
+ int32 code_point;
+ CBU8_NEXT(src, *char_index, src_len, code_point);
+ *code_point_out = static_cast<uint32>(code_point);
+
+ // The ICU macro above moves to the next char, we want to point to the last
+ // char consumed.
+ (*char_index)--;
+
+ // Validate the decoded value.
+ return IsValidCodepoint(code_point);
+}
+
+bool ReadUnicodeCharacter(const char16* src,
+ int32 src_len,
+ int32* char_index,
+ uint32* code_point) {
+ if (CBU16_IS_SURROGATE(src[*char_index])) {
+ if (!CBU16_IS_SURROGATE_LEAD(src[*char_index]) ||
+ *char_index + 1 >= src_len ||
+ !CBU16_IS_TRAIL(src[*char_index + 1])) {
+ // Invalid surrogate pair.
+ return false;
+ }
+
+ // Valid surrogate pair.
+ *code_point = CBU16_GET_SUPPLEMENTARY(src[*char_index],
+ src[*char_index + 1]);
+ (*char_index)++;
+ } else {
+ // Not a surrogate, just one 16-bit word.
+ *code_point = src[*char_index];
+ }
+
+ return IsValidCodepoint(*code_point);
+}
+
+#if defined(WCHAR_T_IS_UTF32)
+bool ReadUnicodeCharacter(const wchar_t* src,
+ int32 src_len,
+ int32* char_index,
+ uint32* code_point) {
+ // Conversion is easy since the source is 32-bit.
+ *code_point = src[*char_index];
+
+ // Validate the value.
+ return IsValidCodepoint(*code_point);
+}
+#endif // defined(WCHAR_T_IS_UTF32)
+
+// WriteUnicodeCharacter -------------------------------------------------------
+
+size_t WriteUnicodeCharacter(uint32 code_point, std::string* output) {
+ if (code_point <= 0x7f) {
+ // Fast path the common case of one byte.
+ output->push_back(code_point);
+ return 1;
+ }
+
+
+ // CBU8_APPEND_UNSAFE can append up to 4 bytes.
+ size_t char_offset = output->length();
+ size_t original_char_offset = char_offset;
+ output->resize(char_offset + CBU8_MAX_LENGTH);
+
+ CBU8_APPEND_UNSAFE(&(*output)[0], char_offset, code_point);
+
+ // CBU8_APPEND_UNSAFE will advance our pointer past the inserted character, so
+ // it will represent the new length of the string.
+ output->resize(char_offset);
+ return char_offset - original_char_offset;
+}
+
+size_t WriteUnicodeCharacter(uint32 code_point, string16* output) {
+ if (CBU16_LENGTH(code_point) == 1) {
+ // Thie code point is in the Basic Multilingual Plane (BMP).
+ output->push_back(static_cast<char16>(code_point));
+ return 1;
+ }
+ // Non-BMP characters use a double-character encoding.
+ size_t char_offset = output->length();
+ output->resize(char_offset + CBU16_MAX_LENGTH);
+ CBU16_APPEND_UNSAFE(&(*output)[0], char_offset, code_point);
+ return CBU16_MAX_LENGTH;
+}
+
+// Generalized Unicode converter -----------------------------------------------
+
+template<typename CHAR>
+void PrepareForUTF8Output(const CHAR* src,
+ size_t src_len,
+ std::string* output) {
+ output->clear();
+ if (src_len == 0)
+ return;
+ if (src[0] < 0x80) {
+ // Assume that the entire input will be ASCII.
+ output->reserve(src_len);
+ } else {
+ // Assume that the entire input is non-ASCII and will have 3 bytes per char.
+ output->reserve(src_len * 3);
+ }
+}
+
+// Instantiate versions we know callers will need.
+template void PrepareForUTF8Output(const wchar_t*, size_t, std::string*);
+#if defined(WCHAR_T_IS_UTF32)
+template void PrepareForUTF8Output(const char16*, size_t, std::string*);
+#endif
+
+template<typename STRING>
+void PrepareForUTF16Or32Output(const char* src,
+ size_t src_len,
+ STRING* output) {
+ output->clear();
+ if (src_len == 0)
+ return;
+ if (static_cast<unsigned char>(src[0]) < 0x80) {
+ // Assume the input is all ASCII, which means 1:1 correspondence.
+ output->reserve(src_len);
+ } else {
+ // Otherwise assume that the UTF-8 sequences will have 2 bytes for each
+ // character.
+ output->reserve(src_len / 2);
+ }
+}
+
+// Instantiate versions we know callers will need.
+template void PrepareForUTF16Or32Output(const char*, size_t, std::wstring*);
+#if defined(WCHAR_T_IS_UTF32)
+template void PrepareForUTF16Or32Output(const char*, size_t, string16*);
+#endif
+
+} // namespace base
diff --git a/src/base/utf_string_conversion_utils.h b/src/base/utf_string_conversion_utils.h
new file mode 100644
index 0000000..17e04f1
--- /dev/null
+++ b/src/base/utf_string_conversion_utils.h
@@ -0,0 +1,97 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_UTF_STRING_CONVERSION_UTILS_H_
+#define BASE_UTF_STRING_CONVERSION_UTILS_H_
+
+// This should only be used by the various UTF string conversion files.
+
+#include "base/base_export.h"
+#include "base/string16.h"
+
+namespace base {
+
+inline bool IsValidCodepoint(uint32 code_point) {
+ // Excludes the surrogate code points ([0xD800, 0xDFFF]) and
+ // codepoints larger than 0x10FFFF (the highest codepoint allowed).
+ // Non-characters and unassigned codepoints are allowed.
+ return code_point < 0xD800u ||
+ (code_point >= 0xE000u && code_point <= 0x10FFFFu);
+}
+
+inline bool IsValidCharacter(uint32 code_point) {
+ // Excludes non-characters (U+FDD0..U+FDEF, and all codepoints ending in
+ // 0xFFFE or 0xFFFF) from the set of valid code points.
+ return code_point < 0xD800u || (code_point >= 0xE000u &&
+ code_point < 0xFDD0u) || (code_point > 0xFDEFu &&
+ code_point <= 0x10FFFFu && (code_point & 0xFFFEu) != 0xFFFEu);
+}
+
+// ReadUnicodeCharacter --------------------------------------------------------
+
+// Reads a UTF-8 stream, placing the next code point into the given output
+// |*code_point|. |src| represents the entire string to read, and |*char_index|
+// is the character offset within the string to start reading at. |*char_index|
+// will be updated to index the last character read, such that incrementing it
+// (as in a for loop) will take the reader to the next character.
+//
+// Returns true on success. On false, |*code_point| will be invalid.
+BASE_EXPORT bool ReadUnicodeCharacter(const char* src,
+ int32 src_len,
+ int32* char_index,
+ uint32* code_point_out);
+
+// Reads a UTF-16 character. The usage is the same as the 8-bit version above.
+BASE_EXPORT bool ReadUnicodeCharacter(const char16* src,
+ int32 src_len,
+ int32* char_index,
+ uint32* code_point);
+
+#if defined(WCHAR_T_IS_UTF32)
+// Reads UTF-32 character. The usage is the same as the 8-bit version above.
+BASE_EXPORT bool ReadUnicodeCharacter(const wchar_t* src,
+ int32 src_len,
+ int32* char_index,
+ uint32* code_point);
+#endif // defined(WCHAR_T_IS_UTF32)
+
+// WriteUnicodeCharacter -------------------------------------------------------
+
+// Appends a UTF-8 character to the given 8-bit string. Returns the number of
+// bytes written.
+// TODO(brettw) Bug 79631: This function should not be exposed.
+BASE_EXPORT size_t WriteUnicodeCharacter(uint32 code_point,
+ std::string* output);
+
+// Appends the given code point as a UTF-16 character to the given 16-bit
+// string. Returns the number of 16-bit values written.
+BASE_EXPORT size_t WriteUnicodeCharacter(uint32 code_point, string16* output);
+
+#if defined(WCHAR_T_IS_UTF32)
+// Appends the given UTF-32 character to the given 32-bit string. Returns the
+// number of 32-bit values written.
+inline size_t WriteUnicodeCharacter(uint32 code_point, std::wstring* output) {
+ // This is the easy case, just append the character.
+ output->push_back(code_point);
+ return 1;
+}
+#endif // defined(WCHAR_T_IS_UTF32)
+
+// Generalized Unicode converter -----------------------------------------------
+
+// Guesses the length of the output in UTF-8 in bytes, clears that output
+// string, and reserves that amount of space. We assume that the input
+// character types are unsigned, which will be true for UTF-16 and -32 on our
+// systems.
+template<typename CHAR>
+void PrepareForUTF8Output(const CHAR* src, size_t src_len, std::string* output);
+
+// Prepares an output buffer (containing either UTF-16 or -32 data) given some
+// UTF-8 input that will be converted to it. See PrepareForUTF8Output().
+template<typename STRING>
+void PrepareForUTF16Or32Output(const char* src, size_t src_len, STRING* output);
+
+} // namespace base
+
+#endif // BASE_UTF_STRING_CONVERSION_UTILS_H_
diff --git a/src/base/utf_string_conversions.cc b/src/base/utf_string_conversions.cc
new file mode 100644
index 0000000..7b73696
--- /dev/null
+++ b/src/base/utf_string_conversions.cc
@@ -0,0 +1,186 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/utf_string_conversions.h"
+
+#include "base/string_piece.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversion_utils.h"
+
+using base::PrepareForUTF8Output;
+using base::PrepareForUTF16Or32Output;
+using base::ReadUnicodeCharacter;
+using base::WriteUnicodeCharacter;
+
+namespace {
+
+// Generalized Unicode converter -----------------------------------------------
+
+// Converts the given source Unicode character type to the given destination
+// Unicode character type as a STL string. The given input buffer and size
+// determine the source, and the given output STL string will be replaced by
+// the result.
+template<typename SRC_CHAR, typename DEST_STRING>
+bool ConvertUnicode(const SRC_CHAR* src,
+ size_t src_len,
+ DEST_STRING* output) {
+ // ICU requires 32-bit numbers.
+ bool success = true;
+ int32 src_len32 = static_cast<int32>(src_len);
+ for (int32 i = 0; i < src_len32; i++) {
+ uint32 code_point;
+ if (ReadUnicodeCharacter(src, src_len32, &i, &code_point)) {
+ WriteUnicodeCharacter(code_point, output);
+ } else {
+ WriteUnicodeCharacter(0xFFFD, output);
+ success = false;
+ }
+ }
+
+ return success;
+}
+
+} // namespace
+
+// UTF-8 <-> Wide --------------------------------------------------------------
+
+bool WideToUTF8(const wchar_t* src, size_t src_len, std::string* output) {
+ PrepareForUTF8Output(src, src_len, output);
+ return ConvertUnicode(src, src_len, output);
+}
+
+std::string WideToUTF8(const std::wstring& wide) {
+ std::string ret;
+ // Ignore the success flag of this call, it will do the best it can for
+ // invalid input, which is what we want here.
+ WideToUTF8(wide.data(), wide.length(), &ret);
+ return ret;
+}
+
+bool UTF8ToWide(const char* src, size_t src_len, std::wstring* output) {
+ PrepareForUTF16Or32Output(src, src_len, output);
+ return ConvertUnicode(src, src_len, output);
+}
+
+std::wstring UTF8ToWide(const base::StringPiece& utf8) {
+ std::wstring ret;
+ UTF8ToWide(utf8.data(), utf8.length(), &ret);
+ return ret;
+}
+
+// UTF-16 <-> Wide -------------------------------------------------------------
+
+#if defined(WCHAR_T_IS_UTF16)
+
+// When wide == UTF-16, then conversions are a NOP.
+bool WideToUTF16(const wchar_t* src, size_t src_len, string16* output) {
+ output->assign(src, src_len);
+ return true;
+}
+
+string16 WideToUTF16(const std::wstring& wide) {
+ return wide;
+}
+
+bool UTF16ToWide(const char16* src, size_t src_len, std::wstring* output) {
+ output->assign(src, src_len);
+ return true;
+}
+
+std::wstring UTF16ToWide(const string16& utf16) {
+ return utf16;
+}
+
+#elif defined(WCHAR_T_IS_UTF32)
+
+bool WideToUTF16(const wchar_t* src, size_t src_len, string16* output) {
+ output->clear();
+ // Assume that normally we won't have any non-BMP characters so the counts
+ // will be the same.
+ output->reserve(src_len);
+ return ConvertUnicode(src, src_len, output);
+}
+
+string16 WideToUTF16(const std::wstring& wide) {
+ string16 ret;
+ WideToUTF16(wide.data(), wide.length(), &ret);
+ return ret;
+}
+
+bool UTF16ToWide(const char16* src, size_t src_len, std::wstring* output) {
+ output->clear();
+ // Assume that normally we won't have any non-BMP characters so the counts
+ // will be the same.
+ output->reserve(src_len);
+ return ConvertUnicode(src, src_len, output);
+}
+
+std::wstring UTF16ToWide(const string16& utf16) {
+ std::wstring ret;
+ UTF16ToWide(utf16.data(), utf16.length(), &ret);
+ return ret;
+}
+
+#endif // defined(WCHAR_T_IS_UTF32)
+
+// UTF16 <-> UTF8 --------------------------------------------------------------
+
+#if defined(WCHAR_T_IS_UTF32)
+
+bool UTF8ToUTF16(const char* src, size_t src_len, string16* output) {
+ PrepareForUTF16Or32Output(src, src_len, output);
+ return ConvertUnicode(src, src_len, output);
+}
+
+string16 UTF8ToUTF16(const base::StringPiece& utf8) {
+ string16 ret;
+ // Ignore the success flag of this call, it will do the best it can for
+ // invalid input, which is what we want here.
+ UTF8ToUTF16(utf8.data(), utf8.length(), &ret);
+ return ret;
+}
+
+bool UTF16ToUTF8(const char16* src, size_t src_len, std::string* output) {
+ PrepareForUTF8Output(src, src_len, output);
+ return ConvertUnicode(src, src_len, output);
+}
+
+std::string UTF16ToUTF8(const string16& utf16) {
+ std::string ret;
+ // Ignore the success flag of this call, it will do the best it can for
+ // invalid input, which is what we want here.
+ UTF16ToUTF8(utf16.data(), utf16.length(), &ret);
+ return ret;
+}
+
+#elif defined(WCHAR_T_IS_UTF16)
+// Easy case since we can use the "wide" versions we already wrote above.
+
+bool UTF8ToUTF16(const char* src, size_t src_len, string16* output) {
+ return UTF8ToWide(src, src_len, output);
+}
+
+string16 UTF8ToUTF16(const base::StringPiece& utf8) {
+ return UTF8ToWide(utf8);
+}
+
+bool UTF16ToUTF8(const char16* src, size_t src_len, std::string* output) {
+ return WideToUTF8(src, src_len, output);
+}
+
+std::string UTF16ToUTF8(const string16& utf16) {
+ return WideToUTF8(utf16);
+}
+
+#endif
+
+std::wstring ASCIIToWide(const base::StringPiece& ascii) {
+ DCHECK(IsStringASCII(ascii)) << ascii;
+ return std::wstring(ascii.begin(), ascii.end());
+}
+
+string16 ASCIIToUTF16(const base::StringPiece& ascii) {
+ DCHECK(IsStringASCII(ascii)) << ascii;
+ return string16(ascii.begin(), ascii.end());
+}
diff --git a/src/base/utf_string_conversions.h b/src/base/utf_string_conversions.h
new file mode 100644
index 0000000..5a391fa
--- /dev/null
+++ b/src/base/utf_string_conversions.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_UTF_STRING_CONVERSIONS_H_
+#define BASE_UTF_STRING_CONVERSIONS_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/string16.h"
+#include "base/string_piece.h"
+
+// These convert between UTF-8, -16, and -32 strings. They are potentially slow,
+// so avoid unnecessary conversions. The low-level versions return a boolean
+// indicating whether the conversion was 100% valid. In this case, it will still
+// do the best it can and put the result in the output buffer. The versions that
+// return strings ignore this error and just return the best conversion
+// possible.
+BASE_EXPORT bool WideToUTF8(const wchar_t* src, size_t src_len,
+ std::string* output);
+BASE_EXPORT std::string WideToUTF8(const std::wstring& wide);
+BASE_EXPORT bool UTF8ToWide(const char* src, size_t src_len,
+ std::wstring* output);
+BASE_EXPORT std::wstring UTF8ToWide(const base::StringPiece& utf8);
+
+BASE_EXPORT bool WideToUTF16(const wchar_t* src, size_t src_len,
+ string16* output);
+BASE_EXPORT string16 WideToUTF16(const std::wstring& wide);
+BASE_EXPORT bool UTF16ToWide(const char16* src, size_t src_len,
+ std::wstring* output);
+BASE_EXPORT std::wstring UTF16ToWide(const string16& utf16);
+
+BASE_EXPORT bool UTF8ToUTF16(const char* src, size_t src_len, string16* output);
+BASE_EXPORT string16 UTF8ToUTF16(const base::StringPiece& utf8);
+BASE_EXPORT bool UTF16ToUTF8(const char16* src, size_t src_len,
+ std::string* output);
+BASE_EXPORT std::string UTF16ToUTF8(const string16& utf16);
+
+// We are trying to get rid of wstring as much as possible, but it's too big
+// a mess to do it all at once. These conversions should be used when we
+// really should just be passing a string16 around, but we haven't finished
+// porting whatever module uses wstring and the conversion is being used as a
+// stopcock. This makes it easy to grep for the ones that should be removed.
+#if defined(OS_WIN)
+# define WideToUTF16Hack
+# define UTF16ToWideHack
+#else
+# define WideToUTF16Hack WideToUTF16
+# define UTF16ToWideHack UTF16ToWide
+#endif
+
+// These convert an ASCII string, typically a hardcoded constant, to a
+// UTF16/Wide string.
+BASE_EXPORT std::wstring ASCIIToWide(const base::StringPiece& ascii);
+BASE_EXPORT string16 ASCIIToUTF16(const base::StringPiece& ascii);
+
+#endif // BASE_UTF_STRING_CONVERSIONS_H_
diff --git a/src/base/utf_string_conversions_unittest.cc b/src/base/utf_string_conversions_unittest.cc
new file mode 100644
index 0000000..7c180a6
--- /dev/null
+++ b/src/base/utf_string_conversions_unittest.cc
@@ -0,0 +1,211 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/string_piece.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+const wchar_t* const kConvertRoundtripCases[] = {
+ L"Google Video",
+ // "网页 图片 资讯更多 »"
+ L"\x7f51\x9875\x0020\x56fe\x7247\x0020\x8d44\x8baf\x66f4\x591a\x0020\x00bb",
+ // "Παγκόσμιος Ιστός"
+ L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
+ L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2",
+ // "Поиск страниц на русском"
+ L"\x041f\x043e\x0438\x0441\x043a\x0020\x0441\x0442"
+ L"\x0440\x0430\x043d\x0438\x0446\x0020\x043d\x0430"
+ L"\x0020\x0440\x0443\x0441\x0441\x043a\x043e\x043c",
+ // "전체서비스"
+ L"\xc804\xccb4\xc11c\xbe44\xc2a4",
+
+ // Test characters that take more than 16 bits. This will depend on whether
+ // wchar_t is 16 or 32 bits.
+#if defined(WCHAR_T_IS_UTF16)
+ L"\xd800\xdf00",
+ // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+ L"\xd807\xdd40\xd807\xdd41\xd807\xdd42\xd807\xdd43\xd807\xdd44",
+#elif defined(WCHAR_T_IS_UTF32)
+ L"\x10300",
+ // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+ L"\x11d40\x11d41\x11d42\x11d43\x11d44",
+#endif
+};
+
+} // namespace
+
+TEST(UTFStringConversionsTest, ConvertUTF8AndWide) {
+ // we round-trip all the wide strings through UTF-8 to make sure everything
+ // agrees on the conversion. This uses the stream operators to test them
+ // simultaneously.
+ for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) {
+ std::ostringstream utf8;
+ utf8 << WideToUTF8(kConvertRoundtripCases[i]);
+ std::wostringstream wide;
+ wide << UTF8ToWide(utf8.str());
+
+ EXPECT_EQ(kConvertRoundtripCases[i], wide.str());
+ }
+}
+
+TEST(UTFStringConversionsTest, ConvertUTF8AndWideEmptyString) {
+ // An empty std::wstring should be converted to an empty std::string,
+ // and vice versa.
+ std::wstring wempty;
+ std::string empty;
+ EXPECT_EQ(empty, WideToUTF8(wempty));
+ EXPECT_EQ(wempty, UTF8ToWide(empty));
+}
+
+TEST(UTFStringConversionsTest, ConvertUTF8ToWide) {
+ struct UTF8ToWideCase {
+ const char* utf8;
+ const wchar_t* wide;
+ bool success;
+ } convert_cases[] = {
+ // Regular UTF-8 input.
+ {"\xe4\xbd\xa0\xe5\xa5\xbd", L"\x4f60\x597d", true},
+ // Non-character is passed through.
+ {"\xef\xbf\xbfHello", L"\xffffHello", true},
+ // Truncated UTF-8 sequence.
+ {"\xe4\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
+ // Truncated off the end.
+ {"\xe5\xa5\xbd\xe4\xa0", L"\x597d\xfffd", false},
+ // Non-shortest-form UTF-8.
+ {"\xf0\x84\xbd\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
+ // This UTF-8 character decodes to a UTF-16 surrogate, which is illegal.
+ {"\xed\xb0\x80", L"\xfffd", false},
+ // Non-BMP characters. The second is a non-character regarded as valid.
+ // The result will either be in UTF-16 or UTF-32.
+#if defined(WCHAR_T_IS_UTF16)
+ {"A\xF0\x90\x8C\x80z", L"A\xd800\xdf00z", true},
+ {"A\xF4\x8F\xBF\xBEz", L"A\xdbff\xdffez", true},
+#elif defined(WCHAR_T_IS_UTF32)
+ {"A\xF0\x90\x8C\x80z", L"A\x10300z", true},
+ {"A\xF4\x8F\xBF\xBEz", L"A\x10fffez", true},
+#endif
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(convert_cases); i++) {
+ std::wstring converted;
+ EXPECT_EQ(convert_cases[i].success,
+ UTF8ToWide(convert_cases[i].utf8,
+ strlen(convert_cases[i].utf8),
+ &converted));
+ std::wstring expected(convert_cases[i].wide);
+ EXPECT_EQ(expected, converted);
+ }
+
+ // Manually test an embedded NULL.
+ std::wstring converted;
+ EXPECT_TRUE(UTF8ToWide("\00Z\t", 3, &converted));
+ ASSERT_EQ(3U, converted.length());
+ EXPECT_EQ(static_cast<wchar_t>(0), converted[0]);
+ EXPECT_EQ('Z', converted[1]);
+ EXPECT_EQ('\t', converted[2]);
+
+ // Make sure that conversion replaces, not appends.
+ EXPECT_TRUE(UTF8ToWide("B", 1, &converted));
+ ASSERT_EQ(1U, converted.length());
+ EXPECT_EQ('B', converted[0]);
+}
+
+#if defined(WCHAR_T_IS_UTF16)
+// This test is only valid when wchar_t == UTF-16.
+TEST(UTFStringConversionsTest, ConvertUTF16ToUTF8) {
+ struct WideToUTF8Case {
+ const wchar_t* utf16;
+ const char* utf8;
+ bool success;
+ } convert_cases[] = {
+ // Regular UTF-16 input.
+ {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
+ // Test a non-BMP character.
+ {L"\xd800\xdf00", "\xF0\x90\x8C\x80", true},
+ // Non-characters are passed through.
+ {L"\xffffHello", "\xEF\xBF\xBFHello", true},
+ {L"\xdbff\xdffeHello", "\xF4\x8F\xBF\xBEHello", true},
+ // The first character is a truncated UTF-16 character.
+ {L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd", false},
+ // Truncated at the end.
+ {L"\x597d\xd800", "\xe5\xa5\xbd\xef\xbf\xbd", false},
+ };
+
+ for (int i = 0; i < arraysize(convert_cases); i++) {
+ std::string converted;
+ EXPECT_EQ(convert_cases[i].success,
+ WideToUTF8(convert_cases[i].utf16,
+ wcslen(convert_cases[i].utf16),
+ &converted));
+ std::string expected(convert_cases[i].utf8);
+ EXPECT_EQ(expected, converted);
+ }
+}
+
+#elif defined(WCHAR_T_IS_UTF32)
+// This test is only valid when wchar_t == UTF-32.
+TEST(UTFStringConversionsTest, ConvertUTF32ToUTF8) {
+ struct WideToUTF8Case {
+ const wchar_t* utf32;
+ const char* utf8;
+ bool success;
+ } convert_cases[] = {
+ // Regular 16-bit input.
+ {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
+ // Test a non-BMP character.
+ {L"A\x10300z", "A\xF0\x90\x8C\x80z", true},
+ // Non-characters are passed through.
+ {L"\xffffHello", "\xEF\xBF\xBFHello", true},
+ {L"\x10fffeHello", "\xF4\x8F\xBF\xBEHello", true},
+ // Invalid Unicode code points.
+ {L"\xfffffffHello", "\xEF\xBF\xBDHello", false},
+ // The first character is a truncated UTF-16 character.
+ {L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd", false},
+ {L"\xdc01Hello", "\xef\xbf\xbdHello", false},
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(convert_cases); i++) {
+ std::string converted;
+ EXPECT_EQ(convert_cases[i].success,
+ WideToUTF8(convert_cases[i].utf32,
+ wcslen(convert_cases[i].utf32),
+ &converted));
+ std::string expected(convert_cases[i].utf8);
+ EXPECT_EQ(expected, converted);
+ }
+}
+#endif // defined(WCHAR_T_IS_UTF32)
+
+TEST(UTFStringConversionsTest, ConvertMultiString) {
+ static wchar_t wmulti[] = {
+ L'f', L'o', L'o', L'\0',
+ L'b', L'a', L'r', L'\0',
+ L'b', L'a', L'z', L'\0',
+ L'\0'
+ };
+ static char multi[] = {
+ 'f', 'o', 'o', '\0',
+ 'b', 'a', 'r', '\0',
+ 'b', 'a', 'z', '\0',
+ '\0'
+ };
+ std::wstring wmultistring;
+ memcpy(WriteInto(&wmultistring, arraysize(wmulti)), wmulti, sizeof(wmulti));
+ EXPECT_EQ(arraysize(wmulti) - 1, wmultistring.length());
+ std::string expected;
+ memcpy(WriteInto(&expected, arraysize(multi)), multi, sizeof(multi));
+ EXPECT_EQ(arraysize(multi) - 1, expected.length());
+ const std::string& converted = WideToUTF8(wmultistring);
+ EXPECT_EQ(arraysize(multi) - 1, converted.length());
+ EXPECT_EQ(expected, converted);
+}
+
+} // base
diff --git a/src/base/value_conversions.cc b/src/base/value_conversions.cc
new file mode 100644
index 0000000..6bde1ae
--- /dev/null
+++ b/src/base/value_conversions.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2012 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/value_conversions.h"
+
+#include "base/file_path.h"
+#include "base/string_number_conversions.h"
+#include "base/time.h"
+#include "base/values.h"
+
+namespace base {
+
+// |Value| internally stores strings in UTF-8, so we have to convert from the
+// system native code to UTF-8 and back.
+StringValue* CreateFilePathValue(const FilePath& in_value) {
+ return new StringValue(in_value.AsUTF8Unsafe());
+}
+
+bool GetValueAsFilePath(const Value& value, FilePath* file_path) {
+ std::string str;
+ if (!value.GetAsString(&str))
+ return false;
+ if (file_path)
+ *file_path = FilePath::FromUTF8Unsafe(str);
+ return true;
+}
+
+// |Value| does not support 64-bit integers, and doubles do not have enough
+// precision, so we store the 64-bit time value as a string instead.
+StringValue* CreateTimeDeltaValue(const TimeDelta& time) {
+ std::string string_value = base::Int64ToString(time.ToInternalValue());
+ return new StringValue(string_value);
+}
+
+bool GetValueAsTimeDelta(const Value& value, TimeDelta* time) {
+ std::string str;
+ int64 int_value;
+ if (!value.GetAsString(&str) || !base::StringToInt64(str, &int_value))
+ return false;
+ if (time)
+ *time = TimeDelta::FromInternalValue(int_value);
+ return true;
+}
+
+} // namespace base
diff --git a/src/base/value_conversions.h b/src/base/value_conversions.h
new file mode 100644
index 0000000..99cd514
--- /dev/null
+++ b/src/base/value_conversions.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 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_VALUE_CONVERSIONS_H_
+#define BASE_VALUE_CONVERSIONS_H_
+
+// This file contains methods to convert things to a |Value| and back.
+
+#include "base/base_export.h"
+
+class FilePath;
+
+namespace base {
+
+class TimeDelta;
+class StringValue;
+class Value;
+
+// The caller takes ownership of the returned value.
+BASE_EXPORT StringValue* CreateFilePathValue(const FilePath& in_value);
+BASE_EXPORT bool GetValueAsFilePath(const Value& value, FilePath* file_path);
+
+BASE_EXPORT StringValue* CreateTimeDeltaValue(const TimeDelta& time);
+BASE_EXPORT bool GetValueAsTimeDelta(const Value& value, TimeDelta* time);
+
+} // namespace
+
+#endif // BASE_VALUE_CONVERSIONS_H_
diff --git a/src/base/values.cc b/src/base/values.cc
new file mode 100644
index 0000000..459d56f
--- /dev/null
+++ b/src/base/values.cc
@@ -0,0 +1,1144 @@
+// Copyright (c) 2012 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/values.h"
+
+#include <algorithm>
+#include <ostream>
+
+#include "base/float_util.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+
+namespace {
+
+// Make a deep copy of |node|, but don't include empty lists or dictionaries
+// in the copy. It's possible for this function to return NULL and it
+// expects |node| to always be non-NULL.
+Value* CopyWithoutEmptyChildren(Value* node) {
+ DCHECK(node);
+ switch (node->GetType()) {
+ case Value::TYPE_LIST: {
+ ListValue* list = static_cast<ListValue*>(node);
+ ListValue* copy = new ListValue;
+ for (ListValue::const_iterator it = list->begin(); it != list->end();
+ ++it) {
+ Value* child_copy = CopyWithoutEmptyChildren(*it);
+ if (child_copy)
+ copy->Append(child_copy);
+ }
+ if (!copy->empty())
+ return copy;
+
+ delete copy;
+ return NULL;
+ }
+
+ case Value::TYPE_DICTIONARY: {
+ DictionaryValue* dict = static_cast<DictionaryValue*>(node);
+ DictionaryValue* copy = new DictionaryValue;
+ for (DictionaryValue::key_iterator it = dict->begin_keys();
+ it != dict->end_keys(); ++it) {
+ Value* child = NULL;
+ bool rv = dict->GetWithoutPathExpansion(*it, &child);
+ DCHECK(rv);
+ Value* child_copy = CopyWithoutEmptyChildren(child);
+ if (child_copy)
+ copy->SetWithoutPathExpansion(*it, child_copy);
+ }
+ if (!copy->empty())
+ return copy;
+
+ delete copy;
+ return NULL;
+ }
+
+ default:
+ // For everything else, just make a copy.
+ return node->DeepCopy();
+ }
+}
+
+// A small functor for comparing Values for std::find_if and similar.
+class ValueEquals {
+ public:
+ // Pass the value against which all consecutive calls of the () operator will
+ // compare their argument to. This Value object must not be destroyed while
+ // the ValueEquals is in use.
+ ValueEquals(const Value* first) : first_(first) { }
+
+ bool operator ()(const Value* second) const {
+ return first_->Equals(second);
+ }
+
+ private:
+ const Value* first_;
+};
+
+} // namespace
+
+namespace base {
+
+///////////////////// Value ////////////////////
+
+Value::~Value() {
+}
+
+// static
+Value* Value::CreateNullValue() {
+ return new Value(TYPE_NULL);
+}
+
+// static
+FundamentalValue* Value::CreateBooleanValue(bool in_value) {
+ return new FundamentalValue(in_value);
+}
+
+// static
+FundamentalValue* Value::CreateIntegerValue(int in_value) {
+ return new FundamentalValue(in_value);
+}
+
+// static
+FundamentalValue* Value::CreateDoubleValue(double in_value) {
+ return new FundamentalValue(in_value);
+}
+
+// static
+StringValue* Value::CreateStringValue(const std::string& in_value) {
+ return new StringValue(in_value);
+}
+
+// static
+StringValue* Value::CreateStringValue(const string16& in_value) {
+ return new StringValue(in_value);
+}
+
+bool Value::GetAsBoolean(bool* out_value) const {
+ return false;
+}
+
+bool Value::GetAsInteger(int* out_value) const {
+ return false;
+}
+
+bool Value::GetAsDouble(double* out_value) const {
+ return false;
+}
+
+bool Value::GetAsString(std::string* out_value) const {
+ return false;
+}
+
+bool Value::GetAsString(string16* out_value) const {
+ return false;
+}
+
+bool Value::GetAsList(ListValue** out_value) {
+ return false;
+}
+
+bool Value::GetAsList(const ListValue** out_value) const {
+ return false;
+}
+
+bool Value::GetAsDictionary(DictionaryValue** out_value) {
+ return false;
+}
+
+bool Value::GetAsDictionary(const DictionaryValue** out_value) const {
+ return false;
+}
+
+Value* Value::DeepCopy() const {
+ // This method should only be getting called for null Values--all subclasses
+ // need to provide their own implementation;.
+ DCHECK(IsType(TYPE_NULL));
+ return CreateNullValue();
+}
+
+bool Value::Equals(const Value* other) const {
+ // This method should only be getting called for null Values--all subclasses
+ // need to provide their own implementation;.
+ DCHECK(IsType(TYPE_NULL));
+ return other->IsType(TYPE_NULL);
+}
+
+// static
+bool Value::Equals(const Value* a, const Value* b) {
+ if ((a == NULL) && (b == NULL)) return true;
+ if ((a == NULL) ^ (b == NULL)) return false;
+ return a->Equals(b);
+}
+
+Value::Value(Type type) : type_(type) {}
+
+Value::Value(const Value& that) : type_(that.type_) {}
+
+Value& Value::operator=(const Value& that) {
+ type_ = that.type_;
+ return *this;
+}
+
+///////////////////// FundamentalValue ////////////////////
+
+FundamentalValue::FundamentalValue(bool in_value)
+ : Value(TYPE_BOOLEAN), boolean_value_(in_value) {
+}
+
+FundamentalValue::FundamentalValue(int in_value)
+ : Value(TYPE_INTEGER), integer_value_(in_value) {
+}
+
+FundamentalValue::FundamentalValue(double in_value)
+ : Value(TYPE_DOUBLE), double_value_(in_value) {
+ if (!IsFinite(double_value_)) {
+ NOTREACHED() << "Non-finite (i.e. NaN or positive/negative infinity) "
+ << "values cannot be represented in JSON";
+ double_value_ = 0.0;
+ }
+}
+
+FundamentalValue::~FundamentalValue() {
+}
+
+bool FundamentalValue::GetAsBoolean(bool* out_value) const {
+ if (out_value && IsType(TYPE_BOOLEAN))
+ *out_value = boolean_value_;
+ return (IsType(TYPE_BOOLEAN));
+}
+
+bool FundamentalValue::GetAsInteger(int* out_value) const {
+ if (out_value && IsType(TYPE_INTEGER))
+ *out_value = integer_value_;
+ return (IsType(TYPE_INTEGER));
+}
+
+bool FundamentalValue::GetAsDouble(double* out_value) const {
+ if (out_value && IsType(TYPE_DOUBLE))
+ *out_value = double_value_;
+ else if (out_value && IsType(TYPE_INTEGER))
+ *out_value = integer_value_;
+ return (IsType(TYPE_DOUBLE) || IsType(TYPE_INTEGER));
+}
+
+FundamentalValue* FundamentalValue::DeepCopy() const {
+ switch (GetType()) {
+ case TYPE_BOOLEAN:
+ return CreateBooleanValue(boolean_value_);
+
+ case TYPE_INTEGER:
+ return CreateIntegerValue(integer_value_);
+
+ case TYPE_DOUBLE:
+ return CreateDoubleValue(double_value_);
+
+ default:
+ NOTREACHED();
+ return NULL;
+ }
+}
+
+bool FundamentalValue::Equals(const Value* other) const {
+ if (other->GetType() != GetType())
+ return false;
+
+ switch (GetType()) {
+ case TYPE_BOOLEAN: {
+ bool lhs, rhs;
+ return GetAsBoolean(&lhs) && other->GetAsBoolean(&rhs) && lhs == rhs;
+ }
+ case TYPE_INTEGER: {
+ int lhs, rhs;
+ return GetAsInteger(&lhs) && other->GetAsInteger(&rhs) && lhs == rhs;
+ }
+ case TYPE_DOUBLE: {
+ double lhs, rhs;
+ return GetAsDouble(&lhs) && other->GetAsDouble(&rhs) && lhs == rhs;
+ }
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+///////////////////// StringValue ////////////////////
+
+StringValue::StringValue(const std::string& in_value)
+ : Value(TYPE_STRING),
+ value_(in_value) {
+ DCHECK(IsStringUTF8(in_value));
+}
+
+StringValue::StringValue(const string16& in_value)
+ : Value(TYPE_STRING),
+ value_(UTF16ToUTF8(in_value)) {
+}
+
+StringValue::~StringValue() {
+}
+
+bool StringValue::GetAsString(std::string* out_value) const {
+ if (out_value)
+ *out_value = value_;
+ return true;
+}
+
+bool StringValue::GetAsString(string16* out_value) const {
+ if (out_value)
+ *out_value = UTF8ToUTF16(value_);
+ return true;
+}
+
+StringValue* StringValue::DeepCopy() const {
+ return CreateStringValue(value_);
+}
+
+bool StringValue::Equals(const Value* other) const {
+ if (other->GetType() != GetType())
+ return false;
+ std::string lhs, rhs;
+ return GetAsString(&lhs) && other->GetAsString(&rhs) && lhs == rhs;
+}
+
+///////////////////// BinaryValue ////////////////////
+
+BinaryValue::~BinaryValue() {
+ DCHECK(buffer_);
+ if (buffer_)
+ delete[] buffer_;
+}
+
+// static
+BinaryValue* BinaryValue::Create(char* buffer, size_t size) {
+ if (!buffer)
+ return NULL;
+
+ return new BinaryValue(buffer, size);
+}
+
+// static
+BinaryValue* BinaryValue::CreateWithCopiedBuffer(const char* buffer,
+ size_t size) {
+ if (!buffer)
+ return NULL;
+
+ char* buffer_copy = new char[size];
+ memcpy(buffer_copy, buffer, size);
+ return new BinaryValue(buffer_copy, size);
+}
+
+BinaryValue* BinaryValue::DeepCopy() const {
+ return CreateWithCopiedBuffer(buffer_, size_);
+}
+
+bool BinaryValue::Equals(const Value* other) const {
+ if (other->GetType() != GetType())
+ return false;
+ const BinaryValue* other_binary = static_cast<const BinaryValue*>(other);
+ if (other_binary->size_ != size_)
+ return false;
+ return !memcmp(buffer_, other_binary->buffer_, size_);
+}
+
+BinaryValue::BinaryValue(char* buffer, size_t size)
+ : Value(TYPE_BINARY),
+ buffer_(buffer),
+ size_(size) {
+ DCHECK(buffer_);
+}
+
+///////////////////// DictionaryValue ////////////////////
+
+DictionaryValue::DictionaryValue()
+ : Value(TYPE_DICTIONARY) {
+}
+
+DictionaryValue::~DictionaryValue() {
+ Clear();
+}
+
+bool DictionaryValue::GetAsDictionary(DictionaryValue** out_value) {
+ if (out_value)
+ *out_value = this;
+ return true;
+}
+
+bool DictionaryValue::GetAsDictionary(const DictionaryValue** out_value) const {
+ if (out_value)
+ *out_value = this;
+ return true;
+}
+
+bool DictionaryValue::HasKey(const std::string& key) const {
+ DCHECK(IsStringUTF8(key));
+ ValueMap::const_iterator current_entry = dictionary_.find(key);
+ DCHECK((current_entry == dictionary_.end()) || current_entry->second);
+ return current_entry != dictionary_.end();
+}
+
+void DictionaryValue::Clear() {
+ ValueMap::iterator dict_iterator = dictionary_.begin();
+ while (dict_iterator != dictionary_.end()) {
+ delete dict_iterator->second;
+ ++dict_iterator;
+ }
+
+ dictionary_.clear();
+}
+
+void DictionaryValue::Set(const std::string& path, Value* in_value) {
+ DCHECK(IsStringUTF8(path));
+ DCHECK(in_value);
+
+ std::string current_path(path);
+ DictionaryValue* current_dictionary = this;
+ for (size_t delimiter_position = current_path.find('.');
+ delimiter_position != std::string::npos;
+ delimiter_position = current_path.find('.')) {
+ // Assume that we're indexing into a dictionary.
+ std::string key(current_path, 0, delimiter_position);
+ DictionaryValue* child_dictionary = NULL;
+ if (!current_dictionary->GetDictionary(key, &child_dictionary)) {
+ child_dictionary = new DictionaryValue;
+ current_dictionary->SetWithoutPathExpansion(key, child_dictionary);
+ }
+
+ current_dictionary = child_dictionary;
+ current_path.erase(0, delimiter_position + 1);
+ }
+
+ current_dictionary->SetWithoutPathExpansion(current_path, in_value);
+}
+
+void DictionaryValue::SetBoolean(const std::string& path, bool in_value) {
+ Set(path, CreateBooleanValue(in_value));
+}
+
+void DictionaryValue::SetInteger(const std::string& path, int in_value) {
+ Set(path, CreateIntegerValue(in_value));
+}
+
+void DictionaryValue::SetDouble(const std::string& path, double in_value) {
+ Set(path, CreateDoubleValue(in_value));
+}
+
+void DictionaryValue::SetString(const std::string& path,
+ const std::string& in_value) {
+ Set(path, CreateStringValue(in_value));
+}
+
+void DictionaryValue::SetString(const std::string& path,
+ const string16& in_value) {
+ Set(path, CreateStringValue(in_value));
+}
+
+void DictionaryValue::SetWithoutPathExpansion(const std::string& key,
+ Value* in_value) {
+ // If there's an existing value here, we need to delete it, because
+ // we own all our children.
+ std::pair<ValueMap::iterator, bool> ins_res =
+ dictionary_.insert(std::make_pair(key, in_value));
+ if (!ins_res.second) {
+ DCHECK_NE(ins_res.first->second, in_value); // This would be bogus
+ delete ins_res.first->second;
+ ins_res.first->second = in_value;
+ }
+}
+
+void DictionaryValue::SetBooleanWithoutPathExpansion(
+ const std::string& path, bool in_value) {
+ SetWithoutPathExpansion(path, CreateBooleanValue(in_value));
+}
+
+void DictionaryValue::SetIntegerWithoutPathExpansion(
+ const std::string& path, int in_value) {
+ SetWithoutPathExpansion(path, CreateIntegerValue(in_value));
+}
+
+void DictionaryValue::SetDoubleWithoutPathExpansion(
+ const std::string& path, double in_value) {
+ SetWithoutPathExpansion(path, CreateDoubleValue(in_value));
+}
+
+void DictionaryValue::SetStringWithoutPathExpansion(
+ const std::string& path, const std::string& in_value) {
+ SetWithoutPathExpansion(path, CreateStringValue(in_value));
+}
+
+void DictionaryValue::SetStringWithoutPathExpansion(
+ const std::string& path, const string16& in_value) {
+ SetWithoutPathExpansion(path, CreateStringValue(in_value));
+}
+
+bool DictionaryValue::Get(
+ const std::string& path, const Value** out_value) const {
+ DCHECK(IsStringUTF8(path));
+ std::string current_path(path);
+ const DictionaryValue* current_dictionary = this;
+ for (size_t delimiter_position = current_path.find('.');
+ delimiter_position != std::string::npos;
+ delimiter_position = current_path.find('.')) {
+ const DictionaryValue* child_dictionary = NULL;
+ if (!current_dictionary->GetDictionary(
+ current_path.substr(0, delimiter_position), &child_dictionary))
+ return false;
+
+ current_dictionary = child_dictionary;
+ current_path.erase(0, delimiter_position + 1);
+ }
+
+ return current_dictionary->GetWithoutPathExpansion(current_path, out_value);
+}
+
+bool DictionaryValue::Get(const std::string& path, Value** out_value) {
+ return static_cast<const DictionaryValue&>(*this).Get(
+ path,
+ const_cast<const Value**>(out_value));
+}
+
+bool DictionaryValue::GetBoolean(const std::string& path,
+ bool* bool_value) const {
+ const Value* value;
+ if (!Get(path, &value))
+ return false;
+
+ return value->GetAsBoolean(bool_value);
+}
+
+bool DictionaryValue::GetInteger(const std::string& path,
+ int* out_value) const {
+ const Value* value;
+ if (!Get(path, &value))
+ return false;
+
+ return value->GetAsInteger(out_value);
+}
+
+bool DictionaryValue::GetDouble(const std::string& path,
+ double* out_value) const {
+ const Value* value;
+ if (!Get(path, &value))
+ return false;
+
+ return value->GetAsDouble(out_value);
+}
+
+bool DictionaryValue::GetString(const std::string& path,
+ std::string* out_value) const {
+ const Value* value;
+ if (!Get(path, &value))
+ return false;
+
+ return value->GetAsString(out_value);
+}
+
+bool DictionaryValue::GetString(const std::string& path,
+ string16* out_value) const {
+ const Value* value;
+ if (!Get(path, &value))
+ return false;
+
+ return value->GetAsString(out_value);
+}
+
+bool DictionaryValue::GetStringASCII(const std::string& path,
+ std::string* out_value) const {
+ std::string out;
+ if (!GetString(path, &out))
+ return false;
+
+ if (!IsStringASCII(out)) {
+ NOTREACHED();
+ return false;
+ }
+
+ out_value->assign(out);
+ return true;
+}
+
+bool DictionaryValue::GetBinary(const std::string& path,
+ const BinaryValue** out_value) const {
+ const Value* value;
+ bool result = Get(path, &value);
+ if (!result || !value->IsType(TYPE_BINARY))
+ return false;
+
+ if (out_value)
+ *out_value = static_cast<const BinaryValue*>(value);
+
+ return true;
+}
+
+bool DictionaryValue::GetBinary(const std::string& path,
+ BinaryValue** out_value) {
+ return static_cast<const DictionaryValue&>(*this).GetBinary(
+ path,
+ const_cast<const BinaryValue**>(out_value));
+}
+
+bool DictionaryValue::GetDictionary(const std::string& path,
+ const DictionaryValue** out_value) const {
+ const Value* value;
+ bool result = Get(path, &value);
+ if (!result || !value->IsType(TYPE_DICTIONARY))
+ return false;
+
+ if (out_value)
+ *out_value = static_cast<const DictionaryValue*>(value);
+
+ return true;
+}
+
+bool DictionaryValue::GetDictionary(const std::string& path,
+ DictionaryValue** out_value) {
+ return static_cast<const DictionaryValue&>(*this).GetDictionary(
+ path,
+ const_cast<const DictionaryValue**>(out_value));
+}
+
+bool DictionaryValue::GetList(const std::string& path,
+ const ListValue** out_value) const {
+ const Value* value;
+ bool result = Get(path, &value);
+ if (!result || !value->IsType(TYPE_LIST))
+ return false;
+
+ if (out_value)
+ *out_value = static_cast<const ListValue*>(value);
+
+ return true;
+}
+
+bool DictionaryValue::GetList(const std::string& path, ListValue** out_value) {
+ return static_cast<const DictionaryValue&>(*this).GetList(
+ path,
+ const_cast<const ListValue**>(out_value));
+}
+
+bool DictionaryValue::GetWithoutPathExpansion(const std::string& key,
+ const Value** out_value) const {
+ DCHECK(IsStringUTF8(key));
+ ValueMap::const_iterator entry_iterator = dictionary_.find(key);
+ if (entry_iterator == dictionary_.end())
+ return false;
+
+ const Value* entry = entry_iterator->second;
+ if (out_value)
+ *out_value = entry;
+ return true;
+}
+
+bool DictionaryValue::GetWithoutPathExpansion(const std::string& key,
+ Value** out_value) {
+ return static_cast<const DictionaryValue&>(*this).GetWithoutPathExpansion(
+ key,
+ const_cast<const Value**>(out_value));
+}
+
+bool DictionaryValue::GetBooleanWithoutPathExpansion(const std::string& key,
+ bool* out_value) const {
+ const Value* value;
+ if (!GetWithoutPathExpansion(key, &value))
+ return false;
+
+ return value->GetAsBoolean(out_value);
+}
+
+bool DictionaryValue::GetIntegerWithoutPathExpansion(const std::string& key,
+ int* out_value) const {
+ const Value* value;
+ if (!GetWithoutPathExpansion(key, &value))
+ return false;
+
+ return value->GetAsInteger(out_value);
+}
+
+bool DictionaryValue::GetDoubleWithoutPathExpansion(const std::string& key,
+ double* out_value) const {
+ const Value* value;
+ if (!GetWithoutPathExpansion(key, &value))
+ return false;
+
+ return value->GetAsDouble(out_value);
+}
+
+bool DictionaryValue::GetStringWithoutPathExpansion(
+ const std::string& key,
+ std::string* out_value) const {
+ const Value* value;
+ if (!GetWithoutPathExpansion(key, &value))
+ return false;
+
+ return value->GetAsString(out_value);
+}
+
+bool DictionaryValue::GetStringWithoutPathExpansion(const std::string& key,
+ string16* out_value) const {
+ const Value* value;
+ if (!GetWithoutPathExpansion(key, &value))
+ return false;
+
+ return value->GetAsString(out_value);
+}
+
+bool DictionaryValue::GetDictionaryWithoutPathExpansion(
+ const std::string& key,
+ const DictionaryValue** out_value) const {
+ const Value* value;
+ bool result = GetWithoutPathExpansion(key, &value);
+ if (!result || !value->IsType(TYPE_DICTIONARY))
+ return false;
+
+ if (out_value)
+ *out_value = static_cast<const DictionaryValue*>(value);
+
+ return true;
+}
+
+bool DictionaryValue::GetDictionaryWithoutPathExpansion(
+ const std::string& key,
+ DictionaryValue** out_value) {
+ const DictionaryValue& const_this =
+ static_cast<const DictionaryValue&>(*this);
+ return const_this.GetDictionaryWithoutPathExpansion(
+ key,
+ const_cast<const DictionaryValue**>(out_value));
+}
+
+bool DictionaryValue::GetListWithoutPathExpansion(
+ const std::string& key,
+ const ListValue** out_value) const {
+ const Value* value;
+ bool result = GetWithoutPathExpansion(key, &value);
+ if (!result || !value->IsType(TYPE_LIST))
+ return false;
+
+ if (out_value)
+ *out_value = static_cast<const ListValue*>(value);
+
+ return true;
+}
+
+bool DictionaryValue::GetListWithoutPathExpansion(const std::string& key,
+ ListValue** out_value) {
+ return
+ static_cast<const DictionaryValue&>(*this).GetListWithoutPathExpansion(
+ key,
+ const_cast<const ListValue**>(out_value));
+}
+
+bool DictionaryValue::Remove(const std::string& path, Value** out_value) {
+ DCHECK(IsStringUTF8(path));
+ std::string current_path(path);
+ DictionaryValue* current_dictionary = this;
+ size_t delimiter_position = current_path.rfind('.');
+ if (delimiter_position != std::string::npos) {
+ if (!GetDictionary(current_path.substr(0, delimiter_position),
+ ¤t_dictionary))
+ return false;
+ current_path.erase(0, delimiter_position + 1);
+ }
+
+ return current_dictionary->RemoveWithoutPathExpansion(current_path,
+ out_value);
+}
+
+bool DictionaryValue::RemoveWithoutPathExpansion(const std::string& key,
+ Value** out_value) {
+ DCHECK(IsStringUTF8(key));
+ ValueMap::iterator entry_iterator = dictionary_.find(key);
+ if (entry_iterator == dictionary_.end())
+ return false;
+
+ Value* entry = entry_iterator->second;
+ if (out_value)
+ *out_value = entry;
+ else
+ delete entry;
+ dictionary_.erase(entry_iterator);
+ return true;
+}
+
+DictionaryValue* DictionaryValue::DeepCopyWithoutEmptyChildren() {
+ Value* copy = CopyWithoutEmptyChildren(this);
+ return copy ? static_cast<DictionaryValue*>(copy) : new DictionaryValue;
+}
+
+void DictionaryValue::MergeDictionary(const DictionaryValue* dictionary) {
+ for (DictionaryValue::key_iterator key(dictionary->begin_keys());
+ key != dictionary->end_keys(); ++key) {
+ const Value* merge_value;
+ if (dictionary->GetWithoutPathExpansion(*key, &merge_value)) {
+ // Check whether we have to merge dictionaries.
+ if (merge_value->IsType(Value::TYPE_DICTIONARY)) {
+ DictionaryValue* sub_dict;
+ if (GetDictionaryWithoutPathExpansion(*key, &sub_dict)) {
+ sub_dict->MergeDictionary(
+ static_cast<const DictionaryValue*>(merge_value));
+ continue;
+ }
+ }
+ // All other cases: Make a copy and hook it up.
+ SetWithoutPathExpansion(*key, merge_value->DeepCopy());
+ }
+ }
+}
+
+void DictionaryValue::Swap(DictionaryValue* other) {
+ dictionary_.swap(other->dictionary_);
+}
+
+DictionaryValue::key_iterator::key_iterator(ValueMap::const_iterator itr) {
+ itr_ = itr;
+}
+
+DictionaryValue::key_iterator::key_iterator(const key_iterator& rhs) {
+ itr_ = rhs.itr_;
+}
+
+DictionaryValue::Iterator::Iterator(const DictionaryValue& target)
+ : target_(target),
+ it_(target.dictionary_.begin()) {}
+
+DictionaryValue* DictionaryValue::DeepCopy() const {
+ DictionaryValue* result = new DictionaryValue;
+
+ for (ValueMap::const_iterator current_entry(dictionary_.begin());
+ current_entry != dictionary_.end(); ++current_entry) {
+ result->SetWithoutPathExpansion(current_entry->first,
+ current_entry->second->DeepCopy());
+ }
+
+ return result;
+}
+
+bool DictionaryValue::Equals(const Value* other) const {
+ if (other->GetType() != GetType())
+ return false;
+
+ const DictionaryValue* other_dict =
+ static_cast<const DictionaryValue*>(other);
+ key_iterator lhs_it(begin_keys());
+ key_iterator rhs_it(other_dict->begin_keys());
+ while (lhs_it != end_keys() && rhs_it != other_dict->end_keys()) {
+ const Value* lhs;
+ const Value* rhs;
+ if (*lhs_it != *rhs_it ||
+ !GetWithoutPathExpansion(*lhs_it, &lhs) ||
+ !other_dict->GetWithoutPathExpansion(*rhs_it, &rhs) ||
+ !lhs->Equals(rhs)) {
+ return false;
+ }
+ ++lhs_it;
+ ++rhs_it;
+ }
+ if (lhs_it != end_keys() || rhs_it != other_dict->end_keys())
+ return false;
+
+ return true;
+}
+
+///////////////////// ListValue ////////////////////
+
+ListValue::ListValue() : Value(TYPE_LIST) {
+}
+
+ListValue::~ListValue() {
+ Clear();
+}
+
+void ListValue::Clear() {
+ for (ValueVector::iterator i(list_.begin()); i != list_.end(); ++i)
+ delete *i;
+ list_.clear();
+}
+
+bool ListValue::Set(size_t index, Value* in_value) {
+ if (!in_value)
+ return false;
+
+ if (index >= list_.size()) {
+ // Pad out any intermediate indexes with null settings
+ while (index > list_.size())
+ Append(CreateNullValue());
+ Append(in_value);
+ } else {
+ DCHECK(list_[index] != in_value);
+ delete list_[index];
+ list_[index] = in_value;
+ }
+ return true;
+}
+
+bool ListValue::Get(size_t index, const Value** out_value) const {
+ if (index >= list_.size())
+ return false;
+
+ if (out_value)
+ *out_value = list_[index];
+
+ return true;
+}
+
+bool ListValue::Get(size_t index, Value** out_value) {
+ return static_cast<const ListValue&>(*this).Get(
+ index,
+ const_cast<const Value**>(out_value));
+}
+
+bool ListValue::GetBoolean(size_t index, bool* bool_value) const {
+ const Value* value;
+ if (!Get(index, &value))
+ return false;
+
+ return value->GetAsBoolean(bool_value);
+}
+
+bool ListValue::GetInteger(size_t index, int* out_value) const {
+ const Value* value;
+ if (!Get(index, &value))
+ return false;
+
+ return value->GetAsInteger(out_value);
+}
+
+bool ListValue::GetDouble(size_t index, double* out_value) const {
+ const Value* value;
+ if (!Get(index, &value))
+ return false;
+
+ return value->GetAsDouble(out_value);
+}
+
+bool ListValue::GetString(size_t index, std::string* out_value) const {
+ const Value* value;
+ if (!Get(index, &value))
+ return false;
+
+ return value->GetAsString(out_value);
+}
+
+bool ListValue::GetString(size_t index, string16* out_value) const {
+ const Value* value;
+ if (!Get(index, &value))
+ return false;
+
+ return value->GetAsString(out_value);
+}
+
+bool ListValue::GetBinary(size_t index, const BinaryValue** out_value) const {
+ const Value* value;
+ bool result = Get(index, &value);
+ if (!result || !value->IsType(TYPE_BINARY))
+ return false;
+
+ if (out_value)
+ *out_value = static_cast<const BinaryValue*>(value);
+
+ return true;
+}
+
+bool ListValue::GetBinary(size_t index, BinaryValue** out_value) {
+ return static_cast<const ListValue&>(*this).GetBinary(
+ index,
+ const_cast<const BinaryValue**>(out_value));
+}
+
+bool ListValue::GetDictionary(size_t index,
+ const DictionaryValue** out_value) const {
+ const Value* value;
+ bool result = Get(index, &value);
+ if (!result || !value->IsType(TYPE_DICTIONARY))
+ return false;
+
+ if (out_value)
+ *out_value = static_cast<const DictionaryValue*>(value);
+
+ return true;
+}
+
+bool ListValue::GetDictionary(size_t index, DictionaryValue** out_value) {
+ return static_cast<const ListValue&>(*this).GetDictionary(
+ index,
+ const_cast<const DictionaryValue**>(out_value));
+}
+
+bool ListValue::GetList(size_t index, const ListValue** out_value) const {
+ const Value* value;
+ bool result = Get(index, &value);
+ if (!result || !value->IsType(TYPE_LIST))
+ return false;
+
+ if (out_value)
+ *out_value = static_cast<const ListValue*>(value);
+
+ return true;
+}
+
+bool ListValue::GetList(size_t index, ListValue** out_value) {
+ return static_cast<const ListValue&>(*this).GetList(
+ index,
+ const_cast<const ListValue**>(out_value));
+}
+
+bool ListValue::Remove(size_t index, Value** out_value) {
+ if (index >= list_.size())
+ return false;
+
+ if (out_value)
+ *out_value = list_[index];
+ else
+ delete list_[index];
+
+ list_.erase(list_.begin() + index);
+ return true;
+}
+
+bool ListValue::Remove(const Value& value, size_t* index) {
+ for (ValueVector::iterator i(list_.begin()); i != list_.end(); ++i) {
+ if ((*i)->Equals(&value)) {
+ size_t previous_index = i - list_.begin();
+ delete *i;
+ list_.erase(i);
+
+ if (index)
+ *index = previous_index;
+ return true;
+ }
+ }
+ return false;
+}
+
+void ListValue::Erase(iterator iter, Value** out_value) {
+ if (out_value)
+ *out_value = *iter;
+ else
+ delete *iter;
+
+ list_.erase(iter);
+}
+
+void ListValue::Append(Value* in_value) {
+ DCHECK(in_value);
+ list_.push_back(in_value);
+}
+
+void ListValue::AppendBoolean(bool in_value) {
+ Append(CreateBooleanValue(in_value));
+}
+
+void ListValue::AppendInteger(int in_value) {
+ Append(CreateIntegerValue(in_value));
+}
+
+void ListValue::AppendDouble(double in_value) {
+ Append(CreateDoubleValue(in_value));
+}
+
+void ListValue::AppendString(const std::string& in_value) {
+ Append(CreateStringValue(in_value));
+}
+
+void ListValue::AppendString(const string16& in_value) {
+ Append(CreateStringValue(in_value));
+}
+
+void ListValue::AppendStrings(const std::vector<std::string>& in_values) {
+ for (std::vector<std::string>::const_iterator it = in_values.begin();
+ it != in_values.end(); ++it) {
+ AppendString(*it);
+ }
+}
+
+void ListValue::AppendStrings(const std::vector<string16>& in_values) {
+ for (std::vector<string16>::const_iterator it = in_values.begin();
+ it != in_values.end(); ++it) {
+ AppendString(*it);
+ }
+}
+
+bool ListValue::AppendIfNotPresent(Value* in_value) {
+ DCHECK(in_value);
+ for (ValueVector::const_iterator i(list_.begin()); i != list_.end(); ++i) {
+ if ((*i)->Equals(in_value)) {
+ delete in_value;
+ return false;
+ }
+ }
+ list_.push_back(in_value);
+ return true;
+}
+
+bool ListValue::Insert(size_t index, Value* in_value) {
+ DCHECK(in_value);
+ if (index > list_.size())
+ return false;
+
+ list_.insert(list_.begin() + index, in_value);
+ return true;
+}
+
+ListValue::const_iterator ListValue::Find(const Value& value) const {
+ return std::find_if(list_.begin(), list_.end(), ValueEquals(&value));
+}
+
+void ListValue::Swap(ListValue* other) {
+ list_.swap(other->list_);
+}
+
+bool ListValue::GetAsList(ListValue** out_value) {
+ if (out_value)
+ *out_value = this;
+ return true;
+}
+
+bool ListValue::GetAsList(const ListValue** out_value) const {
+ if (out_value)
+ *out_value = this;
+ return true;
+}
+
+ListValue* ListValue::DeepCopy() const {
+ ListValue* result = new ListValue;
+
+ for (ValueVector::const_iterator i(list_.begin()); i != list_.end(); ++i)
+ result->Append((*i)->DeepCopy());
+
+ return result;
+}
+
+bool ListValue::Equals(const Value* other) const {
+ if (other->GetType() != GetType())
+ return false;
+
+ const ListValue* other_list =
+ static_cast<const ListValue*>(other);
+ const_iterator lhs_it, rhs_it;
+ for (lhs_it = begin(), rhs_it = other_list->begin();
+ lhs_it != end() && rhs_it != other_list->end();
+ ++lhs_it, ++rhs_it) {
+ if (!(*lhs_it)->Equals(*rhs_it))
+ return false;
+ }
+ if (lhs_it != end() || rhs_it != other_list->end())
+ return false;
+
+ return true;
+}
+
+ValueSerializer::~ValueSerializer() {
+}
+
+std::ostream& operator<<(std::ostream& out, const Value& value) {
+ std::string json;
+ JSONWriter::WriteWithOptions(&value,
+ JSONWriter::OPTIONS_PRETTY_PRINT,
+ &json);
+ return out << json;
+}
+
+} // namespace base
diff --git a/src/base/values.h b/src/base/values.h
new file mode 100644
index 0000000..e105818
--- /dev/null
+++ b/src/base/values.h
@@ -0,0 +1,531 @@
+// Copyright (c) 2012 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 specifies a recursive data storage class called Value intended for
+// storing setting and other persistable data. It includes the ability to
+// specify (recursive) lists and dictionaries, so it's fairly expressive.
+// However, the API is optimized for the common case, namely storing a
+// hierarchical tree of simple values. Given a DictionaryValue root, you can
+// easily do things like:
+//
+// root->SetString("global.pages.homepage", "http://goateleporter.com");
+// std::string homepage = "http://google.com"; // default/fallback value
+// root->GetString("global.pages.homepage", &homepage);
+//
+// where "global" and "pages" are also DictionaryValues, and "homepage" is a
+// string setting. If some elements of the path didn't exist yet, the
+// SetString() method would create the missing elements and attach them to root
+// before attaching the homepage value.
+
+#ifndef BASE_VALUES_H_
+#define BASE_VALUES_H_
+
+#include <iterator>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/string16.h"
+
+// This file declares "using base::Value", etc. at the bottom, so that
+// current code can use these classes without the base namespace. In
+// new code, please always use base::Value, etc. or add your own
+// "using" declaration.
+// http://crbug.com/88666
+namespace base {
+
+class BinaryValue;
+class DictionaryValue;
+class FundamentalValue;
+class ListValue;
+class StringValue;
+class Value;
+
+typedef std::vector<Value*> ValueVector;
+typedef std::map<std::string, Value*> ValueMap;
+
+// The Value class is the base class for Values. A Value can be instantiated
+// via the Create*Value() factory methods, or by directly creating instances of
+// the subclasses.
+class BASE_EXPORT Value {
+ public:
+ enum Type {
+ TYPE_NULL = 0,
+ TYPE_BOOLEAN,
+ TYPE_INTEGER,
+ TYPE_DOUBLE,
+ TYPE_STRING,
+ TYPE_BINARY,
+ TYPE_DICTIONARY,
+ TYPE_LIST
+ };
+
+ virtual ~Value();
+
+ static Value* CreateNullValue();
+ // DEPRECATED: Do not use the following 5 functions. Instead, use
+ // new FundamentalValue or new StringValue.
+ static FundamentalValue* CreateBooleanValue(bool in_value);
+ static FundamentalValue* CreateIntegerValue(int in_value);
+ static FundamentalValue* CreateDoubleValue(double in_value);
+ static StringValue* CreateStringValue(const std::string& in_value);
+ static StringValue* CreateStringValue(const string16& in_value);
+
+ // Returns the type of the value stored by the current Value object.
+ // Each type will be implemented by only one subclass of Value, so it's
+ // safe to use the Type to determine whether you can cast from
+ // Value* to (Implementing Class)*. Also, a Value object never changes
+ // its type after construction.
+ Type GetType() const { return type_; }
+
+ // Returns true if the current object represents a given type.
+ bool IsType(Type type) const { return type == type_; }
+
+ // These methods allow the convenient retrieval of settings.
+ // If the current setting object can be converted into the given type,
+ // the value is returned through the |out_value| parameter and true is
+ // returned; otherwise, false is returned and |out_value| is unchanged.
+ virtual bool GetAsBoolean(bool* out_value) const;
+ virtual bool GetAsInteger(int* out_value) const;
+ virtual bool GetAsDouble(double* out_value) const;
+ virtual bool GetAsString(std::string* out_value) const;
+ virtual bool GetAsString(string16* out_value) const;
+ virtual bool GetAsList(ListValue** out_value);
+ virtual bool GetAsList(const ListValue** out_value) const;
+ virtual bool GetAsDictionary(DictionaryValue** out_value);
+ virtual bool GetAsDictionary(const DictionaryValue** out_value) const;
+
+ // This creates a deep copy of the entire Value tree, and returns a pointer
+ // to the copy. The caller gets ownership of the copy, of course.
+ //
+ // Subclasses return their own type directly in their overrides;
+ // this works because C++ supports covariant return types.
+ virtual Value* DeepCopy() const;
+
+ // Compares if two Value objects have equal contents.
+ virtual bool Equals(const Value* other) const;
+
+ // Compares if two Value objects have equal contents. Can handle NULLs.
+ // NULLs are considered equal but different from Value::CreateNullValue().
+ static bool Equals(const Value* a, const Value* b);
+
+ protected:
+ // These aren't safe for end-users, but they are useful for subclasses.
+ explicit Value(Type type);
+ Value(const Value& that);
+ Value& operator=(const Value& that);
+
+ private:
+ Type type_;
+};
+
+// FundamentalValue represents the simple fundamental types of values.
+class BASE_EXPORT FundamentalValue : public Value {
+ public:
+ explicit FundamentalValue(bool in_value);
+ explicit FundamentalValue(int in_value);
+ explicit FundamentalValue(double in_value);
+ virtual ~FundamentalValue();
+
+ // Overridden from Value:
+ virtual bool GetAsBoolean(bool* out_value) const OVERRIDE;
+ virtual bool GetAsInteger(int* out_value) const OVERRIDE;
+ virtual bool GetAsDouble(double* out_value) const OVERRIDE;
+ virtual FundamentalValue* DeepCopy() const OVERRIDE;
+ virtual bool Equals(const Value* other) const OVERRIDE;
+
+ private:
+ union {
+ bool boolean_value_;
+ int integer_value_;
+ double double_value_;
+ };
+};
+
+class BASE_EXPORT StringValue : public Value {
+ public:
+ // Initializes a StringValue with a UTF-8 narrow character string.
+ explicit StringValue(const std::string& in_value);
+
+ // Initializes a StringValue with a string16.
+ explicit StringValue(const string16& in_value);
+
+ virtual ~StringValue();
+
+ // Overridden from Value:
+ virtual bool GetAsString(std::string* out_value) const OVERRIDE;
+ virtual bool GetAsString(string16* out_value) const OVERRIDE;
+ virtual StringValue* DeepCopy() const OVERRIDE;
+ virtual bool Equals(const Value* other) const OVERRIDE;
+
+ private:
+ std::string value_;
+};
+
+class BASE_EXPORT BinaryValue: public Value {
+ public:
+ virtual ~BinaryValue();
+
+ // Creates a Value to represent a binary buffer. The new object takes
+ // ownership of the pointer passed in, if successful.
+ // Returns NULL if buffer is NULL.
+ static BinaryValue* Create(char* buffer, size_t size);
+
+ // For situations where you want to keep ownership of your buffer, this
+ // factory method creates a new BinaryValue by copying the contents of the
+ // buffer that's passed in.
+ // Returns NULL if buffer is NULL.
+ static BinaryValue* CreateWithCopiedBuffer(const char* buffer, size_t size);
+
+ size_t GetSize() const { return size_; }
+ char* GetBuffer() { return buffer_; }
+ const char* GetBuffer() const { return buffer_; }
+
+ // Overridden from Value:
+ virtual BinaryValue* DeepCopy() const OVERRIDE;
+ virtual bool Equals(const Value* other) const OVERRIDE;
+
+ private:
+ // Constructor is private so that only objects with valid buffer pointers
+ // and size values can be created.
+ BinaryValue(char* buffer, size_t size);
+
+ char* buffer_;
+ size_t size_;
+
+ DISALLOW_COPY_AND_ASSIGN(BinaryValue);
+};
+
+// DictionaryValue provides a key-value dictionary with (optional) "path"
+// parsing for recursive access; see the comment at the top of the file. Keys
+// are |std::string|s and should be UTF-8 encoded.
+class BASE_EXPORT DictionaryValue : public Value {
+ public:
+ DictionaryValue();
+ virtual ~DictionaryValue();
+
+ // Overridden from Value:
+ virtual bool GetAsDictionary(DictionaryValue** out_value) OVERRIDE;
+ virtual bool GetAsDictionary(
+ const DictionaryValue** out_value) const OVERRIDE;
+
+ // Returns true if the current dictionary has a value for the given key.
+ bool HasKey(const std::string& key) const;
+
+ // Returns the number of Values in this dictionary.
+ size_t size() const { return dictionary_.size(); }
+
+ // Returns whether the dictionary is empty.
+ bool empty() const { return dictionary_.empty(); }
+
+ // Clears any current contents of this dictionary.
+ void Clear();
+
+ // Sets the Value associated with the given path starting from this object.
+ // A path has the form "<key>" or "<key>.<key>.[...]", where "." indexes
+ // into the next DictionaryValue down. Obviously, "." can't be used
+ // within a key, but there are no other restrictions on keys.
+ // If the key at any step of the way doesn't exist, or exists but isn't
+ // a DictionaryValue, a new DictionaryValue will be created and attached
+ // to the path in that location.
+ // Note that the dictionary takes ownership of the value referenced by
+ // |in_value|, and therefore |in_value| must be non-NULL.
+ void Set(const std::string& path, Value* in_value);
+
+ // Convenience forms of Set(). These methods will replace any existing
+ // value at that path, even if it has a different type.
+ void SetBoolean(const std::string& path, bool in_value);
+ void SetInteger(const std::string& path, int in_value);
+ void SetDouble(const std::string& path, double in_value);
+ void SetString(const std::string& path, const std::string& in_value);
+ void SetString(const std::string& path, const string16& in_value);
+
+ // Like Set(), but without special treatment of '.'. This allows e.g. URLs to
+ // be used as paths.
+ void SetWithoutPathExpansion(const std::string& key, Value* in_value);
+
+ // Convenience forms of SetWithoutPathExpansion().
+ void SetBooleanWithoutPathExpansion(const std::string& path, bool in_value);
+ void SetIntegerWithoutPathExpansion(const std::string& path, int in_value);
+ void SetDoubleWithoutPathExpansion(const std::string& path, double in_value);
+ void SetStringWithoutPathExpansion(const std::string& path,
+ const std::string& in_value);
+ void SetStringWithoutPathExpansion(const std::string& path,
+ const string16& in_value);
+
+ // Gets the Value associated with the given path starting from this object.
+ // A path has the form "<key>" or "<key>.<key>.[...]", where "." indexes
+ // into the next DictionaryValue down. If the path can be resolved
+ // successfully, the value for the last key in the path will be returned
+ // through the |out_value| parameter, and the function will return true.
+ // Otherwise, it will return false and |out_value| will be untouched.
+ // Note that the dictionary always owns the value that's returned.
+ bool Get(const std::string& path, const Value** out_value) const;
+ bool Get(const std::string& path, Value** out_value);
+
+ // These are convenience forms of Get(). The value will be retrieved
+ // and the return value will be true if the path is valid and the value at
+ // the end of the path can be returned in the form specified.
+ bool GetBoolean(const std::string& path, bool* out_value) const;
+ bool GetInteger(const std::string& path, int* out_value) const;
+ bool GetDouble(const std::string& path, double* out_value) const;
+ bool GetString(const std::string& path, std::string* out_value) const;
+ bool GetString(const std::string& path, string16* out_value) const;
+ bool GetStringASCII(const std::string& path, std::string* out_value) const;
+ bool GetBinary(const std::string& path, const BinaryValue** out_value) const;
+ bool GetBinary(const std::string& path, BinaryValue** out_value);
+ bool GetDictionary(const std::string& path,
+ const DictionaryValue** out_value) const;
+ bool GetDictionary(const std::string& path, DictionaryValue** out_value);
+ bool GetList(const std::string& path, const ListValue** out_value) const;
+ bool GetList(const std::string& path, ListValue** out_value);
+
+ // Like Get(), but without special treatment of '.'. This allows e.g. URLs to
+ // be used as paths.
+ bool GetWithoutPathExpansion(const std::string& key,
+ const Value** out_value) const;
+ bool GetWithoutPathExpansion(const std::string& key, Value** out_value);
+ bool GetBooleanWithoutPathExpansion(const std::string& key,
+ bool* out_value) const;
+ bool GetIntegerWithoutPathExpansion(const std::string& key,
+ int* out_value) const;
+ bool GetDoubleWithoutPathExpansion(const std::string& key,
+ double* out_value) const;
+ bool GetStringWithoutPathExpansion(const std::string& key,
+ std::string* out_value) const;
+ bool GetStringWithoutPathExpansion(const std::string& key,
+ string16* out_value) const;
+ bool GetDictionaryWithoutPathExpansion(
+ const std::string& key,
+ const DictionaryValue** out_value) const;
+ bool GetDictionaryWithoutPathExpansion(const std::string& key,
+ DictionaryValue** out_value);
+ bool GetListWithoutPathExpansion(const std::string& key,
+ const ListValue** out_value) const;
+ bool GetListWithoutPathExpansion(const std::string& key,
+ ListValue** out_value);
+
+ // Removes the Value with the specified path from this dictionary (or one
+ // of its child dictionaries, if the path is more than just a local key).
+ // If |out_value| is non-NULL, the removed Value AND ITS OWNERSHIP will be
+ // passed out via out_value. If |out_value| is NULL, the removed value will
+ // be deleted. This method returns true if |path| is a valid path; otherwise
+ // it will return false and the DictionaryValue object will be unchanged.
+ virtual bool Remove(const std::string& path, Value** out_value);
+
+ // Like Remove(), but without special treatment of '.'. This allows e.g. URLs
+ // to be used as paths.
+ virtual bool RemoveWithoutPathExpansion(const std::string& key,
+ Value** out_value);
+
+ // Makes a copy of |this| but doesn't include empty dictionaries and lists in
+ // the copy. This never returns NULL, even if |this| itself is empty.
+ DictionaryValue* DeepCopyWithoutEmptyChildren();
+
+ // Merge |dictionary| into this dictionary. This is done recursively, i.e. any
+ // sub-dictionaries will be merged as well. In case of key collisions, the
+ // passed in dictionary takes precedence and data already present will be
+ // replaced. Values within |dictionary| are deep-copied, so |dictionary| may
+ // be freed any time after this call.
+ void MergeDictionary(const DictionaryValue* dictionary);
+
+ // Swaps contents with the |other| dictionary.
+ virtual void Swap(DictionaryValue* other);
+
+ // This class provides an iterator for the keys in the dictionary.
+ // It can't be used to modify the dictionary.
+ //
+ // YOU SHOULD ALWAYS USE THE XXXWithoutPathExpansion() APIs WITH THESE, NOT
+ // THE NORMAL XXX() APIs. This makes sure things will work correctly if any
+ // keys have '.'s in them.
+ class BASE_EXPORT key_iterator
+ : private std::iterator<std::input_iterator_tag, const std::string> {
+ public:
+ explicit key_iterator(ValueMap::const_iterator itr);
+ // Not explicit, because this is a copy constructor.
+ key_iterator(const key_iterator& rhs);
+ key_iterator operator++() {
+ ++itr_;
+ return *this;
+ }
+ const std::string& operator*() { return itr_->first; }
+ bool operator!=(const key_iterator& other) { return itr_ != other.itr_; }
+ bool operator==(const key_iterator& other) { return itr_ == other.itr_; }
+
+ private:
+ ValueMap::const_iterator itr_;
+ };
+
+ key_iterator begin_keys() const { return key_iterator(dictionary_.begin()); }
+ key_iterator end_keys() const { return key_iterator(dictionary_.end()); }
+
+ // This class provides an iterator over both keys and values in the
+ // dictionary. It can't be used to modify the dictionary.
+ class BASE_EXPORT Iterator {
+ public:
+ explicit Iterator(const DictionaryValue& target);
+
+ bool HasNext() const { return it_ != target_.dictionary_.end(); }
+ void Advance() { ++it_; }
+
+ const std::string& key() const { return it_->first; }
+ const Value& value() const { return *it_->second; }
+
+ private:
+ const DictionaryValue& target_;
+ ValueMap::const_iterator it_;
+ };
+
+ // Overridden from Value:
+ virtual DictionaryValue* DeepCopy() const OVERRIDE;
+ virtual bool Equals(const Value* other) const OVERRIDE;
+
+ private:
+ ValueMap dictionary_;
+
+ DISALLOW_COPY_AND_ASSIGN(DictionaryValue);
+};
+
+// This type of Value represents a list of other Value values.
+class BASE_EXPORT ListValue : public Value {
+ public:
+ typedef ValueVector::iterator iterator;
+ typedef ValueVector::const_iterator const_iterator;
+
+ ListValue();
+ virtual ~ListValue();
+
+ // Clears the contents of this ListValue
+ void Clear();
+
+ // Returns the number of Values in this list.
+ size_t GetSize() const { return list_.size(); }
+
+ // Returns whether the list is empty.
+ bool empty() const { return list_.empty(); }
+
+ // Sets the list item at the given index to be the Value specified by
+ // the value given. If the index beyond the current end of the list, null
+ // Values will be used to pad out the list.
+ // Returns true if successful, or false if the index was negative or
+ // the value is a null pointer.
+ bool Set(size_t index, Value* in_value);
+
+ // Gets the Value at the given index. Modifies |out_value| (and returns true)
+ // only if the index falls within the current list range.
+ // Note that the list always owns the Value passed out via |out_value|.
+ bool Get(size_t index, const Value** out_value) const;
+ bool Get(size_t index, Value** out_value);
+
+ // Convenience forms of Get(). Modifies |out_value| (and returns true)
+ // only if the index is valid and the Value at that index can be returned
+ // in the specified form.
+ bool GetBoolean(size_t index, bool* out_value) const;
+ bool GetInteger(size_t index, int* out_value) const;
+ bool GetDouble(size_t index, double* out_value) const;
+ bool GetString(size_t index, std::string* out_value) const;
+ bool GetString(size_t index, string16* out_value) const;
+ bool GetBinary(size_t index, const BinaryValue** out_value) const;
+ bool GetBinary(size_t index, BinaryValue** out_value);
+ bool GetDictionary(size_t index, const DictionaryValue** out_value) const;
+ bool GetDictionary(size_t index, DictionaryValue** out_value);
+ bool GetList(size_t index, const ListValue** out_value) const;
+ bool GetList(size_t index, ListValue** out_value);
+
+ // Removes the Value with the specified index from this list.
+ // If |out_value| is non-NULL, the removed Value AND ITS OWNERSHIP will be
+ // passed out via |out_value|. If |out_value| is NULL, the removed value will
+ // be deleted. This method returns true if |index| is valid; otherwise
+ // it will return false and the ListValue object will be unchanged.
+ virtual bool Remove(size_t index, Value** out_value);
+
+ // Removes the first instance of |value| found in the list, if any, and
+ // deletes it. |index| is the location where |value| was found. Returns false
+ // if not found.
+ bool Remove(const Value& value, size_t* index);
+
+ // Removes the element at |iter|. If |out_value| is NULL, the value will be
+ // deleted, otherwise ownership of the value is passed back to the caller.
+ void Erase(iterator iter, Value** out_value);
+
+ // Appends a Value to the end of the list.
+ void Append(Value* in_value);
+
+ // Convenience forms of Append.
+ void AppendBoolean(bool in_value);
+ void AppendInteger(int in_value);
+ void AppendDouble(double in_value);
+ void AppendString(const std::string& in_value);
+ void AppendString(const string16& in_value);
+ void AppendStrings(const std::vector<std::string>& in_values);
+ void AppendStrings(const std::vector<string16>& in_values);
+
+ // Appends a Value if it's not already present. Takes ownership of the
+ // |in_value|. Returns true if successful, or false if the value was already
+ // present. If the value was already present the |in_value| is deleted.
+ bool AppendIfNotPresent(Value* in_value);
+
+ // Insert a Value at index.
+ // Returns true if successful, or false if the index was out of range.
+ bool Insert(size_t index, Value* in_value);
+
+ // Searches for the first instance of |value| in the list using the Equals
+ // method of the Value type.
+ // Returns a const_iterator to the found item or to end() if none exists.
+ const_iterator Find(const Value& value) const;
+
+ // Swaps contents with the |other| list.
+ virtual void Swap(ListValue* other);
+
+ // Iteration.
+ iterator begin() { return list_.begin(); }
+ iterator end() { return list_.end(); }
+
+ const_iterator begin() const { return list_.begin(); }
+ const_iterator end() const { return list_.end(); }
+
+ // Overridden from Value:
+ virtual bool GetAsList(ListValue** out_value) OVERRIDE;
+ virtual bool GetAsList(const ListValue** out_value) const OVERRIDE;
+ virtual ListValue* DeepCopy() const OVERRIDE;
+ virtual bool Equals(const Value* other) const OVERRIDE;
+
+ private:
+ ValueVector list_;
+
+ DISALLOW_COPY_AND_ASSIGN(ListValue);
+};
+
+// This interface is implemented by classes that know how to serialize and
+// deserialize Value objects.
+class BASE_EXPORT ValueSerializer {
+ public:
+ virtual ~ValueSerializer();
+
+ virtual bool Serialize(const Value& root) = 0;
+
+ // This method deserializes the subclass-specific format into a Value object.
+ // If the return value is non-NULL, the caller takes ownership of returned
+ // Value. If the return value is NULL, and if error_code is non-NULL,
+ // error_code will be set with the underlying error.
+ // If |error_message| is non-null, it will be filled in with a formatted
+ // error message including the location of the error if appropriate.
+ virtual Value* Deserialize(int* error_code, std::string* error_str) = 0;
+};
+
+// Stream operator so Values can be used in assertion statements.
+BASE_EXPORT std::ostream& operator<<(std::ostream& out, const Value& value);
+
+} // namespace base
+
+// http://crbug.com/88666
+using base::DictionaryValue;
+using base::ListValue;
+using base::StringValue;
+using base::Value;
+
+#endif // BASE_VALUES_H_
diff --git a/src/base/values_unittest.cc b/src/base/values_unittest.cc
new file mode 100644
index 0000000..8a42f78
--- /dev/null
+++ b/src/base/values_unittest.cc
@@ -0,0 +1,786 @@
+// Copyright (c) 2012 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 <limits>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/string16.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(ValuesTest, Basic) {
+ // Test basic dictionary getting/setting
+ DictionaryValue settings;
+ std::string homepage = "http://google.com";
+ ASSERT_FALSE(settings.GetString("global.homepage", &homepage));
+ ASSERT_EQ(std::string("http://google.com"), homepage);
+
+ ASSERT_FALSE(settings.Get("global", NULL));
+ settings.Set("global", new FundamentalValue(true));
+ ASSERT_TRUE(settings.Get("global", NULL));
+ settings.SetString("global.homepage", "http://scurvy.com");
+ ASSERT_TRUE(settings.Get("global", NULL));
+ homepage = "http://google.com";
+ ASSERT_TRUE(settings.GetString("global.homepage", &homepage));
+ ASSERT_EQ(std::string("http://scurvy.com"), homepage);
+
+ // Test storing a dictionary in a list.
+ ListValue* toolbar_bookmarks;
+ ASSERT_FALSE(
+ settings.GetList("global.toolbar.bookmarks", &toolbar_bookmarks));
+
+ toolbar_bookmarks = new ListValue;
+ settings.Set("global.toolbar.bookmarks", toolbar_bookmarks);
+ ASSERT_TRUE(settings.GetList("global.toolbar.bookmarks", &toolbar_bookmarks));
+
+ DictionaryValue* new_bookmark = new DictionaryValue;
+ new_bookmark->SetString("name", "Froogle");
+ new_bookmark->SetString("url", "http://froogle.com");
+ toolbar_bookmarks->Append(new_bookmark);
+
+ ListValue* bookmark_list;
+ ASSERT_TRUE(settings.GetList("global.toolbar.bookmarks", &bookmark_list));
+ DictionaryValue* bookmark;
+ ASSERT_EQ(1U, bookmark_list->GetSize());
+ ASSERT_TRUE(bookmark_list->GetDictionary(0, &bookmark));
+ std::string bookmark_name = "Unnamed";
+ ASSERT_TRUE(bookmark->GetString("name", &bookmark_name));
+ ASSERT_EQ(std::string("Froogle"), bookmark_name);
+ std::string bookmark_url;
+ ASSERT_TRUE(bookmark->GetString("url", &bookmark_url));
+ ASSERT_EQ(std::string("http://froogle.com"), bookmark_url);
+}
+
+TEST(ValuesTest, List) {
+ scoped_ptr<ListValue> mixed_list(new ListValue());
+ mixed_list->Set(0, new FundamentalValue(true));
+ mixed_list->Set(1, new FundamentalValue(42));
+ mixed_list->Set(2, new FundamentalValue(88.8));
+ mixed_list->Set(3, new StringValue("foo"));
+ ASSERT_EQ(4u, mixed_list->GetSize());
+
+ Value *value = NULL;
+ bool bool_value = false;
+ int int_value = 0;
+ double double_value = 0.0;
+ std::string string_value;
+
+ ASSERT_FALSE(mixed_list->Get(4, &value));
+
+ ASSERT_FALSE(mixed_list->GetInteger(0, &int_value));
+ ASSERT_EQ(0, int_value);
+ ASSERT_FALSE(mixed_list->GetBoolean(1, &bool_value));
+ ASSERT_FALSE(bool_value);
+ ASSERT_FALSE(mixed_list->GetString(2, &string_value));
+ ASSERT_EQ("", string_value);
+ ASSERT_FALSE(mixed_list->GetInteger(2, &int_value));
+ ASSERT_EQ(0, int_value);
+ ASSERT_FALSE(mixed_list->GetBoolean(3, &bool_value));
+ ASSERT_FALSE(bool_value);
+
+ ASSERT_TRUE(mixed_list->GetBoolean(0, &bool_value));
+ ASSERT_TRUE(bool_value);
+ ASSERT_TRUE(mixed_list->GetInteger(1, &int_value));
+ ASSERT_EQ(42, int_value);
+ // implicit conversion from Integer to Double should be possible.
+ ASSERT_TRUE(mixed_list->GetDouble(1, &double_value));
+ ASSERT_EQ(42, double_value);
+ ASSERT_TRUE(mixed_list->GetDouble(2, &double_value));
+ ASSERT_EQ(88.8, double_value);
+ ASSERT_TRUE(mixed_list->GetString(3, &string_value));
+ ASSERT_EQ("foo", string_value);
+
+ // Try searching in the mixed list.
+ base::FundamentalValue sought_value(42);
+ base::FundamentalValue not_found_value(false);
+
+ ASSERT_NE(mixed_list->end(), mixed_list->Find(sought_value));
+ ASSERT_TRUE((*mixed_list->Find(sought_value))->GetAsInteger(&int_value));
+ ASSERT_EQ(42, int_value);
+ ASSERT_EQ(mixed_list->end(), mixed_list->Find(not_found_value));
+}
+
+TEST(ValuesTest, BinaryValue) {
+ char* buffer = NULL;
+ // Passing a null buffer pointer doesn't yield a BinaryValue
+ scoped_ptr<BinaryValue> binary(BinaryValue::Create(buffer, 0));
+ ASSERT_FALSE(binary.get());
+
+ // If you want to represent an empty binary value, use a zero-length buffer.
+ buffer = new char[1];
+ ASSERT_TRUE(buffer);
+ binary.reset(BinaryValue::Create(buffer, 0));
+ ASSERT_TRUE(binary.get());
+ ASSERT_TRUE(binary->GetBuffer());
+ ASSERT_EQ(buffer, binary->GetBuffer());
+ ASSERT_EQ(0U, binary->GetSize());
+
+ // Test the common case of a non-empty buffer
+ buffer = new char[15];
+ binary.reset(BinaryValue::Create(buffer, 15));
+ ASSERT_TRUE(binary.get());
+ ASSERT_TRUE(binary->GetBuffer());
+ ASSERT_EQ(buffer, binary->GetBuffer());
+ ASSERT_EQ(15U, binary->GetSize());
+
+ char stack_buffer[42];
+ memset(stack_buffer, '!', 42);
+ binary.reset(BinaryValue::CreateWithCopiedBuffer(stack_buffer, 42));
+ ASSERT_TRUE(binary.get());
+ ASSERT_TRUE(binary->GetBuffer());
+ ASSERT_NE(stack_buffer, binary->GetBuffer());
+ ASSERT_EQ(42U, binary->GetSize());
+ ASSERT_EQ(0, memcmp(stack_buffer, binary->GetBuffer(), binary->GetSize()));
+}
+
+TEST(ValuesTest, StringValue) {
+ // Test overloaded CreateStringValue.
+ scoped_ptr<Value> narrow_value(new StringValue("narrow"));
+ ASSERT_TRUE(narrow_value.get());
+ ASSERT_TRUE(narrow_value->IsType(Value::TYPE_STRING));
+ scoped_ptr<Value> utf16_value(new StringValue(ASCIIToUTF16("utf16")));
+ ASSERT_TRUE(utf16_value.get());
+ ASSERT_TRUE(utf16_value->IsType(Value::TYPE_STRING));
+
+ // Test overloaded GetString.
+ std::string narrow = "http://google.com";
+ string16 utf16 = ASCIIToUTF16("http://google.com");
+ ASSERT_TRUE(narrow_value->GetAsString(&narrow));
+ ASSERT_TRUE(narrow_value->GetAsString(&utf16));
+ ASSERT_EQ(std::string("narrow"), narrow);
+ ASSERT_EQ(ASCIIToUTF16("narrow"), utf16);
+
+ ASSERT_TRUE(utf16_value->GetAsString(&narrow));
+ ASSERT_TRUE(utf16_value->GetAsString(&utf16));
+ ASSERT_EQ(std::string("utf16"), narrow);
+ ASSERT_EQ(ASCIIToUTF16("utf16"), utf16);
+}
+
+// This is a Value object that allows us to tell if it's been
+// properly deleted by modifying the value of external flag on destruction.
+class DeletionTestValue : public Value {
+ public:
+ explicit DeletionTestValue(bool* deletion_flag) : Value(TYPE_NULL) {
+ Init(deletion_flag); // Separate function so that we can use ASSERT_*
+ }
+
+ void Init(bool* deletion_flag) {
+ ASSERT_TRUE(deletion_flag);
+ deletion_flag_ = deletion_flag;
+ *deletion_flag_ = false;
+ }
+
+ virtual ~DeletionTestValue() {
+ *deletion_flag_ = true;
+ }
+
+ private:
+ bool* deletion_flag_;
+};
+
+TEST(ValuesTest, ListDeletion) {
+ bool deletion_flag = true;
+
+ {
+ ListValue list;
+ list.Append(new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ }
+ EXPECT_TRUE(deletion_flag);
+
+ {
+ ListValue list;
+ list.Append(new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ list.Clear();
+ EXPECT_TRUE(deletion_flag);
+ }
+
+ {
+ ListValue list;
+ list.Append(new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ EXPECT_TRUE(list.Set(0, Value::CreateNullValue()));
+ EXPECT_TRUE(deletion_flag);
+ }
+}
+
+TEST(ValuesTest, ListRemoval) {
+ bool deletion_flag = true;
+ Value* removed_item = NULL;
+
+ {
+ ListValue list;
+ list.Append(new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ EXPECT_EQ(1U, list.GetSize());
+ EXPECT_FALSE(list.Remove(std::numeric_limits<size_t>::max(),
+ &removed_item));
+ EXPECT_FALSE(list.Remove(1, &removed_item));
+ EXPECT_TRUE(list.Remove(0, &removed_item));
+ ASSERT_TRUE(removed_item);
+ EXPECT_EQ(0U, list.GetSize());
+ }
+ EXPECT_FALSE(deletion_flag);
+ delete removed_item;
+ removed_item = NULL;
+ EXPECT_TRUE(deletion_flag);
+
+ {
+ ListValue list;
+ list.Append(new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ EXPECT_TRUE(list.Remove(0, NULL));
+ EXPECT_TRUE(deletion_flag);
+ EXPECT_EQ(0U, list.GetSize());
+ }
+
+ {
+ ListValue list;
+ DeletionTestValue* value = new DeletionTestValue(&deletion_flag);
+ list.Append(value);
+ EXPECT_FALSE(deletion_flag);
+ size_t index = 0;
+ list.Remove(*value, &index);
+ EXPECT_EQ(0U, index);
+ EXPECT_TRUE(deletion_flag);
+ EXPECT_EQ(0U, list.GetSize());
+ }
+}
+
+TEST(ValuesTest, DictionaryDeletion) {
+ std::string key = "test";
+ bool deletion_flag = true;
+
+ {
+ DictionaryValue dict;
+ dict.Set(key, new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ }
+ EXPECT_TRUE(deletion_flag);
+
+ {
+ DictionaryValue dict;
+ dict.Set(key, new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ dict.Clear();
+ EXPECT_TRUE(deletion_flag);
+ }
+
+ {
+ DictionaryValue dict;
+ dict.Set(key, new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ dict.Set(key, Value::CreateNullValue());
+ EXPECT_TRUE(deletion_flag);
+ }
+}
+
+TEST(ValuesTest, DictionaryRemoval) {
+ std::string key = "test";
+ bool deletion_flag = true;
+ Value* removed_item = NULL;
+
+ {
+ DictionaryValue dict;
+ dict.Set(key, new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ EXPECT_TRUE(dict.HasKey(key));
+ EXPECT_FALSE(dict.Remove("absent key", &removed_item));
+ EXPECT_TRUE(dict.Remove(key, &removed_item));
+ EXPECT_FALSE(dict.HasKey(key));
+ ASSERT_TRUE(removed_item);
+ }
+ EXPECT_FALSE(deletion_flag);
+ delete removed_item;
+ removed_item = NULL;
+ EXPECT_TRUE(deletion_flag);
+
+ {
+ DictionaryValue dict;
+ dict.Set(key, new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ EXPECT_TRUE(dict.HasKey(key));
+ EXPECT_TRUE(dict.Remove(key, NULL));
+ EXPECT_TRUE(deletion_flag);
+ EXPECT_FALSE(dict.HasKey(key));
+ }
+}
+
+TEST(ValuesTest, DictionaryWithoutPathExpansion) {
+ DictionaryValue dict;
+ dict.Set("this.is.expanded", Value::CreateNullValue());
+ dict.SetWithoutPathExpansion("this.isnt.expanded", Value::CreateNullValue());
+
+ EXPECT_FALSE(dict.HasKey("this.is.expanded"));
+ EXPECT_TRUE(dict.HasKey("this"));
+ Value* value1;
+ EXPECT_TRUE(dict.Get("this", &value1));
+ DictionaryValue* value2;
+ ASSERT_TRUE(dict.GetDictionaryWithoutPathExpansion("this", &value2));
+ EXPECT_EQ(value1, value2);
+ EXPECT_EQ(1U, value2->size());
+
+ EXPECT_TRUE(dict.HasKey("this.isnt.expanded"));
+ Value* value3;
+ EXPECT_FALSE(dict.Get("this.isnt.expanded", &value3));
+ Value* value4;
+ ASSERT_TRUE(dict.GetWithoutPathExpansion("this.isnt.expanded", &value4));
+ EXPECT_EQ(Value::TYPE_NULL, value4->GetType());
+}
+
+TEST(ValuesTest, DeepCopy) {
+ DictionaryValue original_dict;
+ Value* original_null = Value::CreateNullValue();
+ original_dict.Set("null", original_null);
+ FundamentalValue* original_bool = new FundamentalValue(true);
+ original_dict.Set("bool", original_bool);
+ FundamentalValue* original_int = new FundamentalValue(42);
+ original_dict.Set("int", original_int);
+ FundamentalValue* original_double = new FundamentalValue(3.14);
+ original_dict.Set("double", original_double);
+ StringValue* original_string = new StringValue("hello");
+ original_dict.Set("string", original_string);
+ StringValue* original_string16 = new StringValue(ASCIIToUTF16("hello16"));
+ original_dict.Set("string16", original_string16);
+
+ char* original_buffer = new char[42];
+ memset(original_buffer, '!', 42);
+ BinaryValue* original_binary = BinaryValue::Create(original_buffer, 42);
+ original_dict.Set("binary", original_binary);
+
+ ListValue* original_list = new ListValue();
+ FundamentalValue* original_list_element_0 = new FundamentalValue(0);
+ original_list->Append(original_list_element_0);
+ FundamentalValue* original_list_element_1 = new FundamentalValue(1);
+ original_list->Append(original_list_element_1);
+ original_dict.Set("list", original_list);
+
+ DictionaryValue* original_nested_dictionary = new DictionaryValue();
+ original_nested_dictionary->Set("key", new StringValue("value"));
+ original_dict.Set("dictionary", original_nested_dictionary);
+
+ scoped_ptr<DictionaryValue> copy_dict(original_dict.DeepCopy());
+ ASSERT_TRUE(copy_dict.get());
+ ASSERT_NE(copy_dict.get(), &original_dict);
+
+ Value* copy_null = NULL;
+ ASSERT_TRUE(copy_dict->Get("null", ©_null));
+ ASSERT_TRUE(copy_null);
+ ASSERT_NE(copy_null, original_null);
+ ASSERT_TRUE(copy_null->IsType(Value::TYPE_NULL));
+
+ Value* copy_bool = NULL;
+ ASSERT_TRUE(copy_dict->Get("bool", ©_bool));
+ ASSERT_TRUE(copy_bool);
+ ASSERT_NE(copy_bool, original_bool);
+ ASSERT_TRUE(copy_bool->IsType(Value::TYPE_BOOLEAN));
+ bool copy_bool_value = false;
+ ASSERT_TRUE(copy_bool->GetAsBoolean(©_bool_value));
+ ASSERT_TRUE(copy_bool_value);
+
+ Value* copy_int = NULL;
+ ASSERT_TRUE(copy_dict->Get("int", ©_int));
+ ASSERT_TRUE(copy_int);
+ ASSERT_NE(copy_int, original_int);
+ ASSERT_TRUE(copy_int->IsType(Value::TYPE_INTEGER));
+ int copy_int_value = 0;
+ ASSERT_TRUE(copy_int->GetAsInteger(©_int_value));
+ ASSERT_EQ(42, copy_int_value);
+
+ Value* copy_double = NULL;
+ ASSERT_TRUE(copy_dict->Get("double", ©_double));
+ ASSERT_TRUE(copy_double);
+ ASSERT_NE(copy_double, original_double);
+ ASSERT_TRUE(copy_double->IsType(Value::TYPE_DOUBLE));
+ double copy_double_value = 0;
+ ASSERT_TRUE(copy_double->GetAsDouble(©_double_value));
+ ASSERT_EQ(3.14, copy_double_value);
+
+ Value* copy_string = NULL;
+ ASSERT_TRUE(copy_dict->Get("string", ©_string));
+ ASSERT_TRUE(copy_string);
+ ASSERT_NE(copy_string, original_string);
+ ASSERT_TRUE(copy_string->IsType(Value::TYPE_STRING));
+ std::string copy_string_value;
+ string16 copy_string16_value;
+ ASSERT_TRUE(copy_string->GetAsString(©_string_value));
+ ASSERT_TRUE(copy_string->GetAsString(©_string16_value));
+ ASSERT_EQ(std::string("hello"), copy_string_value);
+ ASSERT_EQ(ASCIIToUTF16("hello"), copy_string16_value);
+
+ Value* copy_string16 = NULL;
+ ASSERT_TRUE(copy_dict->Get("string16", ©_string16));
+ ASSERT_TRUE(copy_string16);
+ ASSERT_NE(copy_string16, original_string16);
+ ASSERT_TRUE(copy_string16->IsType(Value::TYPE_STRING));
+ ASSERT_TRUE(copy_string16->GetAsString(©_string_value));
+ ASSERT_TRUE(copy_string16->GetAsString(©_string16_value));
+ ASSERT_EQ(std::string("hello16"), copy_string_value);
+ ASSERT_EQ(ASCIIToUTF16("hello16"), copy_string16_value);
+
+ Value* copy_binary = NULL;
+ ASSERT_TRUE(copy_dict->Get("binary", ©_binary));
+ ASSERT_TRUE(copy_binary);
+ ASSERT_NE(copy_binary, original_binary);
+ ASSERT_TRUE(copy_binary->IsType(Value::TYPE_BINARY));
+ ASSERT_NE(original_binary->GetBuffer(),
+ static_cast<BinaryValue*>(copy_binary)->GetBuffer());
+ ASSERT_EQ(original_binary->GetSize(),
+ static_cast<BinaryValue*>(copy_binary)->GetSize());
+ ASSERT_EQ(0, memcmp(original_binary->GetBuffer(),
+ static_cast<BinaryValue*>(copy_binary)->GetBuffer(),
+ original_binary->GetSize()));
+
+ Value* copy_value = NULL;
+ ASSERT_TRUE(copy_dict->Get("list", ©_value));
+ ASSERT_TRUE(copy_value);
+ ASSERT_NE(copy_value, original_list);
+ ASSERT_TRUE(copy_value->IsType(Value::TYPE_LIST));
+ ListValue* copy_list = NULL;
+ ASSERT_TRUE(copy_value->GetAsList(©_list));
+ ASSERT_TRUE(copy_list);
+ ASSERT_EQ(2U, copy_list->GetSize());
+
+ Value* copy_list_element_0;
+ ASSERT_TRUE(copy_list->Get(0, ©_list_element_0));
+ ASSERT_TRUE(copy_list_element_0);
+ ASSERT_NE(copy_list_element_0, original_list_element_0);
+ int copy_list_element_0_value;
+ ASSERT_TRUE(copy_list_element_0->GetAsInteger(©_list_element_0_value));
+ ASSERT_EQ(0, copy_list_element_0_value);
+
+ Value* copy_list_element_1;
+ ASSERT_TRUE(copy_list->Get(1, ©_list_element_1));
+ ASSERT_TRUE(copy_list_element_1);
+ ASSERT_NE(copy_list_element_1, original_list_element_1);
+ int copy_list_element_1_value;
+ ASSERT_TRUE(copy_list_element_1->GetAsInteger(©_list_element_1_value));
+ ASSERT_EQ(1, copy_list_element_1_value);
+
+ copy_value = NULL;
+ ASSERT_TRUE(copy_dict->Get("dictionary", ©_value));
+ ASSERT_TRUE(copy_value);
+ ASSERT_NE(copy_value, original_nested_dictionary);
+ ASSERT_TRUE(copy_value->IsType(Value::TYPE_DICTIONARY));
+ DictionaryValue* copy_nested_dictionary = NULL;
+ ASSERT_TRUE(copy_value->GetAsDictionary(©_nested_dictionary));
+ ASSERT_TRUE(copy_nested_dictionary);
+ EXPECT_TRUE(copy_nested_dictionary->HasKey("key"));
+}
+
+TEST(ValuesTest, Equals) {
+ Value* null1 = Value::CreateNullValue();
+ Value* null2 = Value::CreateNullValue();
+ EXPECT_NE(null1, null2);
+ EXPECT_TRUE(null1->Equals(null2));
+
+ Value* boolean = new FundamentalValue(false);
+ EXPECT_FALSE(null1->Equals(boolean));
+ delete null1;
+ delete null2;
+ delete boolean;
+
+ DictionaryValue dv;
+ dv.SetBoolean("a", false);
+ dv.SetInteger("b", 2);
+ dv.SetDouble("c", 2.5);
+ dv.SetString("d1", "string");
+ dv.SetString("d2", ASCIIToUTF16("http://google.com"));
+ dv.Set("e", Value::CreateNullValue());
+
+ scoped_ptr<DictionaryValue> copy;
+ copy.reset(dv.DeepCopy());
+ EXPECT_TRUE(dv.Equals(copy.get()));
+
+ ListValue* list = new ListValue;
+ list->Append(Value::CreateNullValue());
+ list->Append(new DictionaryValue);
+ dv.Set("f", list);
+
+ EXPECT_FALSE(dv.Equals(copy.get()));
+ copy->Set("f", list->DeepCopy());
+ EXPECT_TRUE(dv.Equals(copy.get()));
+
+ list->Append(new FundamentalValue(true));
+ EXPECT_FALSE(dv.Equals(copy.get()));
+
+ // Check if Equals detects differences in only the keys.
+ copy.reset(dv.DeepCopy());
+ EXPECT_TRUE(dv.Equals(copy.get()));
+ copy->Remove("a", NULL);
+ copy->SetBoolean("aa", false);
+ EXPECT_FALSE(dv.Equals(copy.get()));
+}
+
+TEST(ValuesTest, StaticEquals) {
+ scoped_ptr<Value> null1(Value::CreateNullValue());
+ scoped_ptr<Value> null2(Value::CreateNullValue());
+ EXPECT_TRUE(Value::Equals(null1.get(), null2.get()));
+ EXPECT_TRUE(Value::Equals(NULL, NULL));
+
+ scoped_ptr<Value> i42(new FundamentalValue(42));
+ scoped_ptr<Value> j42(new FundamentalValue(42));
+ scoped_ptr<Value> i17(new FundamentalValue(17));
+ EXPECT_TRUE(Value::Equals(i42.get(), i42.get()));
+ EXPECT_TRUE(Value::Equals(j42.get(), i42.get()));
+ EXPECT_TRUE(Value::Equals(i42.get(), j42.get()));
+ EXPECT_FALSE(Value::Equals(i42.get(), i17.get()));
+ EXPECT_FALSE(Value::Equals(i42.get(), NULL));
+ EXPECT_FALSE(Value::Equals(NULL, i42.get()));
+
+ // NULL and Value::CreateNullValue() are intentionally different: We need
+ // support for NULL as a return value for "undefined" without caring for
+ // ownership of the pointer.
+ EXPECT_FALSE(Value::Equals(null1.get(), NULL));
+ EXPECT_FALSE(Value::Equals(NULL, null1.get()));
+}
+
+TEST(ValuesTest, DeepCopyCovariantReturnTypes) {
+ DictionaryValue original_dict;
+ Value* original_null = Value::CreateNullValue();
+ original_dict.Set("null", original_null);
+ FundamentalValue* original_bool = new FundamentalValue(true);
+ original_dict.Set("bool", original_bool);
+ FundamentalValue* original_int = new FundamentalValue(42);
+ original_dict.Set("int", original_int);
+ FundamentalValue* original_double = new FundamentalValue(3.14);
+ original_dict.Set("double", original_double);
+ StringValue* original_string = new StringValue("hello");
+ original_dict.Set("string", original_string);
+ StringValue* original_string16 = new StringValue(ASCIIToUTF16("hello16"));
+ original_dict.Set("string16", original_string16);
+
+ char* original_buffer = new char[42];
+ memset(original_buffer, '!', 42);
+ BinaryValue* original_binary = BinaryValue::Create(original_buffer, 42);
+ original_dict.Set("binary", original_binary);
+
+ ListValue* original_list = new ListValue();
+ FundamentalValue* original_list_element_0 = new FundamentalValue(0);
+ original_list->Append(original_list_element_0);
+ FundamentalValue* original_list_element_1 = new FundamentalValue(1);
+ original_list->Append(original_list_element_1);
+ original_dict.Set("list", original_list);
+
+ Value* original_dict_value = &original_dict;
+ Value* original_bool_value = original_bool;
+ Value* original_int_value = original_int;
+ Value* original_double_value = original_double;
+ Value* original_string_value = original_string;
+ Value* original_string16_value = original_string16;
+ Value* original_binary_value = original_binary;
+ Value* original_list_value = original_list;
+
+ scoped_ptr<Value> copy_dict_value(original_dict_value->DeepCopy());
+ scoped_ptr<Value> copy_bool_value(original_bool_value->DeepCopy());
+ scoped_ptr<Value> copy_int_value(original_int_value->DeepCopy());
+ scoped_ptr<Value> copy_double_value(original_double_value->DeepCopy());
+ scoped_ptr<Value> copy_string_value(original_string_value->DeepCopy());
+ scoped_ptr<Value> copy_string16_value(original_string16_value->DeepCopy());
+ scoped_ptr<Value> copy_binary_value(original_binary_value->DeepCopy());
+ scoped_ptr<Value> copy_list_value(original_list_value->DeepCopy());
+
+ EXPECT_TRUE(original_dict_value->Equals(copy_dict_value.get()));
+ EXPECT_TRUE(original_bool_value->Equals(copy_bool_value.get()));
+ EXPECT_TRUE(original_int_value->Equals(copy_int_value.get()));
+ EXPECT_TRUE(original_double_value->Equals(copy_double_value.get()));
+ EXPECT_TRUE(original_string_value->Equals(copy_string_value.get()));
+ EXPECT_TRUE(original_string16_value->Equals(copy_string16_value.get()));
+ EXPECT_TRUE(original_binary_value->Equals(copy_binary_value.get()));
+ EXPECT_TRUE(original_list_value->Equals(copy_list_value.get()));
+}
+
+TEST(ValuesTest, RemoveEmptyChildren) {
+ scoped_ptr<DictionaryValue> root(new DictionaryValue);
+ // Remove empty lists and dictionaries.
+ root->Set("empty_dict", new DictionaryValue);
+ root->Set("empty_list", new ListValue);
+ root->SetWithoutPathExpansion("a.b.c.d.e", new DictionaryValue);
+ root.reset(root->DeepCopyWithoutEmptyChildren());
+ EXPECT_TRUE(root->empty());
+
+ // Make sure we don't prune too much.
+ root->SetBoolean("bool", true);
+ root->Set("empty_dict", new DictionaryValue);
+ root->SetString("empty_string", "");
+ root.reset(root->DeepCopyWithoutEmptyChildren());
+ EXPECT_EQ(2U, root->size());
+
+ // Should do nothing.
+ root.reset(root->DeepCopyWithoutEmptyChildren());
+ EXPECT_EQ(2U, root->size());
+
+ // Nested test cases. These should all reduce back to the bool and string
+ // set above.
+ {
+ root->Set("a.b.c.d.e", new DictionaryValue);
+ root.reset(root->DeepCopyWithoutEmptyChildren());
+ EXPECT_EQ(2U, root->size());
+ }
+ {
+ DictionaryValue* inner = new DictionaryValue;
+ root->Set("dict_with_emtpy_children", inner);
+ inner->Set("empty_dict", new DictionaryValue);
+ inner->Set("empty_list", new ListValue);
+ root.reset(root->DeepCopyWithoutEmptyChildren());
+ EXPECT_EQ(2U, root->size());
+ }
+ {
+ ListValue* inner = new ListValue;
+ root->Set("list_with_empty_children", inner);
+ inner->Append(new DictionaryValue);
+ inner->Append(new ListValue);
+ root.reset(root->DeepCopyWithoutEmptyChildren());
+ EXPECT_EQ(2U, root->size());
+ }
+
+ // Nested with siblings.
+ {
+ ListValue* inner = new ListValue;
+ root->Set("list_with_empty_children", inner);
+ inner->Append(new DictionaryValue);
+ inner->Append(new ListValue);
+ DictionaryValue* inner2 = new DictionaryValue;
+ root->Set("dict_with_empty_children", inner2);
+ inner2->Set("empty_dict", new DictionaryValue);
+ inner2->Set("empty_list", new ListValue);
+ root.reset(root->DeepCopyWithoutEmptyChildren());
+ EXPECT_EQ(2U, root->size());
+ }
+
+ // Make sure nested values don't get pruned.
+ {
+ ListValue* inner = new ListValue;
+ root->Set("list_with_empty_children", inner);
+ ListValue* inner2 = new ListValue;
+ inner->Append(new DictionaryValue);
+ inner->Append(inner2);
+ inner2->Append(new StringValue("hello"));
+ root.reset(root->DeepCopyWithoutEmptyChildren());
+ EXPECT_EQ(3U, root->size());
+ EXPECT_TRUE(root->GetList("list_with_empty_children", &inner));
+ EXPECT_EQ(1U, inner->GetSize()); // Dictionary was pruned.
+ EXPECT_TRUE(inner->GetList(0, &inner2));
+ EXPECT_EQ(1U, inner2->GetSize());
+ }
+}
+
+TEST(ValuesTest, MergeDictionary) {
+ scoped_ptr<DictionaryValue> base(new DictionaryValue);
+ base->SetString("base_key", "base_key_value_base");
+ base->SetString("collide_key", "collide_key_value_base");
+ DictionaryValue* base_sub_dict = new DictionaryValue;
+ base_sub_dict->SetString("sub_base_key", "sub_base_key_value_base");
+ base_sub_dict->SetString("sub_collide_key", "sub_collide_key_value_base");
+ base->Set("sub_dict_key", base_sub_dict);
+
+ scoped_ptr<DictionaryValue> merge(new DictionaryValue);
+ merge->SetString("merge_key", "merge_key_value_merge");
+ merge->SetString("collide_key", "collide_key_value_merge");
+ DictionaryValue* merge_sub_dict = new DictionaryValue;
+ merge_sub_dict->SetString("sub_merge_key", "sub_merge_key_value_merge");
+ merge_sub_dict->SetString("sub_collide_key", "sub_collide_key_value_merge");
+ merge->Set("sub_dict_key", merge_sub_dict);
+
+ base->MergeDictionary(merge.get());
+
+ EXPECT_EQ(4U, base->size());
+ std::string base_key_value;
+ EXPECT_TRUE(base->GetString("base_key", &base_key_value));
+ EXPECT_EQ("base_key_value_base", base_key_value); // Base value preserved.
+ std::string collide_key_value;
+ EXPECT_TRUE(base->GetString("collide_key", &collide_key_value));
+ EXPECT_EQ("collide_key_value_merge", collide_key_value); // Replaced.
+ std::string merge_key_value;
+ EXPECT_TRUE(base->GetString("merge_key", &merge_key_value));
+ EXPECT_EQ("merge_key_value_merge", merge_key_value); // Merged in.
+
+ DictionaryValue* res_sub_dict;
+ EXPECT_TRUE(base->GetDictionary("sub_dict_key", &res_sub_dict));
+ EXPECT_EQ(3U, res_sub_dict->size());
+ std::string sub_base_key_value;
+ EXPECT_TRUE(res_sub_dict->GetString("sub_base_key", &sub_base_key_value));
+ EXPECT_EQ("sub_base_key_value_base", sub_base_key_value); // Preserved.
+ std::string sub_collide_key_value;
+ EXPECT_TRUE(res_sub_dict->GetString("sub_collide_key",
+ &sub_collide_key_value));
+ EXPECT_EQ("sub_collide_key_value_merge", sub_collide_key_value); // Replaced.
+ std::string sub_merge_key_value;
+ EXPECT_TRUE(res_sub_dict->GetString("sub_merge_key", &sub_merge_key_value));
+ EXPECT_EQ("sub_merge_key_value_merge", sub_merge_key_value); // Merged in.
+}
+
+TEST(ValuesTest, MergeDictionaryDeepCopy) {
+ DictionaryValue* child = new DictionaryValue;
+ child->SetString("test", "value");
+ EXPECT_EQ(1U, child->size());
+
+ std::string value;
+ EXPECT_TRUE(child->GetString("test", &value));
+ EXPECT_EQ("value", value);
+
+ scoped_ptr<DictionaryValue> base(new DictionaryValue);
+ base->Set("dict", child);
+ EXPECT_EQ(1U, base->size());
+
+ DictionaryValue* ptr;
+ EXPECT_TRUE(base->GetDictionary("dict", &ptr));
+ EXPECT_EQ(child, ptr);
+
+ scoped_ptr<DictionaryValue> merged(new DictionaryValue);
+ merged->MergeDictionary(base.get());
+ EXPECT_EQ(1U, merged->size());
+ EXPECT_TRUE(merged->GetDictionary("dict", &ptr));
+ EXPECT_NE(child, ptr);
+ EXPECT_TRUE(ptr->GetString("test", &value));
+ EXPECT_EQ("value", value);
+
+ child->SetString("test", "overwrite");
+ base.reset();
+ EXPECT_TRUE(ptr->GetString("test", &value));
+ EXPECT_EQ("value", value);
+}
+
+TEST(ValuesTest, DictionaryIterator) {
+ DictionaryValue dict;
+ for (DictionaryValue::Iterator it(dict); it.HasNext(); it.Advance()) {
+ ADD_FAILURE();
+ }
+
+ StringValue value1("value1");
+ dict.Set("key1", value1.DeepCopy());
+ bool seen1 = false;
+ for (DictionaryValue::Iterator it(dict); it.HasNext(); it.Advance()) {
+ EXPECT_FALSE(seen1);
+ EXPECT_EQ("key1", it.key());
+ EXPECT_TRUE(value1.Equals(&it.value()));
+ seen1 = true;
+ }
+ EXPECT_TRUE(seen1);
+
+ StringValue value2("value2");
+ dict.Set("key2", value2.DeepCopy());
+ bool seen2 = seen1 = false;
+ for (DictionaryValue::Iterator it(dict); it.HasNext(); it.Advance()) {
+ if (it.key() == "key1") {
+ EXPECT_FALSE(seen1);
+ EXPECT_TRUE(value1.Equals(&it.value()));
+ seen1 = true;
+ } else if (it.key() == "key2") {
+ EXPECT_FALSE(seen2);
+ EXPECT_TRUE(value2.Equals(&it.value()));
+ seen2 = true;
+ } else {
+ ADD_FAILURE();
+ }
+ }
+ EXPECT_TRUE(seen1);
+ EXPECT_TRUE(seen2);
+}
+
+} // namespace base
diff --git a/src/base/version.cc b/src/base/version.cc
new file mode 100644
index 0000000..b7db4b5
--- /dev/null
+++ b/src/base/version.cc
@@ -0,0 +1,172 @@
+// Copyright (c) 2012 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/version.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
+#include "base/string_util.h"
+
+namespace {
+
+// Parses the |numbers| vector representing the different numbers
+// inside the version string and constructs a vector of valid integers. It stops
+// when it reaches an invalid item (including the wildcard character). |parsed|
+// is the resulting integer vector. Function returns true if all numbers were
+// parsed successfully, false otherwise.
+bool ParseVersionNumbers(const std::string& version_str,
+ std::vector<uint16>* parsed) {
+ std::vector<std::string> numbers;
+ base::SplitString(version_str, '.', &numbers);
+ if (numbers.empty())
+ return false;
+
+ for (std::vector<std::string>::const_iterator it = numbers.begin();
+ it != numbers.end(); ++it) {
+ int num;
+ if (!base::StringToInt(*it, &num))
+ return false;
+
+ if (num < 0)
+ return false;
+
+ const uint16 max = 0xFFFF;
+ if (num > max)
+ return false;
+
+ // This throws out things like +3, or 032.
+ if (base::IntToString(num) != *it)
+ return false;
+
+ parsed->push_back(static_cast<uint16>(num));
+ }
+ return true;
+}
+
+// Compares version components in |components1| with components in
+// |components2|. Returns -1, 0 or 1 if |components1| is greater than, equal to,
+// or less than |components2|, respectively.
+int CompareVersionComponents(const std::vector<uint16>& components1,
+ const std::vector<uint16>& components2) {
+ const size_t count = std::min(components1.size(), components2.size());
+ for (size_t i = 0; i < count; ++i) {
+ if (components1[i] > components2[i])
+ return 1;
+ if (components1[i] < components2[i])
+ return -1;
+ }
+ if (components1.size() > components2.size()) {
+ for (size_t i = count; i < components1.size(); ++i) {
+ if (components1[i] > 0)
+ return 1;
+ }
+ } else if (components1.size() < components2.size()) {
+ for (size_t i = count; i < components2.size(); ++i) {
+ if (components2[i] > 0)
+ return -1;
+ }
+ }
+ return 0;
+}
+
+} // namespace
+
+Version::Version() {
+}
+
+Version::~Version() {
+}
+
+Version::Version(const std::string& version_str) {
+ std::vector<uint16> parsed;
+ if (!ParseVersionNumbers(version_str, &parsed))
+ return;
+
+ components_.swap(parsed);
+}
+
+bool Version::IsValid() const {
+ return (!components_.empty());
+}
+
+// static
+bool Version::IsValidWildcardString(const std::string& wildcard_string) {
+ std::string version_string = wildcard_string;
+ if (EndsWith(wildcard_string.c_str(), ".*", false))
+ version_string = wildcard_string.substr(0, wildcard_string.size() - 2);
+
+ Version version(version_string);
+ return version.IsValid();
+}
+
+bool Version::IsOlderThan(const std::string& version_str) const {
+ Version proposed_ver(version_str);
+ if (!proposed_ver.IsValid())
+ return false;
+ return (CompareTo(proposed_ver) < 0);
+}
+
+int Version::CompareToWildcardString(const std::string& wildcard_string) const {
+ DCHECK(IsValid());
+ DCHECK(Version::IsValidWildcardString(wildcard_string));
+
+ // Default behavior if the string doesn't end with a wildcard.
+ if (!EndsWith(wildcard_string.c_str(), ".*", false)) {
+ Version version(wildcard_string);
+ DCHECK(version.IsValid());
+ return CompareTo(version);
+ }
+
+ std::vector<uint16> parsed;
+ const bool success = ParseVersionNumbers(
+ wildcard_string.substr(0, wildcard_string.length() - 2), &parsed);
+ DCHECK(success);
+ const int comparison = CompareVersionComponents(components_, parsed);
+ // If the version is smaller than the wildcard version's |parsed| vector,
+ // then the wildcard has no effect (e.g. comparing 1.2.3 and 1.3.*) and the
+ // version is still smaller. Same logic for equality (e.g. comparing 1.2.2 to
+ // 1.2.2.* is 0 regardless of the wildcard). Under this logic,
+ // 1.2.0.0.0.0 compared to 1.2.* is 0.
+ if (comparison == -1 || comparison == 0)
+ return comparison;
+
+ // Catch the case where the digits of |parsed| are found in |components_|,
+ // which means that the two are equal since |parsed| has a trailing "*".
+ // (e.g. 1.2.3 vs. 1.2.* will return 0). All other cases return 1 since
+ // components is greater (e.g. 3.2.3 vs 1.*).
+ DCHECK_GT(parsed.size(), 0UL);
+ const size_t min_num_comp = std::min(components_.size(), parsed.size());
+ for (size_t i = 0; i < min_num_comp; ++i) {
+ if (components_[i] != parsed[i])
+ return 1;
+ }
+ return 0;
+}
+
+bool Version::Equals(const Version& that) const {
+ DCHECK(IsValid());
+ DCHECK(that.IsValid());
+ return (CompareTo(that) == 0);
+}
+
+int Version::CompareTo(const Version& other) const {
+ DCHECK(IsValid());
+ DCHECK(other.IsValid());
+ return CompareVersionComponents(components_, other.components_);
+}
+
+const std::string Version::GetString() const {
+ DCHECK(IsValid());
+ std::string version_str;
+ size_t count = components_.size();
+ for (size_t i = 0; i < count - 1; ++i) {
+ version_str.append(base::IntToString(components_[i]));
+ version_str.append(".");
+ }
+ version_str.append(base::IntToString(components_[count - 1]));
+ return version_str;
+}
diff --git a/src/base/version.h b/src/base/version.h
new file mode 100644
index 0000000..ab7145e
--- /dev/null
+++ b/src/base/version.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2012 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_VERSION_H_
+#define BASE_VERSION_H_
+
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+// Version represents a dotted version number, like "1.2.3.4", supporting
+// parsing and comparison.
+class BASE_EXPORT Version {
+ public:
+ // The only thing you can legally do to a default constructed
+ // Version object is assign to it.
+ Version();
+
+ ~Version();
+
+ // Initializes from a decimal dotted version number, like "0.1.1".
+ // Each component is limited to a uint16. Call IsValid() to learn
+ // the outcome.
+ explicit Version(const std::string& version_str);
+
+ // Returns true if the object contains a valid version number.
+ bool IsValid() const;
+
+ // Returns true if the version wildcard string is valid. The version wildcard
+ // string may end with ".*" (e.g. 1.2.*, 1.*). Any other arrangement with "*"
+ // is invalid (e.g. 1.*.3 or 1.2.3*). This functions defaults to standard
+ // Version behavior (IsValid) if no wildcard is present.
+ static bool IsValidWildcardString(const std::string& wildcard_string);
+
+ // Commonly used pattern. Given a valid version object, compare if a
+ // |version_str| results in a newer version. Returns true if the
+ // string represents valid version and if the version is greater than
+ // than the version of this object.
+ bool IsOlderThan(const std::string& version_str) const;
+
+ bool Equals(const Version& other) const;
+
+ // Returns -1, 0, 1 for <, ==, >.
+ int CompareTo(const Version& other) const;
+
+ // Given a valid version object, compare if a |wildcard_string| results in a
+ // newer version. This function will default to CompareTo if the string does
+ // not end in wildcard sequence ".*". IsValidWildcard(wildcard_string) must be
+ // true before using this function.
+ int CompareToWildcardString(const std::string& wildcard_string) const;
+
+ // Return the string representation of this version.
+ const std::string GetString() const;
+
+ const std::vector<uint16>& components() const { return components_; }
+
+ private:
+ std::vector<uint16> components_;
+};
+
+#endif // BASE_VERSION_H_
diff --git a/src/base/version_unittest.cc b/src/base/version_unittest.cc
new file mode 100644
index 0000000..2a2309e
--- /dev/null
+++ b/src/base/version_unittest.cc
@@ -0,0 +1,141 @@
+// Copyright (c) 2012 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/version.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+TEST(VersionTest, DefaultConstructor) {
+ Version v;
+ EXPECT_FALSE(v.IsValid());
+}
+
+TEST(VersionTest, ValueSemantics) {
+ Version v1("1.2.3.4");
+ EXPECT_TRUE(v1.IsValid());
+ Version v3;
+ EXPECT_FALSE(v3.IsValid());
+ {
+ Version v2(v1);
+ v3 = v2;
+ EXPECT_TRUE(v2.IsValid());
+ EXPECT_TRUE(v1.Equals(v2));
+ }
+ EXPECT_TRUE(v3.Equals(v1));
+}
+
+TEST(VersionTest, GetVersionFromString) {
+ static const struct version_string {
+ const char* input;
+ size_t parts;
+ bool success;
+ } cases[] = {
+ {"", 0, false},
+ {" ", 0, false},
+ {"\t", 0, false},
+ {"\n", 0, false},
+ {" ", 0, false},
+ {".", 0, false},
+ {" . ", 0, false},
+ {"0", 1, true},
+ {"0.0", 2, true},
+ {"65537.0", 0, false},
+ {"-1.0", 0, false},
+ {"1.-1.0", 0, false},
+ {"+1.0", 0, false},
+ {"1.+1.0", 0, false},
+ {"1.0a", 0, false},
+ {"1.2.3.4.5.6.7.8.9.0", 10, true},
+ {"02.1", 0, false},
+ {"f.1", 0, false},
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+ Version version(cases[i].input);
+ EXPECT_EQ(cases[i].success, version.IsValid());
+ if (cases[i].success)
+ EXPECT_EQ(cases[i].parts, version.components().size());
+ }
+}
+
+TEST(VersionTest, Compare) {
+ static const struct version_compare {
+ const char* lhs;
+ const char* rhs;
+ int expected;
+ } cases[] = {
+ {"1.0", "1.0", 0},
+ {"1.0", "0.0", 1},
+ {"1.0", "2.0", -1},
+ {"1.0", "1.1", -1},
+ {"1.1", "1.0", 1},
+ {"1.0", "1.0.1", -1},
+ {"1.1", "1.0.1", 1},
+ {"1.1", "1.0.1", 1},
+ {"1.0.0", "1.0", 0},
+ {"1.0.3", "1.0.20", -1},
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+ Version lhs(cases[i].lhs);
+ Version rhs(cases[i].rhs);
+ EXPECT_EQ(lhs.CompareTo(rhs), cases[i].expected) <<
+ cases[i].lhs << " ? " << cases[i].rhs;
+
+ EXPECT_EQ(lhs.IsOlderThan(cases[i].rhs), (cases[i].expected == -1));
+ }
+}
+
+TEST(VersionTest, CompareToWildcardString) {
+ static const struct version_compare {
+ const char* lhs;
+ const char* rhs;
+ int expected;
+ } cases[] = {
+ {"1.0", "1.*", 0},
+ {"1.0", "0.*", 1},
+ {"1.0", "2.*", -1},
+ {"1.2.3", "1.2.3.*", 0},
+ {"10.0", "1.0.*", 1},
+ {"1.0", "3.0.*", -1},
+ {"1.4", "1.3.0.*", 1},
+ {"1.3.9", "1.3.*", 0},
+ {"1.4.1", "1.3.*", 1},
+ {"1.3", "1.4.5.*", -1},
+ {"1.5", "1.4.5.*", 1},
+ {"1.3.9", "1.3.*", 0},
+ {"1.2.0.0.0.0", "1.2.*", 0},
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+ const Version version(cases[i].lhs);
+ const int result = version.CompareToWildcardString(cases[i].rhs);
+ EXPECT_EQ(result, cases[i].expected) << cases[i].lhs << "?" << cases[i].rhs;
+ }
+}
+
+TEST(VersionTest, IsValidWildcardString) {
+ static const struct version_compare {
+ const char* version;
+ bool expected;
+ } cases[] = {
+ {"1.0", true},
+ {"", false},
+ {"1.2.3.4.5.6", true},
+ {"1.2.3.*", true},
+ {"1.2.3.5*", false},
+ {"1.2.3.56*", false},
+ {"1.*.3", false},
+ {"20.*", true},
+ {"+2.*", false},
+ {"*", false},
+ {"*.2", false},
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+ EXPECT_EQ(Version::IsValidWildcardString(cases[i].version),
+ cases[i].expected) << cases[i].version << "?" << cases[i].expected;
+ }
+}
+
+} // namespace
diff --git a/src/base/vlog.cc b/src/base/vlog.cc
new file mode 100644
index 0000000..332de38
--- /dev/null
+++ b/src/base/vlog.cc
@@ -0,0 +1,177 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/vlog.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
+
+namespace logging {
+
+const int VlogInfo::kDefaultVlogLevel = 0;
+
+struct VlogInfo::VmodulePattern {
+ enum MatchTarget { MATCH_MODULE, MATCH_FILE };
+
+ explicit VmodulePattern(const std::string& pattern);
+
+ VmodulePattern();
+
+ std::string pattern;
+ int vlog_level;
+ MatchTarget match_target;
+};
+
+VlogInfo::VmodulePattern::VmodulePattern(const std::string& pattern)
+ : pattern(pattern),
+ vlog_level(VlogInfo::kDefaultVlogLevel),
+ match_target(MATCH_MODULE) {
+ // If the pattern contains a {forward,back} slash, we assume that
+ // it's meant to be tested against the entire __FILE__ string.
+ std::string::size_type first_slash = pattern.find_first_of("\\/");
+ if (first_slash != std::string::npos)
+ match_target = MATCH_FILE;
+}
+
+VlogInfo::VmodulePattern::VmodulePattern()
+ : vlog_level(VlogInfo::kDefaultVlogLevel),
+ match_target(MATCH_MODULE) {}
+
+VlogInfo::VlogInfo(const std::string& v_switch,
+ const std::string& vmodule_switch,
+ int* min_log_level)
+ : min_log_level_(min_log_level) {
+ DCHECK(min_log_level != NULL);
+
+ typedef std::pair<std::string, std::string> KVPair;
+ int vlog_level = 0;
+ if (!v_switch.empty()) {
+ if (base::StringToInt(v_switch, &vlog_level)) {
+ SetMaxVlogLevel(vlog_level);
+ } else {
+ DLOG(WARNING) << "Could not parse v switch \"" << v_switch << "\"";
+ }
+ }
+
+ std::vector<KVPair> kv_pairs;
+ if (!base::SplitStringIntoKeyValuePairs(
+ vmodule_switch, '=', ',', &kv_pairs)) {
+ DLOG(WARNING) << "Could not fully parse vmodule switch \""
+ << vmodule_switch << "\"";
+ }
+ for (std::vector<KVPair>::const_iterator it = kv_pairs.begin();
+ it != kv_pairs.end(); ++it) {
+ VmodulePattern pattern(it->first);
+ if (!base::StringToInt(it->second, &pattern.vlog_level)) {
+ DLOG(WARNING) << "Parsed vlog level for \""
+ << it->first << "=" << it->second
+ << "\" as " << pattern.vlog_level;
+ }
+ vmodule_levels_.push_back(pattern);
+ }
+}
+
+VlogInfo::~VlogInfo() {}
+
+namespace {
+
+// Given a path, returns the basename with the extension chopped off
+// (and any -inl suffix). We avoid using FilePath to minimize the
+// number of dependencies the logging system has.
+base::StringPiece GetModule(const base::StringPiece& file) {
+ base::StringPiece module(file);
+ base::StringPiece::size_type last_slash_pos =
+ module.find_last_of("\\/");
+ if (last_slash_pos != base::StringPiece::npos)
+ module.remove_prefix(last_slash_pos + 1);
+ base::StringPiece::size_type extension_start = module.rfind('.');
+ module = module.substr(0, extension_start);
+ static const char kInlSuffix[] = "-inl";
+ static const int kInlSuffixLen = arraysize(kInlSuffix) - 1;
+ if (module.ends_with(kInlSuffix))
+ module.remove_suffix(kInlSuffixLen);
+ return module;
+}
+
+} // namespace
+
+int VlogInfo::GetVlogLevel(const base::StringPiece& file) const {
+ if (!vmodule_levels_.empty()) {
+ base::StringPiece module(GetModule(file));
+ for (std::vector<VmodulePattern>::const_iterator it =
+ vmodule_levels_.begin(); it != vmodule_levels_.end(); ++it) {
+ base::StringPiece target(
+ (it->match_target == VmodulePattern::MATCH_FILE) ? file : module);
+ if (MatchVlogPattern(target, it->pattern))
+ return it->vlog_level;
+ }
+ }
+ return GetMaxVlogLevel();
+}
+
+void VlogInfo::SetMaxVlogLevel(int level) {
+ // Log severity is the negative verbosity.
+ *min_log_level_ = -level;
+}
+
+int VlogInfo::GetMaxVlogLevel() const {
+ return -*min_log_level_;
+}
+
+bool MatchVlogPattern(const base::StringPiece& string,
+ const base::StringPiece& vlog_pattern) {
+ base::StringPiece p(vlog_pattern);
+ base::StringPiece s(string);
+ // Consume characters until the next star.
+ while (!p.empty() && !s.empty() && (p[0] != '*')) {
+ switch (p[0]) {
+ // A slash (forward or back) must match a slash (forward or back).
+ case '/':
+ case '\\':
+ if ((s[0] != '/') && (s[0] != '\\'))
+ return false;
+ break;
+
+ // A '?' matches anything.
+ case '?':
+ break;
+
+ // Anything else must match literally.
+ default:
+ if (p[0] != s[0])
+ return false;
+ break;
+ }
+ p.remove_prefix(1), s.remove_prefix(1);
+ }
+
+ // An empty pattern here matches only an empty string.
+ if (p.empty())
+ return s.empty();
+
+ // Coalesce runs of consecutive stars. There should be at least
+ // one.
+ while (!p.empty() && (p[0] == '*'))
+ p.remove_prefix(1);
+
+ // Since we moved past the stars, an empty pattern here matches
+ // anything.
+ if (p.empty())
+ return true;
+
+ // Since we moved past the stars and p is non-empty, if some
+ // non-empty substring of s matches p, then we ourselves match.
+ while (!s.empty()) {
+ if (MatchVlogPattern(s, p))
+ return true;
+ s.remove_prefix(1);
+ }
+
+ // Otherwise, we couldn't find a match.
+ return false;
+}
+
+} // namespace
diff --git a/src/base/vlog.h b/src/base/vlog.h
new file mode 100644
index 0000000..987730f
--- /dev/null
+++ b/src/base/vlog.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_VLOG_H_
+#define BASE_VLOG_H_
+
+#include <cstddef>
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/string_piece.h"
+
+namespace logging {
+
+// A helper class containing all the settings for vlogging.
+class BASE_EXPORT VlogInfo {
+ public:
+ static const int kDefaultVlogLevel;
+
+ // |v_switch| gives the default maximal active V-logging level; 0 is
+ // the default. Normally positive values are used for V-logging
+ // levels.
+ //
+ // |vmodule_switch| gives the per-module maximal V-logging levels to
+ // override the value given by |v_switch|.
+ // E.g. "my_module=2,foo*=3" would change the logging level for all
+ // code in source files "my_module.*" and "foo*.*" ("-inl" suffixes
+ // are also disregarded for this matching).
+ //
+ // |log_severity| points to an int that stores the log level. If a valid
+ // |v_switch| is provided, it will set the log level, and the default
+ // vlog severity will be read from there..
+ //
+ // Any pattern containing a forward or backward slash will be tested
+ // against the whole pathname and not just the module. E.g.,
+ // "*/foo/bar/*=2" would change the logging level for all code in
+ // source files under a "foo/bar" directory.
+ VlogInfo(const std::string& v_switch,
+ const std::string& vmodule_switch,
+ int* min_log_level);
+ ~VlogInfo();
+
+ // Returns the vlog level for a given file (usually taken from
+ // __FILE__).
+ int GetVlogLevel(const base::StringPiece& file) const;
+
+ private:
+ void SetMaxVlogLevel(int level);
+ int GetMaxVlogLevel() const;
+
+ // VmodulePattern holds all the information for each pattern parsed
+ // from |vmodule_switch|.
+ struct VmodulePattern;
+ std::vector<VmodulePattern> vmodule_levels_;
+ int* min_log_level_;
+
+ DISALLOW_COPY_AND_ASSIGN(VlogInfo);
+};
+
+// Returns true if the string passed in matches the vlog pattern. The
+// vlog pattern string can contain wildcards like * and ?. ? matches
+// exactly one character while * matches 0 or more characters. Also,
+// as a special case, a / or \ character matches either / or \.
+//
+// Examples:
+// "kh?n" matches "khan" but not "khn" or "khaan"
+// "kh*n" matches "khn", "khan", or even "khaaaaan"
+// "/foo\bar" matches "/foo/bar", "\foo\bar", or "/foo\bar"
+// (disregarding C escaping rules)
+BASE_EXPORT bool MatchVlogPattern(const base::StringPiece& string,
+ const base::StringPiece& vlog_pattern);
+
+} // namespace logging
+
+#endif // BASE_VLOG_H_
diff --git a/src/base/vlog_unittest.cc b/src/base/vlog_unittest.cc
new file mode 100644
index 0000000..ef7247f
--- /dev/null
+++ b/src/base/vlog_unittest.cc
@@ -0,0 +1,118 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/vlog.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace logging {
+
+namespace {
+
+TEST(VlogTest, NoVmodule) {
+ int min_log_level = 0;
+ EXPECT_EQ(0, VlogInfo("", "", &min_log_level).GetVlogLevel("test1"));
+ EXPECT_EQ(0, VlogInfo("0", "", &min_log_level).GetVlogLevel("test2"));
+ EXPECT_EQ(0, VlogInfo("blah", "", &min_log_level).GetVlogLevel("test3"));
+ EXPECT_EQ(0, VlogInfo("0blah1", "", &min_log_level).GetVlogLevel("test4"));
+ EXPECT_EQ(1, VlogInfo("1", "", &min_log_level).GetVlogLevel("test5"));
+ EXPECT_EQ(5, VlogInfo("5", "", &min_log_level).GetVlogLevel("test6"));
+}
+
+TEST(VlogTest, MatchVlogPattern) {
+ // Degenerate cases.
+ EXPECT_TRUE(MatchVlogPattern("", ""));
+ EXPECT_TRUE(MatchVlogPattern("", "****"));
+ EXPECT_FALSE(MatchVlogPattern("", "x"));
+ EXPECT_FALSE(MatchVlogPattern("x", ""));
+
+ // Basic.
+ EXPECT_TRUE(MatchVlogPattern("blah", "blah"));
+
+ // ? should match exactly one character.
+ EXPECT_TRUE(MatchVlogPattern("blah", "bl?h"));
+ EXPECT_FALSE(MatchVlogPattern("blh", "bl?h"));
+ EXPECT_FALSE(MatchVlogPattern("blaah", "bl?h"));
+ EXPECT_TRUE(MatchVlogPattern("blah", "?lah"));
+ EXPECT_FALSE(MatchVlogPattern("lah", "?lah"));
+ EXPECT_FALSE(MatchVlogPattern("bblah", "?lah"));
+
+ // * can match any number (even 0) of characters.
+ EXPECT_TRUE(MatchVlogPattern("blah", "bl*h"));
+ EXPECT_TRUE(MatchVlogPattern("blabcdefh", "bl*h"));
+ EXPECT_TRUE(MatchVlogPattern("blh", "bl*h"));
+ EXPECT_TRUE(MatchVlogPattern("blah", "*blah"));
+ EXPECT_TRUE(MatchVlogPattern("ohblah", "*blah"));
+ EXPECT_TRUE(MatchVlogPattern("blah", "blah*"));
+ EXPECT_TRUE(MatchVlogPattern("blahhhh", "blah*"));
+ EXPECT_TRUE(MatchVlogPattern("blahhhh", "blah*"));
+ EXPECT_TRUE(MatchVlogPattern("blah", "*blah*"));
+ EXPECT_TRUE(MatchVlogPattern("blahhhh", "*blah*"));
+ EXPECT_TRUE(MatchVlogPattern("bbbblahhhh", "*blah*"));
+
+ // Multiple *s should work fine.
+ EXPECT_TRUE(MatchVlogPattern("ballaah", "b*la*h"));
+ EXPECT_TRUE(MatchVlogPattern("blah", "b*la*h"));
+ EXPECT_TRUE(MatchVlogPattern("bbbblah", "b*la*h"));
+ EXPECT_TRUE(MatchVlogPattern("blaaah", "b*la*h"));
+
+ // There should be no escaping going on.
+ EXPECT_TRUE(MatchVlogPattern("bl\\ah", "bl\\?h"));
+ EXPECT_FALSE(MatchVlogPattern("bl?h", "bl\\?h"));
+ EXPECT_TRUE(MatchVlogPattern("bl\\aaaah", "bl\\*h"));
+ EXPECT_FALSE(MatchVlogPattern("bl*h", "bl\\*h"));
+
+ // Any slash matches any slash.
+ EXPECT_TRUE(MatchVlogPattern("/b\\lah", "/b\\lah"));
+ EXPECT_TRUE(MatchVlogPattern("\\b/lah", "/b\\lah"));
+}
+
+TEST(VlogTest, VmoduleBasic) {
+ const char kVSwitch[] = "-1";
+ const char kVModuleSwitch[] =
+ "foo=,bar=0,baz=blah,,qux=0blah1,quux=1,corge.ext=5";
+ int min_log_level = 0;
+ VlogInfo vlog_info(kVSwitch, kVModuleSwitch, &min_log_level);
+ EXPECT_EQ(-1, vlog_info.GetVlogLevel("/path/to/grault.cc"));
+ EXPECT_EQ(0, vlog_info.GetVlogLevel("/path/to/foo.cc"));
+ EXPECT_EQ(0, vlog_info.GetVlogLevel("D:\\Path\\To\\bar-inl.mm"));
+ EXPECT_EQ(-1, vlog_info.GetVlogLevel("D:\\path\\to what/bar_unittest.m"));
+ EXPECT_EQ(0, vlog_info.GetVlogLevel("baz.h"));
+ EXPECT_EQ(0, vlog_info.GetVlogLevel("/another/path/to/qux.h"));
+ EXPECT_EQ(1, vlog_info.GetVlogLevel("/path/to/quux"));
+ EXPECT_EQ(5, vlog_info.GetVlogLevel("c:\\path/to/corge.ext.h"));
+}
+
+TEST(VlogTest, VmoduleDirs) {
+ const char kVModuleSwitch[] =
+ "foo/bar.cc=1,baz\\*\\qux.cc=2,*quux/*=3,*/*-inl.h=4";
+ int min_log_level = 0;
+ VlogInfo vlog_info("", kVModuleSwitch, &min_log_level);
+ EXPECT_EQ(0, vlog_info.GetVlogLevel("/foo/bar.cc"));
+ EXPECT_EQ(0, vlog_info.GetVlogLevel("bar.cc"));
+ EXPECT_EQ(1, vlog_info.GetVlogLevel("foo/bar.cc"));
+
+ EXPECT_EQ(0, vlog_info.GetVlogLevel("baz/grault/qux.h"));
+ EXPECT_EQ(0, vlog_info.GetVlogLevel("/baz/grault/qux.cc"));
+ EXPECT_EQ(2, vlog_info.GetVlogLevel("baz/grault/qux.cc"));
+ EXPECT_EQ(2, vlog_info.GetVlogLevel("baz/grault/blah/qux.cc"));
+ EXPECT_EQ(2, vlog_info.GetVlogLevel("baz\\grault\\qux.cc"));
+ EXPECT_EQ(2, vlog_info.GetVlogLevel("baz\\grault//blah\\qux.cc"));
+
+ EXPECT_EQ(0, vlog_info.GetVlogLevel("/foo/bar/baz/quux.cc"));
+ EXPECT_EQ(3, vlog_info.GetVlogLevel("/foo/bar/baz/quux/grault.cc"));
+ EXPECT_EQ(3, vlog_info.GetVlogLevel("/foo\\bar/baz\\quux/grault.cc"));
+
+ EXPECT_EQ(0, vlog_info.GetVlogLevel("foo/bar/test-inl.cc"));
+ EXPECT_EQ(4, vlog_info.GetVlogLevel("foo/bar/test-inl.h"));
+ EXPECT_EQ(4, vlog_info.GetVlogLevel("foo/bar/baz/blah-inl.h"));
+}
+
+} // namespace
+
+} // namespace logging
diff --git a/src/base/win/dllmain.cc b/src/base/win/dllmain.cc
new file mode 100644
index 0000000..15437e0
--- /dev/null
+++ b/src/base/win/dllmain.cc
@@ -0,0 +1,122 @@
+// Copyright (c) 2012 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.
+
+// Windows doesn't support pthread_key_create's destr_function, and in fact
+// it's a bit tricky to get code to run when a thread exits. This is
+// cargo-cult magic from http://www.codeproject.com/threads/tls.asp.
+// We are trying to be compatible with both a LoadLibrary style invocation, as
+// well as static linking. This code only needs to be included if we use
+// LoadLibrary, but it hooks into the "standard" set of TLS callbacks that are
+// provided for static linking.
+
+// This code is deliberately written to match the style of calls seen in
+// base/threading/thread_local_storage_win.cc. Please keep the two in sync if
+// coding conventions are changed.
+
+// WARNING: Do *NOT* try to include this in the construction of the base
+// library, even though it potentially drives code in
+// base/threading/thread_local_storage_win.cc. If you do, some users will end
+// up getting duplicate definition of DllMain() in some of their later links.
+
+// Force a reference to _tls_used to make the linker create the TLS directory
+// if it's not already there (that is, even if __declspec(thread) is not used).
+// Force a reference to p_thread_callback_dllmain_typical_entry to prevent whole
+// program optimization from discarding the variables.
+
+#include <windows.h>
+
+#include "base/compiler_specific.h"
+#include "base/win/win_util.h"
+
+// Indicate if another service is scanning the callbacks. When this becomes
+// set to true, then DllMain() will stop supporting the callback service. This
+// value is set to true the first time any of our callbacks are called, as that
+// shows that some other service is handling callbacks.
+static bool linker_notifications_are_active = false;
+
+// This will be our mostly no-op callback that we'll list. We won't
+// deliberately call it, and if it is called, that means we don't need to do any
+// of the callbacks anymore. We expect such a call to arrive via a
+// THREAD_ATTACH message, long before we'd have to perform our THREAD_DETACH
+// callbacks.
+static void NTAPI on_callback(PVOID h, DWORD reason, PVOID reserved);
+
+#ifdef _WIN64
+
+#pragma comment(linker, "/INCLUDE:_tls_used")
+#pragma comment(linker, "/INCLUDE:p_thread_callback_dllmain_typical_entry")
+
+#else // _WIN64
+
+#pragma comment(linker, "/INCLUDE:__tls_used")
+#pragma comment(linker, "/INCLUDE:_p_thread_callback_dllmain_typical_entry")
+
+#endif // _WIN64
+
+// Explicitly depend on tlssup.cc variable to bracket the list of TLS callbacks.
+extern "C" PIMAGE_TLS_CALLBACK __xl_a;
+extern "C" PIMAGE_TLS_CALLBACK __xl_z;
+
+// extern "C" suppresses C++ name mangling so we know the symbol names for the
+// linker /INCLUDE:symbol pragmas above.
+extern "C" {
+#ifdef _WIN64
+
+// .CRT section is merged with .rdata on x64 so it must be constant data.
+#pragma data_seg(push, old_seg)
+// Use a typical possible name in the .CRT$XL? list of segments.
+#pragma const_seg(".CRT$XLB")
+// When defining a const variable, it must have external linkage to be sure the
+// linker doesn't discard it.
+extern const PIMAGE_TLS_CALLBACK p_thread_callback_dllmain_typical_entry;
+const PIMAGE_TLS_CALLBACK p_thread_callback_dllmain_typical_entry = on_callback;
+#pragma data_seg(pop, old_seg)
+
+#else // _WIN64
+
+#pragma data_seg(push, old_seg)
+// Use a typical possible name in the .CRT$XL? list of segments.
+#pragma data_seg(".CRT$XLB")
+PIMAGE_TLS_CALLBACK p_thread_callback_dllmain_typical_entry = on_callback;
+#pragma data_seg(pop, old_seg)
+
+#endif // _WIN64
+} // extern "C"
+
+NOINLINE static void CrashOnProcessDetach() {
+ *((int*)0) = 0x356;
+}
+
+// Make DllMain call the listed callbacks. This way any third parties that are
+// linked in will also be called.
+BOOL WINAPI DllMain(PVOID h, DWORD reason, PVOID reserved) {
+ if (DLL_PROCESS_DETACH == reason && base::win::ShouldCrashOnProcessDetach())
+ CrashOnProcessDetach();
+
+ if (DLL_THREAD_DETACH != reason && DLL_PROCESS_DETACH != reason)
+ return true; // We won't service THREAD_ATTACH calls.
+
+ if (linker_notifications_are_active)
+ return true; // Some other service is doing this work.
+
+ for (PIMAGE_TLS_CALLBACK* it = &__xl_a; it < &__xl_z; ++it) {
+ if (*it == NULL || *it == on_callback)
+ continue; // Don't bother to call our own callback.
+ (*it)(h, reason, reserved);
+ }
+ return true;
+}
+
+static void NTAPI on_callback(PVOID h, DWORD reason, PVOID reserved) {
+ // Do nothing. We were just a place holder in the list used to test that we
+ // call all items.
+ // If we are called, it means that some other system is scanning the callbacks
+ // and we don't need to do so in DllMain().
+ linker_notifications_are_active = true;
+ // Note: If some other routine some how plays this same game... we could both
+ // decide not to do the scanning <sigh>, but this trick should suppress
+ // duplicate calls on Vista, where the runtime takes care of the callbacks,
+ // and allow us to do the callbacks on XP, where we are currently devoid of
+ // callbacks (due to an explicit LoadLibrary call).
+}
diff --git a/src/base/win/enum_variant.cc b/src/base/win/enum_variant.cc
new file mode 100644
index 0000000..2975560
--- /dev/null
+++ b/src/base/win/enum_variant.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/enum_variant.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+
+namespace base {
+namespace win {
+
+EnumVariant::EnumVariant(unsigned long count)
+ : items_(new VARIANT[count]),
+ count_(count),
+ current_index_(0) {
+}
+
+EnumVariant::~EnumVariant() {
+}
+
+VARIANT* EnumVariant::ItemAt(unsigned long index) {
+ DCHECK(index < count_);
+ return &items_[index];
+}
+
+ULONG STDMETHODCALLTYPE EnumVariant::AddRef() {
+ return IUnknownImpl::AddRef();
+}
+
+ULONG STDMETHODCALLTYPE EnumVariant::Release() {
+ return IUnknownImpl::Release();
+}
+
+STDMETHODIMP EnumVariant::QueryInterface(REFIID riid, void** ppv) {
+ if (riid == IID_IEnumVARIANT) {
+ *ppv = static_cast<IEnumVARIANT*>(this);
+ AddRef();
+ return S_OK;
+ }
+
+ return IUnknownImpl::QueryInterface(riid, ppv);
+}
+
+STDMETHODIMP EnumVariant::Next(ULONG requested_count,
+ VARIANT* out_elements,
+ ULONG* out_elements_received) {
+ unsigned long count = std::min(requested_count, count_ - current_index_);
+ for (unsigned long i = 0; i < count; ++i)
+ out_elements[i] = items_[current_index_ + i];
+ current_index_ += count;
+ *out_elements_received = count;
+
+ return (count == requested_count ? S_OK : S_FALSE);
+}
+
+STDMETHODIMP EnumVariant::Skip(ULONG skip_count) {
+ unsigned long count = skip_count;
+ if (current_index_ + count > count_)
+ count = count_ - current_index_;
+
+ current_index_ += count;
+ return (count == skip_count ? S_OK : S_FALSE);
+}
+
+STDMETHODIMP EnumVariant::Reset() {
+ current_index_ = 0;
+ return S_OK;
+}
+
+STDMETHODIMP EnumVariant::Clone(IEnumVARIANT** out_cloned_object) {
+ EnumVariant* other = new EnumVariant(count_);
+ if (count_ > 0)
+ memcpy(other->ItemAt(0), &items_[0], count_ * sizeof(VARIANT));
+ other->Skip(current_index_);
+ other->AddRef();
+ *out_cloned_object = other;
+ return S_OK;
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/enum_variant.h b/src/base/win/enum_variant.h
new file mode 100644
index 0000000..7cee91d
--- /dev/null
+++ b/src/base/win/enum_variant.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_ENUM_VARIANT_H_
+#define BASE_WIN_ENUM_VARIANT_H_
+
+#include <unknwn.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/win/iunknown_impl.h"
+
+namespace base {
+namespace win {
+
+// A simple implementation of IEnumVARIANT.
+class BASE_EXPORT EnumVariant
+ : public IEnumVARIANT,
+ public IUnknownImpl {
+ public:
+ // The constructor allocates an array of size |count|. Then use
+ // ItemAt to set the value of each item in the array to initialize it.
+ explicit EnumVariant(unsigned long count);
+
+ // Returns a mutable pointer to the item at position |index|.
+ VARIANT* ItemAt(unsigned long index);
+
+ // IUnknown.
+ ULONG STDMETHODCALLTYPE AddRef() OVERRIDE;
+ ULONG STDMETHODCALLTYPE Release() OVERRIDE;
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppv) OVERRIDE;
+
+ // IEnumVARIANT.
+ STDMETHODIMP Next(ULONG requested_count,
+ VARIANT* out_elements,
+ ULONG* out_elements_received);
+ STDMETHODIMP Skip(ULONG skip_count);
+ STDMETHODIMP Reset();
+ STDMETHODIMP Clone(IEnumVARIANT** out_cloned_object);
+
+ private:
+ ~EnumVariant();
+
+ scoped_array<VARIANT> items_;
+ unsigned long count_;
+ unsigned long current_index_;
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_ENUM_VARIANT_H_
diff --git a/src/base/win/enum_variant_unittest.cc b/src/base/win/enum_variant_unittest.cc
new file mode 100644
index 0000000..99645a2
--- /dev/null
+++ b/src/base/win/enum_variant_unittest.cc
@@ -0,0 +1,118 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/enum_variant.h"
+
+#include "base/win/scoped_com_initializer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace win {
+
+TEST(EnumVariantTest, EmptyEnumVariant) {
+ ScopedCOMInitializer com_initializer;
+
+ EnumVariant* ev = new EnumVariant(0);
+ ev->AddRef();
+
+ IUnknown* iunknown;
+ EXPECT_TRUE(SUCCEEDED(
+ ev->QueryInterface(IID_IUnknown, reinterpret_cast<void**>(&iunknown))));
+ iunknown->Release();
+
+ IEnumVARIANT* ienumvariant;
+ EXPECT_TRUE(SUCCEEDED(
+ ev->QueryInterface(IID_IEnumVARIANT,
+ reinterpret_cast<void**>(&ienumvariant))));
+ EXPECT_EQ(ev, ienumvariant);
+ ienumvariant->Release();
+
+ VARIANT out_element;
+ ULONG out_received = 0;
+ EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received));
+ EXPECT_EQ(0, out_received);
+
+ EXPECT_EQ(S_FALSE, ev->Skip(1));
+
+ EXPECT_EQ(S_OK, ev->Reset());
+
+ IEnumVARIANT* ev2 = NULL;
+ EXPECT_EQ(S_OK, ev->Clone(&ev2));
+
+ EXPECT_NE(static_cast<IEnumVARIANT*>(NULL), ev2);
+ EXPECT_NE(ev, ev2);
+ EXPECT_EQ(S_FALSE, ev2->Skip(1));
+ EXPECT_EQ(S_OK, ev2->Reset());
+
+ ULONG ev2_finalrefcount = ev2->Release();
+ EXPECT_EQ(0, ev2_finalrefcount);
+
+ ULONG ev_finalrefcount = ev->Release();
+ EXPECT_EQ(0, ev_finalrefcount);
+}
+
+TEST(EnumVariantTest, SimpleEnumVariant) {
+ ScopedCOMInitializer com_initializer;
+
+ EnumVariant* ev = new EnumVariant(3);
+ ev->AddRef();
+ ev->ItemAt(0)->vt = VT_I4;
+ ev->ItemAt(0)->lVal = 10;
+ ev->ItemAt(1)->vt = VT_I4;
+ ev->ItemAt(1)->lVal = 20;
+ ev->ItemAt(2)->vt = VT_I4;
+ ev->ItemAt(2)->lVal = 30;
+
+ // Get elements one at a time.
+ VARIANT out_element;
+ ULONG out_received = 0;
+ EXPECT_EQ(S_OK, ev->Next(1, &out_element, &out_received));
+ EXPECT_EQ(1, out_received);
+ EXPECT_EQ(VT_I4, out_element.vt);
+ EXPECT_EQ(10, out_element.lVal);
+ EXPECT_EQ(S_OK, ev->Skip(1));
+ EXPECT_EQ(S_OK, ev->Next(1, &out_element, &out_received));
+ EXPECT_EQ(1, out_received);
+ EXPECT_EQ(VT_I4, out_element.vt);
+ EXPECT_EQ(30, out_element.lVal);
+ EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received));
+
+ // Reset and get all elements at once.
+ VARIANT out_elements[3];
+ EXPECT_EQ(S_OK, ev->Reset());
+ EXPECT_EQ(S_OK, ev->Next(3, out_elements, &out_received));
+ EXPECT_EQ(3, out_received);
+ EXPECT_EQ(VT_I4, out_elements[0].vt);
+ EXPECT_EQ(10, out_elements[0].lVal);
+ EXPECT_EQ(VT_I4, out_elements[1].vt);
+ EXPECT_EQ(20, out_elements[1].lVal);
+ EXPECT_EQ(VT_I4, out_elements[2].vt);
+ EXPECT_EQ(30, out_elements[2].lVal);
+ EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received));
+
+ // Clone it.
+ IEnumVARIANT* ev2 = NULL;
+ EXPECT_EQ(S_OK, ev->Clone(&ev2));
+ EXPECT_TRUE(ev2 != NULL);
+ EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received));
+ EXPECT_EQ(S_OK, ev2->Reset());
+ EXPECT_EQ(S_OK, ev2->Next(3, out_elements, &out_received));
+ EXPECT_EQ(3, out_received);
+ EXPECT_EQ(VT_I4, out_elements[0].vt);
+ EXPECT_EQ(10, out_elements[0].lVal);
+ EXPECT_EQ(VT_I4, out_elements[1].vt);
+ EXPECT_EQ(20, out_elements[1].lVal);
+ EXPECT_EQ(VT_I4, out_elements[2].vt);
+ EXPECT_EQ(30, out_elements[2].lVal);
+ EXPECT_EQ(S_FALSE, ev2->Next(1, &out_element, &out_received));
+
+ ULONG ev2_finalrefcount = ev2->Release();
+ EXPECT_EQ(0, ev2_finalrefcount);
+
+ ULONG ev_finalrefcount = ev->Release();
+ EXPECT_EQ(0, ev_finalrefcount);
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/event_trace_consumer.h b/src/base/win/event_trace_consumer.h
new file mode 100644
index 0000000..c1b42b4
--- /dev/null
+++ b/src/base/win/event_trace_consumer.h
@@ -0,0 +1,148 @@
+// Copyright (c) 2012 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.
+//
+// Declaration of a Windows event trace consumer base class.
+#ifndef BASE_WIN_EVENT_TRACE_CONSUMER_H_
+#define BASE_WIN_EVENT_TRACE_CONSUMER_H_
+
+#include <windows.h>
+#include <wmistr.h>
+#include <evntrace.h>
+#include <vector>
+#include "base/basictypes.h"
+
+namespace base {
+namespace win {
+
+// This class is a base class that makes it easier to consume events
+// from realtime or file sessions. Concrete consumers need to sublass
+// a specialization of this class and override the ProcessEvent and/or
+// the ProcessBuffer methods to implement the event consumption logic.
+// Usage might look like:
+// class MyConsumer: public EtwTraceConsumerBase<MyConsumer, 1> {
+// protected:
+// static VOID WINAPI ProcessEvent(PEVENT_TRACE event);
+// };
+//
+// MyConsumer consumer;
+// consumer.OpenFileSession(file_path);
+// consumer.Consume();
+template <class ImplClass>
+class EtwTraceConsumerBase {
+ public:
+ // Constructs a closed consumer.
+ EtwTraceConsumerBase() {
+ }
+
+ ~EtwTraceConsumerBase() {
+ Close();
+ }
+
+ // Opens the named realtime session, which must be existent.
+ // Note: You can use OpenRealtimeSession or OpenFileSession
+ // to open as many as MAXIMUM_WAIT_OBJECTS (63) sessions at
+ // any one time, though only one of them may be a realtime
+ // session.
+ HRESULT OpenRealtimeSession(const wchar_t* session_name);
+
+ // Opens the event trace log in "file_name", which must be a full or
+ // relative path to an existing event trace log file.
+ // Note: You can use OpenRealtimeSession or OpenFileSession
+ // to open as many as kNumSessions at any one time.
+ HRESULT OpenFileSession(const wchar_t* file_name);
+
+ // Consume all open sessions from beginning to end.
+ HRESULT Consume();
+
+ // Close all open sessions.
+ HRESULT Close();
+
+ protected:
+ // Override in subclasses to handle events.
+ static void ProcessEvent(EVENT_TRACE* event) {
+ }
+ // Override in subclasses to handle buffers.
+ static bool ProcessBuffer(EVENT_TRACE_LOGFILE* buffer) {
+ return true; // keep going
+ }
+
+ protected:
+ // Currently open sessions.
+ std::vector<TRACEHANDLE> trace_handles_;
+
+ private:
+ // These delegate to ImplClass callbacks with saner signatures.
+ static void WINAPI ProcessEventCallback(EVENT_TRACE* event) {
+ ImplClass::ProcessEvent(event);
+ }
+ static ULONG WINAPI ProcessBufferCallback(PEVENT_TRACE_LOGFILE buffer) {
+ return ImplClass::ProcessBuffer(buffer);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(EtwTraceConsumerBase);
+};
+
+template <class ImplClass> inline
+HRESULT EtwTraceConsumerBase<ImplClass>::OpenRealtimeSession(
+ const wchar_t* session_name) {
+ EVENT_TRACE_LOGFILE logfile = {};
+ logfile.LoggerName = const_cast<wchar_t*>(session_name);
+ logfile.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
+ logfile.BufferCallback = &ProcessBufferCallback;
+ logfile.EventCallback = &ProcessEventCallback;
+ logfile.Context = this;
+ TRACEHANDLE trace_handle = ::OpenTrace(&logfile);
+ if (reinterpret_cast<TRACEHANDLE>(INVALID_HANDLE_VALUE) == trace_handle)
+ return HRESULT_FROM_WIN32(::GetLastError());
+
+ trace_handles_.push_back(trace_handle);
+ return S_OK;
+}
+
+template <class ImplClass> inline
+HRESULT EtwTraceConsumerBase<ImplClass>::OpenFileSession(
+ const wchar_t* file_name) {
+ EVENT_TRACE_LOGFILE logfile = {};
+ logfile.LogFileName = const_cast<wchar_t*>(file_name);
+ logfile.BufferCallback = &ProcessBufferCallback;
+ logfile.EventCallback = &ProcessEventCallback;
+ logfile.Context = this;
+ TRACEHANDLE trace_handle = ::OpenTrace(&logfile);
+ if (reinterpret_cast<TRACEHANDLE>(INVALID_HANDLE_VALUE) == trace_handle)
+ return HRESULT_FROM_WIN32(::GetLastError());
+
+ trace_handles_.push_back(trace_handle);
+ return S_OK;
+}
+
+template <class ImplClass> inline
+HRESULT EtwTraceConsumerBase<ImplClass>::Consume() {
+ ULONG err = ::ProcessTrace(&trace_handles_[0],
+ trace_handles_.size(),
+ NULL,
+ NULL);
+ return HRESULT_FROM_WIN32(err);
+}
+
+template <class ImplClass> inline
+HRESULT EtwTraceConsumerBase<ImplClass>::Close() {
+ HRESULT hr = S_OK;
+ for (size_t i = 0; i < trace_handles_.size(); ++i) {
+ if (NULL != trace_handles_[i]) {
+ ULONG ret = ::CloseTrace(trace_handles_[i]);
+ trace_handles_[i] = NULL;
+
+ if (FAILED(HRESULT_FROM_WIN32(ret)))
+ hr = HRESULT_FROM_WIN32(ret);
+ }
+ }
+ trace_handles_.clear();
+
+ return hr;
+}
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_EVENT_TRACE_CONSUMER_H_
diff --git a/src/base/win/event_trace_consumer_unittest.cc b/src/base/win/event_trace_consumer_unittest.cc
new file mode 100644
index 0000000..7af9452
--- /dev/null
+++ b/src/base/win/event_trace_consumer_unittest.cc
@@ -0,0 +1,383 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Unit tests for event trace consumer base class.
+#include "base/win/event_trace_consumer.h"
+
+#include <list>
+
+#include <objbase.h>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
+#include "base/process.h"
+#include "base/stringprintf.h"
+#include "base/win/event_trace_controller.h"
+#include "base/win/event_trace_provider.h"
+#include "base/win/scoped_handle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include <initguid.h> // NOLINT - has to be last
+
+namespace {
+
+using base::win::EtwMofEvent;
+using base::win::EtwTraceController;
+using base::win::EtwTraceConsumerBase;
+using base::win::EtwTraceProperties;
+using base::win::EtwTraceProvider;
+
+typedef std::list<EVENT_TRACE> EventQueue;
+
+class TestConsumer: public EtwTraceConsumerBase<TestConsumer> {
+ public:
+ TestConsumer() {
+ sank_event_.Set(::CreateEvent(NULL, TRUE, FALSE, NULL));
+ ClearQueue();
+ }
+
+ ~TestConsumer() {
+ ClearQueue();
+ sank_event_.Close();
+ }
+
+ void ClearQueue() {
+ EventQueue::const_iterator it(events_.begin()), end(events_.end());
+
+ for (; it != end; ++it) {
+ delete [] it->MofData;
+ }
+
+ events_.clear();
+ }
+
+ static void EnqueueEvent(EVENT_TRACE* event) {
+ events_.push_back(*event);
+ EVENT_TRACE& back = events_.back();
+
+ if (NULL != event->MofData && 0 != event->MofLength) {
+ back.MofData = new char[event->MofLength];
+ memcpy(back.MofData, event->MofData, event->MofLength);
+ }
+ }
+
+ static void ProcessEvent(EVENT_TRACE* event) {
+ EnqueueEvent(event);
+ ::SetEvent(sank_event_.Get());
+ }
+
+ static base::win::ScopedHandle sank_event_;
+ static EventQueue events_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestConsumer);
+};
+
+base::win::ScopedHandle TestConsumer::sank_event_;
+EventQueue TestConsumer::events_;
+
+class EtwTraceConsumerBaseTest: public testing::Test {
+ public:
+ EtwTraceConsumerBaseTest()
+ : session_name_(base::StringPrintf(L"TestSession-%d",
+ base::Process::Current().pid())) {
+ }
+
+ virtual void SetUp() {
+ // Cleanup any potentially dangling sessions.
+ EtwTraceProperties ignore;
+ EtwTraceController::Stop(session_name_.c_str(), &ignore);
+
+ // Allocate a new GUID for each provider test.
+ ASSERT_HRESULT_SUCCEEDED(::CoCreateGuid(&test_provider_));
+ }
+
+ virtual void TearDown() {
+ // Cleanup any potentially danging sessions.
+ EtwTraceProperties ignore;
+ EtwTraceController::Stop(session_name_.c_str(), &ignore);
+ }
+
+ protected:
+ GUID test_provider_;
+ std::wstring session_name_;
+};
+
+} // namespace
+
+TEST_F(EtwTraceConsumerBaseTest, Initialize) {
+ TestConsumer consumer_;
+}
+
+TEST_F(EtwTraceConsumerBaseTest, OpenRealtimeSucceedsWhenNoSession) {
+ TestConsumer consumer_;
+
+ ASSERT_HRESULT_SUCCEEDED(
+ consumer_.OpenRealtimeSession(session_name_.c_str()));
+}
+
+TEST_F(EtwTraceConsumerBaseTest, ConsumerImmediateFailureWhenNoSession) {
+ TestConsumer consumer_;
+
+ ASSERT_HRESULT_SUCCEEDED(
+ consumer_.OpenRealtimeSession(session_name_.c_str()));
+ ASSERT_HRESULT_FAILED(consumer_.Consume());
+}
+
+namespace {
+
+class EtwTraceConsumerRealtimeTest: public EtwTraceConsumerBaseTest {
+ public:
+ virtual void SetUp() {
+ EtwTraceConsumerBaseTest::SetUp();
+
+ ASSERT_HRESULT_SUCCEEDED(
+ consumer_.OpenRealtimeSession(session_name_.c_str()));
+ }
+
+ virtual void TearDown() {
+ consumer_.Close();
+
+ EtwTraceConsumerBaseTest::TearDown();
+ }
+
+ DWORD ConsumerThread() {
+ ::SetEvent(consumer_ready_.Get());
+
+ HRESULT hr = consumer_.Consume();
+ return hr;
+ }
+
+ static DWORD WINAPI ConsumerThreadMainProc(void* arg) {
+ return reinterpret_cast<EtwTraceConsumerRealtimeTest*>(arg)->
+ ConsumerThread();
+ }
+
+ HRESULT StartConsumerThread() {
+ consumer_ready_.Set(::CreateEvent(NULL, TRUE, FALSE, NULL));
+ EXPECT_TRUE(consumer_ready_ != NULL);
+ consumer_thread_.Set(::CreateThread(NULL, 0, ConsumerThreadMainProc,
+ this, 0, NULL));
+ if (NULL == consumer_thread_.Get())
+ return HRESULT_FROM_WIN32(::GetLastError());
+
+ HRESULT hr = S_OK;
+ HANDLE events[] = { consumer_ready_, consumer_thread_ };
+ DWORD result = ::WaitForMultipleObjects(arraysize(events), events,
+ FALSE, INFINITE);
+ switch (result) {
+ case WAIT_OBJECT_0:
+ // The event was set, the consumer_ is ready.
+ return S_OK;
+ case WAIT_OBJECT_0 + 1: {
+ // The thread finished. This may race with the event, so check
+ // explicitly for the event here, before concluding there's trouble.
+ if (WAIT_OBJECT_0 == ::WaitForSingleObject(consumer_ready_, 0))
+ return S_OK;
+ DWORD exit_code = 0;
+ if (::GetExitCodeThread(consumer_thread_, &exit_code))
+ return exit_code;
+ else
+ return HRESULT_FROM_WIN32(::GetLastError());
+ break;
+ }
+ default:
+ return E_UNEXPECTED;
+ break;
+ }
+
+ return hr;
+ }
+
+ // Waits for consumer_ thread to exit, and returns its exit code.
+ HRESULT JoinConsumerThread() {
+ if (WAIT_OBJECT_0 != ::WaitForSingleObject(consumer_thread_, INFINITE))
+ return HRESULT_FROM_WIN32(::GetLastError());
+
+ DWORD exit_code = 0;
+ if (::GetExitCodeThread(consumer_thread_, &exit_code))
+ return exit_code;
+
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+
+ TestConsumer consumer_;
+ base::win::ScopedHandle consumer_ready_;
+ base::win::ScopedHandle consumer_thread_;
+};
+
+} // namespace
+
+TEST_F(EtwTraceConsumerRealtimeTest, ConsumerReturnsWhenSessionClosed) {
+ EtwTraceController controller;
+
+ HRESULT hr = controller.StartRealtimeSession(session_name_.c_str(),
+ 100 * 1024);
+ if (hr == E_ACCESSDENIED) {
+ VLOG(1) << "You must be an administrator to run this test on Vista";
+ return;
+ }
+
+ // Start the consumer_.
+ ASSERT_HRESULT_SUCCEEDED(StartConsumerThread());
+
+ // Wait around for the consumer_ thread a bit.
+ ASSERT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(consumer_thread_, 50));
+
+ ASSERT_HRESULT_SUCCEEDED(controller.Stop(NULL));
+
+ // The consumer_ returns success on session stop.
+ ASSERT_HRESULT_SUCCEEDED(JoinConsumerThread());
+}
+
+namespace {
+
+// {57E47923-A549-476f-86CA-503D57F59E62}
+DEFINE_GUID(kTestEventType,
+ 0x57e47923, 0xa549, 0x476f, 0x86, 0xca, 0x50, 0x3d, 0x57, 0xf5, 0x9e, 0x62);
+
+} // namespace
+
+TEST_F(EtwTraceConsumerRealtimeTest, ConsumeEvent) {
+ EtwTraceController controller;
+ HRESULT hr = controller.StartRealtimeSession(session_name_.c_str(),
+ 100 * 1024);
+ if (hr == E_ACCESSDENIED) {
+ VLOG(1) << "You must be an administrator to run this test on Vista";
+ return;
+ }
+
+ ASSERT_HRESULT_SUCCEEDED(controller.EnableProvider(test_provider_,
+ TRACE_LEVEL_VERBOSE, 0xFFFFFFFF));
+
+ EtwTraceProvider provider(test_provider_);
+ ASSERT_EQ(ERROR_SUCCESS, provider.Register());
+
+ // Start the consumer_.
+ ASSERT_HRESULT_SUCCEEDED(StartConsumerThread());
+
+ ASSERT_EQ(0, TestConsumer::events_.size());
+
+ EtwMofEvent<1> event(kTestEventType, 1, TRACE_LEVEL_ERROR);
+ EXPECT_EQ(ERROR_SUCCESS, provider.Log(&event.header));
+
+ EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(TestConsumer::sank_event_,
+ INFINITE));
+ ASSERT_HRESULT_SUCCEEDED(controller.Stop(NULL));
+ ASSERT_HRESULT_SUCCEEDED(JoinConsumerThread());
+ ASSERT_NE(0u, TestConsumer::events_.size());
+}
+
+namespace {
+
+// We run events through a file session to assert that
+// the content comes through.
+class EtwTraceConsumerDataTest: public EtwTraceConsumerBaseTest {
+ public:
+ EtwTraceConsumerDataTest() {
+ }
+
+ virtual void SetUp() {
+ EtwTraceConsumerBaseTest::SetUp();
+
+ EtwTraceProperties prop;
+ EtwTraceController::Stop(session_name_.c_str(), &prop);
+
+ // Create a temp dir for this test.
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ // Construct a temp file name in our dir.
+ temp_file_ = temp_dir_.path().Append(L"test.etl");
+ }
+
+ virtual void TearDown() {
+ EXPECT_TRUE(file_util::Delete(temp_file_, false));
+
+ EtwTraceConsumerBaseTest::TearDown();
+ }
+
+ HRESULT LogEventToTempSession(PEVENT_TRACE_HEADER header) {
+ EtwTraceController controller;
+
+ // Set up a file session.
+ HRESULT hr = controller.StartFileSession(session_name_.c_str(),
+ temp_file_.value().c_str());
+ if (FAILED(hr))
+ return hr;
+
+ // Enable our provider.
+ EXPECT_HRESULT_SUCCEEDED(controller.EnableProvider(test_provider_,
+ TRACE_LEVEL_VERBOSE, 0xFFFFFFFF));
+
+ EtwTraceProvider provider(test_provider_);
+ // Then register our provider, means we get a session handle immediately.
+ EXPECT_EQ(ERROR_SUCCESS, provider.Register());
+ // Trace the event, it goes to the temp file.
+ EXPECT_EQ(ERROR_SUCCESS, provider.Log(header));
+ EXPECT_HRESULT_SUCCEEDED(controller.DisableProvider(test_provider_));
+ EXPECT_HRESULT_SUCCEEDED(provider.Unregister());
+ EXPECT_HRESULT_SUCCEEDED(controller.Flush(NULL));
+ EXPECT_HRESULT_SUCCEEDED(controller.Stop(NULL));
+
+ return S_OK;
+ }
+
+ HRESULT ConsumeEventFromTempSession() {
+ // Now consume the event(s).
+ TestConsumer consumer_;
+ HRESULT hr = consumer_.OpenFileSession(temp_file_.value().c_str());
+ if (SUCCEEDED(hr))
+ hr = consumer_.Consume();
+ consumer_.Close();
+ // And nab the result.
+ events_.swap(TestConsumer::events_);
+ return hr;
+ }
+
+ HRESULT RoundTripEvent(PEVENT_TRACE_HEADER header, PEVENT_TRACE* trace) {
+ file_util::Delete(temp_file_, false);
+
+ HRESULT hr = LogEventToTempSession(header);
+ if (SUCCEEDED(hr))
+ hr = ConsumeEventFromTempSession();
+
+ if (FAILED(hr))
+ return hr;
+
+ // We should now have the event in the queue.
+ if (events_.empty())
+ return E_FAIL;
+
+ *trace = &events_.back();
+ return S_OK;
+ }
+
+ EventQueue events_;
+ base::ScopedTempDir temp_dir_;
+ FilePath temp_file_;
+};
+
+} // namespace
+
+
+TEST_F(EtwTraceConsumerDataTest, RoundTrip) {
+ EtwMofEvent<1> event(kTestEventType, 1, TRACE_LEVEL_ERROR);
+
+ static const char kData[] = "This is but test data";
+ event.fields[0].DataPtr = reinterpret_cast<ULONG64>(kData);
+ event.fields[0].Length = sizeof(kData);
+
+ PEVENT_TRACE trace = NULL;
+ HRESULT hr = RoundTripEvent(&event.header, &trace);
+ if (hr == E_ACCESSDENIED) {
+ VLOG(1) << "You must be an administrator to run this test on Vista";
+ return;
+ }
+ ASSERT_HRESULT_SUCCEEDED(hr) << "RoundTripEvent failed";
+ ASSERT_TRUE(NULL != trace);
+ ASSERT_EQ(sizeof(kData), trace->MofLength);
+ ASSERT_STREQ(kData, reinterpret_cast<const char*>(trace->MofData));
+}
diff --git a/src/base/win/event_trace_controller.cc b/src/base/win/event_trace_controller.cc
new file mode 100644
index 0000000..0391fbc
--- /dev/null
+++ b/src/base/win/event_trace_controller.cc
@@ -0,0 +1,173 @@
+// 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.
+//
+// Implementation of a Windows event trace controller class.
+#include "base/win/event_trace_controller.h"
+#include "base/logging.h"
+
+namespace base {
+namespace win {
+
+EtwTraceProperties::EtwTraceProperties() {
+ memset(buffer_, 0, sizeof(buffer_));
+ EVENT_TRACE_PROPERTIES* prop = get();
+
+ prop->Wnode.BufferSize = sizeof(buffer_);
+ prop->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
+ prop->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
+ prop->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) +
+ sizeof(wchar_t) * kMaxStringLen;
+}
+
+HRESULT EtwTraceProperties::SetLoggerName(const wchar_t* logger_name) {
+ size_t len = wcslen(logger_name) + 1;
+ if (kMaxStringLen < len)
+ return E_INVALIDARG;
+
+ memcpy(buffer_ + get()->LoggerNameOffset,
+ logger_name,
+ sizeof(wchar_t) * len);
+ return S_OK;
+}
+
+HRESULT EtwTraceProperties::SetLoggerFileName(const wchar_t* logger_file_name) {
+ size_t len = wcslen(logger_file_name) + 1;
+ if (kMaxStringLen < len)
+ return E_INVALIDARG;
+
+ memcpy(buffer_ + get()->LogFileNameOffset,
+ logger_file_name,
+ sizeof(wchar_t) * len);
+ return S_OK;
+}
+
+EtwTraceController::EtwTraceController() : session_(NULL) {
+}
+
+EtwTraceController::~EtwTraceController() {
+ Stop(NULL);
+}
+
+HRESULT EtwTraceController::Start(const wchar_t* session_name,
+ EtwTraceProperties* prop) {
+ DCHECK(NULL == session_ && session_name_.empty());
+ EtwTraceProperties ignore;
+ if (prop == NULL)
+ prop = &ignore;
+
+ HRESULT hr = Start(session_name, prop, &session_);
+ if (SUCCEEDED(hr))
+ session_name_ = session_name;
+
+ return hr;
+}
+
+HRESULT EtwTraceController::StartFileSession(const wchar_t* session_name,
+ const wchar_t* logfile_path, bool realtime) {
+ DCHECK(NULL == session_ && session_name_.empty());
+
+ EtwTraceProperties prop;
+ prop.SetLoggerFileName(logfile_path);
+ EVENT_TRACE_PROPERTIES& p = *prop.get();
+ p.Wnode.ClientContext = 1; // QPC timer accuracy.
+ p.LogFileMode = EVENT_TRACE_FILE_MODE_SEQUENTIAL; // Sequential log.
+ if (realtime)
+ p.LogFileMode |= EVENT_TRACE_REAL_TIME_MODE;
+
+ p.MaximumFileSize = 100; // 100M file size.
+ p.FlushTimer = 30; // 30 seconds flush lag.
+ return Start(session_name, &prop);
+}
+
+HRESULT EtwTraceController::StartRealtimeSession(const wchar_t* session_name,
+ size_t buffer_size) {
+ DCHECK(NULL == session_ && session_name_.empty());
+ EtwTraceProperties prop;
+ EVENT_TRACE_PROPERTIES& p = *prop.get();
+ p.LogFileMode = EVENT_TRACE_REAL_TIME_MODE | EVENT_TRACE_USE_PAGED_MEMORY;
+ p.FlushTimer = 1; // flush every second.
+ p.BufferSize = 16; // 16 K buffers.
+ p.LogFileNameOffset = 0;
+ return Start(session_name, &prop);
+}
+
+HRESULT EtwTraceController::EnableProvider(REFGUID provider, UCHAR level,
+ ULONG flags) {
+ ULONG error = ::EnableTrace(TRUE, flags, level, &provider, session_);
+ return HRESULT_FROM_WIN32(error);
+}
+
+HRESULT EtwTraceController::DisableProvider(REFGUID provider) {
+ ULONG error = ::EnableTrace(FALSE, 0, 0, &provider, session_);
+ return HRESULT_FROM_WIN32(error);
+}
+
+HRESULT EtwTraceController::Stop(EtwTraceProperties* properties) {
+ EtwTraceProperties ignore;
+ if (properties == NULL)
+ properties = &ignore;
+
+ ULONG error = ::ControlTrace(session_, NULL, properties->get(),
+ EVENT_TRACE_CONTROL_STOP);
+ if (ERROR_SUCCESS != error)
+ return HRESULT_FROM_WIN32(error);
+
+ session_ = NULL;
+ session_name_.clear();
+ return S_OK;
+}
+
+HRESULT EtwTraceController::Flush(EtwTraceProperties* properties) {
+ EtwTraceProperties ignore;
+ if (properties == NULL)
+ properties = &ignore;
+
+ ULONG error = ::ControlTrace(session_, NULL, properties->get(),
+ EVENT_TRACE_CONTROL_FLUSH);
+ if (ERROR_SUCCESS != error)
+ return HRESULT_FROM_WIN32(error);
+
+ return S_OK;
+}
+
+HRESULT EtwTraceController::Start(const wchar_t* session_name,
+ EtwTraceProperties* properties, TRACEHANDLE* session_handle) {
+ DCHECK(properties != NULL);
+ ULONG err = ::StartTrace(session_handle, session_name, properties->get());
+ return HRESULT_FROM_WIN32(err);
+}
+
+HRESULT EtwTraceController::Query(const wchar_t* session_name,
+ EtwTraceProperties* properties) {
+ ULONG err = ::ControlTrace(NULL, session_name, properties->get(),
+ EVENT_TRACE_CONTROL_QUERY);
+ return HRESULT_FROM_WIN32(err);
+};
+
+HRESULT EtwTraceController::Update(const wchar_t* session_name,
+ EtwTraceProperties* properties) {
+ DCHECK(properties != NULL);
+ ULONG err = ::ControlTrace(NULL, session_name, properties->get(),
+ EVENT_TRACE_CONTROL_UPDATE);
+ return HRESULT_FROM_WIN32(err);
+}
+
+HRESULT EtwTraceController::Stop(const wchar_t* session_name,
+ EtwTraceProperties* properties) {
+ DCHECK(properties != NULL);
+ ULONG err = ::ControlTrace(NULL, session_name, properties->get(),
+ EVENT_TRACE_CONTROL_STOP);
+ return HRESULT_FROM_WIN32(err);
+}
+
+HRESULT EtwTraceController::Flush(const wchar_t* session_name,
+ EtwTraceProperties* properties) {
+ DCHECK(properties != NULL);
+ ULONG err = ::ControlTrace(NULL, session_name, properties->get(),
+ EVENT_TRACE_CONTROL_FLUSH);
+ return HRESULT_FROM_WIN32(err);
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/event_trace_controller.h b/src/base/win/event_trace_controller.h
new file mode 100644
index 0000000..69e755b
--- /dev/null
+++ b/src/base/win/event_trace_controller.h
@@ -0,0 +1,151 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Declaration of a Windows event trace controller class.
+// The controller takes care of creating and manipulating event trace
+// sessions.
+//
+// Event tracing for Windows is a system-provided service that provides
+// logging control and high-performance transport for generic, binary trace
+// events. Event trace providers register with the system by their name,
+// which is a GUID, and can from that point forward receive callbacks that
+// start or end tracing and that change their trace level and enable mask.
+//
+// A trace controller can create an event tracing session, which either
+// sends events to a binary file, or to a realtime consumer, or both.
+//
+// A trace consumer consumes events from zero or one realtime session,
+// as well as potentially from multiple binary trace files.
+#ifndef BASE_WIN_EVENT_TRACE_CONTROLLER_H_
+#define BASE_WIN_EVENT_TRACE_CONTROLLER_H_
+
+#include <windows.h>
+#include <wmistr.h>
+#include <evntrace.h>
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+namespace win {
+
+// Utility class to make it easier to work with EVENT_TRACE_PROPERTIES.
+// The EVENT_TRACE_PROPERTIES structure contains information about an
+// event tracing session.
+class BASE_EXPORT EtwTraceProperties {
+ public:
+ EtwTraceProperties();
+
+ EVENT_TRACE_PROPERTIES* get() {
+ return &properties_;
+ }
+
+ const EVENT_TRACE_PROPERTIES* get() const {
+ return reinterpret_cast<const EVENT_TRACE_PROPERTIES*>(&properties_);
+ }
+
+ const wchar_t* GetLoggerName() const {
+ return reinterpret_cast<const wchar_t *>(buffer_ + get()->LoggerNameOffset);
+ }
+
+ // Copies logger_name to the properties structure.
+ HRESULT SetLoggerName(const wchar_t* logger_name);
+ const wchar_t* GetLoggerFileName() const {
+ return reinterpret_cast<const wchar_t*>(buffer_ + get()->LogFileNameOffset);
+ }
+
+ // Copies logger_file_name to the properties structure.
+ HRESULT SetLoggerFileName(const wchar_t* logger_file_name);
+
+ // Max string len for name and session name is 1024 per documentation.
+ static const size_t kMaxStringLen = 1024;
+ // Properties buffer allocates space for header and for
+ // max length for name and session name.
+ static const size_t kBufSize = sizeof(EVENT_TRACE_PROPERTIES)
+ + 2 * sizeof(wchar_t) * (kMaxStringLen);
+
+ private:
+ // The EVENT_TRACE_PROPERTIES structure needs to be overlaid on a
+ // larger buffer to allow storing the logger name and logger file
+ // name contiguously with the structure.
+ union {
+ public:
+ // Our properties header.
+ EVENT_TRACE_PROPERTIES properties_;
+ // The actual size of the buffer is forced by this member.
+ char buffer_[kBufSize];
+ };
+
+ DISALLOW_COPY_AND_ASSIGN(EtwTraceProperties);
+};
+
+// This class implements an ETW controller, which knows how to start and
+// stop event tracing sessions, as well as controlling ETW provider
+// log levels and enable bit masks under the session.
+class BASE_EXPORT EtwTraceController {
+ public:
+ EtwTraceController();
+ ~EtwTraceController();
+
+ // Start a session with given name and properties.
+ HRESULT Start(const wchar_t* session_name, EtwTraceProperties* prop);
+
+ // Starts a session tracing to a file with some default properties.
+ HRESULT StartFileSession(const wchar_t* session_name,
+ const wchar_t* logfile_path,
+ bool realtime = false);
+
+ // Starts a realtime session with some default properties.
+ HRESULT StartRealtimeSession(const wchar_t* session_name,
+ size_t buffer_size);
+
+ // Enables "provider" at "level" for this session.
+ // This will cause all providers registered with the GUID
+ // "provider" to start tracing at the new level, systemwide.
+ HRESULT EnableProvider(const GUID& provider, UCHAR level,
+ ULONG flags = 0xFFFFFFFF);
+ // Disables "provider".
+ HRESULT DisableProvider(const GUID& provider);
+
+ // Stops our session and retrieve the new properties of the session,
+ // properties may be NULL.
+ HRESULT Stop(EtwTraceProperties* properties);
+
+ // Flushes our session and retrieve the current properties,
+ // properties may be NULL.
+ HRESULT Flush(EtwTraceProperties* properties);
+
+ // Static utility functions for controlling
+ // sessions we don't necessarily own.
+ static HRESULT Start(const wchar_t* session_name,
+ EtwTraceProperties* properties,
+ TRACEHANDLE* session_handle);
+
+ static HRESULT Query(const wchar_t* session_name,
+ EtwTraceProperties* properties);
+
+ static HRESULT Update(const wchar_t* session_name,
+ EtwTraceProperties* properties);
+
+ static HRESULT Stop(const wchar_t* session_name,
+ EtwTraceProperties* properties);
+ static HRESULT Flush(const wchar_t* session_name,
+ EtwTraceProperties* properties);
+
+ // Accessors.
+ TRACEHANDLE session() const { return session_; }
+ const wchar_t* session_name() const { return session_name_.c_str(); }
+
+ private:
+ std::wstring session_name_;
+ TRACEHANDLE session_;
+
+ DISALLOW_COPY_AND_ASSIGN(EtwTraceController);
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_EVENT_TRACE_CONTROLLER_H_
diff --git a/src/base/win/event_trace_controller_unittest.cc b/src/base/win/event_trace_controller_unittest.cc
new file mode 100644
index 0000000..fae606e
--- /dev/null
+++ b/src/base/win/event_trace_controller_unittest.cc
@@ -0,0 +1,236 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Unit tests for event trace controller.
+
+#include <objbase.h>
+#include <initguid.h>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
+#include "base/process.h"
+#include "base/stringprintf.h"
+#include "base/sys_info.h"
+#include "base/win/event_trace_controller.h"
+#include "base/win/event_trace_provider.h"
+#include "base/win/scoped_handle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+using base::win::EtwTraceController;
+using base::win::EtwTraceProvider;
+using base::win::EtwTraceProperties;
+
+DEFINE_GUID(kGuidNull,
+ 0x0000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0);
+
+const ULONG kTestProviderFlags = 0xCAFEBABE;
+
+class TestingProvider: public EtwTraceProvider {
+ public:
+ explicit TestingProvider(const GUID& provider_name)
+ : EtwTraceProvider(provider_name) {
+ callback_event_.Set(::CreateEvent(NULL, TRUE, FALSE, NULL));
+ }
+
+ void WaitForCallback() {
+ ::WaitForSingleObject(callback_event_.Get(), INFINITE);
+ ::ResetEvent(callback_event_.Get());
+ }
+
+ private:
+ virtual void OnEventsEnabled() {
+ ::SetEvent(callback_event_.Get());
+ }
+ virtual void PostEventsDisabled() {
+ ::SetEvent(callback_event_.Get());
+ }
+
+ base::win::ScopedHandle callback_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestingProvider);
+};
+
+} // namespace
+
+TEST(EtwTracePropertiesTest, Initialization) {
+ EtwTraceProperties prop;
+
+ EVENT_TRACE_PROPERTIES* p = prop.get();
+ EXPECT_NE(0u, p->Wnode.BufferSize);
+ EXPECT_EQ(0u, p->Wnode.ProviderId);
+ EXPECT_EQ(0u, p->Wnode.HistoricalContext);
+
+ EXPECT_TRUE(kGuidNull == p->Wnode.Guid);
+ EXPECT_EQ(0, p->Wnode.ClientContext);
+ EXPECT_EQ(WNODE_FLAG_TRACED_GUID, p->Wnode.Flags);
+
+ EXPECT_EQ(0, p->BufferSize);
+ EXPECT_EQ(0, p->MinimumBuffers);
+ EXPECT_EQ(0, p->MaximumBuffers);
+ EXPECT_EQ(0, p->MaximumFileSize);
+ EXPECT_EQ(0, p->LogFileMode);
+ EXPECT_EQ(0, p->FlushTimer);
+ EXPECT_EQ(0, p->EnableFlags);
+ EXPECT_EQ(0, p->AgeLimit);
+
+ EXPECT_EQ(0, p->NumberOfBuffers);
+ EXPECT_EQ(0, p->FreeBuffers);
+ EXPECT_EQ(0, p->EventsLost);
+ EXPECT_EQ(0, p->BuffersWritten);
+ EXPECT_EQ(0, p->LogBuffersLost);
+ EXPECT_EQ(0, p->RealTimeBuffersLost);
+ EXPECT_EQ(0, p->LoggerThreadId);
+ EXPECT_NE(0u, p->LogFileNameOffset);
+ EXPECT_NE(0u, p->LoggerNameOffset);
+}
+
+TEST(EtwTracePropertiesTest, Strings) {
+ EtwTraceProperties prop;
+
+ ASSERT_STREQ(L"", prop.GetLoggerFileName());
+ ASSERT_STREQ(L"", prop.GetLoggerName());
+
+ std::wstring name(1023, L'A');
+ ASSERT_HRESULT_SUCCEEDED(prop.SetLoggerFileName(name.c_str()));
+ ASSERT_HRESULT_SUCCEEDED(prop.SetLoggerName(name.c_str()));
+ ASSERT_STREQ(name.c_str(), prop.GetLoggerFileName());
+ ASSERT_STREQ(name.c_str(), prop.GetLoggerName());
+
+ std::wstring name2(1024, L'A');
+ ASSERT_HRESULT_FAILED(prop.SetLoggerFileName(name2.c_str()));
+ ASSERT_HRESULT_FAILED(prop.SetLoggerName(name2.c_str()));
+}
+
+namespace {
+
+class EtwTraceControllerTest : public testing::Test {
+ public:
+ EtwTraceControllerTest() : session_name_(
+ base::StringPrintf(L"TestSession-%d", base::Process::Current().pid())) {
+ }
+
+ virtual void SetUp() {
+ EtwTraceProperties ignore;
+ EtwTraceController::Stop(session_name_.c_str(), &ignore);
+
+ // Allocate a new provider name GUID for each test.
+ ASSERT_HRESULT_SUCCEEDED(::CoCreateGuid(&test_provider_));
+ }
+
+ virtual void TearDown() {
+ EtwTraceProperties prop;
+ EtwTraceController::Stop(session_name_.c_str(), &prop);
+ }
+
+ protected:
+ GUID test_provider_;
+ std::wstring session_name_;
+};
+
+} // namespace
+
+TEST_F(EtwTraceControllerTest, Initialize) {
+ EtwTraceController controller;
+
+ EXPECT_EQ(NULL, controller.session());
+ EXPECT_STREQ(L"", controller.session_name());
+}
+
+
+TEST_F(EtwTraceControllerTest, StartRealTimeSession) {
+ EtwTraceController controller;
+
+ HRESULT hr = controller.StartRealtimeSession(session_name_.c_str(),
+ 100 * 1024);
+ if (hr == E_ACCESSDENIED) {
+ VLOG(1) << "You must be an administrator to run this test on Vista";
+ return;
+ }
+
+ EXPECT_TRUE(NULL != controller.session());
+ EXPECT_STREQ(session_name_.c_str(), controller.session_name());
+
+ EXPECT_HRESULT_SUCCEEDED(controller.Stop(NULL));
+ EXPECT_EQ(NULL, controller.session());
+ EXPECT_STREQ(L"", controller.session_name());
+}
+
+TEST_F(EtwTraceControllerTest, StartFileSession) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath temp;
+ ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir.path(), &temp));
+
+ EtwTraceController controller;
+ HRESULT hr = controller.StartFileSession(session_name_.c_str(),
+ temp.value().c_str());
+ if (hr == E_ACCESSDENIED) {
+ VLOG(1) << "You must be an administrator to run this test on Vista";
+ file_util::Delete(temp, false);
+ return;
+ }
+
+ EXPECT_TRUE(NULL != controller.session());
+ EXPECT_STREQ(session_name_.c_str(), controller.session_name());
+
+ EXPECT_HRESULT_SUCCEEDED(controller.Stop(NULL));
+ EXPECT_EQ(NULL, controller.session());
+ EXPECT_STREQ(L"", controller.session_name());
+ file_util::Delete(temp, false);
+}
+
+TEST_F(EtwTraceControllerTest, EnableDisable) {
+ TestingProvider provider(test_provider_);
+
+ EXPECT_EQ(ERROR_SUCCESS, provider.Register());
+ EXPECT_EQ(NULL, provider.session_handle());
+
+ EtwTraceController controller;
+ HRESULT hr = controller.StartRealtimeSession(session_name_.c_str(),
+ 100 * 1024);
+ if (hr == E_ACCESSDENIED) {
+ VLOG(1) << "You must be an administrator to run this test on Vista";
+ return;
+ }
+
+ EXPECT_HRESULT_SUCCEEDED(controller.EnableProvider(test_provider_,
+ TRACE_LEVEL_VERBOSE, kTestProviderFlags));
+
+ provider.WaitForCallback();
+
+ EXPECT_EQ(TRACE_LEVEL_VERBOSE, provider.enable_level());
+ EXPECT_EQ(kTestProviderFlags, provider.enable_flags());
+
+ EXPECT_HRESULT_SUCCEEDED(controller.DisableProvider(test_provider_));
+
+ provider.WaitForCallback();
+
+ EXPECT_EQ(0, provider.enable_level());
+ EXPECT_EQ(0, provider.enable_flags());
+
+ EXPECT_EQ(ERROR_SUCCESS, provider.Unregister());
+
+ // Enable the provider again, before registering.
+ EXPECT_HRESULT_SUCCEEDED(controller.EnableProvider(test_provider_,
+ TRACE_LEVEL_VERBOSE, kTestProviderFlags));
+
+ // Register the provider again, the settings above
+ // should take immediate effect.
+ EXPECT_EQ(ERROR_SUCCESS, provider.Register());
+
+ EXPECT_EQ(TRACE_LEVEL_VERBOSE, provider.enable_level());
+ EXPECT_EQ(kTestProviderFlags, provider.enable_flags());
+
+ EXPECT_HRESULT_SUCCEEDED(controller.Stop(NULL));
+
+ provider.WaitForCallback();
+
+ // Session should have wound down.
+ EXPECT_EQ(0, provider.enable_level());
+ EXPECT_EQ(0, provider.enable_flags());
+}
diff --git a/src/base/win/event_trace_provider.cc b/src/base/win/event_trace_provider.cc
new file mode 100644
index 0000000..8fcf67d
--- /dev/null
+++ b/src/base/win/event_trace_provider.cc
@@ -0,0 +1,134 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+#include "base/win/event_trace_provider.h"
+#include <windows.h>
+#include <cguid.h>
+
+namespace base {
+namespace win {
+
+TRACE_GUID_REGISTRATION EtwTraceProvider::obligatory_guid_registration_ = {
+ &GUID_NULL,
+ NULL
+};
+
+EtwTraceProvider::EtwTraceProvider(const GUID& provider_name)
+ : provider_name_(provider_name), registration_handle_(NULL),
+ session_handle_(NULL), enable_flags_(0), enable_level_(0) {
+}
+
+EtwTraceProvider::EtwTraceProvider()
+ : provider_name_(GUID_NULL), registration_handle_(NULL),
+ session_handle_(NULL), enable_flags_(0), enable_level_(0) {
+}
+
+EtwTraceProvider::~EtwTraceProvider() {
+ Unregister();
+}
+
+ULONG EtwTraceProvider::EnableEvents(void* buffer) {
+ session_handle_ = ::GetTraceLoggerHandle(buffer);
+ if (NULL == session_handle_) {
+ return ::GetLastError();
+ }
+
+ enable_flags_ = ::GetTraceEnableFlags(session_handle_);
+ enable_level_ = ::GetTraceEnableLevel(session_handle_);
+
+ // Give subclasses a chance to digest the state change.
+ OnEventsEnabled();
+
+ return ERROR_SUCCESS;
+}
+
+ULONG EtwTraceProvider::DisableEvents() {
+ // Give subclasses a chance to digest the state change.
+ OnEventsDisabled();
+
+ enable_level_ = 0;
+ enable_flags_ = 0;
+ session_handle_ = NULL;
+
+ PostEventsDisabled();
+
+ return ERROR_SUCCESS;
+}
+
+ULONG EtwTraceProvider::Callback(WMIDPREQUESTCODE request, void* buffer) {
+ switch (request) {
+ case WMI_ENABLE_EVENTS:
+ return EnableEvents(buffer);
+ case WMI_DISABLE_EVENTS:
+ return DisableEvents();
+ default:
+ return ERROR_INVALID_PARAMETER;
+ }
+ // Not reached.
+}
+
+ULONG WINAPI EtwTraceProvider::ControlCallback(WMIDPREQUESTCODE request,
+ void* context, ULONG *reserved, void* buffer) {
+ EtwTraceProvider *provider = reinterpret_cast<EtwTraceProvider*>(context);
+
+ return provider->Callback(request, buffer);
+}
+
+ULONG EtwTraceProvider::Register() {
+ if (provider_name_ == GUID_NULL)
+ return ERROR_INVALID_NAME;
+
+ return ::RegisterTraceGuids(ControlCallback, this, &provider_name_,
+ 1, &obligatory_guid_registration_, NULL, NULL, ®istration_handle_);
+}
+
+ULONG EtwTraceProvider::Unregister() {
+ // If a session is active, notify subclasses that it's going away.
+ if (session_handle_ != NULL)
+ DisableEvents();
+
+ ULONG ret = ::UnregisterTraceGuids(registration_handle_);
+
+ registration_handle_ = NULL;
+
+ return ret;
+}
+
+ULONG EtwTraceProvider::Log(const EtwEventClass& event_class,
+ EtwEventType type, EtwEventLevel level, const char *message) {
+ if (NULL == session_handle_ || enable_level_ < level)
+ return ERROR_SUCCESS; // No one listening.
+
+ EtwMofEvent<1> event(event_class, type, level);
+
+ event.fields[0].DataPtr = reinterpret_cast<ULONG64>(message);
+ event.fields[0].Length = message ?
+ static_cast<ULONG>(sizeof(message[0]) * (1 + strlen(message))) : 0;
+
+ return ::TraceEvent(session_handle_, &event.header);
+}
+
+ULONG EtwTraceProvider::Log(const EtwEventClass& event_class,
+ EtwEventType type, EtwEventLevel level, const wchar_t *message) {
+ if (NULL == session_handle_ || enable_level_ < level)
+ return ERROR_SUCCESS; // No one listening.
+
+ EtwMofEvent<1> event(event_class, type, level);
+
+ event.fields[0].DataPtr = reinterpret_cast<ULONG64>(message);
+ event.fields[0].Length = message ?
+ static_cast<ULONG>(sizeof(message[0]) * (1 + wcslen(message))) : 0;
+
+ return ::TraceEvent(session_handle_, &event.header);
+}
+
+ULONG EtwTraceProvider::Log(EVENT_TRACE_HEADER* event) {
+ if (enable_level_ < event->Class.Level)
+ return ERROR_SUCCESS;
+
+ return ::TraceEvent(session_handle_, event);
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/event_trace_provider.h b/src/base/win/event_trace_provider.h
new file mode 100644
index 0000000..9f6e7c4
--- /dev/null
+++ b/src/base/win/event_trace_provider.h
@@ -0,0 +1,175 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Declaration of a Windows event trace provider class, to allow using
+// Windows Event Tracing for logging transport and control.
+#ifndef BASE_WIN_EVENT_TRACE_PROVIDER_H_
+#define BASE_WIN_EVENT_TRACE_PROVIDER_H_
+
+#include <windows.h>
+#include <wmistr.h>
+#include <evntrace.h>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+namespace win {
+
+typedef GUID EtwEventClass;
+typedef UCHAR EtwEventType;
+typedef UCHAR EtwEventLevel;
+typedef USHORT EtwEventVersion;
+typedef ULONG EtwEventFlags;
+
+// Base class is a POD for correctness.
+template <size_t N> struct EtwMofEventBase {
+ EVENT_TRACE_HEADER header;
+ MOF_FIELD fields[N];
+};
+
+// Utility class to auto-initialize event trace header structures.
+template <size_t N> class EtwMofEvent: public EtwMofEventBase<N> {
+ public:
+ typedef EtwMofEventBase<N> Super;
+
+ EtwMofEvent() {
+ memset(static_cast<Super*>(this), 0, sizeof(Super));
+ }
+
+ EtwMofEvent(const EtwEventClass& event_class, EtwEventType type,
+ EtwEventLevel level) {
+ memset(static_cast<Super*>(this), 0, sizeof(Super));
+ header.Size = sizeof(Super);
+ header.Guid = event_class;
+ header.Class.Type = type;
+ header.Class.Level = level;
+ header.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
+ }
+
+ EtwMofEvent(const EtwEventClass& event_class, EtwEventType type,
+ EtwEventVersion version, EtwEventLevel level) {
+ memset(static_cast<Super*>(this), 0, sizeof(Super));
+ header.Size = sizeof(Super);
+ header.Guid = event_class;
+ header.Class.Type = type;
+ header.Class.Version = version;
+ header.Class.Level = level;
+ header.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
+ }
+
+ void SetField(int field, size_t size, const void *data) {
+ // DCHECK(field < N);
+ if ((field < N) && (size <= kuint32max)) {
+ fields[field].DataPtr = reinterpret_cast<ULONG64>(data);
+ fields[field].Length = static_cast<ULONG>(size);
+ }
+ }
+
+ EVENT_TRACE_HEADER* get() { return& header; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(EtwMofEvent);
+};
+
+// Trace provider with Event Tracing for Windows. The trace provider
+// registers with ETW by its name which is a GUID. ETW calls back to
+// the object whenever the trace level or enable flags for this provider
+// name changes.
+// Users of this class can test whether logging is currently enabled at
+// a particular trace level, and whether particular enable flags are set,
+// before other resources are consumed to generate and issue the log
+// messages themselves.
+class BASE_EXPORT EtwTraceProvider {
+ public:
+ // Creates an event trace provider identified by provider_name, which
+ // will be the name registered with Event Tracing for Windows (ETW).
+ explicit EtwTraceProvider(const GUID& provider_name);
+
+ // Creates an unnamed event trace provider, the provider must be given
+ // a name before registration.
+ EtwTraceProvider();
+ virtual ~EtwTraceProvider();
+
+ // Registers the trace provider with Event Tracing for Windows.
+ // Note: from this point forward ETW may call the provider's control
+ // callback. If the provider's name is enabled in some trace session
+ // already, the callback may occur recursively from this call, so
+ // call this only when you're ready to handle callbacks.
+ ULONG Register();
+ // Unregisters the trace provider with ETW.
+ ULONG Unregister();
+
+ // Accessors.
+ void set_provider_name(const GUID& provider_name) {
+ provider_name_ = provider_name;
+ }
+ const GUID& provider_name() const { return provider_name_; }
+ TRACEHANDLE registration_handle() const { return registration_handle_; }
+ TRACEHANDLE session_handle() const { return session_handle_; }
+ EtwEventFlags enable_flags() const { return enable_flags_; }
+ EtwEventLevel enable_level() const { return enable_level_; }
+
+ // Returns true iff logging should be performed for "level" and "flags".
+ // Note: flags is treated as a bitmask, and should normally have a single
+ // bit set, to test whether to log for a particular sub "facility".
+ bool ShouldLog(EtwEventLevel level, EtwEventFlags flags) {
+ return NULL != session_handle_ && level >= enable_level_ &&
+ (0 != (flags & enable_flags_));
+ }
+
+ // Simple wrappers to log Unicode and ANSI strings.
+ // Do nothing if !ShouldLog(level, 0xFFFFFFFF).
+ ULONG Log(const EtwEventClass& event_class, EtwEventType type,
+ EtwEventLevel level, const char *message);
+ ULONG Log(const EtwEventClass& event_class, EtwEventType type,
+ EtwEventLevel level, const wchar_t *message);
+
+ // Log the provided event.
+ ULONG Log(EVENT_TRACE_HEADER* event);
+
+ protected:
+ // Called after events have been enabled, override in subclasses
+ // to set up state or log at the start of a session.
+ // Note: This function may be called ETW's thread and may be racy,
+ // bring your own locking if needed.
+ virtual void OnEventsEnabled() {}
+
+ // Called just before events are disabled, override in subclasses
+ // to tear down state or log at the end of a session.
+ // Note: This function may be called ETW's thread and may be racy,
+ // bring your own locking if needed.
+ virtual void OnEventsDisabled() {}
+
+ // Called just after events have been disabled, override in subclasses
+ // to tear down state at the end of a session. At this point it's
+ // to late to log anything to the session.
+ // Note: This function may be called ETW's thread and may be racy,
+ // bring your own locking if needed.
+ virtual void PostEventsDisabled() {}
+
+ private:
+ ULONG EnableEvents(PVOID buffer);
+ ULONG DisableEvents();
+ ULONG Callback(WMIDPREQUESTCODE request, PVOID buffer);
+ static ULONG WINAPI ControlCallback(WMIDPREQUESTCODE request, PVOID context,
+ ULONG *reserved, PVOID buffer);
+
+ GUID provider_name_;
+ TRACEHANDLE registration_handle_;
+ TRACEHANDLE session_handle_;
+ EtwEventFlags enable_flags_;
+ EtwEventLevel enable_level_;
+
+ // We don't use this, but on XP we're obliged to pass one in to
+ // RegisterTraceGuids. Non-const, because that's how the API needs it.
+ static TRACE_GUID_REGISTRATION obligatory_guid_registration_;
+
+ DISALLOW_COPY_AND_ASSIGN(EtwTraceProvider);
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_EVENT_TRACE_PROVIDER_H_
diff --git a/src/base/win/event_trace_provider_unittest.cc b/src/base/win/event_trace_provider_unittest.cc
new file mode 100644
index 0000000..55b5ae6
--- /dev/null
+++ b/src/base/win/event_trace_provider_unittest.cc
@@ -0,0 +1,110 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Unit tests for event trace provider.
+#include "base/win/event_trace_provider.h"
+#include <new>
+#include "testing/gtest/include/gtest/gtest.h"
+#include <initguid.h> // NOLINT - has to be last
+
+namespace {
+
+using base::win::EtwTraceProvider;
+using base::win::EtwMofEvent;
+
+// {7F0FD37F-FA3C-4cd6-9242-DF60967A2CB2}
+DEFINE_GUID(kTestProvider,
+ 0x7f0fd37f, 0xfa3c, 0x4cd6, 0x92, 0x42, 0xdf, 0x60, 0x96, 0x7a, 0x2c, 0xb2);
+
+// {7F0FD37F-FA3C-4cd6-9242-DF60967A2CB2}
+DEFINE_GUID(kTestEventClass,
+ 0x7f0fd37f, 0xfa3c, 0x4cd6, 0x92, 0x42, 0xdf, 0x60, 0x96, 0x7a, 0x2c, 0xb2);
+
+} // namespace
+
+TEST(EtwTraceProviderTest, ToleratesPreCreateInvocations) {
+ // Because the trace provider is used in logging, it's important that
+ // it be possible to use static provider instances without regard to
+ // whether they've been constructed or destructed.
+ // The interface of the class is designed to tolerate this usage.
+ char buf[sizeof(EtwTraceProvider)] = {0};
+ EtwTraceProvider& provider = reinterpret_cast<EtwTraceProvider&>(buf);
+
+ EXPECT_EQ(NULL, provider.registration_handle());
+ EXPECT_EQ(NULL, provider.session_handle());
+ EXPECT_EQ(0, provider.enable_flags());
+ EXPECT_EQ(0, provider.enable_level());
+
+ EXPECT_FALSE(provider.ShouldLog(TRACE_LEVEL_FATAL, 0xfffffff));
+
+ // We expect these not to crash.
+ provider.Log(kTestEventClass, 0, TRACE_LEVEL_FATAL, "foo");
+ provider.Log(kTestEventClass, 0, TRACE_LEVEL_FATAL, L"foo");
+
+ EtwMofEvent<1> dummy(kTestEventClass, 0, TRACE_LEVEL_FATAL);
+ DWORD data = 0;
+ dummy.SetField(0, sizeof(data), &data);
+ provider.Log(dummy.get());
+
+ // Placement-new the provider into our buffer.
+ new (buf) EtwTraceProvider(kTestProvider);
+
+ // Registration is now safe.
+ EXPECT_EQ(ERROR_SUCCESS, provider.Register());
+
+ // Destruct the instance, this should unregister it.
+ provider.EtwTraceProvider::~EtwTraceProvider();
+
+ // And post-destruction, all of the above should still be safe.
+ EXPECT_EQ(NULL, provider.registration_handle());
+ EXPECT_EQ(NULL, provider.session_handle());
+ EXPECT_EQ(0, provider.enable_flags());
+ EXPECT_EQ(0, provider.enable_level());
+
+ EXPECT_FALSE(provider.ShouldLog(TRACE_LEVEL_FATAL, 0xfffffff));
+
+ // We expect these not to crash.
+ provider.Log(kTestEventClass, 0, TRACE_LEVEL_FATAL, "foo");
+ provider.Log(kTestEventClass, 0, TRACE_LEVEL_FATAL, L"foo");
+ provider.Log(dummy.get());
+}
+
+TEST(EtwTraceProviderTest, Initialize) {
+ EtwTraceProvider provider(kTestProvider);
+
+ EXPECT_EQ(NULL, provider.registration_handle());
+ EXPECT_EQ(NULL, provider.session_handle());
+ EXPECT_EQ(0, provider.enable_flags());
+ EXPECT_EQ(0, provider.enable_level());
+}
+
+TEST(EtwTraceProviderTest, Register) {
+ EtwTraceProvider provider(kTestProvider);
+
+ ASSERT_EQ(ERROR_SUCCESS, provider.Register());
+ EXPECT_NE(NULL, provider.registration_handle());
+ ASSERT_EQ(ERROR_SUCCESS, provider.Unregister());
+ EXPECT_EQ(NULL, provider.registration_handle());
+}
+
+TEST(EtwTraceProviderTest, RegisterWithNoNameFails) {
+ EtwTraceProvider provider;
+
+ EXPECT_TRUE(provider.Register() != ERROR_SUCCESS);
+}
+
+TEST(EtwTraceProviderTest, Enable) {
+ EtwTraceProvider provider(kTestProvider);
+
+ ASSERT_EQ(ERROR_SUCCESS, provider.Register());
+ EXPECT_NE(NULL, provider.registration_handle());
+
+ // No session so far.
+ EXPECT_EQ(NULL, provider.session_handle());
+ EXPECT_EQ(0, provider.enable_flags());
+ EXPECT_EQ(0, provider.enable_level());
+
+ ASSERT_EQ(ERROR_SUCCESS, provider.Unregister());
+ EXPECT_EQ(NULL, provider.registration_handle());
+}
diff --git a/src/base/win/i18n.cc b/src/base/win/i18n.cc
new file mode 100644
index 0000000..9e523a1
--- /dev/null
+++ b/src/base/win/i18n.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/i18n.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+
+namespace {
+
+// Keep this enum in sync with kLanguageFunctionNames.
+enum LanguageFunction {
+ SYSTEM_LANGUAGES,
+ USER_LANGUAGES,
+ PROCESS_LANGUAGES,
+ THREAD_LANGUAGES,
+ NUM_FUNCTIONS
+};
+
+const char kSystemLanguagesFunctionName[] = "GetSystemPreferredUILanguages";
+const char kUserLanguagesFunctionName[] = "GetUserPreferredUILanguages";
+const char kProcessLanguagesFunctionName[] = "GetProcessPreferredUILanguages";
+const char kThreadLanguagesFunctionName[] = "GetThreadPreferredUILanguages";
+
+// Keep this array in sync with enum LanguageFunction.
+const char *const kLanguageFunctionNames[] = {
+ &kSystemLanguagesFunctionName[0],
+ &kUserLanguagesFunctionName[0],
+ &kProcessLanguagesFunctionName[0],
+ &kThreadLanguagesFunctionName[0]
+};
+
+COMPILE_ASSERT(NUM_FUNCTIONS == arraysize(kLanguageFunctionNames),
+ language_function_enum_and_names_out_of_sync);
+
+// Calls one of the MUI Get*PreferredUILanguages functions, placing the result
+// in |languages|. |function| identifies the function to call and |flags| is
+// the function-specific flags (callers must not specify MUI_LANGUAGE_ID or
+// MUI_LANGUAGE_NAME). Returns true if at least one language is placed in
+// |languages|.
+bool GetMUIPreferredUILanguageList(LanguageFunction function, ULONG flags,
+ std::vector<wchar_t>* languages) {
+ DCHECK(0 <= function && NUM_FUNCTIONS > function);
+ DCHECK_EQ(0U, (flags & (MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME)));
+ DCHECK(languages);
+
+ HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
+ if (NULL != kernel32) {
+ typedef BOOL (WINAPI* GetPreferredUILanguages_Fn)(
+ DWORD, PULONG, PZZWSTR, PULONG);
+ GetPreferredUILanguages_Fn get_preferred_ui_languages =
+ reinterpret_cast<GetPreferredUILanguages_Fn>(
+ GetProcAddress(kernel32, kLanguageFunctionNames[function]));
+ if (NULL != get_preferred_ui_languages) {
+ const ULONG call_flags = flags | MUI_LANGUAGE_NAME;
+ ULONG language_count = 0;
+ ULONG buffer_length = 0;
+ if (get_preferred_ui_languages(call_flags, &language_count, NULL,
+ &buffer_length) &&
+ 0 != buffer_length) {
+ languages->resize(buffer_length);
+ if (get_preferred_ui_languages(call_flags, &language_count,
+ &(*languages)[0], &buffer_length) &&
+ 0 != language_count) {
+ DCHECK(languages->size() == buffer_length);
+ return true;
+ } else {
+ DPCHECK(0 == language_count)
+ << "Failed getting preferred UI languages.";
+ }
+ } else {
+ DPCHECK(0 == buffer_length)
+ << "Failed getting size of preferred UI languages.";
+ }
+ } else {
+ DVLOG(2) << "MUI not available.";
+ }
+ } else {
+ NOTREACHED() << "kernel32.dll not found.";
+ }
+
+ return false;
+}
+
+bool GetUserDefaultUILanguage(std::wstring* language, std::wstring* region) {
+ DCHECK(language);
+
+ LANGID lang_id = ::GetUserDefaultUILanguage();
+ if (LOCALE_CUSTOM_UI_DEFAULT != lang_id) {
+ const LCID locale_id = MAKELCID(lang_id, SORT_DEFAULT);
+ // max size for LOCALE_SISO639LANGNAME and LOCALE_SISO3166CTRYNAME is 9
+ wchar_t result_buffer[9];
+ int result_length =
+ GetLocaleInfo(locale_id, LOCALE_SISO639LANGNAME, &result_buffer[0],
+ arraysize(result_buffer));
+ DPCHECK(0 != result_length) << "Failed getting language id";
+ if (1 < result_length) {
+ language->assign(&result_buffer[0], result_length - 1);
+ region->clear();
+ if (SUBLANG_NEUTRAL != SUBLANGID(lang_id)) {
+ result_length =
+ GetLocaleInfo(locale_id, LOCALE_SISO3166CTRYNAME, &result_buffer[0],
+ arraysize(result_buffer));
+ DPCHECK(0 != result_length) << "Failed getting region id";
+ if (1 < result_length)
+ region->assign(&result_buffer[0], result_length - 1);
+ }
+ return true;
+ }
+ } else {
+ // This is entirely unexpected on pre-Vista, which is the only time we
+ // should try GetUserDefaultUILanguage anyway.
+ NOTREACHED() << "Cannot determine language for a supplemental locale.";
+ }
+ return false;
+}
+
+bool GetPreferredUILanguageList(LanguageFunction function, ULONG flags,
+ std::vector<std::wstring>* languages) {
+ std::vector<wchar_t> buffer;
+ std::wstring language;
+ std::wstring region;
+
+ if (GetMUIPreferredUILanguageList(function, flags, &buffer)) {
+ std::vector<wchar_t>::const_iterator scan = buffer.begin();
+ language.assign(&*scan);
+ while (!language.empty()) {
+ languages->push_back(language);
+ scan += language.size() + 1;
+ language.assign(&*scan);
+ }
+ } else if (GetUserDefaultUILanguage(&language, ®ion)) {
+ // Mimic the MUI behavior of putting the neutral version of the lang after
+ // the regional one (e.g., "fr-CA, fr").
+ if (!region.empty())
+ languages->push_back(std::wstring(language)
+ .append(1, L'-')
+ .append(region));
+ languages->push_back(language);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace base {
+namespace win {
+namespace i18n {
+
+bool GetUserPreferredUILanguageList(std::vector<std::wstring>* languages) {
+ DCHECK(languages);
+ return GetPreferredUILanguageList(USER_LANGUAGES, 0, languages);
+}
+
+bool GetThreadPreferredUILanguageList(std::vector<std::wstring>* languages) {
+ DCHECK(languages);
+ return GetPreferredUILanguageList(
+ THREAD_LANGUAGES, MUI_MERGE_SYSTEM_FALLBACK | MUI_MERGE_USER_FALLBACK,
+ languages);
+}
+
+} // namespace i18n
+} // namespace win
+} // namespace base
diff --git a/src/base/win/i18n.h b/src/base/win/i18n.h
new file mode 100644
index 0000000..c0379c1
--- /dev/null
+++ b/src/base/win/i18n.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_I18N_H_
+#define BASE_WIN_I18N_H_
+
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+namespace win {
+namespace i18n {
+
+// Adds to |languages| the list of user preferred UI languages from MUI, if
+// available, falling-back on the user default UI language otherwise. Returns
+// true if at least one language is added.
+BASE_EXPORT bool GetUserPreferredUILanguageList(
+ std::vector<std::wstring>* languages);
+
+// Adds to |languages| the list of thread, process, user, and system preferred
+// UI languages from MUI, if available, falling-back on the user default UI
+// language otherwise. Returns true if at least one language is added.
+BASE_EXPORT bool GetThreadPreferredUILanguageList(
+ std::vector<std::wstring>* languages);
+
+} // namespace i18n
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_I18N_H_
diff --git a/src/base/win/i18n_unittest.cc b/src/base/win/i18n_unittest.cc
new file mode 100644
index 0000000..781fc39
--- /dev/null
+++ b/src/base/win/i18n_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains unit tests for Windows internationalization funcs.
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "base/win/i18n.h"
+#include "base/win/windows_version.h"
+
+namespace base {
+namespace win {
+namespace i18n {
+
+// Tests that at least one user preferred UI language can be obtained.
+TEST(I18NTest, GetUserPreferredUILanguageList) {
+ std::vector<std::wstring> languages;
+ EXPECT_TRUE(GetUserPreferredUILanguageList(&languages));
+ EXPECT_NE(static_cast<std::vector<std::wstring>::size_type>(0),
+ languages.size());
+ for (std::vector<std::wstring>::const_iterator scan = languages.begin(),
+ end = languages.end(); scan != end; ++scan) {
+ EXPECT_FALSE((*scan).empty());
+ }
+}
+
+// Tests that at least one thread preferred UI language can be obtained.
+TEST(I18NTest, GetThreadPreferredUILanguageList) {
+ std::vector<std::wstring> languages;
+ EXPECT_TRUE(GetThreadPreferredUILanguageList(&languages));
+ EXPECT_NE(static_cast<std::vector<std::wstring>::size_type>(0),
+ languages.size());
+ for (std::vector<std::wstring>::const_iterator scan = languages.begin(),
+ end = languages.end(); scan != end; ++scan) {
+ EXPECT_FALSE((*scan).empty());
+ }
+}
+
+} // namespace i18n
+} // namespace win
+} // namespace base
diff --git a/src/base/win/iat_patch_function.cc b/src/base/win/iat_patch_function.cc
new file mode 100644
index 0000000..a4a8902
--- /dev/null
+++ b/src/base/win/iat_patch_function.cc
@@ -0,0 +1,278 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/iat_patch_function.h"
+
+#include "base/logging.h"
+#include "base/win/pe_image.h"
+
+namespace base {
+namespace win {
+
+namespace {
+
+struct InterceptFunctionInformation {
+ bool finished_operation;
+ const char* imported_from_module;
+ const char* function_name;
+ void* new_function;
+ void** old_function;
+ IMAGE_THUNK_DATA** iat_thunk;
+ DWORD return_code;
+};
+
+void* GetIATFunction(IMAGE_THUNK_DATA* iat_thunk) {
+ if (NULL == iat_thunk) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ // Works around the 64 bit portability warning:
+ // The Function member inside IMAGE_THUNK_DATA is really a pointer
+ // to the IAT function. IMAGE_THUNK_DATA correctly maps to IMAGE_THUNK_DATA32
+ // or IMAGE_THUNK_DATA64 for correct pointer size.
+ union FunctionThunk {
+ IMAGE_THUNK_DATA thunk;
+ void* pointer;
+ } iat_function;
+
+ iat_function.thunk = *iat_thunk;
+ return iat_function.pointer;
+}
+// Change the page protection (of code pages) to writable and copy
+// the data at the specified location
+//
+// Arguments:
+// old_code Target location to copy
+// new_code Source
+// length Number of bytes to copy
+//
+// Returns: Windows error code (winerror.h). NO_ERROR if successful
+DWORD ModifyCode(void* old_code, void* new_code, int length) {
+ if ((NULL == old_code) || (NULL == new_code) || (0 == length)) {
+ NOTREACHED();
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ // Change the page protection so that we can write.
+ DWORD error = NO_ERROR;
+ DWORD old_page_protection = 0;
+ if (VirtualProtect(old_code,
+ length,
+ PAGE_READWRITE,
+ &old_page_protection)) {
+
+ // Write the data.
+ CopyMemory(old_code, new_code, length);
+
+ // Restore the old page protection.
+ error = ERROR_SUCCESS;
+ VirtualProtect(old_code,
+ length,
+ old_page_protection,
+ &old_page_protection);
+ } else {
+ error = GetLastError();
+ NOTREACHED();
+ }
+
+ return error;
+}
+
+bool InterceptEnumCallback(const base::win::PEImage& image, const char* module,
+ DWORD ordinal, const char* name, DWORD hint,
+ IMAGE_THUNK_DATA* iat, void* cookie) {
+ InterceptFunctionInformation* intercept_information =
+ reinterpret_cast<InterceptFunctionInformation*>(cookie);
+
+ if (NULL == intercept_information) {
+ NOTREACHED();
+ return false;
+ }
+
+ DCHECK(module);
+
+ if ((0 == lstrcmpiA(module, intercept_information->imported_from_module)) &&
+ (NULL != name) &&
+ (0 == lstrcmpiA(name, intercept_information->function_name))) {
+ // Save the old pointer.
+ if (NULL != intercept_information->old_function) {
+ *(intercept_information->old_function) = GetIATFunction(iat);
+ }
+
+ if (NULL != intercept_information->iat_thunk) {
+ *(intercept_information->iat_thunk) = iat;
+ }
+
+ // portability check
+ COMPILE_ASSERT(sizeof(iat->u1.Function) ==
+ sizeof(intercept_information->new_function), unknown_IAT_thunk_format);
+
+ // Patch the function.
+ intercept_information->return_code =
+ ModifyCode(&(iat->u1.Function),
+ &(intercept_information->new_function),
+ sizeof(intercept_information->new_function));
+
+ // Terminate further enumeration.
+ intercept_information->finished_operation = true;
+ return false;
+ }
+
+ return true;
+}
+
+// Helper to intercept a function in an import table of a specific
+// module.
+//
+// Arguments:
+// module_handle Module to be intercepted
+// imported_from_module Module that exports the symbol
+// function_name Name of the API to be intercepted
+// new_function Interceptor function
+// old_function Receives the original function pointer
+// iat_thunk Receives pointer to IAT_THUNK_DATA
+// for the API from the import table.
+//
+// Returns: Returns NO_ERROR on success or Windows error code
+// as defined in winerror.h
+DWORD InterceptImportedFunction(HMODULE module_handle,
+ const char* imported_from_module,
+ const char* function_name, void* new_function,
+ void** old_function,
+ IMAGE_THUNK_DATA** iat_thunk) {
+ if ((NULL == module_handle) || (NULL == imported_from_module) ||
+ (NULL == function_name) || (NULL == new_function)) {
+ NOTREACHED();
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ base::win::PEImage target_image(module_handle);
+ if (!target_image.VerifyMagic()) {
+ NOTREACHED();
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ InterceptFunctionInformation intercept_information = {
+ false,
+ imported_from_module,
+ function_name,
+ new_function,
+ old_function,
+ iat_thunk,
+ ERROR_GEN_FAILURE};
+
+ // First go through the IAT. If we don't find the import we are looking
+ // for in IAT, search delay import table.
+ target_image.EnumAllImports(InterceptEnumCallback, &intercept_information);
+ if (!intercept_information.finished_operation) {
+ target_image.EnumAllDelayImports(InterceptEnumCallback,
+ &intercept_information);
+ }
+
+ return intercept_information.return_code;
+}
+
+// Restore intercepted IAT entry with the original function.
+//
+// Arguments:
+// intercept_function Interceptor function
+// original_function Receives the original function pointer
+//
+// Returns: Returns NO_ERROR on success or Windows error code
+// as defined in winerror.h
+DWORD RestoreImportedFunction(void* intercept_function,
+ void* original_function,
+ IMAGE_THUNK_DATA* iat_thunk) {
+ if ((NULL == intercept_function) || (NULL == original_function) ||
+ (NULL == iat_thunk)) {
+ NOTREACHED();
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ if (GetIATFunction(iat_thunk) != intercept_function) {
+ // Check if someone else has intercepted on top of us.
+ // We cannot unpatch in this case, just raise a red flag.
+ NOTREACHED();
+ return ERROR_INVALID_FUNCTION;
+ }
+
+ return ModifyCode(&(iat_thunk->u1.Function),
+ &original_function,
+ sizeof(original_function));
+}
+
+} // namespace
+
+IATPatchFunction::IATPatchFunction()
+ : module_handle_(NULL),
+ original_function_(NULL),
+ iat_thunk_(NULL),
+ intercept_function_(NULL) {
+}
+
+IATPatchFunction::~IATPatchFunction() {
+ if (NULL != intercept_function_) {
+ DWORD error = Unpatch();
+ DCHECK_EQ(static_cast<DWORD>(NO_ERROR), error);
+ }
+}
+
+DWORD IATPatchFunction::Patch(const wchar_t* module,
+ const char* imported_from_module,
+ const char* function_name,
+ void* new_function) {
+ DCHECK_EQ(static_cast<void*>(NULL), original_function_);
+ DCHECK_EQ(static_cast<IMAGE_THUNK_DATA*>(NULL), iat_thunk_);
+ DCHECK_EQ(static_cast<void*>(NULL), intercept_function_);
+
+ HMODULE module_handle = LoadLibraryW(module);
+
+ if (module_handle == NULL) {
+ NOTREACHED();
+ return GetLastError();
+ }
+
+ DWORD error = InterceptImportedFunction(module_handle,
+ imported_from_module,
+ function_name,
+ new_function,
+ &original_function_,
+ &iat_thunk_);
+
+ if (NO_ERROR == error) {
+ DCHECK_NE(original_function_, intercept_function_);
+ module_handle_ = module_handle;
+ intercept_function_ = new_function;
+ } else {
+ FreeLibrary(module_handle);
+ }
+
+ return error;
+}
+
+DWORD IATPatchFunction::Unpatch() {
+ DWORD error = RestoreImportedFunction(intercept_function_,
+ original_function_,
+ iat_thunk_);
+ DCHECK_EQ(static_cast<DWORD>(NO_ERROR), error);
+
+ // Hands off the intercept if we fail to unpatch.
+ // If IATPatchFunction::Unpatch fails during RestoreImportedFunction
+ // it means that we cannot safely unpatch the import address table
+ // patch. In this case its better to be hands off the intercept as
+ // trying to unpatch again in the destructor of IATPatchFunction is
+ // not going to be any safer
+ if (module_handle_)
+ FreeLibrary(module_handle_);
+ module_handle_ = NULL;
+ intercept_function_ = NULL;
+ original_function_ = NULL;
+ iat_thunk_ = NULL;
+
+ return error;
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/iat_patch_function.h b/src/base/win/iat_patch_function.h
new file mode 100644
index 0000000..3ae1f3c
--- /dev/null
+++ b/src/base/win/iat_patch_function.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_IAT_PATCH_FUNCTION_H_
+#define BASE_WIN_IAT_PATCH_FUNCTION_H_
+
+#include <windows.h>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+namespace win {
+
+// A class that encapsulates Import Address Table patching helpers and restores
+// the original function in the destructor.
+//
+// It will intercept functions for a specific DLL imported from another DLL.
+// This is the case when, for example, we want to intercept
+// CertDuplicateCertificateContext function (exported from crypt32.dll) called
+// by wininet.dll.
+class BASE_EXPORT IATPatchFunction {
+ public:
+ IATPatchFunction();
+ ~IATPatchFunction();
+
+ // Intercept a function in an import table of a specific
+ // module. Save the original function and the import
+ // table address. These values will be used later
+ // during Unpatch
+ //
+ // Arguments:
+ // module Module to be intercepted
+ // imported_from_module Module that exports the 'function_name'
+ // function_name Name of the API to be intercepted
+ //
+ // Returns: Windows error code (winerror.h). NO_ERROR if successful
+ //
+ // Note: Patching a function will make the IAT patch take some "ownership" on
+ // |module|. It will LoadLibrary(module) to keep the DLL alive until a call
+ // to Unpatch(), which will call FreeLibrary() and allow the module to be
+ // unloaded. The idea is to help prevent the DLL from going away while a
+ // patch is still active.
+ DWORD Patch(const wchar_t* module,
+ const char* imported_from_module,
+ const char* function_name,
+ void* new_function);
+
+ // Unpatch the IAT entry using internally saved original
+ // function.
+ //
+ // Returns: Windows error code (winerror.h). NO_ERROR if successful
+ DWORD Unpatch();
+
+ bool is_patched() const {
+ return (NULL != intercept_function_);
+ }
+
+ private:
+ HMODULE module_handle_;
+ void* intercept_function_;
+ void* original_function_;
+ IMAGE_THUNK_DATA* iat_thunk_;
+
+ DISALLOW_COPY_AND_ASSIGN(IATPatchFunction);
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_IAT_PATCH_FUNCTION_H_
diff --git a/src/base/win/iunknown_impl.cc b/src/base/win/iunknown_impl.cc
new file mode 100644
index 0000000..9baa0f3
--- /dev/null
+++ b/src/base/win/iunknown_impl.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/iunknown_impl.h"
+
+namespace base {
+namespace win {
+
+IUnknownImpl::IUnknownImpl()
+ : ref_count_(0) {
+}
+
+IUnknownImpl::~IUnknownImpl() {
+}
+
+ULONG STDMETHODCALLTYPE IUnknownImpl::AddRef() {
+ base::AtomicRefCountInc(&ref_count_);
+ return 1;
+}
+
+ULONG STDMETHODCALLTYPE IUnknownImpl::Release() {
+ if (!base::AtomicRefCountDec(&ref_count_)) {
+ delete this;
+ return 0;
+ }
+ return 1;
+}
+
+STDMETHODIMP IUnknownImpl::QueryInterface(REFIID riid, void** ppv) {
+ if (riid == IID_IUnknown) {
+ *ppv = static_cast<IUnknown*>(this);
+ AddRef();
+ return S_OK;
+ }
+
+ *ppv = NULL;
+ return E_NOINTERFACE;
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/iunknown_impl.h b/src/base/win/iunknown_impl.h
new file mode 100644
index 0000000..ff7e870
--- /dev/null
+++ b/src/base/win/iunknown_impl.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_IUNKNOWN_IMPL_H_
+#define BASE_WIN_IUNKNOWN_IMPL_H_
+
+#include <unknwn.h>
+
+#include "base/atomic_ref_count.h"
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+
+namespace base {
+namespace win {
+
+// IUnknown implementation for other classes to derive from.
+class BASE_EXPORT IUnknownImpl : public IUnknown {
+ public:
+ IUnknownImpl();
+
+ virtual ULONG STDMETHODCALLTYPE AddRef() OVERRIDE;
+ virtual ULONG STDMETHODCALLTYPE Release() OVERRIDE;
+
+ // Subclasses should extend this to return any interfaces they provide.
+ virtual STDMETHODIMP QueryInterface(REFIID riid, void** ppv) OVERRIDE;
+
+ protected:
+ virtual ~IUnknownImpl();
+
+ private:
+ AtomicRefCount ref_count_;
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_IUNKNOWN_IMPL_H_
diff --git a/src/base/win/iunknown_impl_unittest.cc b/src/base/win/iunknown_impl_unittest.cc
new file mode 100644
index 0000000..db86214
--- /dev/null
+++ b/src/base/win/iunknown_impl_unittest.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/iunknown_impl.h"
+
+#include "base/win/scoped_com_initializer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace win {
+
+class TestIUnknownImplSubclass : public IUnknownImpl {
+ public:
+ TestIUnknownImplSubclass() {
+ ++instance_count;
+ }
+ virtual ~TestIUnknownImplSubclass() {
+ --instance_count;
+ }
+ static int instance_count;
+};
+
+// static
+int TestIUnknownImplSubclass::instance_count = 0;
+
+TEST(IUnknownImplTest, IUnknownImpl) {
+ ScopedCOMInitializer com_initializer;
+
+ EXPECT_EQ(0, TestIUnknownImplSubclass::instance_count);
+ IUnknown* u = new TestIUnknownImplSubclass();
+
+ EXPECT_EQ(1, TestIUnknownImplSubclass::instance_count);
+
+ EXPECT_EQ(1, u->AddRef());
+ EXPECT_EQ(1, u->AddRef());
+
+ IUnknown* other = NULL;
+ EXPECT_EQ(E_NOINTERFACE, u->QueryInterface(
+ IID_IDispatch, reinterpret_cast<void**>(&other)));
+ EXPECT_EQ(S_OK, u->QueryInterface(
+ IID_IUnknown, reinterpret_cast<void**>(&other)));
+ other->Release();
+
+ EXPECT_EQ(1, u->Release());
+ EXPECT_EQ(0, u->Release());
+ EXPECT_EQ(0, TestIUnknownImplSubclass::instance_count);
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/metro.cc b/src/base/win/metro.cc
new file mode 100644
index 0000000..22bc5e8
--- /dev/null
+++ b/src/base/win/metro.cc
@@ -0,0 +1,189 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/metro.h"
+
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/win/scoped_comptr.h"
+#include "base/win/windows_version.h"
+
+namespace base {
+namespace win {
+
+namespace {
+bool g_should_tsf_aware_required = false;
+}
+
+HMODULE GetMetroModule() {
+ const HMODULE kUninitialized = reinterpret_cast<HMODULE>(1);
+ static HMODULE metro_module = kUninitialized;
+
+ if (metro_module == kUninitialized) {
+ // Initialize the cache, note that the initialization is idempotent
+ // under the assumption that metro_driver is never unloaded, so the
+ // race to this assignment is safe.
+ metro_module = GetModuleHandleA("metro_driver.dll");
+ if (metro_module != NULL) {
+ // This must be a metro process if the metro_driver is loaded.
+ DCHECK(IsMetroProcess());
+ }
+ }
+
+ DCHECK(metro_module != kUninitialized);
+ return metro_module;
+}
+
+bool IsMetroProcess() {
+ enum ImmersiveState {
+ kImmersiveUnknown,
+ kImmersiveTrue,
+ kImmersiveFalse
+ };
+ // The immersive state of a process can never change.
+ // Look it up once and cache it here.
+ static ImmersiveState state = kImmersiveUnknown;
+
+ if (state == kImmersiveUnknown) {
+ if (IsProcessImmersive(::GetCurrentProcess())) {
+ state = kImmersiveTrue;
+ } else {
+ state = kImmersiveFalse;
+ }
+ }
+ DCHECK_NE(kImmersiveUnknown, state);
+ return state == kImmersiveTrue;
+}
+
+bool IsProcessImmersive(HANDLE process) {
+ typedef BOOL (WINAPI* IsImmersiveProcessFunc)(HANDLE process);
+ HMODULE user32 = ::GetModuleHandleA("user32.dll");
+ DCHECK(user32 != NULL);
+
+ IsImmersiveProcessFunc is_immersive_process =
+ reinterpret_cast<IsImmersiveProcessFunc>(
+ ::GetProcAddress(user32, "IsImmersiveProcess"));
+
+ if (is_immersive_process)
+ return is_immersive_process(process) ? true: false;
+ return false;
+}
+
+bool IsTSFAwareRequired() {
+ // Although this function is equal to IsMetroProcess at this moment,
+ // Chrome for Win7 and Vista may support TSF in the future.
+ return g_should_tsf_aware_required || IsMetroProcess();
+}
+
+void SetForceToUseTSF() {
+ g_should_tsf_aware_required = true;
+
+ // Since Windows 8 Metro mode disables CUAS (Cicero Unaware Application
+ // Support) via ImmDisableLegacyIME API, Chrome must be fully TSF-aware on
+ // Metro mode. For debugging purposes, explicitly call ImmDisableLegacyIME so
+ // that one can test TSF functionality even on Windows 8 desktop mode. Note
+ // that CUAS cannot be disabled on Windows Vista/7 where ImmDisableLegacyIME
+ // is not available.
+ typedef BOOL (* ImmDisableLegacyIMEFunc)();
+ HMODULE imm32 = ::GetModuleHandleA("imm32.dll");
+ if (imm32 == NULL)
+ return;
+
+ ImmDisableLegacyIMEFunc imm_disable_legacy_ime =
+ reinterpret_cast<ImmDisableLegacyIMEFunc>(
+ ::GetProcAddress(imm32, "ImmDisableLegacyIME"));
+
+ if (imm_disable_legacy_ime == NULL) {
+ // Unsupported API, just do nothing.
+ return;
+ }
+
+ if (!imm_disable_legacy_ime()) {
+ DVLOG(1) << "Failed to disable legacy IME.";
+ }
+}
+
+wchar_t* LocalAllocAndCopyString(const string16& src) {
+ size_t dest_size = (src.length() + 1) * sizeof(wchar_t);
+ wchar_t* dest = reinterpret_cast<wchar_t*>(LocalAlloc(LPTR, dest_size));
+ base::wcslcpy(dest, src.c_str(), dest_size);
+ return dest;
+}
+
+bool IsTouchEnabled() {
+ int value = GetSystemMetrics(SM_DIGITIZER);
+ return (value & (NID_READY | NID_INTEGRATED_TOUCH)) ==
+ (NID_READY | NID_INTEGRATED_TOUCH);
+}
+
+bool IsParentalControlActivityLoggingOn() {
+ // Query this info on Windows Vista and above.
+ if (base::win::GetVersion() < base::win::VERSION_VISTA)
+ return false;
+
+ static bool parental_control_logging_required = false;
+ static bool parental_control_status_determined = false;
+
+ if (parental_control_status_determined)
+ return parental_control_logging_required;
+
+ parental_control_status_determined = true;
+
+ ScopedComPtr<IWindowsParentalControlsCore> parent_controls;
+ HRESULT hr = parent_controls.CreateInstance(
+ __uuidof(WindowsParentalControls));
+ if (FAILED(hr))
+ return false;
+
+ ScopedComPtr<IWPCSettings> settings;
+ hr = parent_controls->GetUserSettings(NULL, settings.Receive());
+ if (FAILED(hr))
+ return false;
+
+ unsigned long restrictions = 0;
+ settings->GetRestrictions(&restrictions);
+
+ parental_control_logging_required =
+ (restrictions & WPCFLAG_LOGGING_REQUIRED) == WPCFLAG_LOGGING_REQUIRED;
+ return parental_control_logging_required;
+}
+
+// Metro driver exports for getting the launch type, initial url, initial
+// search term, etc.
+extern "C" {
+typedef const wchar_t* (*GetInitialUrl)();
+typedef const wchar_t* (*GetInitialSearchString)();
+typedef base::win::MetroLaunchType (*GetLaunchType)(
+ base::win::MetroPreviousExecutionState* previous_state);
+}
+
+MetroLaunchType GetMetroLaunchParams(string16* params) {
+ HMODULE metro = base::win::GetMetroModule();
+ if (!metro)
+ return base::win::METRO_LAUNCH_ERROR;
+
+ GetLaunchType get_launch_type = reinterpret_cast<GetLaunchType>(
+ ::GetProcAddress(metro, "GetLaunchType"));
+ DCHECK(get_launch_type);
+
+ base::win::MetroLaunchType launch_type = get_launch_type(NULL);
+
+ if ((launch_type == base::win::METRO_PROTOCOL) ||
+ (launch_type == base::win::METRO_LAUNCH)) {
+ GetInitialUrl initial_metro_url = reinterpret_cast<GetInitialUrl>(
+ ::GetProcAddress(metro, "GetInitialUrl"));
+ DCHECK(initial_metro_url);
+ *params = initial_metro_url();
+ } else if (launch_type == base::win::METRO_SEARCH) {
+ GetInitialSearchString initial_search_string =
+ reinterpret_cast<GetInitialSearchString>(
+ ::GetProcAddress(metro, "GetInitialSearchString"));
+ DCHECK(initial_search_string);
+ *params = initial_search_string();
+ }
+ return launch_type;
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/metro.h b/src/base/win/metro.h
new file mode 100644
index 0000000..9354de8
--- /dev/null
+++ b/src/base/win/metro.h
@@ -0,0 +1,142 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_METRO_H_
+#define BASE_WIN_METRO_H_
+
+#include <windows.h>
+#include <wpcapi.h>
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/file_path.h"
+#include "base/string16.h"
+
+namespace base {
+namespace win {
+
+// Identifies the type of the metro launch.
+enum MetroLaunchType {
+ METRO_LAUNCH,
+ METRO_SEARCH,
+ METRO_SHARE,
+ METRO_FILE,
+ METRO_PROTOCOL,
+ METRO_LAUNCH_ERROR,
+ METRO_LASTLAUNCHTYPE,
+};
+
+// In metro mode, this enum identifies the last execution state, i.e. whether
+// we crashed, terminated, etc.
+enum MetroPreviousExecutionState {
+ NOTRUNNING,
+ RUNNING,
+ SUSPENDED,
+ TERMINATED,
+ CLOSEDBYUSER,
+ LASTEXECUTIONSTATE,
+};
+
+// Enum values for UMA histogram reporting of site-specific tile pinning.
+// TODO(tapted): Move this to win8/util when ready (http://crbug.com/160288).
+enum MetroSecondaryTilePinUmaResult {
+ METRO_PIN_STATE_NONE,
+ METRO_PIN_INITIATED,
+ METRO_PIN_LOGO_READY,
+ METRO_PIN_REQUEST_SHOW_ERROR,
+ METRO_PIN_RESULT_CANCEL,
+ METRO_PIN_RESULT_OK,
+ METRO_PIN_RESULT_OTHER,
+ METRO_PIN_RESULT_ERROR,
+ METRO_UNPIN_INITIATED,
+ METRO_UNPIN_REQUEST_SHOW_ERROR,
+ METRO_UNPIN_RESULT_CANCEL,
+ METRO_UNPIN_RESULT_OK,
+ METRO_UNPIN_RESULT_OTHER,
+ METRO_UNPIN_RESULT_ERROR,
+ METRO_PIN_STATE_LIMIT
+};
+
+// Contains information about the currently displayed tab in metro mode.
+struct CurrentTabInfo {
+ wchar_t* title;
+ wchar_t* url;
+};
+
+// Returns the handle to the metro dll loaded in the process. A NULL return
+// indicates that the metro dll was not loaded in the process.
+BASE_EXPORT HMODULE GetMetroModule();
+
+// Returns true if this process is running as an immersive program
+// in Windows Metro mode.
+BASE_EXPORT bool IsMetroProcess();
+
+// Returns true if the process identified by the handle passed in is an
+// immersive (Metro) process.
+BASE_EXPORT bool IsProcessImmersive(HANDLE process);
+
+// Returns true if this process is running under Text Services Framework (TSF)
+// and browser must be TSF-aware.
+BASE_EXPORT bool IsTSFAwareRequired();
+
+// Sets browser to use Text Services Framework (TSF) regardless of process
+// status. On Windows 8, this function also disables CUAS (Cicero Unaware
+// Application Support) to emulate Windows Metro mode in terms of IME
+// functionality. This should be beneficial in QA process because on can test
+// IME functionality in Windows 8 desktop mode.
+BASE_EXPORT void SetForceToUseTSF();
+
+// Allocates and returns the destination string via the LocalAlloc API after
+// copying the src to it.
+BASE_EXPORT wchar_t* LocalAllocAndCopyString(const string16& src);
+
+// Returns true if the screen supports touch.
+BASE_EXPORT bool IsTouchEnabled();
+
+// Returns true if Windows Parental control activity logging is enabled. This
+// feature is available on Windows Vista and beyond.
+// This function should ideally be called on the UI thread.
+BASE_EXPORT bool IsParentalControlActivityLoggingOn();
+
+// Returns the type of launch and the activation params. For example if the
+// the launch is for METRO_PROTOCOL then the params is a url.
+BASE_EXPORT MetroLaunchType GetMetroLaunchParams(string16* params);
+
+// Handler function for the buttons on a metro dialog box
+typedef void (*MetroDialogButtonPressedHandler)();
+
+// Handler function invoked when a metro style notification is clicked.
+typedef void (*MetroNotificationClickedHandler)(const wchar_t* context);
+
+// Function to display metro style notifications.
+typedef void (*MetroNotification)(const char* origin_url,
+ const char* icon_url,
+ const wchar_t* title,
+ const wchar_t* body,
+ const wchar_t* display_source,
+ const char* notification_id,
+ MetroNotificationClickedHandler handler,
+ const wchar_t* handler_context);
+
+// Callback for UMA invoked by Metro Pin and UnPin functions after user gesture.
+typedef base::Callback<void(MetroSecondaryTilePinUmaResult)>
+ MetroPinUmaResultCallback;
+
+// Function to pin a site-specific tile (bookmark) to the start screen.
+typedef void (*MetroPinToStartScreen)(
+ const string16& tile_id,
+ const string16& title,
+ const string16& url,
+ const FilePath& logo_path,
+ const MetroPinUmaResultCallback& callback);
+
+// Function to un-pin a site-specific tile (bookmark) from the start screen.
+typedef void (*MetroUnPinFromStartScreen)(
+ const string16& title_id,
+ const MetroPinUmaResultCallback& callback);
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_METRO_H_
diff --git a/src/base/win/object_watcher.cc b/src/base/win/object_watcher.cc
new file mode 100644
index 0000000..ebe596f
--- /dev/null
+++ b/src/base/win/object_watcher.cc
@@ -0,0 +1,111 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/object_watcher.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+
+namespace base {
+namespace win {
+
+//-----------------------------------------------------------------------------
+
+ObjectWatcher::ObjectWatcher()
+ : weak_factory_(this),
+ object_(NULL),
+ wait_object_(NULL),
+ origin_loop_(NULL) {
+}
+
+ObjectWatcher::~ObjectWatcher() {
+ StopWatching();
+}
+
+bool ObjectWatcher::StartWatching(HANDLE object, Delegate* delegate) {
+ CHECK(delegate);
+ if (wait_object_) {
+ NOTREACHED() << "Already watching an object";
+ return false;
+ }
+
+ // Since our job is to just notice when an object is signaled and report the
+ // result back to this thread, we can just run on a Windows wait thread.
+ DWORD wait_flags = WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE;
+
+ // DoneWaiting can be synchronously called from RegisterWaitForSingleObject,
+ // so set up all state now.
+ callback_ = base::Bind(&ObjectWatcher::Signal, weak_factory_.GetWeakPtr(),
+ delegate);
+ object_ = object;
+ origin_loop_ = MessageLoop::current();
+
+ if (!RegisterWaitForSingleObject(&wait_object_, object, DoneWaiting,
+ this, INFINITE, wait_flags)) {
+ NOTREACHED() << "RegisterWaitForSingleObject failed: " << GetLastError();
+ object_ = NULL;
+ wait_object_ = NULL;
+ return false;
+ }
+
+ // We need to know if the current message loop is going away so we can
+ // prevent the wait thread from trying to access a dead message loop.
+ MessageLoop::current()->AddDestructionObserver(this);
+ return true;
+}
+
+bool ObjectWatcher::StopWatching() {
+ if (!wait_object_)
+ return false;
+
+ // Make sure ObjectWatcher is used in a single-threaded fashion.
+ DCHECK(origin_loop_ == MessageLoop::current());
+
+ // Blocking call to cancel the wait. Any callbacks already in progress will
+ // finish before we return from this call.
+ if (!UnregisterWaitEx(wait_object_, INVALID_HANDLE_VALUE)) {
+ NOTREACHED() << "UnregisterWaitEx failed: " << GetLastError();
+ return false;
+ }
+
+ weak_factory_.InvalidateWeakPtrs();
+ object_ = NULL;
+ wait_object_ = NULL;
+
+ MessageLoop::current()->RemoveDestructionObserver(this);
+ return true;
+}
+
+HANDLE ObjectWatcher::GetWatchedObject() {
+ return object_;
+}
+
+// static
+void CALLBACK ObjectWatcher::DoneWaiting(void* param, BOOLEAN timed_out) {
+ DCHECK(!timed_out);
+
+ // The destructor blocks on any callbacks that are in flight, so we know that
+ // that is always a pointer to a valid ObjectWater.
+ ObjectWatcher* that = static_cast<ObjectWatcher*>(param);
+ that->origin_loop_->PostTask(FROM_HERE, that->callback_);
+ that->callback_.Reset();
+}
+
+void ObjectWatcher::Signal(Delegate* delegate) {
+ // Signaling the delegate may result in our destruction or a nested call to
+ // StartWatching(). As a result, we save any state we need and clear previous
+ // watcher state before signaling the delegate.
+ HANDLE object = object_;
+ StopWatching();
+ delegate->OnObjectSignaled(object);
+}
+
+void ObjectWatcher::WillDestroyCurrentMessageLoop() {
+ // Need to shutdown the watch so that we don't try to access the MessageLoop
+ // after this point.
+ StopWatching();
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/object_watcher.h b/src/base/win/object_watcher.h
new file mode 100644
index 0000000..742f2b0
--- /dev/null
+++ b/src/base/win/object_watcher.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_OBJECT_WATCHER_H_
+#define BASE_WIN_OBJECT_WATCHER_H_
+
+#include <windows.h>
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop.h"
+
+namespace base {
+namespace win {
+
+// A class that provides a means to asynchronously wait for a Windows object to
+// become signaled. It is an abstraction around RegisterWaitForSingleObject
+// that provides a notification callback, OnObjectSignaled, that runs back on
+// the origin thread (i.e., the thread that called StartWatching).
+//
+// This class acts like a smart pointer such that when it goes out-of-scope,
+// UnregisterWaitEx is automatically called, and any in-flight notification is
+// suppressed.
+//
+// Typical usage:
+//
+// class MyClass : public base::ObjectWatcher::Delegate {
+// public:
+// void DoStuffWhenSignaled(HANDLE object) {
+// watcher_.StartWatching(object, this);
+// }
+// virtual void OnObjectSignaled(HANDLE object) {
+// // OK, time to do stuff!
+// }
+// private:
+// base::ObjectWatcher watcher_;
+// };
+//
+// In the above example, MyClass wants to "do stuff" when object becomes
+// signaled. ObjectWatcher makes this task easy. When MyClass goes out of
+// scope, the watcher_ will be destroyed, and there is no need to worry about
+// OnObjectSignaled being called on a deleted MyClass pointer. Easy!
+// If the object is already signaled before being watched, OnObjectSignaled is
+// still called after (but not necessarily immediately after) watch is started.
+//
+class BASE_EXPORT ObjectWatcher : public MessageLoop::DestructionObserver {
+ public:
+ class BASE_EXPORT Delegate {
+ public:
+ virtual ~Delegate() {}
+ // Called from the MessageLoop when a signaled object is detected. To
+ // continue watching the object, StartWatching must be called again.
+ virtual void OnObjectSignaled(HANDLE object) = 0;
+ };
+
+ ObjectWatcher();
+ ~ObjectWatcher();
+
+ // When the object is signaled, the given delegate is notified on the thread
+ // where StartWatching is called. The ObjectWatcher is not responsible for
+ // deleting the delegate.
+ //
+ // Returns true if the watch was started. Otherwise, false is returned.
+ //
+ bool StartWatching(HANDLE object, Delegate* delegate);
+
+ // Stops watching. Does nothing if the watch has already completed. If the
+ // watch is still active, then it is canceled, and the associated delegate is
+ // not notified.
+ //
+ // Returns true if the watch was canceled. Otherwise, false is returned.
+ //
+ bool StopWatching();
+
+ // Returns the handle of the object being watched, or NULL if the object
+ // watcher is stopped.
+ HANDLE GetWatchedObject();
+
+ private:
+ // Called on a background thread when done waiting.
+ static void CALLBACK DoneWaiting(void* param, BOOLEAN timed_out);
+
+ void Signal(Delegate* delegate);
+
+ // MessageLoop::DestructionObserver implementation:
+ virtual void WillDestroyCurrentMessageLoop();
+
+ // Internal state.
+ WeakPtrFactory<ObjectWatcher> weak_factory_;
+ Closure callback_;
+ HANDLE object_; // The object being watched
+ HANDLE wait_object_; // Returned by RegisterWaitForSingleObject
+ MessageLoop* origin_loop_; // Used to get back to the origin thread
+
+ DISALLOW_COPY_AND_ASSIGN(ObjectWatcher);
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_OBJECT_WATCHER_H_
diff --git a/src/base/win/object_watcher_unittest.cc b/src/base/win/object_watcher_unittest.cc
new file mode 100644
index 0000000..e8484b9
--- /dev/null
+++ b/src/base/win/object_watcher_unittest.cc
@@ -0,0 +1,172 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <process.h>
+
+#include "base/message_loop.h"
+#include "base/win/object_watcher.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace win {
+
+namespace {
+
+class QuitDelegate : public ObjectWatcher::Delegate {
+ public:
+ virtual void OnObjectSignaled(HANDLE object) {
+ MessageLoop::current()->Quit();
+ }
+};
+
+class DecrementCountDelegate : public ObjectWatcher::Delegate {
+ public:
+ explicit DecrementCountDelegate(int* counter) : counter_(counter) {
+ }
+ virtual void OnObjectSignaled(HANDLE object) {
+ --(*counter_);
+ }
+ private:
+ int* counter_;
+};
+
+void RunTest_BasicSignal(MessageLoop::Type message_loop_type) {
+ MessageLoop message_loop(message_loop_type);
+
+ ObjectWatcher watcher;
+ EXPECT_EQ(NULL, watcher.GetWatchedObject());
+
+ // A manual-reset event that is not yet signaled.
+ HANDLE event = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ QuitDelegate delegate;
+ bool ok = watcher.StartWatching(event, &delegate);
+ EXPECT_TRUE(ok);
+ EXPECT_EQ(event, watcher.GetWatchedObject());
+
+ SetEvent(event);
+
+ MessageLoop::current()->Run();
+
+ EXPECT_EQ(NULL, watcher.GetWatchedObject());
+ CloseHandle(event);
+}
+
+void RunTest_BasicCancel(MessageLoop::Type message_loop_type) {
+ MessageLoop message_loop(message_loop_type);
+
+ ObjectWatcher watcher;
+
+ // A manual-reset event that is not yet signaled.
+ HANDLE event = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ QuitDelegate delegate;
+ bool ok = watcher.StartWatching(event, &delegate);
+ EXPECT_TRUE(ok);
+
+ watcher.StopWatching();
+
+ CloseHandle(event);
+}
+
+void RunTest_CancelAfterSet(MessageLoop::Type message_loop_type) {
+ MessageLoop message_loop(message_loop_type);
+
+ ObjectWatcher watcher;
+
+ int counter = 1;
+ DecrementCountDelegate delegate(&counter);
+
+ // A manual-reset event that is not yet signaled.
+ HANDLE event = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ bool ok = watcher.StartWatching(event, &delegate);
+ EXPECT_TRUE(ok);
+
+ SetEvent(event);
+
+ // Let the background thread do its business
+ Sleep(30);
+
+ watcher.StopWatching();
+
+ MessageLoop::current()->RunUntilIdle();
+
+ // Our delegate should not have fired.
+ EXPECT_EQ(1, counter);
+
+ CloseHandle(event);
+}
+
+void RunTest_SignalBeforeWatch(MessageLoop::Type message_loop_type) {
+ MessageLoop message_loop(message_loop_type);
+
+ ObjectWatcher watcher;
+
+ // A manual-reset event that is signaled before we begin watching.
+ HANDLE event = CreateEvent(NULL, TRUE, TRUE, NULL);
+
+ QuitDelegate delegate;
+ bool ok = watcher.StartWatching(event, &delegate);
+ EXPECT_TRUE(ok);
+
+ MessageLoop::current()->Run();
+
+ EXPECT_EQ(NULL, watcher.GetWatchedObject());
+ CloseHandle(event);
+}
+
+void RunTest_OutlivesMessageLoop(MessageLoop::Type message_loop_type) {
+ // Simulate a MessageLoop that dies before an ObjectWatcher. This ordinarily
+ // doesn't happen when people use the Thread class, but it can happen when
+ // people use the Singleton pattern or atexit.
+ HANDLE event = CreateEvent(NULL, TRUE, FALSE, NULL); // not signaled
+ {
+ ObjectWatcher watcher;
+ {
+ MessageLoop message_loop(message_loop_type);
+
+ QuitDelegate delegate;
+ watcher.StartWatching(event, &delegate);
+ }
+ }
+ CloseHandle(event);
+}
+
+} // namespace
+
+//-----------------------------------------------------------------------------
+
+TEST(ObjectWatcherTest, BasicSignal) {
+ RunTest_BasicSignal(MessageLoop::TYPE_DEFAULT);
+ RunTest_BasicSignal(MessageLoop::TYPE_IO);
+ RunTest_BasicSignal(MessageLoop::TYPE_UI);
+}
+
+TEST(ObjectWatcherTest, BasicCancel) {
+ RunTest_BasicCancel(MessageLoop::TYPE_DEFAULT);
+ RunTest_BasicCancel(MessageLoop::TYPE_IO);
+ RunTest_BasicCancel(MessageLoop::TYPE_UI);
+}
+
+TEST(ObjectWatcherTest, CancelAfterSet) {
+ RunTest_CancelAfterSet(MessageLoop::TYPE_DEFAULT);
+ RunTest_CancelAfterSet(MessageLoop::TYPE_IO);
+ RunTest_CancelAfterSet(MessageLoop::TYPE_UI);
+}
+
+TEST(ObjectWatcherTest, SignalBeforeWatch) {
+ RunTest_SignalBeforeWatch(MessageLoop::TYPE_DEFAULT);
+ RunTest_SignalBeforeWatch(MessageLoop::TYPE_IO);
+ RunTest_SignalBeforeWatch(MessageLoop::TYPE_UI);
+}
+
+TEST(ObjectWatcherTest, OutlivesMessageLoop) {
+ RunTest_OutlivesMessageLoop(MessageLoop::TYPE_DEFAULT);
+ RunTest_OutlivesMessageLoop(MessageLoop::TYPE_IO);
+ RunTest_OutlivesMessageLoop(MessageLoop::TYPE_UI);
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/pe_image.cc b/src/base/win/pe_image.cc
new file mode 100644
index 0000000..fcf03c1
--- /dev/null
+++ b/src/base/win/pe_image.cc
@@ -0,0 +1,570 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file implements PEImage, a generic class to manipulate PE files.
+// This file was adapted from GreenBorder's Code.
+
+#include "base/win/pe_image.h"
+
+namespace base {
+namespace win {
+
+#if defined(_WIN64) && !defined(NACL_WIN64)
+// TODO(rvargas): Bug 27218. Make sure this is ok.
+#error This code is not tested on x64. Please make sure all the base unit tests\
+ pass before doing any real work. The current unit tests don't test the\
+ differences between 32- and 64-bits implementations. Bugs may slip through.\
+ You need to improve the coverage before continuing.
+#endif
+
+// Structure to perform imports enumerations.
+struct EnumAllImportsStorage {
+ PEImage::EnumImportsFunction callback;
+ PVOID cookie;
+};
+
+namespace {
+
+ // Compare two strings byte by byte on an unsigned basis.
+ // if s1 == s2, return 0
+ // if s1 < s2, return negative
+ // if s1 > s2, return positive
+ // Exception if inputs are invalid.
+ int StrCmpByByte(LPCSTR s1, LPCSTR s2) {
+ while (*s1 != '\0' && *s1 == *s2) {
+ ++s1;
+ ++s2;
+ }
+
+ return (*reinterpret_cast<const unsigned char*>(s1) -
+ *reinterpret_cast<const unsigned char*>(s2));
+ }
+
+} // namespace
+
+// Callback used to enumerate imports. See EnumImportChunksFunction.
+bool ProcessImportChunk(const PEImage &image, LPCSTR module,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat, PVOID cookie) {
+ EnumAllImportsStorage &storage = *reinterpret_cast<EnumAllImportsStorage*>(
+ cookie);
+
+ return image.EnumOneImportChunk(storage.callback, module, name_table, iat,
+ storage.cookie);
+}
+
+// Callback used to enumerate delay imports. See EnumDelayImportChunksFunction.
+bool ProcessDelayImportChunk(const PEImage &image,
+ PImgDelayDescr delay_descriptor,
+ LPCSTR module, PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat, PIMAGE_THUNK_DATA bound_iat,
+ PIMAGE_THUNK_DATA unload_iat, PVOID cookie) {
+ EnumAllImportsStorage &storage = *reinterpret_cast<EnumAllImportsStorage*>(
+ cookie);
+
+ return image.EnumOneDelayImportChunk(storage.callback, delay_descriptor,
+ module, name_table, iat, bound_iat,
+ unload_iat, storage.cookie);
+}
+
+void PEImage::set_module(HMODULE module) {
+ module_ = module;
+}
+
+PIMAGE_DOS_HEADER PEImage::GetDosHeader() const {
+ return reinterpret_cast<PIMAGE_DOS_HEADER>(module_);
+}
+
+PIMAGE_NT_HEADERS PEImage::GetNTHeaders() const {
+ PIMAGE_DOS_HEADER dos_header = GetDosHeader();
+
+ return reinterpret_cast<PIMAGE_NT_HEADERS>(
+ reinterpret_cast<char*>(dos_header) + dos_header->e_lfanew);
+}
+
+PIMAGE_SECTION_HEADER PEImage::GetSectionHeader(UINT section) const {
+ PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
+ PIMAGE_SECTION_HEADER first_section = IMAGE_FIRST_SECTION(nt_headers);
+
+ if (section < nt_headers->FileHeader.NumberOfSections)
+ return first_section + section;
+ else
+ return NULL;
+}
+
+WORD PEImage::GetNumSections() const {
+ return GetNTHeaders()->FileHeader.NumberOfSections;
+}
+
+DWORD PEImage::GetImageDirectoryEntrySize(UINT directory) const {
+ PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
+
+ return nt_headers->OptionalHeader.DataDirectory[directory].Size;
+}
+
+PVOID PEImage::GetImageDirectoryEntryAddr(UINT directory) const {
+ PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
+
+ return RVAToAddr(
+ nt_headers->OptionalHeader.DataDirectory[directory].VirtualAddress);
+}
+
+PIMAGE_SECTION_HEADER PEImage::GetImageSectionFromAddr(PVOID address) const {
+ PBYTE target = reinterpret_cast<PBYTE>(address);
+ PIMAGE_SECTION_HEADER section;
+
+ for (UINT i = 0; NULL != (section = GetSectionHeader(i)); i++) {
+ // Don't use the virtual RVAToAddr.
+ PBYTE start = reinterpret_cast<PBYTE>(
+ PEImage::RVAToAddr(section->VirtualAddress));
+
+ DWORD size = section->Misc.VirtualSize;
+
+ if ((start <= target) && (start + size > target))
+ return section;
+ }
+
+ return NULL;
+}
+
+PIMAGE_SECTION_HEADER PEImage::GetImageSectionHeaderByName(
+ LPCSTR section_name) const {
+ if (NULL == section_name)
+ return NULL;
+
+ PIMAGE_SECTION_HEADER ret = NULL;
+ int num_sections = GetNumSections();
+
+ for (int i = 0; i < num_sections; i++) {
+ PIMAGE_SECTION_HEADER section = GetSectionHeader(i);
+ if (0 == _strnicmp(reinterpret_cast<LPCSTR>(section->Name), section_name,
+ sizeof(section->Name))) {
+ ret = section;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+PDWORD PEImage::GetExportEntry(LPCSTR name) const {
+ PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
+
+ if (NULL == exports)
+ return NULL;
+
+ WORD ordinal = 0;
+ if (!GetProcOrdinal(name, &ordinal))
+ return NULL;
+
+ PDWORD functions = reinterpret_cast<PDWORD>(
+ RVAToAddr(exports->AddressOfFunctions));
+
+ return functions + ordinal - exports->Base;
+}
+
+FARPROC PEImage::GetProcAddress(LPCSTR function_name) const {
+ PDWORD export_entry = GetExportEntry(function_name);
+ if (NULL == export_entry)
+ return NULL;
+
+ PBYTE function = reinterpret_cast<PBYTE>(RVAToAddr(*export_entry));
+
+ PBYTE exports = reinterpret_cast<PBYTE>(
+ GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT));
+ DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
+
+ // Check for forwarded exports as a special case.
+ if (exports <= function && exports + size > function)
+#pragma warning(push)
+#pragma warning(disable: 4312)
+ // This cast generates a warning because it is 32 bit specific.
+ return reinterpret_cast<FARPROC>(0xFFFFFFFF);
+#pragma warning(pop)
+
+ return reinterpret_cast<FARPROC>(function);
+}
+
+bool PEImage::GetProcOrdinal(LPCSTR function_name, WORD *ordinal) const {
+ if (NULL == ordinal)
+ return false;
+
+ PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
+
+ if (NULL == exports)
+ return false;
+
+ if (IsOrdinal(function_name)) {
+ *ordinal = ToOrdinal(function_name);
+ } else {
+ PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
+ PDWORD lower = names;
+ PDWORD upper = names + exports->NumberOfNames;
+ int cmp = -1;
+
+ // Binary Search for the name.
+ while (lower != upper) {
+ PDWORD middle = lower + (upper - lower) / 2;
+ LPCSTR name = reinterpret_cast<LPCSTR>(RVAToAddr(*middle));
+
+ // This may be called by sandbox before MSVCRT dll loads, so can't use
+ // CRT function here.
+ cmp = StrCmpByByte(function_name, name);
+
+ if (cmp == 0) {
+ lower = middle;
+ break;
+ }
+
+ if (cmp > 0)
+ lower = middle + 1;
+ else
+ upper = middle;
+ }
+
+ if (cmp != 0)
+ return false;
+
+
+ PWORD ordinals = reinterpret_cast<PWORD>(
+ RVAToAddr(exports->AddressOfNameOrdinals));
+
+ *ordinal = ordinals[lower - names] + static_cast<WORD>(exports->Base);
+ }
+
+ return true;
+}
+
+bool PEImage::EnumSections(EnumSectionsFunction callback, PVOID cookie) const {
+ PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
+ UINT num_sections = nt_headers->FileHeader.NumberOfSections;
+ PIMAGE_SECTION_HEADER section = GetSectionHeader(0);
+
+ for (UINT i = 0; i < num_sections; i++, section++) {
+ PVOID section_start = RVAToAddr(section->VirtualAddress);
+ DWORD size = section->Misc.VirtualSize;
+
+ if (!callback(*this, section, section_start, size, cookie))
+ return false;
+ }
+
+ return true;
+}
+
+bool PEImage::EnumExports(EnumExportsFunction callback, PVOID cookie) const {
+ PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT);
+ DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
+
+ // Check if there are any exports at all.
+ if (NULL == directory || 0 == size)
+ return true;
+
+ PIMAGE_EXPORT_DIRECTORY exports = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(
+ directory);
+ UINT ordinal_base = exports->Base;
+ UINT num_funcs = exports->NumberOfFunctions;
+ UINT num_names = exports->NumberOfNames;
+ PDWORD functions = reinterpret_cast<PDWORD>(RVAToAddr(
+ exports->AddressOfFunctions));
+ PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
+ PWORD ordinals = reinterpret_cast<PWORD>(RVAToAddr(
+ exports->AddressOfNameOrdinals));
+
+ for (UINT count = 0; count < num_funcs; count++) {
+ PVOID func = RVAToAddr(functions[count]);
+ if (NULL == func)
+ continue;
+
+ // Check for a name.
+ LPCSTR name = NULL;
+ UINT hint;
+ for (hint = 0; hint < num_names; hint++) {
+ if (ordinals[hint] == count) {
+ name = reinterpret_cast<LPCSTR>(RVAToAddr(names[hint]));
+ break;
+ }
+ }
+
+ if (name == NULL)
+ hint = 0;
+
+ // Check for forwarded exports.
+ LPCSTR forward = NULL;
+ if (reinterpret_cast<char*>(func) >= reinterpret_cast<char*>(directory) &&
+ reinterpret_cast<char*>(func) <= reinterpret_cast<char*>(directory) +
+ size) {
+ forward = reinterpret_cast<LPCSTR>(func);
+ func = 0;
+ }
+
+ if (!callback(*this, ordinal_base + count, hint, name, func, forward,
+ cookie))
+ return false;
+ }
+
+ return true;
+}
+
+bool PEImage::EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const {
+ PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_BASERELOC);
+ DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_BASERELOC);
+ PIMAGE_BASE_RELOCATION base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
+ directory);
+
+ if (directory == NULL || size < sizeof(IMAGE_BASE_RELOCATION))
+ return true;
+
+ while (base->SizeOfBlock) {
+ PWORD reloc = reinterpret_cast<PWORD>(base + 1);
+ UINT num_relocs = (base->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) /
+ sizeof(WORD);
+
+ for (UINT i = 0; i < num_relocs; i++, reloc++) {
+ WORD type = *reloc >> 12;
+ PVOID address = RVAToAddr(base->VirtualAddress + (*reloc & 0x0FFF));
+
+ if (!callback(*this, type, address, cookie))
+ return false;
+ }
+
+ base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
+ reinterpret_cast<char*>(base) + base->SizeOfBlock);
+ }
+
+ return true;
+}
+
+bool PEImage::EnumImportChunks(EnumImportChunksFunction callback,
+ PVOID cookie) const {
+ DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT);
+ PIMAGE_IMPORT_DESCRIPTOR import = GetFirstImportChunk();
+
+ if (import == NULL || size < sizeof(IMAGE_IMPORT_DESCRIPTOR))
+ return true;
+
+ for (; import->FirstThunk; import++) {
+ LPCSTR module_name = reinterpret_cast<LPCSTR>(RVAToAddr(import->Name));
+ PIMAGE_THUNK_DATA name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ RVAToAddr(import->OriginalFirstThunk));
+ PIMAGE_THUNK_DATA iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ RVAToAddr(import->FirstThunk));
+
+ if (!callback(*this, module_name, name_table, iat, cookie))
+ return false;
+ }
+
+ return true;
+}
+
+bool PEImage::EnumOneImportChunk(EnumImportsFunction callback,
+ LPCSTR module_name,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat, PVOID cookie) const {
+ if (NULL == name_table)
+ return false;
+
+ for (; name_table && name_table->u1.Ordinal; name_table++, iat++) {
+ LPCSTR name = NULL;
+ WORD ordinal = 0;
+ WORD hint = 0;
+
+ if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
+ ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
+ } else {
+ PIMAGE_IMPORT_BY_NAME import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
+ RVAToAddr(name_table->u1.ForwarderString));
+
+ hint = import->Hint;
+ name = reinterpret_cast<LPCSTR>(&import->Name);
+ }
+
+ if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
+ return false;
+ }
+
+ return true;
+}
+
+bool PEImage::EnumAllImports(EnumImportsFunction callback, PVOID cookie) const {
+ EnumAllImportsStorage temp = { callback, cookie };
+ return EnumImportChunks(ProcessImportChunk, &temp);
+}
+
+bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback,
+ PVOID cookie) const {
+ PVOID directory = GetImageDirectoryEntryAddr(
+ IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
+ DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
+ PImgDelayDescr delay_descriptor = reinterpret_cast<PImgDelayDescr>(directory);
+
+ if (directory == NULL || size == 0)
+ return true;
+
+ for (; delay_descriptor->rvaHmod; delay_descriptor++) {
+ PIMAGE_THUNK_DATA name_table;
+ PIMAGE_THUNK_DATA iat;
+ PIMAGE_THUNK_DATA bound_iat; // address of the optional bound IAT
+ PIMAGE_THUNK_DATA unload_iat; // address of optional copy of original IAT
+ LPCSTR module_name;
+
+ // check if VC7-style imports, using RVAs instead of
+ // VC6-style addresses.
+ bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
+
+ if (rvas) {
+ module_name = reinterpret_cast<LPCSTR>(
+ RVAToAddr(delay_descriptor->rvaDLLName));
+ name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ RVAToAddr(delay_descriptor->rvaINT));
+ iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ RVAToAddr(delay_descriptor->rvaIAT));
+ bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ RVAToAddr(delay_descriptor->rvaBoundIAT));
+ unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ RVAToAddr(delay_descriptor->rvaUnloadIAT));
+ } else {
+#pragma warning(push)
+#pragma warning(disable: 4312)
+ // These casts generate warnings because they are 32 bit specific.
+ module_name = reinterpret_cast<LPCSTR>(delay_descriptor->rvaDLLName);
+ name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ delay_descriptor->rvaINT);
+ iat = reinterpret_cast<PIMAGE_THUNK_DATA>(delay_descriptor->rvaIAT);
+ bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ delay_descriptor->rvaBoundIAT);
+ unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ delay_descriptor->rvaUnloadIAT);
+#pragma warning(pop)
+ }
+
+ if (!callback(*this, delay_descriptor, module_name, name_table, iat,
+ bound_iat, unload_iat, cookie))
+ return false;
+ }
+
+ return true;
+}
+
+bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback,
+ PImgDelayDescr delay_descriptor,
+ LPCSTR module_name,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat,
+ PIMAGE_THUNK_DATA bound_iat,
+ PIMAGE_THUNK_DATA unload_iat,
+ PVOID cookie) const {
+ UNREFERENCED_PARAMETER(bound_iat);
+ UNREFERENCED_PARAMETER(unload_iat);
+
+ for (; name_table->u1.Ordinal; name_table++, iat++) {
+ LPCSTR name = NULL;
+ WORD ordinal = 0;
+ WORD hint = 0;
+
+ if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
+ ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
+ } else {
+ PIMAGE_IMPORT_BY_NAME import;
+ bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
+
+ if (rvas) {
+ import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
+ RVAToAddr(name_table->u1.ForwarderString));
+ } else {
+#pragma warning(push)
+#pragma warning(disable: 4312)
+ // This cast generates a warning because it is 32 bit specific.
+ import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
+ name_table->u1.ForwarderString);
+#pragma warning(pop)
+ }
+
+ hint = import->Hint;
+ name = reinterpret_cast<LPCSTR>(&import->Name);
+ }
+
+ if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
+ return false;
+ }
+
+ return true;
+}
+
+bool PEImage::EnumAllDelayImports(EnumImportsFunction callback,
+ PVOID cookie) const {
+ EnumAllImportsStorage temp = { callback, cookie };
+ return EnumDelayImportChunks(ProcessDelayImportChunk, &temp);
+}
+
+bool PEImage::VerifyMagic() const {
+ PIMAGE_DOS_HEADER dos_header = GetDosHeader();
+
+ if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
+ return false;
+
+ PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
+
+ if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
+ return false;
+
+ if (nt_headers->FileHeader.SizeOfOptionalHeader !=
+ sizeof(IMAGE_OPTIONAL_HEADER))
+ return false;
+
+ if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
+ return false;
+
+ return true;
+}
+
+bool PEImage::ImageRVAToOnDiskOffset(DWORD rva, DWORD *on_disk_offset) const {
+ LPVOID address = RVAToAddr(rva);
+ return ImageAddrToOnDiskOffset(address, on_disk_offset);
+}
+
+bool PEImage::ImageAddrToOnDiskOffset(LPVOID address,
+ DWORD *on_disk_offset) const {
+ if (NULL == address)
+ return false;
+
+ // Get the section that this address belongs to.
+ PIMAGE_SECTION_HEADER section_header = GetImageSectionFromAddr(address);
+ if (NULL == section_header)
+ return false;
+
+#pragma warning(push)
+#pragma warning(disable: 4311)
+ // These casts generate warnings because they are 32 bit specific.
+ // Don't follow the virtual RVAToAddr, use the one on the base.
+ DWORD offset_within_section = reinterpret_cast<DWORD>(address) -
+ reinterpret_cast<DWORD>(PEImage::RVAToAddr(
+ section_header->VirtualAddress));
+#pragma warning(pop)
+
+ *on_disk_offset = section_header->PointerToRawData + offset_within_section;
+ return true;
+}
+
+PVOID PEImage::RVAToAddr(DWORD rva) const {
+ if (rva == 0)
+ return NULL;
+
+ return reinterpret_cast<char*>(module_) + rva;
+}
+
+PVOID PEImageAsData::RVAToAddr(DWORD rva) const {
+ if (rva == 0)
+ return NULL;
+
+ PVOID in_memory = PEImage::RVAToAddr(rva);
+ DWORD disk_offset;
+
+ if (!ImageAddrToOnDiskOffset(in_memory, &disk_offset))
+ return NULL;
+
+ return PEImage::RVAToAddr(disk_offset);
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/pe_image.h b/src/base/win/pe_image.h
new file mode 100644
index 0000000..878ef52
--- /dev/null
+++ b/src/base/win/pe_image.h
@@ -0,0 +1,268 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file was adapted from GreenBorder's Code.
+// To understand what this class is about (for other than well known functions
+// as GetProcAddress), a good starting point is "An In-Depth Look into the
+// Win32 Portable Executable File Format" by Matt Pietrek:
+// http://msdn.microsoft.com/msdnmag/issues/02/02/PE/default.aspx
+
+#ifndef BASE_WIN_PE_IMAGE_H_
+#define BASE_WIN_PE_IMAGE_H_
+
+#include <windows.h>
+
+#if defined(_WIN32_WINNT_WIN8)
+// The Windows 8 SDK defines FACILITY_VISUALCPP in winerror.h.
+#undef FACILITY_VISUALCPP
+#endif
+#include <DelayIMP.h>
+
+namespace base {
+namespace win {
+
+// This class is a wrapper for the Portable Executable File Format (PE).
+// It's main purpose is to provide an easy way to work with imports and exports
+// from a file, mapped in memory as image.
+class PEImage {
+ public:
+ // Callback to enumerate sections.
+ // cookie is the value passed to the enumerate method.
+ // Returns true to continue the enumeration.
+ typedef bool (*EnumSectionsFunction)(const PEImage &image,
+ PIMAGE_SECTION_HEADER header,
+ PVOID section_start, DWORD section_size,
+ PVOID cookie);
+
+ // Callback to enumerate exports.
+ // function is the actual address of the symbol. If forward is not null, it
+ // contains the dll and symbol to forward this export to. cookie is the value
+ // passed to the enumerate method.
+ // Returns true to continue the enumeration.
+ typedef bool (*EnumExportsFunction)(const PEImage &image, DWORD ordinal,
+ DWORD hint, LPCSTR name, PVOID function,
+ LPCSTR forward, PVOID cookie);
+
+ // Callback to enumerate import blocks.
+ // name_table and iat point to the imports name table and address table for
+ // this block. cookie is the value passed to the enumerate method.
+ // Returns true to continue the enumeration.
+ typedef bool (*EnumImportChunksFunction)(const PEImage &image, LPCSTR module,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat, PVOID cookie);
+
+ // Callback to enumerate imports.
+ // module is the dll that exports this symbol. cookie is the value passed to
+ // the enumerate method.
+ // Returns true to continue the enumeration.
+ typedef bool (*EnumImportsFunction)(const PEImage &image, LPCSTR module,
+ DWORD ordinal, LPCSTR name, DWORD hint,
+ PIMAGE_THUNK_DATA iat, PVOID cookie);
+
+ // Callback to enumerate dalayed import blocks.
+ // module is the dll that exports this block of symbols. cookie is the value
+ // passed to the enumerate method.
+ // Returns true to continue the enumeration.
+ typedef bool (*EnumDelayImportChunksFunction)(const PEImage &image,
+ PImgDelayDescr delay_descriptor,
+ LPCSTR module,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat,
+ PIMAGE_THUNK_DATA bound_iat,
+ PIMAGE_THUNK_DATA unload_iat,
+ PVOID cookie);
+
+ // Callback to enumerate relocations.
+ // cookie is the value passed to the enumerate method.
+ // Returns true to continue the enumeration.
+ typedef bool (*EnumRelocsFunction)(const PEImage &image, WORD type,
+ PVOID address, PVOID cookie);
+
+ explicit PEImage(HMODULE module) : module_(module) {}
+ explicit PEImage(const void* module) {
+ module_ = reinterpret_cast<HMODULE>(const_cast<void*>(module));
+ }
+
+ // Gets the HMODULE for this object.
+ HMODULE module() const;
+
+ // Sets this object's HMODULE.
+ void set_module(HMODULE module);
+
+ // Checks if this symbol is actually an ordinal.
+ static bool IsOrdinal(LPCSTR name);
+
+ // Converts a named symbol to the corresponding ordinal.
+ static WORD ToOrdinal(LPCSTR name);
+
+ // Returns the DOS_HEADER for this PE.
+ PIMAGE_DOS_HEADER GetDosHeader() const;
+
+ // Returns the NT_HEADER for this PE.
+ PIMAGE_NT_HEADERS GetNTHeaders() const;
+
+ // Returns number of sections of this PE.
+ WORD GetNumSections() const;
+
+ // Returns the header for a given section.
+ // returns NULL if there is no such section.
+ PIMAGE_SECTION_HEADER GetSectionHeader(UINT section) const;
+
+ // Returns the size of a given directory entry.
+ DWORD GetImageDirectoryEntrySize(UINT directory) const;
+
+ // Returns the address of a given directory entry.
+ PVOID GetImageDirectoryEntryAddr(UINT directory) const;
+
+ // Returns the section header for a given address.
+ // Use: s = image.GetImageSectionFromAddr(a);
+ // Post: 's' is the section header of the section that contains 'a'
+ // or NULL if there is no such section.
+ PIMAGE_SECTION_HEADER GetImageSectionFromAddr(PVOID address) const;
+
+ // Returns the section header for a given section.
+ PIMAGE_SECTION_HEADER GetImageSectionHeaderByName(LPCSTR section_name) const;
+
+ // Returns the first block of imports.
+ PIMAGE_IMPORT_DESCRIPTOR GetFirstImportChunk() const;
+
+ // Returns the exports directory.
+ PIMAGE_EXPORT_DIRECTORY GetExportDirectory() const;
+
+ // Returns a given export entry.
+ // Use: e = image.GetExportEntry(f);
+ // Pre: 'f' is either a zero terminated string or ordinal
+ // Post: 'e' is a pointer to the export directory entry
+ // that contains 'f's export RVA, or NULL if 'f'
+ // is not exported from this image
+ PDWORD GetExportEntry(LPCSTR name) const;
+
+ // Returns the address for a given exported symbol.
+ // Use: p = image.GetProcAddress(f);
+ // Pre: 'f' is either a zero terminated string or ordinal.
+ // Post: if 'f' is a non-forwarded export from image, 'p' is
+ // the exported function. If 'f' is a forwarded export
+ // then p is the special value 0xFFFFFFFF. In this case
+ // RVAToAddr(*GetExportEntry) can be used to resolve
+ // the string that describes the forward.
+ FARPROC GetProcAddress(LPCSTR function_name) const;
+
+ // Retrieves the ordinal for a given exported symbol.
+ // Returns true if the symbol was found.
+ bool GetProcOrdinal(LPCSTR function_name, WORD *ordinal) const;
+
+ // Enumerates PE sections.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumSections(EnumSectionsFunction callback, PVOID cookie) const;
+
+ // Enumerates PE exports.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumExports(EnumExportsFunction callback, PVOID cookie) const;
+
+ // Enumerates PE imports.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumAllImports(EnumImportsFunction callback, PVOID cookie) const;
+
+ // Enumerates PE import blocks.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumImportChunks(EnumImportChunksFunction callback, PVOID cookie) const;
+
+ // Enumerates the imports from a single PE import block.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumOneImportChunk(EnumImportsFunction callback, LPCSTR module_name,
+ PIMAGE_THUNK_DATA name_table, PIMAGE_THUNK_DATA iat,
+ PVOID cookie) const;
+
+
+ // Enumerates PE delay imports.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumAllDelayImports(EnumImportsFunction callback, PVOID cookie) const;
+
+ // Enumerates PE delay import blocks.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumDelayImportChunks(EnumDelayImportChunksFunction callback,
+ PVOID cookie) const;
+
+ // Enumerates imports from a single PE delay import block.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumOneDelayImportChunk(EnumImportsFunction callback,
+ PImgDelayDescr delay_descriptor,
+ LPCSTR module_name,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat,
+ PIMAGE_THUNK_DATA bound_iat,
+ PIMAGE_THUNK_DATA unload_iat,
+ PVOID cookie) const;
+
+ // Enumerates PE relocation entries.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const;
+
+ // Verifies the magic values on the PE file.
+ // Returns true if all values are correct.
+ bool VerifyMagic() const;
+
+ // Converts an rva value to the appropriate address.
+ virtual PVOID RVAToAddr(DWORD rva) const;
+
+ // Converts an rva value to an offset on disk.
+ // Returns true on success.
+ bool ImageRVAToOnDiskOffset(DWORD rva, DWORD *on_disk_offset) const;
+
+ // Converts an address to an offset on disk.
+ // Returns true on success.
+ bool ImageAddrToOnDiskOffset(LPVOID address, DWORD *on_disk_offset) const;
+
+ private:
+ HMODULE module_;
+};
+
+// This class is an extension to the PEImage class that allows working with PE
+// files mapped as data instead of as image file.
+class PEImageAsData : public PEImage {
+ public:
+ explicit PEImageAsData(HMODULE hModule) : PEImage(hModule) {}
+
+ virtual PVOID RVAToAddr(DWORD rva) const;
+};
+
+inline bool PEImage::IsOrdinal(LPCSTR name) {
+#pragma warning(push)
+#pragma warning(disable: 4311)
+ // This cast generates a warning because it is 32 bit specific.
+ return reinterpret_cast<DWORD>(name) <= 0xFFFF;
+#pragma warning(pop)
+}
+
+inline WORD PEImage::ToOrdinal(LPCSTR name) {
+ return reinterpret_cast<WORD>(name);
+}
+
+inline HMODULE PEImage::module() const {
+ return module_;
+}
+
+inline PIMAGE_IMPORT_DESCRIPTOR PEImage::GetFirstImportChunk() const {
+ return reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(
+ GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_IMPORT));
+}
+
+inline PIMAGE_EXPORT_DIRECTORY PEImage::GetExportDirectory() const {
+ return reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(
+ GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT));
+}
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_PE_IMAGE_H_
diff --git a/src/base/win/pe_image_unittest.cc b/src/base/win/pe_image_unittest.cc
new file mode 100644
index 0000000..e308eae
--- /dev/null
+++ b/src/base/win/pe_image_unittest.cc
@@ -0,0 +1,256 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains unit tests for PEImage.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/win/pe_image.h"
+#include "base/win/windows_version.h"
+
+namespace base {
+namespace win {
+
+// Just counts the number of invocations.
+bool ExportsCallback(const PEImage &image,
+ DWORD ordinal,
+ DWORD hint,
+ LPCSTR name,
+ PVOID function,
+ LPCSTR forward,
+ PVOID cookie) {
+ int* count = reinterpret_cast<int*>(cookie);
+ (*count)++;
+ return true;
+}
+
+// Just counts the number of invocations.
+bool ImportsCallback(const PEImage &image,
+ LPCSTR module,
+ DWORD ordinal,
+ LPCSTR name,
+ DWORD hint,
+ PIMAGE_THUNK_DATA iat,
+ PVOID cookie) {
+ int* count = reinterpret_cast<int*>(cookie);
+ (*count)++;
+ return true;
+}
+
+// Just counts the number of invocations.
+bool SectionsCallback(const PEImage &image,
+ PIMAGE_SECTION_HEADER header,
+ PVOID section_start,
+ DWORD section_size,
+ PVOID cookie) {
+ int* count = reinterpret_cast<int*>(cookie);
+ (*count)++;
+ return true;
+}
+
+// Just counts the number of invocations.
+bool RelocsCallback(const PEImage &image,
+ WORD type,
+ PVOID address,
+ PVOID cookie) {
+ int* count = reinterpret_cast<int*>(cookie);
+ (*count)++;
+ return true;
+}
+
+// Just counts the number of invocations.
+bool ImportChunksCallback(const PEImage &image,
+ LPCSTR module,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat,
+ PVOID cookie) {
+ int* count = reinterpret_cast<int*>(cookie);
+ (*count)++;
+ return true;
+}
+
+// Just counts the number of invocations.
+bool DelayImportChunksCallback(const PEImage &image,
+ PImgDelayDescr delay_descriptor,
+ LPCSTR module,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat,
+ PIMAGE_THUNK_DATA bound_iat,
+ PIMAGE_THUNK_DATA unload_iat,
+ PVOID cookie) {
+ int* count = reinterpret_cast<int*>(cookie);
+ (*count)++;
+ return true;
+}
+
+// Identifiers for the set of supported expectations.
+enum ExpectationSet {
+ WIN_2K_SET,
+ WIN_XP_SET,
+ WIN_VISTA_SET,
+ WIN_7_SET,
+ WIN_8_SET,
+ UNSUPPORTED_SET,
+};
+
+// We'll be using some known values for the tests.
+enum Value {
+ sections = 0,
+ imports_dlls,
+ delay_dlls,
+ exports,
+ imports,
+ delay_imports,
+ relocs
+};
+
+ExpectationSet GetExpectationSet(DWORD os) {
+ if (os == 50)
+ return WIN_2K_SET;
+ if (os == 51)
+ return WIN_XP_SET;
+ if (os == 60)
+ return WIN_VISTA_SET;
+ if (os == 61)
+ return WIN_7_SET;
+ if (os >= 62)
+ return WIN_8_SET;
+ return UNSUPPORTED_SET;
+}
+
+// Retrieves the expected value from advapi32.dll based on the OS.
+int GetExpectedValue(Value value, DWORD os) {
+ const int xp_delay_dlls = 2;
+ const int xp_exports = 675;
+ const int xp_imports = 422;
+ const int xp_delay_imports = 8;
+ const int xp_relocs = 9180;
+ const int vista_delay_dlls = 4;
+ const int vista_exports = 799;
+ const int vista_imports = 476;
+ const int vista_delay_imports = 24;
+ const int vista_relocs = 10188;
+ const int w2k_delay_dlls = 0;
+ const int w2k_exports = 566;
+ const int w2k_imports = 357;
+ const int w2k_delay_imports = 0;
+ const int w2k_relocs = 7388;
+ const int win7_delay_dlls = 7;
+ const int win7_exports = 806;
+ const int win7_imports = 568;
+ const int win7_delay_imports = 71;
+ const int win7_relocs = 7812;
+ const int win8_delay_dlls = 9;
+ const int win8_exports = 806;
+ const int win8_imports = 568;
+ const int win8_delay_imports = 113;
+ const int win8_relocs = 9478;
+ int win8_sections = 4;
+ int win8_import_dlls = 17;
+
+ base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
+ if (os_info->architecture() == base::win::OSInfo::X86_ARCHITECTURE) {
+ win8_sections = 5;
+ win8_import_dlls = 19;
+ }
+
+ // Contains the expected value, for each enumerated property (Value), and the
+ // OS version: [Value][os_version]
+ const int expected[][5] = {
+ {4, 4, 4, 4, win8_sections},
+ {3, 3, 3, 13, win8_import_dlls},
+ {w2k_delay_dlls, xp_delay_dlls, vista_delay_dlls, win7_delay_dlls,
+ win8_delay_dlls},
+ {w2k_exports, xp_exports, vista_exports, win7_exports, win8_exports},
+ {w2k_imports, xp_imports, vista_imports, win7_imports, win8_imports},
+ {w2k_delay_imports, xp_delay_imports,
+ vista_delay_imports, win7_delay_imports, win8_delay_imports},
+ {w2k_relocs, xp_relocs, vista_relocs, win7_relocs, win8_relocs}
+ };
+ COMPILE_ASSERT(arraysize(expected[0]) == UNSUPPORTED_SET,
+ expected_value_set_mismatch);
+
+ if (value > relocs)
+ return 0;
+ ExpectationSet expected_set = GetExpectationSet(os);
+ if (expected_set >= arraysize(expected)) {
+ // This should never happen. Log a failure if it does.
+ EXPECT_NE(UNSUPPORTED_SET, expected_set);
+ expected_set = WIN_2K_SET;
+ }
+
+ return expected[value][expected_set];
+}
+
+// Tests that we are able to enumerate stuff from a PE file, and that
+// the actual number of items found is within the expected range.
+TEST(PEImageTest, EnumeratesPE) {
+ HMODULE module = LoadLibrary(L"advapi32.dll");
+ ASSERT_TRUE(NULL != module);
+
+ PEImage pe(module);
+ int count = 0;
+ EXPECT_TRUE(pe.VerifyMagic());
+
+ DWORD os = pe.GetNTHeaders()->OptionalHeader.MajorOperatingSystemVersion;
+ os = os * 10 + pe.GetNTHeaders()->OptionalHeader.MinorOperatingSystemVersion;
+
+ // Skip this test for unsupported OS versions.
+ if (GetExpectationSet(os) == UNSUPPORTED_SET)
+ return;
+
+ pe.EnumSections(SectionsCallback, &count);
+ EXPECT_EQ(GetExpectedValue(sections, os), count);
+
+ count = 0;
+ pe.EnumImportChunks(ImportChunksCallback, &count);
+ EXPECT_EQ(GetExpectedValue(imports_dlls, os), count);
+
+ count = 0;
+ pe.EnumDelayImportChunks(DelayImportChunksCallback, &count);
+ EXPECT_EQ(GetExpectedValue(delay_dlls, os), count);
+
+ count = 0;
+ pe.EnumExports(ExportsCallback, &count);
+ EXPECT_GT(count, GetExpectedValue(exports, os) - 20);
+ EXPECT_LT(count, GetExpectedValue(exports, os) + 100);
+
+ count = 0;
+ pe.EnumAllImports(ImportsCallback, &count);
+ EXPECT_GT(count, GetExpectedValue(imports, os) - 20);
+ EXPECT_LT(count, GetExpectedValue(imports, os) + 100);
+
+ count = 0;
+ pe.EnumAllDelayImports(ImportsCallback, &count);
+ EXPECT_GT(count, GetExpectedValue(delay_imports, os) - 2);
+ EXPECT_LT(count, GetExpectedValue(delay_imports, os) + 8);
+
+ count = 0;
+ pe.EnumRelocs(RelocsCallback, &count);
+ EXPECT_GT(count, GetExpectedValue(relocs, os) - 150);
+ EXPECT_LT(count, GetExpectedValue(relocs, os) + 1500);
+
+ FreeLibrary(module);
+}
+
+// Tests that we can locate an specific exported symbol, by name and by ordinal.
+TEST(PEImageTest, RetrievesExports) {
+ HMODULE module = LoadLibrary(L"advapi32.dll");
+ ASSERT_TRUE(NULL != module);
+
+ PEImage pe(module);
+ WORD ordinal;
+
+ EXPECT_TRUE(pe.GetProcOrdinal("RegEnumKeyExW", &ordinal));
+
+ FARPROC address1 = pe.GetProcAddress("RegEnumKeyExW");
+ FARPROC address2 = pe.GetProcAddress(reinterpret_cast<char*>(ordinal));
+ EXPECT_TRUE(address1 != NULL);
+ EXPECT_TRUE(address2 != NULL);
+ EXPECT_TRUE(address1 == address2);
+
+ FreeLibrary(module);
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/registry.cc b/src/base/win/registry.cc
new file mode 100644
index 0000000..83eb590
--- /dev/null
+++ b/src/base/win/registry.cc
@@ -0,0 +1,464 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/registry.h"
+
+#include <shlwapi.h>
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/threading/thread_restrictions.h"
+
+#pragma comment(lib, "shlwapi.lib") // for SHDeleteKey
+
+namespace base {
+namespace win {
+
+namespace {
+
+// RegEnumValue() reports the number of characters from the name that were
+// written to the buffer, not how many there are. This constant is the maximum
+// name size, such that a buffer with this size should read any name.
+const DWORD MAX_REGISTRY_NAME_SIZE = 16384;
+
+// Registry values are read as BYTE* but can have wchar_t* data whose last
+// wchar_t is truncated. This function converts the reported |byte_size| to
+// a size in wchar_t that can store a truncated wchar_t if necessary.
+inline DWORD to_wchar_size(DWORD byte_size) {
+ return (byte_size + sizeof(wchar_t) - 1) / sizeof(wchar_t);
+}
+
+} // namespace
+
+// RegKey ----------------------------------------------------------------------
+
+RegKey::RegKey()
+ : key_(NULL),
+ watch_event_(0) {
+}
+
+RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access)
+ : key_(NULL),
+ watch_event_(0) {
+ if (rootkey) {
+ if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK))
+ Create(rootkey, subkey, access);
+ else
+ Open(rootkey, subkey, access);
+ } else {
+ DCHECK(!subkey);
+ }
+}
+
+RegKey::~RegKey() {
+ Close();
+}
+
+LONG RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
+ DWORD disposition_value;
+ return CreateWithDisposition(rootkey, subkey, &disposition_value, access);
+}
+
+LONG RegKey::CreateWithDisposition(HKEY rootkey, const wchar_t* subkey,
+ DWORD* disposition, REGSAM access) {
+ DCHECK(rootkey && subkey && access && disposition);
+ Close();
+
+ LONG result = RegCreateKeyEx(rootkey, subkey, 0, NULL,
+ REG_OPTION_NON_VOLATILE, access, NULL, &key_,
+ disposition);
+ return result;
+}
+
+LONG RegKey::CreateKey(const wchar_t* name, REGSAM access) {
+ DCHECK(name && access);
+ HKEY subkey = NULL;
+ LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE,
+ access, NULL, &subkey, NULL);
+ Close();
+
+ key_ = subkey;
+ return result;
+}
+
+LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
+ DCHECK(rootkey && subkey && access);
+ Close();
+
+ LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &key_);
+ return result;
+}
+
+LONG RegKey::OpenKey(const wchar_t* relative_key_name, REGSAM access) {
+ DCHECK(relative_key_name && access);
+ HKEY subkey = NULL;
+ LONG result = RegOpenKeyEx(key_, relative_key_name, 0, access, &subkey);
+
+ // We have to close the current opened key before replacing it with the new
+ // one.
+ Close();
+
+ key_ = subkey;
+ return result;
+}
+
+void RegKey::Close() {
+ StopWatching();
+ if (key_) {
+ ::RegCloseKey(key_);
+ key_ = NULL;
+ }
+}
+
+bool RegKey::HasValue(const wchar_t* name) const {
+ return RegQueryValueEx(key_, name, 0, NULL, NULL, NULL) == ERROR_SUCCESS;
+}
+
+DWORD RegKey::GetValueCount() const {
+ DWORD count = 0;
+ LONG result = RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
+ NULL, NULL, NULL, NULL);
+ return (result == ERROR_SUCCESS) ? count : 0;
+}
+
+LONG RegKey::GetValueNameAt(int index, std::wstring* name) const {
+ wchar_t buf[256];
+ DWORD bufsize = arraysize(buf);
+ LONG r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, NULL, NULL);
+ if (r == ERROR_SUCCESS)
+ *name = buf;
+
+ return r;
+}
+
+LONG RegKey::DeleteKey(const wchar_t* name) {
+ DCHECK(key_);
+ DCHECK(name);
+ LONG result = SHDeleteKey(key_, name);
+ return result;
+}
+
+LONG RegKey::DeleteValue(const wchar_t* value_name) {
+ DCHECK(key_);
+ LONG result = RegDeleteValue(key_, value_name);
+ return result;
+}
+
+LONG RegKey::ReadValueDW(const wchar_t* name, DWORD* out_value) const {
+ DCHECK(out_value);
+ DWORD type = REG_DWORD;
+ DWORD size = sizeof(DWORD);
+ DWORD local_value = 0;
+ LONG result = ReadValue(name, &local_value, &size, &type);
+ if (result == ERROR_SUCCESS) {
+ if ((type == REG_DWORD || type == REG_BINARY) && size == sizeof(DWORD))
+ *out_value = local_value;
+ else
+ result = ERROR_CANTREAD;
+ }
+
+ return result;
+}
+
+LONG RegKey::ReadInt64(const wchar_t* name, int64* out_value) const {
+ DCHECK(out_value);
+ DWORD type = REG_QWORD;
+ int64 local_value = 0;
+ DWORD size = sizeof(local_value);
+ LONG result = ReadValue(name, &local_value, &size, &type);
+ if (result == ERROR_SUCCESS) {
+ if ((type == REG_QWORD || type == REG_BINARY) &&
+ size == sizeof(local_value))
+ *out_value = local_value;
+ else
+ result = ERROR_CANTREAD;
+ }
+
+ return result;
+}
+
+LONG RegKey::ReadValue(const wchar_t* name, std::wstring* out_value) const {
+ DCHECK(out_value);
+ const size_t kMaxStringLength = 1024; // This is after expansion.
+ // Use the one of the other forms of ReadValue if 1024 is too small for you.
+ wchar_t raw_value[kMaxStringLength];
+ DWORD type = REG_SZ, size = sizeof(raw_value);
+ LONG result = ReadValue(name, raw_value, &size, &type);
+ if (result == ERROR_SUCCESS) {
+ if (type == REG_SZ) {
+ *out_value = raw_value;
+ } else if (type == REG_EXPAND_SZ) {
+ wchar_t expanded[kMaxStringLength];
+ size = ExpandEnvironmentStrings(raw_value, expanded, kMaxStringLength);
+ // Success: returns the number of wchar_t's copied
+ // Fail: buffer too small, returns the size required
+ // Fail: other, returns 0
+ if (size == 0 || size > kMaxStringLength) {
+ result = ERROR_MORE_DATA;
+ } else {
+ *out_value = expanded;
+ }
+ } else {
+ // Not a string. Oops.
+ result = ERROR_CANTREAD;
+ }
+ }
+
+ return result;
+}
+
+LONG RegKey::ReadValue(const wchar_t* name,
+ void* data,
+ DWORD* dsize,
+ DWORD* dtype) const {
+ LONG result = RegQueryValueEx(key_, name, 0, dtype,
+ reinterpret_cast<LPBYTE>(data), dsize);
+ return result;
+}
+
+LONG RegKey::ReadValues(const wchar_t* name,
+ std::vector<std::wstring>* values) {
+ values->clear();
+
+ DWORD type = REG_MULTI_SZ;
+ DWORD size = 0;
+ LONG result = ReadValue(name, NULL, &size, &type);
+ if (FAILED(result) || size == 0)
+ return result;
+
+ if (type != REG_MULTI_SZ)
+ return ERROR_CANTREAD;
+
+ std::vector<wchar_t> buffer(size / sizeof(wchar_t));
+ result = ReadValue(name, &buffer[0], &size, NULL);
+ if (FAILED(result) || size == 0)
+ return result;
+
+ // Parse the double-null-terminated list of strings.
+ // Note: This code is paranoid to not read outside of |buf|, in the case where
+ // it may not be properly terminated.
+ const wchar_t* entry = &buffer[0];
+ const wchar_t* buffer_end = entry + (size / sizeof(wchar_t));
+ while (entry < buffer_end && entry[0] != '\0') {
+ const wchar_t* entry_end = std::find(entry, buffer_end, L'\0');
+ values->push_back(std::wstring(entry, entry_end));
+ entry = entry_end + 1;
+ }
+ return 0;
+}
+
+LONG RegKey::WriteValue(const wchar_t* name, DWORD in_value) {
+ return WriteValue(
+ name, &in_value, static_cast<DWORD>(sizeof(in_value)), REG_DWORD);
+}
+
+LONG RegKey::WriteValue(const wchar_t * name, const wchar_t* in_value) {
+ return WriteValue(name, in_value,
+ static_cast<DWORD>(sizeof(*in_value) * (wcslen(in_value) + 1)), REG_SZ);
+}
+
+LONG RegKey::WriteValue(const wchar_t* name,
+ const void* data,
+ DWORD dsize,
+ DWORD dtype) {
+ DCHECK(data || !dsize);
+
+ LONG result = RegSetValueEx(key_, name, 0, dtype,
+ reinterpret_cast<LPBYTE>(const_cast<void*>(data)), dsize);
+ return result;
+}
+
+LONG RegKey::StartWatching() {
+ DCHECK(key_);
+ if (!watch_event_)
+ watch_event_ = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ DWORD filter = REG_NOTIFY_CHANGE_NAME |
+ REG_NOTIFY_CHANGE_ATTRIBUTES |
+ REG_NOTIFY_CHANGE_LAST_SET |
+ REG_NOTIFY_CHANGE_SECURITY;
+
+ // Watch the registry key for a change of value.
+ LONG result = RegNotifyChangeKeyValue(key_, TRUE, filter, watch_event_, TRUE);
+ if (result != ERROR_SUCCESS) {
+ CloseHandle(watch_event_);
+ watch_event_ = 0;
+ }
+
+ return result;
+}
+
+bool RegKey::HasChanged() {
+ if (watch_event_) {
+ if (WaitForSingleObject(watch_event_, 0) == WAIT_OBJECT_0) {
+ StartWatching();
+ return true;
+ }
+ }
+ return false;
+}
+
+LONG RegKey::StopWatching() {
+ LONG result = ERROR_INVALID_HANDLE;
+ if (watch_event_) {
+ CloseHandle(watch_event_);
+ watch_event_ = 0;
+ result = ERROR_SUCCESS;
+ }
+ return result;
+}
+
+// RegistryValueIterator ------------------------------------------------------
+
+RegistryValueIterator::RegistryValueIterator(HKEY root_key,
+ const wchar_t* folder_key)
+ : name_(MAX_PATH, L'\0'),
+ value_(MAX_PATH, L'\0') {
+ LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
+ if (result != ERROR_SUCCESS) {
+ key_ = NULL;
+ } else {
+ DWORD count = 0;
+ result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
+ NULL, NULL, NULL, NULL);
+
+ if (result != ERROR_SUCCESS) {
+ ::RegCloseKey(key_);
+ key_ = NULL;
+ } else {
+ index_ = count - 1;
+ }
+ }
+
+ Read();
+}
+
+RegistryValueIterator::~RegistryValueIterator() {
+ if (key_)
+ ::RegCloseKey(key_);
+}
+
+DWORD RegistryValueIterator::ValueCount() const {
+ DWORD count = 0;
+ LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL,
+ &count, NULL, NULL, NULL, NULL);
+ if (result != ERROR_SUCCESS)
+ return 0;
+
+ return count;
+}
+
+bool RegistryValueIterator::Valid() const {
+ return key_ != NULL && index_ >= 0;
+}
+
+void RegistryValueIterator::operator++() {
+ --index_;
+ Read();
+}
+
+bool RegistryValueIterator::Read() {
+ if (Valid()) {
+ DWORD capacity = static_cast<DWORD>(name_.capacity());
+ DWORD name_size = capacity;
+ // |value_size_| is in bytes. Reserve the last character for a NUL.
+ value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t));
+ LONG result = ::RegEnumValue(
+ key_, index_, WriteInto(&name_, name_size), &name_size, NULL, &type_,
+ reinterpret_cast<BYTE*>(vector_as_array(&value_)), &value_size_);
+
+ if (result == ERROR_MORE_DATA) {
+ // Registry key names are limited to 255 characters and fit within
+ // MAX_PATH (which is 260) but registry value names can use up to 16,383
+ // characters and the value itself is not limited
+ // (from http://msdn.microsoft.com/en-us/library/windows/desktop/
+ // ms724872(v=vs.85).aspx).
+ // Resize the buffers and retry if their size caused the failure.
+ DWORD value_size_in_wchars = to_wchar_size(value_size_);
+ if (value_size_in_wchars + 1 > value_.size())
+ value_.resize(value_size_in_wchars + 1, L'\0');
+ value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t));
+ name_size = name_size == capacity ? MAX_REGISTRY_NAME_SIZE : capacity;
+ result = ::RegEnumValue(
+ key_, index_, WriteInto(&name_, name_size), &name_size, NULL, &type_,
+ reinterpret_cast<BYTE*>(vector_as_array(&value_)), &value_size_);
+ }
+
+ if (result == ERROR_SUCCESS) {
+ DCHECK_LT(to_wchar_size(value_size_), value_.size());
+ value_[to_wchar_size(value_size_)] = L'\0';
+ return true;
+ }
+ }
+
+ name_[0] = L'\0';
+ value_[0] = L'\0';
+ value_size_ = 0;
+ return false;
+}
+
+// RegistryKeyIterator --------------------------------------------------------
+
+RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
+ const wchar_t* folder_key) {
+ LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
+ if (result != ERROR_SUCCESS) {
+ key_ = NULL;
+ } else {
+ DWORD count = 0;
+ LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL);
+
+ if (result != ERROR_SUCCESS) {
+ ::RegCloseKey(key_);
+ key_ = NULL;
+ } else {
+ index_ = count - 1;
+ }
+ }
+
+ Read();
+}
+
+RegistryKeyIterator::~RegistryKeyIterator() {
+ if (key_)
+ ::RegCloseKey(key_);
+}
+
+DWORD RegistryKeyIterator::SubkeyCount() const {
+ DWORD count = 0;
+ LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL);
+ if (result != ERROR_SUCCESS)
+ return 0;
+
+ return count;
+}
+
+bool RegistryKeyIterator::Valid() const {
+ return key_ != NULL && index_ >= 0;
+}
+
+void RegistryKeyIterator::operator++() {
+ --index_;
+ Read();
+}
+
+bool RegistryKeyIterator::Read() {
+ if (Valid()) {
+ DWORD ncount = arraysize(name_);
+ FILETIME written;
+ LONG r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL,
+ NULL, &written);
+ if (ERROR_SUCCESS == r)
+ return true;
+ }
+
+ name_[0] = '\0';
+ return false;
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/registry.h b/src/base/win/registry.h
new file mode 100644
index 0000000..7a3d970
--- /dev/null
+++ b/src/base/win/registry.h
@@ -0,0 +1,212 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_REGISTRY_H_
+#define BASE_WIN_REGISTRY_H_
+
+#include <windows.h>
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/stl_util.h"
+
+namespace base {
+namespace win {
+
+// Utility class to read, write and manipulate the Windows Registry.
+// Registry vocabulary primer: a "key" is like a folder, in which there
+// are "values", which are <name, data> pairs, with an associated data type.
+//
+// Note:
+// ReadValue family of functions guarantee that the return arguments
+// are not touched in case of failure.
+class BASE_EXPORT RegKey {
+ public:
+ RegKey();
+ RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access);
+ ~RegKey();
+
+ LONG Create(HKEY rootkey, const wchar_t* subkey, REGSAM access);
+
+ LONG CreateWithDisposition(HKEY rootkey, const wchar_t* subkey,
+ DWORD* disposition, REGSAM access);
+
+ // Creates a subkey or open it if it already exists.
+ LONG CreateKey(const wchar_t* name, REGSAM access);
+
+ // Opens an existing reg key.
+ LONG Open(HKEY rootkey, const wchar_t* subkey, REGSAM access);
+
+ // Opens an existing reg key, given the relative key name.
+ LONG OpenKey(const wchar_t* relative_key_name, REGSAM access);
+
+ // Closes this reg key.
+ void Close();
+
+ // Returns false if this key does not have the specified value, of if an error
+ // occurrs while attempting to access it.
+ bool HasValue(const wchar_t* value_name) const;
+
+ // Returns the number of values for this key, of 0 if the number cannot be
+ // determined.
+ DWORD GetValueCount() const;
+
+ // Determine the nth value's name.
+ LONG GetValueNameAt(int index, std::wstring* name) const;
+
+ // True while the key is valid.
+ bool Valid() const { return key_ != NULL; }
+
+ // Kill a key and everything that live below it; please be careful when using
+ // it.
+ LONG DeleteKey(const wchar_t* name);
+
+ // Deletes a single value within the key.
+ LONG DeleteValue(const wchar_t* name);
+
+ // Getters:
+
+ // Returns an int32 value. If |name| is NULL or empty, returns the default
+ // value, if any.
+ LONG ReadValueDW(const wchar_t* name, DWORD* out_value) const;
+
+ // Returns an int64 value. If |name| is NULL or empty, returns the default
+ // value, if any.
+ LONG ReadInt64(const wchar_t* name, int64* out_value) const;
+
+ // Returns a string value. If |name| is NULL or empty, returns the default
+ // value, if any.
+ LONG ReadValue(const wchar_t* name, std::wstring* out_value) const;
+
+ // Reads a REG_MULTI_SZ registry field into a vector of strings. Clears
+ // |values| initially and adds further strings to the list. Returns
+ // ERROR_CANTREAD if type is not REG_MULTI_SZ.
+ LONG ReadValues(const wchar_t* name, std::vector<std::wstring>* values);
+
+ // Returns raw data. If |name| is NULL or empty, returns the default
+ // value, if any.
+ LONG ReadValue(const wchar_t* name,
+ void* data,
+ DWORD* dsize,
+ DWORD* dtype) const;
+
+ // Setters:
+
+ // Sets an int32 value.
+ LONG WriteValue(const wchar_t* name, DWORD in_value);
+
+ // Sets a string value.
+ LONG WriteValue(const wchar_t* name, const wchar_t* in_value);
+
+ // Sets raw data, including type.
+ LONG WriteValue(const wchar_t* name,
+ const void* data,
+ DWORD dsize,
+ DWORD dtype);
+
+ // Starts watching the key to see if any of its values have changed.
+ // The key must have been opened with the KEY_NOTIFY access privilege.
+ LONG StartWatching();
+
+ // If StartWatching hasn't been called, always returns false.
+ // Otherwise, returns true if anything under the key has changed.
+ // This can't be const because the |watch_event_| may be refreshed.
+ bool HasChanged();
+
+ // Will automatically be called by destructor if not manually called
+ // beforehand. Returns true if it was watching, false otherwise.
+ LONG StopWatching();
+
+ inline bool IsWatching() const { return watch_event_ != 0; }
+ HANDLE watch_event() const { return watch_event_; }
+ HKEY Handle() const { return key_; }
+
+ private:
+ HKEY key_; // The registry key being iterated.
+ HANDLE watch_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(RegKey);
+};
+
+// Iterates the entries found in a particular folder on the registry.
+class BASE_EXPORT RegistryValueIterator {
+ public:
+ RegistryValueIterator(HKEY root_key, const wchar_t* folder_key);
+
+ ~RegistryValueIterator();
+
+ DWORD ValueCount() const;
+
+ // True while the iterator is valid.
+ bool Valid() const;
+
+ // Advances to the next registry entry.
+ void operator++();
+
+ const wchar_t* Name() const { return name_.c_str(); }
+ const wchar_t* Value() const { return vector_as_array(&value_); }
+ // ValueSize() is in bytes.
+ DWORD ValueSize() const { return value_size_; }
+ DWORD Type() const { return type_; }
+
+ int Index() const { return index_; }
+
+ private:
+ // Read in the current values.
+ bool Read();
+
+ // The registry key being iterated.
+ HKEY key_;
+
+ // Current index of the iteration.
+ int index_;
+
+ // Current values.
+ std::wstring name_;
+ std::vector<wchar_t> value_;
+ DWORD value_size_;
+ DWORD type_;
+
+ DISALLOW_COPY_AND_ASSIGN(RegistryValueIterator);
+};
+
+class BASE_EXPORT RegistryKeyIterator {
+ public:
+ RegistryKeyIterator(HKEY root_key, const wchar_t* folder_key);
+
+ ~RegistryKeyIterator();
+
+ DWORD SubkeyCount() const;
+
+ // True while the iterator is valid.
+ bool Valid() const;
+
+ // Advances to the next entry in the folder.
+ void operator++();
+
+ const wchar_t* Name() const { return name_; }
+
+ int Index() const { return index_; }
+
+ private:
+ // Read in the current values.
+ bool Read();
+
+ // The registry key being iterated.
+ HKEY key_;
+
+ // Current index of the iteration.
+ int index_;
+
+ wchar_t name_[MAX_PATH];
+
+ DISALLOW_COPY_AND_ASSIGN(RegistryKeyIterator);
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_REGISTRY_H_
diff --git a/src/base/win/registry_unittest.cc b/src/base/win/registry_unittest.cc
new file mode 100644
index 0000000..155402a
--- /dev/null
+++ b/src/base/win/registry_unittest.cc
@@ -0,0 +1,164 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/registry.h"
+
+#include <cstring>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/stl_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace win {
+
+namespace {
+
+const wchar_t kRootKey[] = L"Base_Registry_Unittest";
+
+class RegistryTest : public testing::Test {
+ public:
+ RegistryTest() {}
+
+ protected:
+ virtual void SetUp() OVERRIDE {
+ // Create a temporary key.
+ RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS);
+ key.DeleteKey(kRootKey);
+ ASSERT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ));
+ ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, kRootKey, KEY_READ));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ // Clean up the temporary key.
+ RegKey key(HKEY_CURRENT_USER, L"", KEY_SET_VALUE);
+ ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RegistryTest);
+};
+
+TEST_F(RegistryTest, ValueTest) {
+ RegKey key;
+
+ std::wstring foo_key(kRootKey);
+ foo_key += L"\\Foo";
+ ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(),
+ KEY_READ));
+
+ {
+ ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, foo_key.c_str(),
+ KEY_READ | KEY_SET_VALUE));
+ ASSERT_TRUE(key.Valid());
+
+ const wchar_t kStringValueName[] = L"StringValue";
+ const wchar_t kDWORDValueName[] = L"DWORDValue";
+ const wchar_t kInt64ValueName[] = L"Int64Value";
+ const wchar_t kStringData[] = L"string data";
+ const DWORD kDWORDData = 0xdeadbabe;
+ const int64 kInt64Data = 0xdeadbabedeadbabeLL;
+
+ // Test value creation
+ ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(kStringValueName, kStringData));
+ ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(kDWORDValueName, kDWORDData));
+ ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(kInt64ValueName, &kInt64Data,
+ sizeof(kInt64Data), REG_QWORD));
+ EXPECT_EQ(3U, key.GetValueCount());
+ EXPECT_TRUE(key.HasValue(kStringValueName));
+ EXPECT_TRUE(key.HasValue(kDWORDValueName));
+ EXPECT_TRUE(key.HasValue(kInt64ValueName));
+
+ // Test Read
+ std::wstring string_value;
+ DWORD dword_value = 0;
+ int64 int64_value = 0;
+ ASSERT_EQ(ERROR_SUCCESS, key.ReadValue(kStringValueName, &string_value));
+ ASSERT_EQ(ERROR_SUCCESS, key.ReadValueDW(kDWORDValueName, &dword_value));
+ ASSERT_EQ(ERROR_SUCCESS, key.ReadInt64(kInt64ValueName, &int64_value));
+ EXPECT_STREQ(kStringData, string_value.c_str());
+ EXPECT_EQ(kDWORDData, dword_value);
+ EXPECT_EQ(kInt64Data, int64_value);
+
+ // Make sure out args are not touched if ReadValue fails
+ const wchar_t* kNonExistent = L"NonExistent";
+ ASSERT_NE(ERROR_SUCCESS, key.ReadValue(kNonExistent, &string_value));
+ ASSERT_NE(ERROR_SUCCESS, key.ReadValueDW(kNonExistent, &dword_value));
+ ASSERT_NE(ERROR_SUCCESS, key.ReadInt64(kNonExistent, &int64_value));
+ EXPECT_STREQ(kStringData, string_value.c_str());
+ EXPECT_EQ(kDWORDData, dword_value);
+ EXPECT_EQ(kInt64Data, int64_value);
+
+ // Test delete
+ ASSERT_EQ(ERROR_SUCCESS, key.DeleteValue(kStringValueName));
+ ASSERT_EQ(ERROR_SUCCESS, key.DeleteValue(kDWORDValueName));
+ ASSERT_EQ(ERROR_SUCCESS, key.DeleteValue(kInt64ValueName));
+ EXPECT_EQ(0U, key.GetValueCount());
+ EXPECT_FALSE(key.HasValue(kStringValueName));
+ EXPECT_FALSE(key.HasValue(kDWORDValueName));
+ EXPECT_FALSE(key.HasValue(kInt64ValueName));
+ }
+}
+
+TEST_F(RegistryTest, BigValueIteratorTest) {
+ RegKey key;
+ std::wstring foo_key(kRootKey);
+ foo_key += L"\\Foo";
+ ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(),
+ KEY_READ));
+ ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, foo_key.c_str(),
+ KEY_READ | KEY_SET_VALUE));
+ ASSERT_TRUE(key.Valid());
+
+ // Create a test value that is larger than MAX_PATH.
+ std::wstring data(MAX_PATH * 2, L'a');
+
+ ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(data.c_str(), data.c_str()));
+
+ RegistryValueIterator iterator(HKEY_CURRENT_USER, foo_key.c_str());
+ ASSERT_TRUE(iterator.Valid());
+ EXPECT_STREQ(data.c_str(), iterator.Name());
+ EXPECT_STREQ(data.c_str(), iterator.Value());
+ // ValueSize() is in bytes, including NUL.
+ EXPECT_EQ((MAX_PATH * 2 + 1) * sizeof(wchar_t), iterator.ValueSize());
+ ++iterator;
+ EXPECT_FALSE(iterator.Valid());
+}
+
+TEST_F(RegistryTest, TruncatedCharTest) {
+ RegKey key;
+ std::wstring foo_key(kRootKey);
+ foo_key += L"\\Foo";
+ ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(),
+ KEY_READ));
+ ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, foo_key.c_str(),
+ KEY_READ | KEY_SET_VALUE));
+ ASSERT_TRUE(key.Valid());
+
+ const wchar_t kName[] = L"name";
+ // kData size is not a multiple of sizeof(wchar_t).
+ const uint8 kData[] = { 1, 2, 3, 4, 5 };
+ EXPECT_EQ(5, arraysize(kData));
+ ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(kName, kData,
+ arraysize(kData), REG_BINARY));
+
+ RegistryValueIterator iterator(HKEY_CURRENT_USER, foo_key.c_str());
+ ASSERT_TRUE(iterator.Valid());
+ EXPECT_STREQ(kName, iterator.Name());
+ // ValueSize() is in bytes.
+ ASSERT_EQ(arraysize(kData), iterator.ValueSize());
+ // Value() is NUL terminated.
+ int end = (iterator.ValueSize() + sizeof(wchar_t) - 1) / sizeof(wchar_t);
+ EXPECT_NE(L'\0', iterator.Value()[end-1]);
+ EXPECT_EQ(L'\0', iterator.Value()[end]);
+ EXPECT_EQ(0, std::memcmp(kData, iterator.Value(), arraysize(kData)));
+ ++iterator;
+ EXPECT_FALSE(iterator.Valid());
+}
+
+} // namespace
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/resource_util.cc b/src/base/win/resource_util.cc
new file mode 100644
index 0000000..de9f583
--- /dev/null
+++ b/src/base/win/resource_util.cc
@@ -0,0 +1,39 @@
+// 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"
+#include "base/win/resource_util.h"
+
+namespace base {
+namespace win {
+bool GetDataResourceFromModule(HMODULE module, int resource_id,
+ void** data, size_t* length) {
+ if (!module)
+ return false;
+
+ if (!IS_INTRESOURCE(resource_id)) {
+ NOTREACHED();
+ return false;
+ }
+
+ HRSRC hres_info = FindResource(module, MAKEINTRESOURCE(resource_id),
+ L"BINDATA");
+ if (NULL == hres_info)
+ return false;
+
+ DWORD data_size = SizeofResource(module, hres_info);
+ HGLOBAL hres = LoadResource(module, hres_info);
+ if (!hres)
+ return false;
+
+ void* resource = LockResource(hres);
+ if (!resource)
+ return false;
+
+ *data = resource;
+ *length = static_cast<size_t>(data_size);
+ return true;
+}
+} // namespace win
+} // namespace base
diff --git a/src/base/win/resource_util.h b/src/base/win/resource_util.h
new file mode 100644
index 0000000..9955402
--- /dev/null
+++ b/src/base/win/resource_util.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains utility functions for accessing resources in external
+// files (DLLs) or embedded in the executable itself.
+
+#ifndef BASE_WIN_RESOURCE_UTIL_H__
+#define BASE_WIN_RESOURCE_UTIL_H__
+
+#include <windows.h>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+namespace win {
+
+// Function for getting a data resource (BINDATA) from a dll. Some
+// resources are optional, especially in unit tests, so this returns false
+// but doesn't raise an error if the resource can't be loaded.
+bool BASE_EXPORT GetDataResourceFromModule(HMODULE module, int resource_id,
+ void** data, size_t* length);
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_RESOURCE_UTIL_H__
diff --git a/src/base/win/sampling_profiler.cc b/src/base/win/sampling_profiler.cc
new file mode 100644
index 0000000..150452c
--- /dev/null
+++ b/src/base/win/sampling_profiler.cc
@@ -0,0 +1,238 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/sampling_profiler.h"
+
+#include <winternl.h> // for NTSTATUS.
+
+#include "base/lazy_instance.h"
+
+// Copied from wdm.h in the WDK as we don't want to take
+// a dependency on the WDK.
+typedef enum _KPROFILE_SOURCE {
+ ProfileTime,
+ ProfileAlignmentFixup,
+ ProfileTotalIssues,
+ ProfilePipelineDry,
+ ProfileLoadInstructions,
+ ProfilePipelineFrozen,
+ ProfileBranchInstructions,
+ ProfileTotalNonissues,
+ ProfileDcacheMisses,
+ ProfileIcacheMisses,
+ ProfileCacheMisses,
+ ProfileBranchMispredictions,
+ ProfileStoreInstructions,
+ ProfileFpInstructions,
+ ProfileIntegerInstructions,
+ Profile2Issue,
+ Profile3Issue,
+ Profile4Issue,
+ ProfileSpecialInstructions,
+ ProfileTotalCycles,
+ ProfileIcacheIssues,
+ ProfileDcacheAccesses,
+ ProfileMemoryBarrierCycles,
+ ProfileLoadLinkedIssues,
+ ProfileMaximum
+} KPROFILE_SOURCE;
+
+
+namespace {
+
+// Signatures for the native functions we need to access the sampling profiler.
+typedef NTSTATUS (NTAPI *ZwSetIntervalProfileFunc)(ULONG, KPROFILE_SOURCE);
+typedef NTSTATUS (NTAPI *ZwQueryIntervalProfileFunc)(KPROFILE_SOURCE, PULONG);
+
+typedef NTSTATUS (NTAPI *ZwCreateProfileFunc)(PHANDLE profile,
+ HANDLE process,
+ PVOID code_start,
+ ULONG code_size,
+ ULONG eip_bucket_shift,
+ PULONG buckets,
+ ULONG buckets_byte_size,
+ KPROFILE_SOURCE source,
+ DWORD_PTR processor_mask);
+
+typedef NTSTATUS (NTAPI *ZwStartProfileFunc)(HANDLE);
+typedef NTSTATUS (NTAPI *ZwStopProfileFunc)(HANDLE);
+
+// This class is used to lazy-initialize pointers to the native
+// functions we need to access.
+class ProfilerFuncs {
+ public:
+ ProfilerFuncs();
+
+ ZwSetIntervalProfileFunc ZwSetIntervalProfile;
+ ZwQueryIntervalProfileFunc ZwQueryIntervalProfile;
+ ZwCreateProfileFunc ZwCreateProfile;
+ ZwStartProfileFunc ZwStartProfile;
+ ZwStopProfileFunc ZwStopProfile;
+
+ // True iff all of the function pointers above were successfully initialized.
+ bool initialized_;
+};
+
+ProfilerFuncs::ProfilerFuncs()
+ : ZwSetIntervalProfile(NULL),
+ ZwQueryIntervalProfile(NULL),
+ ZwCreateProfile(NULL),
+ ZwStartProfile(NULL),
+ ZwStopProfile(NULL),
+ initialized_(false) {
+ HMODULE ntdll = ::GetModuleHandle(L"ntdll.dll");
+ if (ntdll != NULL) {
+ ZwSetIntervalProfile = reinterpret_cast<ZwSetIntervalProfileFunc>(
+ ::GetProcAddress(ntdll, "ZwSetIntervalProfile"));
+ ZwQueryIntervalProfile = reinterpret_cast<ZwQueryIntervalProfileFunc>(
+ ::GetProcAddress(ntdll, "ZwQueryIntervalProfile"));
+ ZwCreateProfile = reinterpret_cast<ZwCreateProfileFunc>(
+ ::GetProcAddress(ntdll, "ZwCreateProfile"));
+ ZwStartProfile = reinterpret_cast<ZwStartProfileFunc>(
+ ::GetProcAddress(ntdll, "ZwStartProfile"));
+ ZwStopProfile = reinterpret_cast<ZwStopProfileFunc>(
+ ::GetProcAddress(ntdll, "ZwStopProfile"));
+
+ if (ZwSetIntervalProfile &&
+ ZwQueryIntervalProfile &&
+ ZwCreateProfile &&
+ ZwStartProfile &&
+ ZwStopProfile) {
+ initialized_ = true;
+ }
+ }
+}
+
+base::LazyInstance<ProfilerFuncs>::Leaky funcs = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+
+namespace base {
+namespace win {
+
+SamplingProfiler::SamplingProfiler() : is_started_(false) {
+}
+
+SamplingProfiler::~SamplingProfiler() {
+ if (is_started_) {
+ CHECK(Stop()) <<
+ "Unable to stop sampling profiler, this will cause memory corruption.";
+ }
+}
+
+bool SamplingProfiler::Initialize(HANDLE process,
+ void* start,
+ size_t size,
+ size_t log2_bucket_size) {
+ // You only get to initialize each instance once.
+ DCHECK(!profile_handle_.IsValid());
+ DCHECK(!is_started_);
+ DCHECK(start != NULL);
+ DCHECK_NE(0U, size);
+ DCHECK_LE(2, log2_bucket_size);
+ DCHECK_GE(32, log2_bucket_size);
+
+ // Bail if the native functions weren't found.
+ if (!funcs.Get().initialized_)
+ return false;
+
+ size_t bucket_size = 1 << log2_bucket_size;
+ size_t num_buckets = (size + bucket_size - 1) / bucket_size;
+ DCHECK(num_buckets != 0);
+ buckets_.resize(num_buckets);
+
+ // Get our affinity mask for the call below.
+ DWORD_PTR process_affinity = 0;
+ DWORD_PTR system_affinity = 0;
+ if (!::GetProcessAffinityMask(process, &process_affinity, &system_affinity)) {
+ LOG(ERROR) << "Failed to get process affinity mask.";
+ return false;
+ }
+
+ HANDLE profile = NULL;
+ NTSTATUS status =
+ funcs.Get().ZwCreateProfile(&profile,
+ process,
+ start,
+ static_cast<ULONG>(size),
+ static_cast<ULONG>(log2_bucket_size),
+ &buckets_[0],
+ static_cast<ULONG>(
+ sizeof(buckets_[0]) * num_buckets),
+ ProfileTime,
+ process_affinity);
+
+ if (!NT_SUCCESS(status)) {
+ // Might as well deallocate the buckets.
+ buckets_.resize(0);
+ LOG(ERROR) << "Failed to create profile, error 0x" << std::hex << status;
+ return false;
+ }
+
+ DCHECK(profile != NULL);
+ profile_handle_.Set(profile);
+
+ return true;
+}
+
+bool SamplingProfiler::Start() {
+ DCHECK(profile_handle_.IsValid());
+ DCHECK(!is_started_);
+ DCHECK(funcs.Get().initialized_);
+
+ NTSTATUS status = funcs.Get().ZwStartProfile(profile_handle_.Get());
+ if (!NT_SUCCESS(status))
+ return false;
+
+ is_started_ = true;
+
+ return true;
+}
+
+bool SamplingProfiler::Stop() {
+ DCHECK(profile_handle_.IsValid());
+ DCHECK(is_started_);
+ DCHECK(funcs.Get().initialized_);
+
+ NTSTATUS status = funcs.Get().ZwStopProfile(profile_handle_.Get());
+ if (!NT_SUCCESS(status))
+ return false;
+ is_started_ = false;
+
+ return true;
+}
+
+bool SamplingProfiler::SetSamplingInterval(base::TimeDelta sampling_interval) {
+ if (!funcs.Get().initialized_)
+ return false;
+
+ // According to Nebbet, the sampling interval is in units of 100ns.
+ ULONG interval = sampling_interval.InMicroseconds() * 10;
+ NTSTATUS status = funcs.Get().ZwSetIntervalProfile(interval, ProfileTime);
+ if (!NT_SUCCESS(status))
+ return false;
+
+ return true;
+}
+
+bool SamplingProfiler::GetSamplingInterval(base::TimeDelta* sampling_interval) {
+ DCHECK(sampling_interval != NULL);
+
+ if (!funcs.Get().initialized_)
+ return false;
+
+ ULONG interval = 0;
+ NTSTATUS status = funcs.Get().ZwQueryIntervalProfile(ProfileTime, &interval);
+ if (!NT_SUCCESS(status))
+ return false;
+
+ // According to Nebbet, the sampling interval is in units of 100ns.
+ *sampling_interval = base::TimeDelta::FromMicroseconds(interval / 10);
+
+ return true;
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/sampling_profiler.h b/src/base/win/sampling_profiler.h
new file mode 100644
index 0000000..e7e76d8
--- /dev/null
+++ b/src/base/win/sampling_profiler.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_SAMPLING_PROFILER_H_
+#define BASE_WIN_SAMPLING_PROFILER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/time.h"
+#include "base/win/scoped_handle.h"
+
+namespace base {
+namespace win {
+
+// This class exposes the functionality of Window's built-in sampling profiler.
+// Each profiler instance covers a range of memory, and while the profiler is
+// running, its buckets will count the number of times the instruction counter
+// lands in the associated range of memory on a sample.
+// The sampling interval is settable, but the setting is system-wide.
+class BASE_EXPORT SamplingProfiler {
+ public:
+ // Create an uninitialized sampling profiler.
+ SamplingProfiler();
+ ~SamplingProfiler();
+
+ // Initializes the profiler to cover the memory range |start| through
+ // |start| + |size|, in the process |process_handle| with bucket size
+ // |2^log2_bucket_size|, |log2_bucket_size| must be in the range 2-31,
+ // for bucket sizes of 4 bytes to 2 gigabytes.
+ // The process handle must grant at least PROCESS_QUERY_INFORMATION.
+ // The memory range should be exectuable code, like e.g. the text segment
+ // of an exectuable (whether DLL or EXE).
+ // Returns true on success.
+ bool Initialize(HANDLE process_handle,
+ void* start,
+ size_t size,
+ size_t log2_bucket_size);
+
+ // Start this profiler, which must be initialized and not started.
+ bool Start();
+ // Stop this profiler, which must be started.
+ bool Stop();
+
+ // Get and set the sampling interval.
+ // Note that this is a system-wide setting.
+ static bool SetSamplingInterval(base::TimeDelta sampling_interval);
+ static bool GetSamplingInterval(base::TimeDelta* sampling_interval);
+
+ // Accessors.
+ bool is_started() const { return is_started_; }
+
+ // It is safe to read the counts in the sampling buckets at any time.
+ // Note however that there's no guarantee that you'll read consistent counts
+ // until the profiler has been stopped, as the counts may be updating on other
+ // CPU cores.
+ const std::vector<ULONG>& buckets() const { return buckets_; }
+
+ private:
+ // Handle to the corresponding kernel object.
+ ScopedHandle profile_handle_;
+ // True iff this profiler is started.
+ bool is_started_;
+ std::vector<ULONG> buckets_;
+
+ DISALLOW_COPY_AND_ASSIGN(SamplingProfiler);
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_SAMPLING_PROFILER_H_
diff --git a/src/base/win/sampling_profiler_unittest.cc b/src/base/win/sampling_profiler_unittest.cc
new file mode 100644
index 0000000..d022026
--- /dev/null
+++ b/src/base/win/sampling_profiler_unittest.cc
@@ -0,0 +1,120 @@
+// Copyright (c) 2012 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"
+#include "base/test/test_timeouts.h"
+#include "base/win/sampling_profiler.h"
+#include "base/win/pe_image.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// The address of our image base.
+extern "C" IMAGE_DOS_HEADER __ImageBase;
+
+namespace base {
+namespace win {
+
+namespace {
+
+class SamplingProfilerTest : public testing::Test {
+ public:
+ SamplingProfilerTest() : code_start(NULL), code_size(0) {
+ }
+
+ virtual void SetUp() {
+ process.Set(::OpenProcess(PROCESS_QUERY_INFORMATION,
+ FALSE,
+ ::GetCurrentProcessId()));
+ ASSERT_TRUE(process.IsValid());
+
+ PEImage image(&__ImageBase);
+
+ // Get the address of the .text section, which is the first section output
+ // by the VS tools.
+ ASSERT_TRUE(image.GetNumSections() > 0);
+ const IMAGE_SECTION_HEADER* text_section = image.GetSectionHeader(0);
+ ASSERT_EQ(0, strncmp(".text",
+ reinterpret_cast<const char*>(text_section->Name),
+ arraysize(text_section->Name)));
+ ASSERT_NE(0U, text_section->Characteristics & IMAGE_SCN_MEM_EXECUTE);
+
+ code_start = reinterpret_cast<uint8*>(&__ImageBase) +
+ text_section->VirtualAddress;
+ code_size = text_section->Misc.VirtualSize;
+ }
+
+ protected:
+ ScopedHandle process;
+ void* code_start;
+ size_t code_size;
+};
+
+} // namespace
+
+TEST_F(SamplingProfilerTest, Initialize) {
+ SamplingProfiler profiler;
+
+ ASSERT_TRUE(profiler.Initialize(process.Get(), code_start, code_size, 8));
+}
+
+TEST_F(SamplingProfilerTest, Sample) {
+ if (base::win::GetVersion() == base::win::VERSION_WIN8) {
+ LOG(INFO) << "Not running test on Windows 8";
+ return;
+ }
+ SamplingProfiler profiler;
+
+ // Initialize with a huge bucket size, aiming for a single bucket.
+ ASSERT_TRUE(
+ profiler.Initialize(process.Get(), code_start, code_size, 31));
+
+ ASSERT_EQ(1, profiler.buckets().size());
+ ASSERT_EQ(0, profiler.buckets()[0]);
+
+ // We use a roomy timeout to make sure this test is not flaky.
+ // On the buildbots, there may not be a whole lot of CPU time
+ // allotted to our process in this wall-clock time duration,
+ // and samples will only accrue while this thread is busy on
+ // a CPU core.
+ base::TimeDelta spin_time = TestTimeouts::action_timeout();
+
+ base::TimeDelta save_sampling_interval;
+ ASSERT_TRUE(SamplingProfiler::GetSamplingInterval(&save_sampling_interval));
+
+ // Sample every 0.5 millisecs.
+ ASSERT_TRUE(SamplingProfiler::SetSamplingInterval(
+ base::TimeDelta::FromMicroseconds(500)));
+
+ ASSERT_TRUE(SamplingProfiler::SetSamplingInterval(
+ base::TimeDelta::FromMicroseconds(500)));
+
+ // Start the profiler.
+ ASSERT_TRUE(profiler.Start());
+
+ // Get a volatile pointer to our bucket to make sure that the compiler
+ // doesn't optimize out the test in the loop that follows.
+ volatile const ULONG* bucket_ptr = &profiler.buckets()[0];
+
+ // Spin for spin_time wall-clock seconds, or until we get some samples.
+ // Note that sleeping isn't going to do us any good, the samples only
+ // accrue while we're executing code.
+ base::Time start = base::Time::Now();
+ base::TimeDelta elapsed;
+ do {
+ elapsed = base::Time::Now() - start;
+ } while((elapsed < spin_time) && *bucket_ptr == 0);
+
+ // Stop the profiler.
+ ASSERT_TRUE(profiler.Stop());
+
+ // Restore the sampling interval we found.
+ ASSERT_TRUE(SamplingProfiler::SetSamplingInterval(save_sampling_interval));
+
+ // Check that we got some samples.
+ ASSERT_NE(0U, profiler.buckets()[0]);
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/scoped_bstr.cc b/src/base/win/scoped_bstr.cc
new file mode 100644
index 0000000..63ade0c
--- /dev/null
+++ b/src/base/win/scoped_bstr.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/scoped_bstr.h"
+
+#include "base/logging.h"
+
+namespace base {
+namespace win {
+
+ScopedBstr::ScopedBstr(const char16* non_bstr)
+ : bstr_(SysAllocString(non_bstr)) {
+}
+
+ScopedBstr::~ScopedBstr() {
+ COMPILE_ASSERT(sizeof(ScopedBstr) == sizeof(BSTR), ScopedBstrSize);
+ SysFreeString(bstr_);
+}
+
+void ScopedBstr::Reset(BSTR bstr) {
+ if (bstr != bstr_) {
+ // if |bstr_| is NULL, SysFreeString does nothing.
+ SysFreeString(bstr_);
+ bstr_ = bstr;
+ }
+}
+
+BSTR ScopedBstr::Release() {
+ BSTR bstr = bstr_;
+ bstr_ = NULL;
+ return bstr;
+}
+
+void ScopedBstr::Swap(ScopedBstr& bstr2) {
+ BSTR tmp = bstr_;
+ bstr_ = bstr2.bstr_;
+ bstr2.bstr_ = tmp;
+}
+
+BSTR* ScopedBstr::Receive() {
+ DCHECK(!bstr_) << "BSTR leak.";
+ return &bstr_;
+}
+
+BSTR ScopedBstr::Allocate(const char16* str) {
+ Reset(SysAllocString(str));
+ return bstr_;
+}
+
+BSTR ScopedBstr::AllocateBytes(size_t bytes) {
+ Reset(SysAllocStringByteLen(NULL, static_cast<UINT>(bytes)));
+ return bstr_;
+}
+
+void ScopedBstr::SetByteLen(size_t bytes) {
+ DCHECK(bstr_ != NULL) << "attempting to modify a NULL bstr";
+ uint32* data = reinterpret_cast<uint32*>(bstr_);
+ data[-1] = static_cast<uint32>(bytes);
+}
+
+size_t ScopedBstr::Length() const {
+ return SysStringLen(bstr_);
+}
+
+size_t ScopedBstr::ByteLength() const {
+ return SysStringByteLen(bstr_);
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/scoped_bstr.h b/src/base/win/scoped_bstr.h
new file mode 100644
index 0000000..ed46d63
--- /dev/null
+++ b/src/base/win/scoped_bstr.h
@@ -0,0 +1,97 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_SCOPED_BSTR_H_
+#define BASE_WIN_SCOPED_BSTR_H_
+
+#include <windows.h>
+#include <oleauto.h>
+
+#include "base/base_export.h"
+#include "base/logging.h"
+#include "base/string16.h"
+
+namespace base {
+namespace win {
+
+// Manages a BSTR string pointer.
+// The class interface is based on scoped_ptr.
+class BASE_EXPORT ScopedBstr {
+ public:
+ ScopedBstr() : bstr_(NULL) {
+ }
+
+ // Constructor to create a new BSTR.
+ //
+ // NOTE: Do not pass a BSTR to this constructor expecting ownership to
+ // be transferred - even though it compiles! ;-)
+ explicit ScopedBstr(const char16* non_bstr);
+ ~ScopedBstr();
+
+ // Give ScopedBstr ownership over an already allocated BSTR or NULL.
+ // If you need to allocate a new BSTR instance, use |allocate| instead.
+ void Reset(BSTR bstr = NULL);
+
+ // Releases ownership of the BSTR to the caller.
+ BSTR Release();
+
+ // Creates a new BSTR from a 16-bit C-style string.
+ //
+ // If you already have a BSTR and want to transfer ownership to the
+ // ScopedBstr instance, call |reset| instead.
+ //
+ // Returns a pointer to the new BSTR, or NULL if allocation failed.
+ BSTR Allocate(const char16* str);
+
+ // Allocates a new BSTR with the specified number of bytes.
+ // Returns a pointer to the new BSTR, or NULL if allocation failed.
+ BSTR AllocateBytes(size_t bytes);
+
+ // Sets the allocated length field of the already-allocated BSTR to be
+ // |bytes|. This is useful when the BSTR was preallocated with e.g.
+ // SysAllocStringLen or SysAllocStringByteLen (call |AllocateBytes|) and then
+ // not all the bytes are being used.
+ //
+ // Note that if you want to set the length to a specific number of
+ // characters, you need to multiply by sizeof(wchar_t). Oddly, there's no
+ // public API to set the length, so we do this ourselves by hand.
+ //
+ // NOTE: The actual allocated size of the BSTR MUST be >= bytes. That
+ // responsibility is with the caller.
+ void SetByteLen(size_t bytes);
+
+ // Swap values of two ScopedBstr's.
+ void Swap(ScopedBstr& bstr2);
+
+ // Retrieves the pointer address.
+ // Used to receive BSTRs as out arguments (and take ownership).
+ // The function DCHECKs on the current value being NULL.
+ // Usage: GetBstr(bstr.Receive());
+ BSTR* Receive();
+
+ // Returns number of chars in the BSTR.
+ size_t Length() const;
+
+ // Returns the number of bytes allocated for the BSTR.
+ size_t ByteLength() const;
+
+ operator BSTR() const {
+ return bstr_;
+ }
+
+ protected:
+ BSTR bstr_;
+
+ private:
+ // Forbid comparison of ScopedBstr types. You should never have the same
+ // BSTR owned by two different scoped_ptrs.
+ bool operator==(const ScopedBstr& bstr2) const;
+ bool operator!=(const ScopedBstr& bstr2) const;
+ DISALLOW_COPY_AND_ASSIGN(ScopedBstr);
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_SCOPED_BSTR_H_
diff --git a/src/base/win/scoped_bstr_unittest.cc b/src/base/win/scoped_bstr_unittest.cc
new file mode 100644
index 0000000..5f6f7df
--- /dev/null
+++ b/src/base/win/scoped_bstr_unittest.cc
@@ -0,0 +1,77 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/scoped_bstr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace win {
+
+namespace {
+
+static const wchar_t kTestString1[] = L"123";
+static const wchar_t kTestString2[] = L"456789";
+size_t test1_len = arraysize(kTestString1) - 1;
+size_t test2_len = arraysize(kTestString2) - 1;
+
+void DumbBstrTests() {
+ ScopedBstr b;
+ EXPECT_TRUE(b == NULL);
+ EXPECT_EQ(0, b.Length());
+ EXPECT_EQ(0, b.ByteLength());
+ b.Reset(NULL);
+ EXPECT_TRUE(b == NULL);
+ EXPECT_TRUE(b.Release() == NULL);
+ ScopedBstr b2;
+ b.Swap(b2);
+ EXPECT_TRUE(b2 == NULL);
+}
+
+void GiveMeABstr(BSTR* ret) {
+ *ret = SysAllocString(kTestString1);
+}
+
+void BasicBstrTests() {
+ ScopedBstr b1(kTestString1);
+ EXPECT_EQ(test1_len, b1.Length());
+ EXPECT_EQ(test1_len * sizeof(kTestString1[0]), b1.ByteLength());
+
+ ScopedBstr b2;
+ b1.Swap(b2);
+ EXPECT_EQ(test1_len, b2.Length());
+ EXPECT_EQ(0, b1.Length());
+ EXPECT_EQ(0, lstrcmp(b2, kTestString1));
+ BSTR tmp = b2.Release();
+ EXPECT_TRUE(tmp != NULL);
+ EXPECT_EQ(0, lstrcmp(tmp, kTestString1));
+ EXPECT_TRUE(b2 == NULL);
+ SysFreeString(tmp);
+
+ GiveMeABstr(b2.Receive());
+ EXPECT_TRUE(b2 != NULL);
+ b2.Reset();
+ EXPECT_TRUE(b2.AllocateBytes(100) != NULL);
+ EXPECT_EQ(100, b2.ByteLength());
+ EXPECT_EQ(100 / sizeof(kTestString1[0]), b2.Length());
+ lstrcpy(static_cast<BSTR>(b2), kTestString1);
+ EXPECT_EQ(test1_len, lstrlen(b2));
+ EXPECT_EQ(100 / sizeof(kTestString1[0]), b2.Length());
+ b2.SetByteLen(lstrlen(b2) * sizeof(kTestString2[0]));
+ EXPECT_EQ(b2.Length(), lstrlen(b2));
+
+ EXPECT_TRUE(b1.Allocate(kTestString2) != NULL);
+ EXPECT_EQ(test2_len, b1.Length());
+ b1.SetByteLen((test2_len - 1) * sizeof(kTestString2[0]));
+ EXPECT_EQ(test2_len - 1, b1.Length());
+}
+
+} // namespace
+
+TEST(ScopedBstrTest, ScopedBstr) {
+ DumbBstrTests();
+ BasicBstrTests();
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/scoped_co_mem.h b/src/base/win/scoped_co_mem.h
new file mode 100644
index 0000000..572999a
--- /dev/null
+++ b/src/base/win/scoped_co_mem.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_SCOPED_CO_MEM_H_
+#define BASE_WIN_SCOPED_CO_MEM_H_
+
+#include <objbase.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace base {
+namespace win {
+
+// Simple scoped memory releaser class for COM allocated memory.
+// Example:
+// base::win::ScopedCoMem<ITEMIDLIST> file_item;
+// SHGetSomeInfo(&file_item, ...);
+// ...
+// return; <-- memory released
+template<typename T>
+class ScopedCoMem {
+ public:
+ ScopedCoMem() : mem_ptr_(NULL) {}
+ ~ScopedCoMem() {
+ Reset(NULL);
+ }
+
+ T** operator&() { // NOLINT
+ DCHECK(mem_ptr_ == NULL); // To catch memory leaks.
+ return &mem_ptr_;
+ }
+
+ operator T*() {
+ return mem_ptr_;
+ }
+
+ T* operator->() {
+ DCHECK(mem_ptr_ != NULL);
+ return mem_ptr_;
+ }
+
+ const T* operator->() const {
+ DCHECK(mem_ptr_ != NULL);
+ return mem_ptr_;
+ }
+
+ void Reset(T* ptr) {
+ if (mem_ptr_)
+ CoTaskMemFree(mem_ptr_);
+ mem_ptr_ = ptr;
+ }
+
+ private:
+ T* mem_ptr_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedCoMem);
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_SCOPED_CO_MEM_H_
diff --git a/src/base/win/scoped_com_initializer.h b/src/base/win/scoped_com_initializer.h
new file mode 100644
index 0000000..392c351
--- /dev/null
+++ b/src/base/win/scoped_com_initializer.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_SCOPED_COM_INITIALIZER_H_
+#define BASE_WIN_SCOPED_COM_INITIALIZER_H_
+
+#include <objbase.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "build/build_config.h"
+
+namespace base {
+namespace win {
+
+// Initializes COM in the constructor (STA or MTA), and uninitializes COM in the
+// destructor.
+class ScopedCOMInitializer {
+ public:
+ // Enum value provided to initialize the thread as an MTA instead of STA.
+ enum SelectMTA { kMTA };
+
+ // Constructor for STA initialization.
+ ScopedCOMInitializer() {
+ Initialize(COINIT_APARTMENTTHREADED);
+ }
+
+ // Constructor for MTA initialization.
+ explicit ScopedCOMInitializer(SelectMTA mta) {
+ Initialize(COINIT_MULTITHREADED);
+ }
+
+ ~ScopedCOMInitializer() {
+#ifndef NDEBUG
+ // Using the windows API directly to avoid dependency on platform_thread.
+ DCHECK_EQ(GetCurrentThreadId(), thread_id_);
+#endif
+ if (succeeded())
+ CoUninitialize();
+ }
+
+ bool succeeded() const { return SUCCEEDED(hr_); }
+
+ private:
+ void Initialize(COINIT init) {
+#ifndef NDEBUG
+ thread_id_ = GetCurrentThreadId();
+#endif
+ hr_ = CoInitializeEx(NULL, init);
+#ifndef NDEBUG
+ if (hr_ == S_FALSE)
+ LOG(ERROR) << "Multiple CoInitialize() calls for thread " << thread_id_;
+ else
+ DCHECK_NE(RPC_E_CHANGED_MODE, hr_) << "Invalid COM thread model change";
+#endif
+ }
+
+ HRESULT hr_;
+#ifndef NDEBUG
+ // In debug builds we use this variable to catch a potential bug where a
+ // ScopedCOMInitializer instance is deleted on a different thread than it
+ // was initially created on. If that ever happens it can have bad
+ // consequences and the cause can be tricky to track down.
+ DWORD thread_id_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedCOMInitializer);
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_SCOPED_COM_INITIALIZER_H_
diff --git a/src/base/win/scoped_comptr.h b/src/base/win/scoped_comptr.h
new file mode 100644
index 0000000..9d5301f
--- /dev/null
+++ b/src/base/win/scoped_comptr.h
@@ -0,0 +1,168 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_SCOPED_COMPTR_H_
+#define BASE_WIN_SCOPED_COMPTR_H_
+
+#include <unknwn.h>
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+
+namespace base {
+namespace win {
+
+// A fairly minimalistic smart class for COM interface pointers.
+// Uses scoped_refptr for the basic smart pointer functionality
+// and adds a few IUnknown specific services.
+template <class Interface, const IID* interface_id = &__uuidof(Interface)>
+class ScopedComPtr : public scoped_refptr<Interface> {
+ public:
+ // Utility template to prevent users of ScopedComPtr from calling AddRef
+ // and/or Release() without going through the ScopedComPtr class.
+ class BlockIUnknownMethods : public Interface {
+ private:
+ STDMETHOD(QueryInterface)(REFIID iid, void** object) = 0;
+ STDMETHOD_(ULONG, AddRef)() = 0;
+ STDMETHOD_(ULONG, Release)() = 0;
+ };
+
+ typedef scoped_refptr<Interface> ParentClass;
+
+ ScopedComPtr() {
+ }
+
+ explicit ScopedComPtr(Interface* p) : ParentClass(p) {
+ }
+
+ ScopedComPtr(const ScopedComPtr<Interface, interface_id>& p)
+ : ParentClass(p) {
+ }
+
+ ~ScopedComPtr() {
+ // We don't want the smart pointer class to be bigger than the pointer
+ // it wraps.
+ COMPILE_ASSERT(sizeof(ScopedComPtr<Interface, interface_id>) ==
+ sizeof(Interface*), ScopedComPtrSize);
+ }
+
+ // Explicit Release() of the held object. Useful for reuse of the
+ // ScopedComPtr instance.
+ // Note that this function equates to IUnknown::Release and should not
+ // be confused with e.g. scoped_ptr::release().
+ void Release() {
+ if (ptr_ != NULL) {
+ ptr_->Release();
+ ptr_ = NULL;
+ }
+ }
+
+ // Sets the internal pointer to NULL and returns the held object without
+ // releasing the reference.
+ Interface* Detach() {
+ Interface* p = ptr_;
+ ptr_ = NULL;
+ return p;
+ }
+
+ // Accepts an interface pointer that has already been addref-ed.
+ void Attach(Interface* p) {
+ DCHECK(!ptr_);
+ ptr_ = p;
+ }
+
+ // Retrieves the pointer address.
+ // Used to receive object pointers as out arguments (and take ownership).
+ // The function DCHECKs on the current value being NULL.
+ // Usage: Foo(p.Receive());
+ Interface** Receive() {
+ DCHECK(!ptr_) << "Object leak. Pointer must be NULL";
+ return &ptr_;
+ }
+
+ // A convenience for whenever a void pointer is needed as an out argument.
+ void** ReceiveVoid() {
+ return reinterpret_cast<void**>(Receive());
+ }
+
+ template <class Query>
+ HRESULT QueryInterface(Query** p) {
+ DCHECK(p != NULL);
+ DCHECK(ptr_ != NULL);
+ // IUnknown already has a template version of QueryInterface
+ // so the iid parameter is implicit here. The only thing this
+ // function adds are the DCHECKs.
+ return ptr_->QueryInterface(p);
+ }
+
+ // QI for times when the IID is not associated with the type.
+ HRESULT QueryInterface(const IID& iid, void** obj) {
+ DCHECK(obj != NULL);
+ DCHECK(ptr_ != NULL);
+ return ptr_->QueryInterface(iid, obj);
+ }
+
+ // Queries |other| for the interface this object wraps and returns the
+ // error code from the other->QueryInterface operation.
+ HRESULT QueryFrom(IUnknown* object) {
+ DCHECK(object != NULL);
+ return object->QueryInterface(Receive());
+ }
+
+ // Convenience wrapper around CoCreateInstance
+ HRESULT CreateInstance(const CLSID& clsid, IUnknown* outer = NULL,
+ DWORD context = CLSCTX_ALL) {
+ DCHECK(!ptr_);
+ HRESULT hr = ::CoCreateInstance(clsid, outer, context, *interface_id,
+ reinterpret_cast<void**>(&ptr_));
+ return hr;
+ }
+
+ // Checks if the identity of |other| and this object is the same.
+ bool IsSameObject(IUnknown* other) {
+ if (!other && !ptr_)
+ return true;
+
+ if (!other || !ptr_)
+ return false;
+
+ ScopedComPtr<IUnknown> my_identity;
+ QueryInterface(my_identity.Receive());
+
+ ScopedComPtr<IUnknown> other_identity;
+ other->QueryInterface(other_identity.Receive());
+
+ return static_cast<IUnknown*>(my_identity) ==
+ static_cast<IUnknown*>(other_identity);
+ }
+
+ // Provides direct access to the interface.
+ // Here we use a well known trick to make sure we block access to
+ // IUknown methods so that something bad like this doesn't happen:
+ // ScopedComPtr<IUnknown> p(Foo());
+ // p->Release();
+ // ... later the destructor runs, which will Release() again.
+ // and to get the benefit of the DCHECKs we add to QueryInterface.
+ // There's still a way to call these methods if you absolutely must
+ // by statically casting the ScopedComPtr instance to the wrapped interface
+ // and then making the call... but generally that shouldn't be necessary.
+ BlockIUnknownMethods* operator->() const {
+ DCHECK(ptr_ != NULL);
+ return reinterpret_cast<BlockIUnknownMethods*>(ptr_);
+ }
+
+ // Pull in operator=() from the parent class.
+ using scoped_refptr<Interface>::operator=;
+
+ // static methods
+
+ static const IID& iid() {
+ return *interface_id;
+ }
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_SCOPED_COMPTR_H_
diff --git a/src/base/win/scoped_comptr_unittest.cc b/src/base/win/scoped_comptr_unittest.cc
new file mode 100644
index 0000000..d8d12be
--- /dev/null
+++ b/src/base/win/scoped_comptr_unittest.cc
@@ -0,0 +1,111 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/scoped_comptr.h"
+
+#include <shlobj.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/win/scoped_com_initializer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace win {
+
+namespace {
+
+struct Dummy {
+ Dummy() : adds(0), releases(0) { }
+ void AddRef() { ++adds; }
+ void Release() { ++releases; }
+
+ int adds;
+ int releases;
+};
+
+extern const IID dummy_iid;
+const IID dummy_iid = { 0x12345678u, 0x1234u, 0x5678u, 01, 23, 45, 67, 89,
+ 01, 23, 45 };
+
+} // namespace
+
+TEST(ScopedComPtrTest, ScopedComPtr) {
+ EXPECT_TRUE(memcmp(&ScopedComPtr<IUnknown>::iid(), &IID_IUnknown,
+ sizeof(IID)) == 0);
+
+ base::win::ScopedCOMInitializer com_initializer;
+ EXPECT_TRUE(com_initializer.succeeded());
+
+ ScopedComPtr<IUnknown> unk;
+ EXPECT_TRUE(SUCCEEDED(unk.CreateInstance(CLSID_ShellLink)));
+ ScopedComPtr<IUnknown> unk2;
+ unk2.Attach(unk.Detach());
+ EXPECT_TRUE(unk == NULL);
+ EXPECT_TRUE(unk2 != NULL);
+
+ ScopedComPtr<IMalloc> mem_alloc;
+ EXPECT_TRUE(SUCCEEDED(CoGetMalloc(1, mem_alloc.Receive())));
+
+ ScopedComPtr<IUnknown> qi_test;
+ EXPECT_HRESULT_SUCCEEDED(mem_alloc.QueryInterface(IID_IUnknown,
+ reinterpret_cast<void**>(qi_test.Receive())));
+ EXPECT_TRUE(qi_test.get() != NULL);
+ qi_test.Release();
+
+ // test ScopedComPtr& constructor
+ ScopedComPtr<IMalloc> copy1(mem_alloc);
+ EXPECT_TRUE(copy1.IsSameObject(mem_alloc));
+ EXPECT_FALSE(copy1.IsSameObject(unk2)); // unk2 is valid but different
+ EXPECT_FALSE(copy1.IsSameObject(unk)); // unk is NULL
+
+ IMalloc* naked_copy = copy1.Detach();
+ copy1 = naked_copy; // Test the =(T*) operator.
+ naked_copy->Release();
+
+ copy1.Release();
+ EXPECT_FALSE(copy1.IsSameObject(unk2)); // unk2 is valid, copy1 is not
+
+ // test Interface* constructor
+ ScopedComPtr<IMalloc> copy2(static_cast<IMalloc*>(mem_alloc));
+ EXPECT_TRUE(copy2.IsSameObject(mem_alloc));
+
+ EXPECT_TRUE(SUCCEEDED(unk.QueryFrom(mem_alloc)));
+ EXPECT_TRUE(unk != NULL);
+ unk.Release();
+ EXPECT_TRUE(unk == NULL);
+ EXPECT_TRUE(unk.IsSameObject(copy1)); // both are NULL
+}
+
+TEST(ScopedComPtrTest, ScopedComPtrVector) {
+ // Verify we don't get error C2558.
+ typedef ScopedComPtr<Dummy, &dummy_iid> Ptr;
+ std::vector<Ptr> bleh;
+
+ scoped_ptr<Dummy> p(new Dummy);
+ {
+ Ptr p2(p.get());
+ EXPECT_EQ(p->adds, 1);
+ EXPECT_EQ(p->releases, 0);
+ Ptr p3 = p2;
+ EXPECT_EQ(p->adds, 2);
+ EXPECT_EQ(p->releases, 0);
+ p3 = p2;
+ EXPECT_EQ(p->adds, 3);
+ EXPECT_EQ(p->releases, 1);
+ // To avoid hitting a reallocation.
+ bleh.reserve(1);
+ bleh.push_back(p2);
+ EXPECT_EQ(p->adds, 4);
+ EXPECT_EQ(p->releases, 1);
+ EXPECT_EQ(bleh[0], p.get());
+ bleh.pop_back();
+ EXPECT_EQ(p->adds, 4);
+ EXPECT_EQ(p->releases, 2);
+ }
+ EXPECT_EQ(p->adds, 4);
+ EXPECT_EQ(p->releases, 4);
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/scoped_gdi_object.h b/src/base/win/scoped_gdi_object.h
new file mode 100644
index 0000000..d44310a
--- /dev/null
+++ b/src/base/win/scoped_gdi_object.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_SCOPED_GDI_OBJECT_H_
+#define BASE_WIN_SCOPED_GDI_OBJECT_H_
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace base {
+namespace win {
+
+// Like ScopedHandle but for GDI objects.
+template<class T>
+class ScopedGDIObject {
+ public:
+ ScopedGDIObject() : object_(NULL) {}
+ explicit ScopedGDIObject(T object) : object_(object) {}
+
+ ~ScopedGDIObject() {
+ Close();
+ }
+
+ T Get() {
+ return object_;
+ }
+
+ void Set(T object) {
+ if (object_ && object != object_)
+ Close();
+ object_ = object;
+ }
+
+ ScopedGDIObject& operator=(T object) {
+ Set(object);
+ return *this;
+ }
+
+ T release() {
+ T object = object_;
+ object_ = NULL;
+ return object;
+ }
+
+ operator T() { return object_; }
+
+ private:
+ void Close() {
+ if (object_)
+ DeleteObject(object_);
+ }
+
+ T object_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedGDIObject);
+};
+
+// An explicit specialization for HICON because we have to call DestroyIcon()
+// instead of DeleteObject() for HICON.
+template<>
+void ScopedGDIObject<HICON>::Close() {
+ if (object_)
+ DestroyIcon(object_);
+}
+
+// Typedefs for some common use cases.
+typedef ScopedGDIObject<HBITMAP> ScopedBitmap;
+typedef ScopedGDIObject<HRGN> ScopedRegion;
+typedef ScopedGDIObject<HFONT> ScopedHFONT;
+typedef ScopedGDIObject<HICON> ScopedHICON;
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_SCOPED_GDI_OBJECT_H_
diff --git a/src/base/win/scoped_handle.cc b/src/base/win/scoped_handle.cc
new file mode 100644
index 0000000..03d026a
--- /dev/null
+++ b/src/base/win/scoped_handle.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/scoped_handle.h"
+
+#include <map>
+#include <set>
+
+#include "base/debug/alias.h"
+#include "base/lazy_instance.h"
+#include "base/synchronization/lock.h"
+#include "base/win/windows_version.h"
+
+namespace {
+
+struct Info {
+ const void* owner;
+ const void* pc1;
+ const void* pc2;
+ DWORD thread_id;
+};
+typedef std::map<HANDLE, Info> HandleMap;
+
+base::LazyInstance<HandleMap>::Leaky g_handle_map = LAZY_INSTANCE_INITIALIZER;
+base::LazyInstance<std::set<const void*> >::Leaky g_owner_set =
+ LAZY_INSTANCE_INITIALIZER;
+base::LazyInstance<base::Lock>::Leaky g_lock = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+namespace base {
+namespace win {
+
+// Static.
+void VerifierTraits::StartTracking(HANDLE handle, const void* owner,
+ const void* pc1, const void* pc2) {
+ // Grab the thread id before the lock.
+ DWORD thread_id = GetCurrentThreadId();
+
+ AutoLock lock(g_lock.Get());
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ // Cannot track this handle.
+ g_owner_set.Get().insert(owner);
+ return;
+ }
+
+ Info handle_info = { owner, pc1, pc2, thread_id };
+ std::pair<HANDLE, Info> item(handle, handle_info);
+ std::pair<HandleMap::iterator, bool> result = g_handle_map.Get().insert(item);
+ if (!result.second) {
+ Info other = result.first->second;
+ debug::Alias(&other);
+ CHECK(false);
+ }
+}
+
+// Static.
+void VerifierTraits::StopTracking(HANDLE handle, const void* owner,
+ const void* pc1, const void* pc2) {
+ AutoLock lock(g_lock.Get());
+ HandleMap::iterator i = g_handle_map.Get().find(handle);
+ if (i == g_handle_map.Get().end()) {
+ std::set<const void*>::iterator j = g_owner_set.Get().find(owner);
+ if (j != g_owner_set.Get().end()) {
+ g_owner_set.Get().erase(j);
+ return;
+ }
+ CHECK(false);
+ }
+
+ Info other = i->second;
+ if (other.owner != owner) {
+ debug::Alias(&other);
+ CHECK(false);
+ }
+
+ g_handle_map.Get().erase(i);
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/scoped_handle.h b/src/base/win/scoped_handle.h
new file mode 100644
index 0000000..4fe3592
--- /dev/null
+++ b/src/base/win/scoped_handle.h
@@ -0,0 +1,188 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_SCOPED_HANDLE_H_
+#define BASE_WIN_SCOPED_HANDLE_H_
+
+#include <windows.h>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/move.h"
+
+namespace base {
+namespace win {
+
+// TODO(rvargas): remove this with the rest of the verifier.
+#if defined(COMPILER_MSVC)
+// MSDN says to #include <intrin.h>, but that breaks the VS2005 build.
+extern "C" {
+ void* _ReturnAddress();
+}
+#define BASE_WIN_GET_CALLER _ReturnAddress()
+#elif defined(COMPILER_GCC)
+#define BASE_WIN_GET_CALLER __builtin_extract_return_addr(\\
+ __builtin_return_address(0))
+#endif
+
+// Generic wrapper for raw handles that takes care of closing handles
+// automatically. The class interface follows the style of
+// the ScopedStdioHandle class with a few additions:
+// - IsValid() method can tolerate multiple invalid handle values such as NULL
+// and INVALID_HANDLE_VALUE (-1) for Win32 handles.
+// - Receive() method allows to receive a handle value from a function that
+// takes a raw handle pointer only.
+template <class Traits, class Verifier>
+class GenericScopedHandle {
+ MOVE_ONLY_TYPE_FOR_CPP_03(GenericScopedHandle, RValue)
+
+ public:
+ typedef typename Traits::Handle Handle;
+
+ GenericScopedHandle() : handle_(Traits::NullHandle()) {}
+
+ explicit GenericScopedHandle(Handle handle) : handle_(Traits::NullHandle()) {
+ Set(handle);
+ }
+
+ // Move constructor for C++03 move emulation of this type.
+ GenericScopedHandle(RValue other) : handle_(Traits::NullHandle()) {
+ Set(other.object->Take());
+ }
+
+ ~GenericScopedHandle() {
+ Close();
+ }
+
+ bool IsValid() const {
+ return Traits::IsHandleValid(handle_);
+ }
+
+ // Move operator= for C++03 move emulation of this type.
+ GenericScopedHandle& operator=(RValue other) {
+ if (this != other.object) {
+ Set(other.object->Take());
+ }
+ return *this;
+ }
+
+ void Set(Handle handle) {
+ if (handle_ != handle) {
+ Close();
+
+ if (Traits::IsHandleValid(handle)) {
+ handle_ = handle;
+ Verifier::StartTracking(handle, this, BASE_WIN_GET_CALLER,
+ tracked_objects::GetProgramCounter());
+ }
+ }
+ }
+
+ Handle Get() const {
+ return handle_;
+ }
+
+ operator Handle() const {
+ return handle_;
+ }
+
+ Handle* Receive() {
+ DCHECK(!Traits::IsHandleValid(handle_)) << "Handle must be NULL";
+
+ // We cannot track this case :(. Just tell the verifier about it.
+ Verifier::StartTracking(INVALID_HANDLE_VALUE, this, BASE_WIN_GET_CALLER,
+ tracked_objects::GetProgramCounter());
+ return &handle_;
+ }
+
+ // Transfers ownership away from this object.
+ Handle Take() {
+ Handle temp = handle_;
+ handle_ = Traits::NullHandle();
+ if (Traits::IsHandleValid(temp)) {
+ Verifier::StopTracking(temp, this, BASE_WIN_GET_CALLER,
+ tracked_objects::GetProgramCounter());
+ }
+ return temp;
+ }
+
+ // Explicitly closes the owned handle.
+ void Close() {
+ if (Traits::IsHandleValid(handle_)) {
+ Verifier::StopTracking(handle_, this, BASE_WIN_GET_CALLER,
+ tracked_objects::GetProgramCounter());
+
+ if (!Traits::CloseHandle(handle_))
+ CHECK(false);
+
+ handle_ = Traits::NullHandle();
+ }
+ }
+
+ private:
+ Handle handle_;
+};
+
+#undef BASE_WIN_GET_CALLER
+
+// The traits class for Win32 handles that can be closed via CloseHandle() API.
+class HandleTraits {
+ public:
+ typedef HANDLE Handle;
+
+ // Closes the handle.
+ static bool CloseHandle(HANDLE handle) {
+ return ::CloseHandle(handle) != FALSE;
+ }
+
+ // Returns true if the handle value is valid.
+ static bool IsHandleValid(HANDLE handle) {
+ return handle != NULL && handle != INVALID_HANDLE_VALUE;
+ }
+
+ // Returns NULL handle value.
+ static HANDLE NullHandle() {
+ return NULL;
+ }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(HandleTraits);
+};
+
+// Do-nothing verifier.
+class DummyVerifierTraits {
+ public:
+ typedef HANDLE Handle;
+
+ static void StartTracking(HANDLE handle, const void* owner,
+ const void* pc1, const void* pc2) {}
+ static void StopTracking(HANDLE handle, const void* owner,
+ const void* pc1, const void* pc2) {}
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(DummyVerifierTraits);
+};
+
+// Performs actual run-time tracking.
+class BASE_EXPORT VerifierTraits {
+ public:
+ typedef HANDLE Handle;
+
+ static void StartTracking(HANDLE handle, const void* owner,
+ const void* pc1, const void* pc2);
+ static void StopTracking(HANDLE handle, const void* owner,
+ const void* pc1, const void* pc2);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(VerifierTraits);
+};
+
+typedef GenericScopedHandle<HandleTraits, DummyVerifierTraits> ScopedHandle;
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_SCOPED_HANDLE_WIN_H_
diff --git a/src/base/win/scoped_hdc.h b/src/base/win/scoped_hdc.h
new file mode 100644
index 0000000..9aead96
--- /dev/null
+++ b/src/base/win/scoped_hdc.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_SCOPED_HDC_H_
+#define BASE_WIN_SCOPED_HDC_H_
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/win/scoped_handle.h"
+
+namespace base {
+namespace win {
+
+// Like ScopedHandle but for HDC. Only use this on HDCs returned from
+// GetDC.
+class ScopedGetDC {
+ public:
+ explicit ScopedGetDC(HWND hwnd)
+ : hwnd_(hwnd),
+ hdc_(GetDC(hwnd)) {
+ if (hwnd_) {
+ DCHECK(IsWindow(hwnd_));
+ DCHECK(hdc_);
+ } else {
+ // If GetDC(NULL) returns NULL, something really bad has happened, like
+ // GDI handle exhaustion. In this case Chrome is going to behave badly no
+ // matter what, so we may as well just force a crash now.
+ CHECK(hdc_);
+ }
+ }
+
+ ~ScopedGetDC() {
+ if (hdc_)
+ ReleaseDC(hwnd_, hdc_);
+ }
+
+ operator HDC() { return hdc_; }
+
+ private:
+ HWND hwnd_;
+ HDC hdc_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedGetDC);
+};
+
+// Like ScopedHandle but for HDC. Only use this on HDCs returned from
+// CreateCompatibleDC, CreateDC and CreateIC.
+class CreateDCTraits {
+ public:
+ typedef HDC Handle;
+
+ static bool CloseHandle(HDC handle) {
+ return ::DeleteDC(handle) != FALSE;
+ }
+
+ static bool IsHandleValid(HDC handle) {
+ return handle != NULL;
+ }
+
+ static HDC NullHandle() {
+ return NULL;
+ }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(CreateDCTraits);
+};
+
+typedef GenericScopedHandle<CreateDCTraits, VerifierTraits> ScopedCreateDC;
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_SCOPED_HDC_H_
diff --git a/src/base/win/scoped_hglobal.h b/src/base/win/scoped_hglobal.h
new file mode 100644
index 0000000..891e6cd
--- /dev/null
+++ b/src/base/win/scoped_hglobal.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_SCOPED_HGLOBAL_H_
+#define BASE_WIN_SCOPED_HGLOBAL_H_
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+
+namespace base {
+namespace win {
+
+// Like ScopedHandle except for HGLOBAL.
+template<class T>
+class ScopedHGlobal {
+ public:
+ explicit ScopedHGlobal(HGLOBAL glob) : glob_(glob) {
+ data_ = static_cast<T*>(GlobalLock(glob_));
+ }
+ ~ScopedHGlobal() {
+ GlobalUnlock(glob_);
+ }
+
+ T* get() { return data_; }
+
+ size_t Size() const { return GlobalSize(glob_); }
+
+ T* operator->() const {
+ assert(data_ != 0);
+ return data_;
+ }
+
+ T* release() {
+ T* data = data_;
+ data_ = NULL;
+ return data;
+ }
+
+ private:
+ HGLOBAL glob_;
+
+ T* data_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedHGlobal);
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_SCOPED_HGLOBAL_H_
diff --git a/src/base/win/scoped_process_information.cc b/src/base/win/scoped_process_information.cc
new file mode 100644
index 0000000..4adb8d4
--- /dev/null
+++ b/src/base/win/scoped_process_information.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/scoped_process_information.h"
+
+#include "base/logging.h"
+#include "base/win/scoped_handle.h"
+
+namespace base {
+namespace win {
+
+namespace {
+
+// Closes the provided handle if it is not NULL.
+void CheckAndCloseHandle(HANDLE handle) {
+ if (!handle)
+ return;
+ if (::CloseHandle(handle))
+ return;
+ CHECK(false);
+}
+
+// Duplicates source into target, returning true upon success. |target| is
+// guaranteed to be untouched in case of failure. Succeeds with no side-effects
+// if source is NULL.
+bool CheckAndDuplicateHandle(HANDLE source, HANDLE* target) {
+ if (!source)
+ return true;
+
+ HANDLE temp = NULL;
+ if (!::DuplicateHandle(::GetCurrentProcess(), source,
+ ::GetCurrentProcess(), &temp, 0, FALSE,
+ DUPLICATE_SAME_ACCESS)) {
+ DPLOG(ERROR) << "Failed to duplicate a handle.";
+ return false;
+ }
+ *target = temp;
+ return true;
+}
+
+} // namespace
+
+ScopedProcessInformation::ScopedProcessInformation()
+ : process_information_() {
+}
+
+ScopedProcessInformation::~ScopedProcessInformation() {
+ Close();
+}
+
+PROCESS_INFORMATION* ScopedProcessInformation::Receive() {
+ DCHECK(!IsValid()) << "process_information_ must be NULL";
+ return &process_information_;
+}
+
+bool ScopedProcessInformation::IsValid() const {
+ return process_information_.hThread || process_information_.hProcess ||
+ process_information_.dwProcessId || process_information_.dwThreadId;
+}
+
+
+void ScopedProcessInformation::Close() {
+ CheckAndCloseHandle(process_information_.hThread);
+ CheckAndCloseHandle(process_information_.hProcess);
+ Reset();
+}
+
+void ScopedProcessInformation::Swap(ScopedProcessInformation* other) {
+ DCHECK(other);
+ PROCESS_INFORMATION temp = other->process_information_;
+ other->process_information_ = process_information_;
+ process_information_ = temp;
+}
+
+bool ScopedProcessInformation::DuplicateFrom(
+ const ScopedProcessInformation& other) {
+ DCHECK(!IsValid()) << "target ScopedProcessInformation must be NULL";
+ DCHECK(other.IsValid()) << "source ScopedProcessInformation must be valid";
+
+ ScopedHandle duplicate_process;
+ ScopedHandle duplicate_thread;
+
+ if (CheckAndDuplicateHandle(other.process_handle(),
+ duplicate_process.Receive()) &&
+ CheckAndDuplicateHandle(other.thread_handle(),
+ duplicate_thread.Receive())) {
+ process_information_.dwProcessId = other.process_id();
+ process_information_.dwThreadId = other.thread_id();
+ process_information_.hProcess = duplicate_process.Take();
+ process_information_.hThread = duplicate_thread.Take();
+ return true;
+ }
+
+ return false;
+}
+
+PROCESS_INFORMATION ScopedProcessInformation::Take() {
+ PROCESS_INFORMATION process_information = process_information_;
+ Reset();
+ return process_information;
+}
+
+HANDLE ScopedProcessInformation::TakeProcessHandle() {
+ HANDLE process = process_information_.hProcess;
+ process_information_.hProcess = NULL;
+ process_information_.dwProcessId = 0;
+ return process;
+}
+
+HANDLE ScopedProcessInformation::TakeThreadHandle() {
+ HANDLE thread = process_information_.hThread;
+ process_information_.hThread = NULL;
+ process_information_.dwThreadId = 0;
+ return thread;
+}
+
+void ScopedProcessInformation::Reset() {
+ process_information_.hThread = NULL;
+ process_information_.hProcess = NULL;
+ process_information_.dwProcessId = 0;
+ process_information_.dwThreadId = 0;
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/scoped_process_information.h b/src/base/win/scoped_process_information.h
new file mode 100644
index 0000000..cfa7dc9
--- /dev/null
+++ b/src/base/win/scoped_process_information.h
@@ -0,0 +1,93 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_SCOPED_PROCESS_INFORMATION_H_
+#define BASE_WIN_SCOPED_PROCESS_INFORMATION_H_
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "base/base_export.h"
+
+namespace base {
+namespace win {
+
+// Manages the closing of process and thread handles from PROCESS_INFORMATION
+// structures. Allows clients to take ownership of either handle independently.
+class BASE_EXPORT ScopedProcessInformation {
+ public:
+ // Creates an instance holding a null PROCESS_INFORMATION.
+ ScopedProcessInformation();
+
+ // Closes the held thread and process handles, if any.
+ ~ScopedProcessInformation();
+
+ // Returns a pointer that may be passed to API calls such as CreateProcess.
+ // DCHECKs that the object is not currently holding any handles.
+ // HANDLEs stored in the returned PROCESS_INFORMATION will be owned by this
+ // instance.
+ PROCESS_INFORMATION* Receive();
+
+ // Returns true iff this instance is holding a thread and/or process handle.
+ bool IsValid() const;
+
+ // Closes the held thread and process handles, if any, and resets the held
+ // PROCESS_INFORMATION to null.
+ void Close();
+
+ // Swaps contents with the other ScopedProcessInformation.
+ void Swap(ScopedProcessInformation* other);
+
+ // Populates this instance with duplicate handles and the thread/process IDs
+ // from |other|. Returns false in case of failure, in which case this instance
+ // will be completely unpopulated.
+ bool DuplicateFrom(const ScopedProcessInformation& other);
+
+ // Transfers ownership of the held PROCESS_INFORMATION, if any, away from this
+ // instance. Resets the held PROCESS_INFORMATION to null.
+ PROCESS_INFORMATION Take();
+
+ // Transfers ownership of the held process handle, if any, away from this
+ // instance. The hProcess and dwProcessId members of the held
+ // PROCESS_INFORMATION will be reset.
+ HANDLE TakeProcessHandle();
+
+ // Transfers ownership of the held thread handle, if any, away from this
+ // instance. The hThread and dwThreadId members of the held
+ // PROCESS_INFORMATION will be reset.
+ HANDLE TakeThreadHandle();
+
+ // Returns the held process handle, if any, while retaining ownership.
+ HANDLE process_handle() const {
+ return process_information_.hProcess;
+ }
+
+ // Returns the held thread handle, if any, while retaining ownership.
+ HANDLE thread_handle() const {
+ return process_information_.hThread;
+ }
+
+ // Returns the held process id, if any.
+ DWORD process_id() const {
+ return process_information_.dwProcessId;
+ }
+
+ // Returns the held thread id, if any.
+ DWORD thread_id() const {
+ return process_information_.dwThreadId;
+ }
+
+ private:
+ // Resets the held PROCESS_INFORMATION to null.
+ void Reset();
+
+ PROCESS_INFORMATION process_information_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedProcessInformation);
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_SCOPED_PROCESS_INFORMATION_H_
diff --git a/src/base/win/scoped_process_information_unittest.cc b/src/base/win/scoped_process_information_unittest.cc
new file mode 100644
index 0000000..906c156
--- /dev/null
+++ b/src/base/win/scoped_process_information_unittest.cc
@@ -0,0 +1,181 @@
+// Copyright (c) 2012 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 <string>
+
+#include "base/command_line.h"
+#include "base/process_util.h"
+#include "base/test/multiprocess_test.h"
+#include "base/win/scoped_process_information.h"
+#include "testing/multiprocess_func_list.h"
+
+class ScopedProcessInformationTest : public base::MultiProcessTest {
+ protected:
+ void DoCreateProcess(const std::string& main_id,
+ PROCESS_INFORMATION* process_handle);
+};
+
+MULTIPROCESS_TEST_MAIN(ReturnSeven) {
+ return 7;
+}
+
+MULTIPROCESS_TEST_MAIN(ReturnNine) {
+ return 9;
+}
+
+void ScopedProcessInformationTest::DoCreateProcess(
+ const std::string& main_id, PROCESS_INFORMATION* process_handle) {
+ std::wstring cmd_line =
+ this->MakeCmdLine(main_id, false).GetCommandLineString();
+ STARTUPINFO startup_info = {};
+ startup_info.cb = sizeof(startup_info);
+
+ EXPECT_TRUE(::CreateProcess(NULL,
+ const_cast<wchar_t*>(cmd_line.c_str()),
+ NULL, NULL, false, 0, NULL, NULL,
+ &startup_info, process_handle));
+}
+
+TEST_F(ScopedProcessInformationTest, TakeProcess) {
+ base::win::ScopedProcessInformation process_info;
+ DoCreateProcess("ReturnSeven", process_info.Receive());
+ int exit_code = 0;
+ ASSERT_TRUE(base::WaitForExitCode(process_info.TakeProcessHandle(),
+ &exit_code));
+ ASSERT_EQ(7, exit_code);
+ ASSERT_TRUE(process_info.IsValid());
+ ASSERT_EQ(0u, process_info.process_id());
+ ASSERT_TRUE(process_info.process_handle() == NULL);
+ ASSERT_NE(0u, process_info.thread_id());
+ ASSERT_FALSE(process_info.thread_handle() == NULL);
+}
+
+TEST_F(ScopedProcessInformationTest, TakeThread) {
+ base::win::ScopedProcessInformation process_info;
+ DoCreateProcess("ReturnSeven", process_info.Receive());
+ ASSERT_TRUE(::CloseHandle(process_info.TakeThreadHandle()));
+ ASSERT_TRUE(process_info.IsValid());
+ ASSERT_NE(0u, process_info.process_id());
+ ASSERT_FALSE(process_info.process_handle() == NULL);
+ ASSERT_EQ(0u, process_info.thread_id());
+ ASSERT_TRUE(process_info.thread_handle() == NULL);
+}
+
+TEST_F(ScopedProcessInformationTest, TakeBoth) {
+ base::win::ScopedProcessInformation process_info;
+ DoCreateProcess("ReturnSeven", process_info.Receive());
+ int exit_code = 0;
+ ASSERT_TRUE(base::WaitForExitCode(process_info.TakeProcessHandle(),
+ &exit_code));
+ ASSERT_EQ(7, exit_code);
+ ASSERT_TRUE(::CloseHandle(process_info.TakeThreadHandle()));
+ ASSERT_FALSE(process_info.IsValid());
+ ASSERT_EQ(0u, process_info.process_id());
+ ASSERT_TRUE(process_info.process_handle() == NULL);
+ ASSERT_EQ(0u, process_info.thread_id());
+ ASSERT_TRUE(process_info.thread_handle() == NULL);
+}
+
+TEST_F(ScopedProcessInformationTest, TakeNothing) {
+ base::win::ScopedProcessInformation process_info;
+ DoCreateProcess("ReturnSeven", process_info.Receive());
+ ASSERT_TRUE(process_info.IsValid());
+ ASSERT_NE(0u, process_info.thread_id());
+ ASSERT_FALSE(process_info.thread_handle() == NULL);
+ ASSERT_NE(0u, process_info.process_id());
+ ASSERT_FALSE(process_info.process_handle() == NULL);
+}
+
+TEST_F(ScopedProcessInformationTest, TakeWholeStruct) {
+ base::win::ScopedProcessInformation process_info;
+ DoCreateProcess("ReturnSeven", process_info.Receive());
+ base::win::ScopedProcessInformation other;
+ *other.Receive() = process_info.Take();
+
+ ASSERT_FALSE(process_info.IsValid());
+ ASSERT_EQ(0u, process_info.process_id());
+ ASSERT_TRUE(process_info.process_handle() == NULL);
+ ASSERT_EQ(0u, process_info.thread_id());
+ ASSERT_TRUE(process_info.thread_handle() == NULL);
+
+ // Validate that what was taken is good.
+ ASSERT_NE(0u, other.thread_id());
+ ASSERT_NE(0u, other.process_id());
+ int exit_code = 0;
+ ASSERT_TRUE(base::WaitForExitCode(other.TakeProcessHandle(),
+ &exit_code));
+ ASSERT_EQ(7, exit_code);
+ ASSERT_TRUE(::CloseHandle(other.TakeThreadHandle()));
+}
+
+TEST_F(ScopedProcessInformationTest, Duplicate) {
+ base::win::ScopedProcessInformation process_info;
+ DoCreateProcess("ReturnSeven", process_info.Receive());
+ base::win::ScopedProcessInformation duplicate;
+ duplicate.DuplicateFrom(process_info);
+
+ ASSERT_TRUE(process_info.IsValid());
+ ASSERT_NE(0u, process_info.process_id());
+ ASSERT_EQ(duplicate.process_id(), process_info.process_id());
+ ASSERT_NE(0u, process_info.thread_id());
+ ASSERT_EQ(duplicate.thread_id(), process_info.thread_id());
+
+ // Validate that we have separate handles that are good.
+ int exit_code = 0;
+ ASSERT_TRUE(base::WaitForExitCode(process_info.TakeProcessHandle(),
+ &exit_code));
+ ASSERT_EQ(7, exit_code);
+
+ exit_code = 0;
+ ASSERT_TRUE(base::WaitForExitCode(duplicate.TakeProcessHandle(),
+ &exit_code));
+ ASSERT_EQ(7, exit_code);
+
+ ASSERT_TRUE(::CloseHandle(process_info.TakeThreadHandle()));
+ ASSERT_TRUE(::CloseHandle(duplicate.TakeThreadHandle()));
+}
+
+TEST_F(ScopedProcessInformationTest, Swap) {
+ base::win::ScopedProcessInformation seven_to_nine_info;
+ DoCreateProcess("ReturnSeven", seven_to_nine_info.Receive());
+ base::win::ScopedProcessInformation nine_to_seven_info;
+ DoCreateProcess("ReturnNine", nine_to_seven_info.Receive());
+
+ HANDLE seven_process = seven_to_nine_info.process_handle();
+ DWORD seven_process_id = seven_to_nine_info.process_id();
+ HANDLE seven_thread = seven_to_nine_info.thread_handle();
+ DWORD seven_thread_id = seven_to_nine_info.thread_id();
+ HANDLE nine_process = nine_to_seven_info.process_handle();
+ DWORD nine_process_id = nine_to_seven_info.process_id();
+ HANDLE nine_thread = nine_to_seven_info.thread_handle();
+ DWORD nine_thread_id = nine_to_seven_info.thread_id();
+
+ seven_to_nine_info.Swap(&nine_to_seven_info);
+
+ ASSERT_EQ(seven_process, nine_to_seven_info.process_handle());
+ ASSERT_EQ(seven_process_id, nine_to_seven_info.process_id());
+ ASSERT_EQ(seven_thread, nine_to_seven_info.thread_handle());
+ ASSERT_EQ(seven_thread_id, nine_to_seven_info.thread_id());
+ ASSERT_EQ(nine_process, seven_to_nine_info.process_handle());
+ ASSERT_EQ(nine_process_id, seven_to_nine_info.process_id());
+ ASSERT_EQ(nine_thread, seven_to_nine_info.thread_handle());
+ ASSERT_EQ(nine_thread_id, seven_to_nine_info.thread_id());
+
+ int exit_code = 0;
+ ASSERT_TRUE(base::WaitForExitCode(seven_to_nine_info.TakeProcessHandle(),
+ &exit_code));
+ ASSERT_EQ(9, exit_code);
+
+ ASSERT_TRUE(base::WaitForExitCode(nine_to_seven_info.TakeProcessHandle(),
+ &exit_code));
+ ASSERT_EQ(7, exit_code);
+
+}
+
+TEST_F(ScopedProcessInformationTest, InitiallyInvalid) {
+ base::win::ScopedProcessInformation process_info;
+ ASSERT_FALSE(process_info.IsValid());
+}
diff --git a/src/base/win/scoped_select_object.h b/src/base/win/scoped_select_object.h
new file mode 100644
index 0000000..347de79
--- /dev/null
+++ b/src/base/win/scoped_select_object.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_SCOPED_SELECT_OBJECT_H_
+#define BASE_WIN_SCOPED_SELECT_OBJECT_H_
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace base {
+namespace win {
+
+// Helper class for deselecting object from DC.
+class ScopedSelectObject {
+ public:
+ ScopedSelectObject(HDC hdc, HGDIOBJ object)
+ : hdc_(hdc),
+ oldobj_(SelectObject(hdc, object)) {
+ DCHECK(hdc_);
+ DCHECK(object);
+ DCHECK(oldobj_ != NULL && oldobj_ != HGDI_ERROR);
+ }
+
+ ~ScopedSelectObject() {
+ HGDIOBJ object = SelectObject(hdc_, oldobj_);
+ DCHECK((GetObjectType(oldobj_) != OBJ_REGION && object != NULL) ||
+ (GetObjectType(oldobj_) == OBJ_REGION && object != HGDI_ERROR));
+ }
+
+ private:
+ HDC hdc_;
+ HGDIOBJ oldobj_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedSelectObject);
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_SCOPED_SELECT_OBJECT_H_
diff --git a/src/base/win/scoped_variant.cc b/src/base/win/scoped_variant.cc
new file mode 100644
index 0000000..f57ab93
--- /dev/null
+++ b/src/base/win/scoped_variant.cc
@@ -0,0 +1,276 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/scoped_variant.h"
+#include "base/logging.h"
+
+namespace base {
+namespace win {
+
+// Global, const instance of an empty variant.
+const VARIANT ScopedVariant::kEmptyVariant = { VT_EMPTY };
+
+ScopedVariant::~ScopedVariant() {
+ COMPILE_ASSERT(sizeof(ScopedVariant) == sizeof(VARIANT), ScopedVariantSize);
+ ::VariantClear(&var_);
+}
+
+ScopedVariant::ScopedVariant(const wchar_t* str) {
+ var_.vt = VT_EMPTY;
+ Set(str);
+}
+
+ScopedVariant::ScopedVariant(const wchar_t* str, UINT length) {
+ var_.vt = VT_BSTR;
+ var_.bstrVal = ::SysAllocStringLen(str, length);
+}
+
+ScopedVariant::ScopedVariant(int value, VARTYPE vt) {
+ var_.vt = vt;
+ var_.lVal = value;
+}
+
+ScopedVariant::ScopedVariant(double value, VARTYPE vt) {
+ DCHECK(vt == VT_R8 || vt == VT_DATE);
+ var_.vt = vt;
+ var_.dblVal = value;
+}
+
+ScopedVariant::ScopedVariant(IDispatch* dispatch) {
+ var_.vt = VT_EMPTY;
+ Set(dispatch);
+}
+
+ScopedVariant::ScopedVariant(IUnknown* unknown) {
+ var_.vt = VT_EMPTY;
+ Set(unknown);
+}
+
+ScopedVariant::ScopedVariant(SAFEARRAY* safearray) {
+ var_.vt = VT_EMPTY;
+ Set(safearray);
+}
+
+ScopedVariant::ScopedVariant(const VARIANT& var) {
+ var_.vt = VT_EMPTY;
+ Set(var);
+}
+
+void ScopedVariant::Reset(const VARIANT& var) {
+ if (&var != &var_) {
+ ::VariantClear(&var_);
+ var_ = var;
+ }
+}
+
+VARIANT ScopedVariant::Release() {
+ VARIANT var = var_;
+ var_.vt = VT_EMPTY;
+ return var;
+}
+
+void ScopedVariant::Swap(ScopedVariant& var) {
+ VARIANT tmp = var_;
+ var_ = var.var_;
+ var.var_ = tmp;
+}
+
+VARIANT* ScopedVariant::Receive() {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "variant leak. type: " << var_.vt;
+ return &var_;
+}
+
+VARIANT ScopedVariant::Copy() const {
+ VARIANT ret = { VT_EMPTY };
+ ::VariantCopy(&ret, &var_);
+ return ret;
+}
+
+int ScopedVariant::Compare(const VARIANT& var, bool ignore_case) const {
+ ULONG flags = ignore_case ? NORM_IGNORECASE : 0;
+ HRESULT hr = ::VarCmp(const_cast<VARIANT*>(&var_), const_cast<VARIANT*>(&var),
+ LOCALE_USER_DEFAULT, flags);
+ int ret = 0;
+
+ switch (hr) {
+ case VARCMP_LT:
+ ret = -1;
+ break;
+
+ case VARCMP_GT:
+ case VARCMP_NULL:
+ ret = 1;
+ break;
+
+ default:
+ // Equal.
+ break;
+ }
+
+ return ret;
+}
+
+void ScopedVariant::Set(const wchar_t* str) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_BSTR;
+ var_.bstrVal = ::SysAllocString(str);
+}
+
+void ScopedVariant::Set(int8 i8) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_I1;
+ var_.cVal = i8;
+}
+
+void ScopedVariant::Set(uint8 ui8) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_UI1;
+ var_.bVal = ui8;
+}
+
+void ScopedVariant::Set(int16 i16) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_I2;
+ var_.iVal = i16;
+}
+
+void ScopedVariant::Set(uint16 ui16) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_UI2;
+ var_.uiVal = ui16;
+}
+
+void ScopedVariant::Set(int32 i32) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_I4;
+ var_.lVal = i32;
+}
+
+void ScopedVariant::Set(uint32 ui32) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_UI4;
+ var_.ulVal = ui32;
+}
+
+void ScopedVariant::Set(int64 i64) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_I8;
+ var_.llVal = i64;
+}
+
+void ScopedVariant::Set(uint64 ui64) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_UI8;
+ var_.ullVal = ui64;
+}
+
+void ScopedVariant::Set(float r32) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_R4;
+ var_.fltVal = r32;
+}
+
+void ScopedVariant::Set(double r64) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_R8;
+ var_.dblVal = r64;
+}
+
+void ScopedVariant::SetDate(DATE date) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_DATE;
+ var_.date = date;
+}
+
+void ScopedVariant::Set(IDispatch* disp) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_DISPATCH;
+ var_.pdispVal = disp;
+ if (disp)
+ disp->AddRef();
+}
+
+void ScopedVariant::Set(bool b) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_BOOL;
+ var_.boolVal = b ? VARIANT_TRUE : VARIANT_FALSE;
+}
+
+void ScopedVariant::Set(IUnknown* unk) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_UNKNOWN;
+ var_.punkVal = unk;
+ if (unk)
+ unk->AddRef();
+}
+
+void ScopedVariant::Set(SAFEARRAY* array) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ if (SUCCEEDED(::SafeArrayGetVartype(array, &var_.vt))) {
+ var_.vt |= VT_ARRAY;
+ var_.parray = array;
+ } else {
+ DCHECK(!array) << "Unable to determine safearray vartype";
+ var_.vt = VT_EMPTY;
+ }
+}
+
+void ScopedVariant::Set(const VARIANT& var) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ if (FAILED(::VariantCopy(&var_, &var))) {
+ DLOG(ERROR) << "VariantCopy failed";
+ var_.vt = VT_EMPTY;
+ }
+}
+
+ScopedVariant& ScopedVariant::operator=(const VARIANT& var) {
+ if (&var != &var_) {
+ VariantClear(&var_);
+ Set(var);
+ }
+ return *this;
+}
+
+bool ScopedVariant::IsLeakableVarType(VARTYPE vt) {
+ bool leakable = false;
+ switch (vt & VT_TYPEMASK) {
+ case VT_BSTR:
+ case VT_DISPATCH:
+ // we treat VT_VARIANT as leakable to err on the safe side.
+ case VT_VARIANT:
+ case VT_UNKNOWN:
+ case VT_SAFEARRAY:
+
+ // very rarely used stuff (if ever):
+ case VT_VOID:
+ case VT_PTR:
+ case VT_CARRAY:
+ case VT_USERDEFINED:
+ case VT_LPSTR:
+ case VT_LPWSTR:
+ case VT_RECORD:
+ case VT_INT_PTR:
+ case VT_UINT_PTR:
+ case VT_FILETIME:
+ case VT_BLOB:
+ case VT_STREAM:
+ case VT_STORAGE:
+ case VT_STREAMED_OBJECT:
+ case VT_STORED_OBJECT:
+ case VT_BLOB_OBJECT:
+ case VT_VERSIONED_STREAM:
+ case VT_BSTR_BLOB:
+ leakable = true;
+ break;
+ }
+
+ if (!leakable && (vt & VT_ARRAY) != 0) {
+ leakable = true;
+ }
+
+ return leakable;
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/scoped_variant.h b/src/base/win/scoped_variant.h
new file mode 100644
index 0000000..b6e6579
--- /dev/null
+++ b/src/base/win/scoped_variant.h
@@ -0,0 +1,166 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_SCOPED_VARIANT_H_
+#define BASE_WIN_SCOPED_VARIANT_H_
+
+#include <windows.h>
+#include <oleauto.h>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+namespace win {
+
+// Scoped VARIANT class for automatically freeing a COM VARIANT at the
+// end of a scope. Additionally provides a few functions to make the
+// encapsulated VARIANT easier to use.
+// Instead of inheriting from VARIANT, we take the containment approach
+// in order to have more control over the usage of the variant and guard
+// against memory leaks.
+class BASE_EXPORT ScopedVariant {
+ public:
+ // Declaration of a global variant variable that's always VT_EMPTY
+ static const VARIANT kEmptyVariant;
+
+ // Default constructor.
+ ScopedVariant() {
+ // This is equivalent to what VariantInit does, but less code.
+ var_.vt = VT_EMPTY;
+ }
+
+ // Constructor to create a new VT_BSTR VARIANT.
+ // NOTE: Do not pass a BSTR to this constructor expecting ownership to
+ // be transferred
+ explicit ScopedVariant(const wchar_t* str);
+
+ // Creates a new VT_BSTR variant of a specified length.
+ ScopedVariant(const wchar_t* str, UINT length);
+
+ // Creates a new integral type variant and assigns the value to
+ // VARIANT.lVal (32 bit sized field).
+ explicit ScopedVariant(int value, VARTYPE vt = VT_I4);
+
+ // Creates a new double-precision type variant. |vt| must be either VT_R8
+ // or VT_DATE.
+ explicit ScopedVariant(double value, VARTYPE vt = VT_R8);
+
+ // VT_DISPATCH
+ explicit ScopedVariant(IDispatch* dispatch);
+
+ // VT_UNKNOWN
+ explicit ScopedVariant(IUnknown* unknown);
+
+ // SAFEARRAY
+ explicit ScopedVariant(SAFEARRAY* safearray);
+
+ // Copies the variant.
+ explicit ScopedVariant(const VARIANT& var);
+
+ ~ScopedVariant();
+
+ inline VARTYPE type() const {
+ return var_.vt;
+ }
+
+ // Give ScopedVariant ownership over an already allocated VARIANT.
+ void Reset(const VARIANT& var = kEmptyVariant);
+
+ // Releases ownership of the VARIANT to the caller.
+ VARIANT Release();
+
+ // Swap two ScopedVariant's.
+ void Swap(ScopedVariant& var);
+
+ // Returns a copy of the variant.
+ VARIANT Copy() const;
+
+ // The return value is 0 if the variants are equal, 1 if this object is
+ // greater than |var|, -1 if it is smaller.
+ int Compare(const VARIANT& var, bool ignore_case = false) const;
+
+ // Retrieves the pointer address.
+ // Used to receive a VARIANT as an out argument (and take ownership).
+ // The function DCHECKs on the current value being empty/null.
+ // Usage: GetVariant(var.receive());
+ VARIANT* Receive();
+
+ void Set(const wchar_t* str);
+
+ // Setters for simple types.
+ void Set(int8 i8);
+ void Set(uint8 ui8);
+ void Set(int16 i16);
+ void Set(uint16 ui16);
+ void Set(int32 i32);
+ void Set(uint32 ui32);
+ void Set(int64 i64);
+ void Set(uint64 ui64);
+ void Set(float r32);
+ void Set(double r64);
+ void Set(bool b);
+
+ // Creates a copy of |var| and assigns as this instance's value.
+ // Note that this is different from the Reset() method that's used to
+ // free the current value and assume ownership.
+ void Set(const VARIANT& var);
+
+ // COM object setters
+ void Set(IDispatch* disp);
+ void Set(IUnknown* unk);
+
+ // SAFEARRAY support
+ void Set(SAFEARRAY* array);
+
+ // Special setter for DATE since DATE is a double and we already have
+ // a setter for double.
+ void SetDate(DATE date);
+
+ // Allows const access to the contained variant without DCHECKs etc.
+ // This support is necessary for the V_XYZ (e.g. V_BSTR) set of macros to
+ // work properly but still doesn't allow modifications since we want control
+ // over that.
+ const VARIANT* operator&() const {
+ return &var_;
+ }
+
+ // Like other scoped classes (e.g scoped_refptr, ScopedComPtr, ScopedBstr)
+ // we support the assignment operator for the type we wrap.
+ ScopedVariant& operator=(const VARIANT& var);
+
+ // A hack to pass a pointer to the variant where the accepting
+ // function treats the variant as an input-only, read-only value
+ // but the function prototype requires a non const variant pointer.
+ // There's no DCHECK or anything here. Callers must know what they're doing.
+ VARIANT* AsInput() const {
+ // The nature of this function is const, so we declare
+ // it as such and cast away the constness here.
+ return const_cast<VARIANT*>(&var_);
+ }
+
+ // Allows the ScopedVariant instance to be passed to functions either by value
+ // or by const reference.
+ operator const VARIANT&() const {
+ return var_;
+ }
+
+ // Used as a debug check to see if we're leaking anything.
+ static bool IsLeakableVarType(VARTYPE vt);
+
+ protected:
+ VARIANT var_;
+
+ private:
+ // Comparison operators for ScopedVariant are not supported at this point.
+ // Use the Compare method instead.
+ bool operator==(const ScopedVariant& var) const;
+ bool operator!=(const ScopedVariant& var) const;
+ DISALLOW_COPY_AND_ASSIGN(ScopedVariant);
+};
+
+} // namespace win
+} // namesoace base
+
+#endif // BASE_WIN_SCOPED_VARIANT_H_
diff --git a/src/base/win/scoped_variant_unittest.cc b/src/base/win/scoped_variant_unittest.cc
new file mode 100644
index 0000000..1f017cf
--- /dev/null
+++ b/src/base/win/scoped_variant_unittest.cc
@@ -0,0 +1,261 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/scoped_variant.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace win {
+
+namespace {
+
+static const wchar_t kTestString1[] = L"Used to create BSTRs";
+static const wchar_t kTestString2[] = L"Also used to create BSTRs";
+
+void GiveMeAVariant(VARIANT* ret) {
+ EXPECT_TRUE(ret != NULL);
+ ret->vt = VT_BSTR;
+ V_BSTR(ret) = ::SysAllocString(kTestString1);
+}
+
+// A dummy IDispatch implementation (if you can call it that).
+// The class does nothing intelligent really. Only increments a counter
+// when AddRef is called and decrements it when Release is called.
+class FakeComObject : public IDispatch {
+ public:
+ FakeComObject() : ref_(0) {
+ }
+
+ STDMETHOD_(DWORD, AddRef)() {
+ ref_++;
+ return ref_;
+ }
+
+ STDMETHOD_(DWORD, Release)() {
+ ref_--;
+ return ref_;
+ }
+
+ STDMETHOD(QueryInterface)(REFIID, void**) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(GetTypeInfoCount)(UINT*) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(GetTypeInfo)(UINT, LCID, ITypeInfo**) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(GetIDsOfNames)(REFIID, LPOLESTR*, UINT, LCID, DISPID*) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(Invoke)(DISPID, REFIID, LCID, WORD, DISPPARAMS*, VARIANT*,
+ EXCEPINFO*, UINT*) {
+ return E_NOTIMPL;
+ }
+
+ // A way to check the internal reference count of the class.
+ int ref_count() const {
+ return ref_;
+ }
+
+ protected:
+ int ref_;
+};
+
+} // namespace
+
+TEST(ScopedVariantTest, ScopedVariant) {
+ ScopedVariant var;
+ EXPECT_TRUE(var.type() == VT_EMPTY);
+ // V_BSTR(&var) = NULL; <- NOTE: Assignment like that is not supported
+
+ ScopedVariant var_bstr(L"VT_BSTR");
+ EXPECT_EQ(VT_BSTR, V_VT(&var_bstr));
+ EXPECT_TRUE(V_BSTR(&var_bstr) != NULL); // can't use EXPECT_NE for BSTR
+ var_bstr.Reset();
+ EXPECT_NE(VT_BSTR, V_VT(&var_bstr));
+ var_bstr.Set(kTestString2);
+ EXPECT_EQ(VT_BSTR, V_VT(&var_bstr));
+
+ VARIANT tmp = var_bstr.Release();
+ EXPECT_EQ(VT_EMPTY, V_VT(&var_bstr));
+ EXPECT_EQ(VT_BSTR, V_VT(&tmp));
+ EXPECT_EQ(0, lstrcmp(V_BSTR(&tmp), kTestString2));
+
+ var.Reset(tmp);
+ EXPECT_EQ(VT_BSTR, V_VT(&var));
+ EXPECT_EQ(0, lstrcmpW(V_BSTR(&var), kTestString2));
+
+ var_bstr.Swap(var);
+ EXPECT_EQ(VT_EMPTY, V_VT(&var));
+ EXPECT_EQ(VT_BSTR, V_VT(&var_bstr));
+ EXPECT_EQ(0, lstrcmpW(V_BSTR(&var_bstr), kTestString2));
+ var_bstr.Reset();
+
+ // Test the Compare and Copy routines.
+ GiveMeAVariant(var_bstr.Receive());
+ ScopedVariant var_bstr2(V_BSTR(&var_bstr));
+ EXPECT_EQ(0, var_bstr.Compare(var_bstr2));
+ var_bstr2.Reset();
+ EXPECT_NE(0, var_bstr.Compare(var_bstr2));
+ var_bstr2.Reset(var_bstr.Copy());
+ EXPECT_EQ(0, var_bstr.Compare(var_bstr2));
+ var_bstr2.Reset();
+ var_bstr2.Set(V_BSTR(&var_bstr));
+ EXPECT_EQ(0, var_bstr.Compare(var_bstr2));
+ var_bstr2.Reset();
+ var_bstr.Reset();
+
+ // Test for the SetDate setter.
+ SYSTEMTIME sys_time;
+ ::GetSystemTime(&sys_time);
+ DATE date;
+ ::SystemTimeToVariantTime(&sys_time, &date);
+ var.Reset();
+ var.SetDate(date);
+ EXPECT_EQ(VT_DATE, var.type());
+ EXPECT_EQ(date, V_DATE(&var));
+
+ // Simple setter tests. These do not require resetting the variant
+ // after each test since the variant type is not "leakable" (i.e. doesn't
+ // need to be freed explicitly).
+
+ // We need static cast here since char defaults to int (!?).
+ var.Set(static_cast<int8>('v'));
+ EXPECT_EQ(VT_I1, var.type());
+ EXPECT_EQ('v', V_I1(&var));
+
+ var.Set(static_cast<short>(123));
+ EXPECT_EQ(VT_I2, var.type());
+ EXPECT_EQ(123, V_I2(&var));
+
+ var.Set(static_cast<int32>(123));
+ EXPECT_EQ(VT_I4, var.type());
+ EXPECT_EQ(123, V_I4(&var));
+
+ var.Set(static_cast<int64>(123));
+ EXPECT_EQ(VT_I8, var.type());
+ EXPECT_EQ(123, V_I8(&var));
+
+ var.Set(static_cast<uint8>(123));
+ EXPECT_EQ(VT_UI1, var.type());
+ EXPECT_EQ(123, V_UI1(&var));
+
+ var.Set(static_cast<unsigned short>(123));
+ EXPECT_EQ(VT_UI2, var.type());
+ EXPECT_EQ(123, V_UI2(&var));
+
+ var.Set(static_cast<uint32>(123));
+ EXPECT_EQ(VT_UI4, var.type());
+ EXPECT_EQ(123, V_UI4(&var));
+
+ var.Set(static_cast<uint64>(123));
+ EXPECT_EQ(VT_UI8, var.type());
+ EXPECT_EQ(123, V_UI8(&var));
+
+ var.Set(123.123f);
+ EXPECT_EQ(VT_R4, var.type());
+ EXPECT_EQ(123.123f, V_R4(&var));
+
+ var.Set(static_cast<double>(123.123));
+ EXPECT_EQ(VT_R8, var.type());
+ EXPECT_EQ(123.123, V_R8(&var));
+
+ var.Set(true);
+ EXPECT_EQ(VT_BOOL, var.type());
+ EXPECT_EQ(VARIANT_TRUE, V_BOOL(&var));
+ var.Set(false);
+ EXPECT_EQ(VT_BOOL, var.type());
+ EXPECT_EQ(VARIANT_FALSE, V_BOOL(&var));
+
+ // Com interface tests
+
+ var.Set(static_cast<IDispatch*>(NULL));
+ EXPECT_EQ(VT_DISPATCH, var.type());
+ EXPECT_EQ(NULL, V_DISPATCH(&var));
+ var.Reset();
+
+ var.Set(static_cast<IUnknown*>(NULL));
+ EXPECT_EQ(VT_UNKNOWN, var.type());
+ EXPECT_EQ(NULL, V_UNKNOWN(&var));
+ var.Reset();
+
+ FakeComObject faker;
+ EXPECT_EQ(0, faker.ref_count());
+ var.Set(static_cast<IDispatch*>(&faker));
+ EXPECT_EQ(VT_DISPATCH, var.type());
+ EXPECT_EQ(&faker, V_DISPATCH(&var));
+ EXPECT_EQ(1, faker.ref_count());
+ var.Reset();
+ EXPECT_EQ(0, faker.ref_count());
+
+ var.Set(static_cast<IUnknown*>(&faker));
+ EXPECT_EQ(VT_UNKNOWN, var.type());
+ EXPECT_EQ(&faker, V_UNKNOWN(&var));
+ EXPECT_EQ(1, faker.ref_count());
+ var.Reset();
+ EXPECT_EQ(0, faker.ref_count());
+
+ {
+ ScopedVariant disp_var(&faker);
+ EXPECT_EQ(VT_DISPATCH, disp_var.type());
+ EXPECT_EQ(&faker, V_DISPATCH(&disp_var));
+ EXPECT_EQ(1, faker.ref_count());
+ }
+ EXPECT_EQ(0, faker.ref_count());
+
+ {
+ ScopedVariant ref1(&faker);
+ EXPECT_EQ(1, faker.ref_count());
+ ScopedVariant ref2(static_cast<const VARIANT&>(ref1));
+ EXPECT_EQ(2, faker.ref_count());
+ ScopedVariant ref3;
+ ref3 = static_cast<const VARIANT&>(ref2);
+ EXPECT_EQ(3, faker.ref_count());
+ }
+ EXPECT_EQ(0, faker.ref_count());
+
+ {
+ ScopedVariant unk_var(static_cast<IUnknown*>(&faker));
+ EXPECT_EQ(VT_UNKNOWN, unk_var.type());
+ EXPECT_EQ(&faker, V_UNKNOWN(&unk_var));
+ EXPECT_EQ(1, faker.ref_count());
+ }
+ EXPECT_EQ(0, faker.ref_count());
+
+ VARIANT raw;
+ raw.vt = VT_UNKNOWN;
+ raw.punkVal = &faker;
+ EXPECT_EQ(0, faker.ref_count());
+ var.Set(raw);
+ EXPECT_EQ(1, faker.ref_count());
+ var.Reset();
+ EXPECT_EQ(0, faker.ref_count());
+
+ {
+ ScopedVariant number(123);
+ EXPECT_EQ(VT_I4, number.type());
+ EXPECT_EQ(123, V_I4(&number));
+ }
+
+ // SAFEARRAY tests
+ var.Set(static_cast<SAFEARRAY*>(NULL));
+ EXPECT_EQ(VT_EMPTY, var.type());
+
+ SAFEARRAY* sa = ::SafeArrayCreateVector(VT_UI1, 0, 100);
+ ASSERT_TRUE(sa != NULL);
+
+ var.Set(sa);
+ EXPECT_TRUE(ScopedVariant::IsLeakableVarType(var.type()));
+ EXPECT_EQ(VT_ARRAY | VT_UI1, var.type());
+ EXPECT_EQ(sa, V_ARRAY(&var));
+ // The array is destroyed in the destructor of var.
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/shortcut.cc b/src/base/win/shortcut.cc
new file mode 100644
index 0000000..8afd55d
--- /dev/null
+++ b/src/base/win/shortcut.cc
@@ -0,0 +1,233 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/shortcut.h"
+
+#include <shellapi.h>
+#include <shlobj.h>
+#include <propkey.h>
+
+#include "base/file_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/win/scoped_comptr.h"
+#include "base/win/win_util.h"
+#include "base/win/windows_version.h"
+
+namespace base {
+namespace win {
+
+namespace {
+
+// Initializes |i_shell_link| and |i_persist_file| (releasing them first if they
+// are already initialized).
+// If |shortcut| is not NULL, loads |shortcut| into |i_persist_file|.
+// If any of the above steps fail, both |i_shell_link| and |i_persist_file| will
+// be released.
+void InitializeShortcutInterfaces(
+ const wchar_t* shortcut,
+ ScopedComPtr<IShellLink>* i_shell_link,
+ ScopedComPtr<IPersistFile>* i_persist_file) {
+ i_shell_link->Release();
+ i_persist_file->Release();
+ if (FAILED(i_shell_link->CreateInstance(CLSID_ShellLink, NULL,
+ CLSCTX_INPROC_SERVER)) ||
+ FAILED(i_persist_file->QueryFrom(*i_shell_link)) ||
+ (shortcut && FAILED((*i_persist_file)->Load(shortcut, STGM_READWRITE)))) {
+ i_shell_link->Release();
+ i_persist_file->Release();
+ }
+}
+
+} // namespace
+
+bool CreateOrUpdateShortcutLink(const FilePath& shortcut_path,
+ const ShortcutProperties& properties,
+ ShortcutOperation operation) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ // A target is required unless |operation| is SHORTCUT_UPDATE_EXISTING.
+ if (operation != SHORTCUT_UPDATE_EXISTING &&
+ !(properties.options & ShortcutProperties::PROPERTIES_TARGET)) {
+ NOTREACHED();
+ return false;
+ }
+
+ bool shortcut_existed = file_util::PathExists(shortcut_path);
+
+ ScopedComPtr<IShellLink> i_shell_link;
+ ScopedComPtr<IPersistFile> i_persist_file;
+ switch (operation) {
+ case SHORTCUT_CREATE_ALWAYS:
+ InitializeShortcutInterfaces(NULL, &i_shell_link, &i_persist_file);
+ break;
+ case SHORTCUT_UPDATE_EXISTING:
+ InitializeShortcutInterfaces(shortcut_path.value().c_str(), &i_shell_link,
+ &i_persist_file);
+ break;
+ case SHORTCUT_REPLACE_EXISTING:
+ InitializeShortcutInterfaces(shortcut_path.value().c_str(), &i_shell_link,
+ &i_persist_file);
+ // Confirm |shortcut_path| exists and is a shortcut by verifying
+ // |i_persist_file| was successfully initialized in the call above. If so,
+ // re-initialize the interfaces to begin writing a new shortcut (to
+ // overwrite the current one if successful).
+ if (i_persist_file.get())
+ InitializeShortcutInterfaces(NULL, &i_shell_link, &i_persist_file);
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ // Return false immediately upon failure to initialize shortcut interfaces.
+ if (!i_persist_file.get())
+ return false;
+
+ if ((properties.options & ShortcutProperties::PROPERTIES_TARGET) &&
+ FAILED(i_shell_link->SetPath(properties.target.value().c_str()))) {
+ return false;
+ }
+
+ if ((properties.options & ShortcutProperties::PROPERTIES_WORKING_DIR) &&
+ FAILED(i_shell_link->SetWorkingDirectory(
+ properties.working_dir.value().c_str()))) {
+ return false;
+ }
+
+ if ((properties.options & ShortcutProperties::PROPERTIES_ARGUMENTS) &&
+ FAILED(i_shell_link->SetArguments(properties.arguments.c_str()))) {
+ return false;
+ }
+
+ if ((properties.options & ShortcutProperties::PROPERTIES_DESCRIPTION) &&
+ FAILED(i_shell_link->SetDescription(properties.description.c_str()))) {
+ return false;
+ }
+
+ if ((properties.options & ShortcutProperties::PROPERTIES_ICON) &&
+ FAILED(i_shell_link->SetIconLocation(properties.icon.value().c_str(),
+ properties.icon_index))) {
+ return false;
+ }
+
+ bool has_app_id =
+ (properties.options & ShortcutProperties::PROPERTIES_APP_ID) != 0;
+ bool has_dual_mode =
+ (properties.options & ShortcutProperties::PROPERTIES_DUAL_MODE) != 0;
+ if ((has_app_id || has_dual_mode) &&
+ GetVersion() >= VERSION_WIN7) {
+ ScopedComPtr<IPropertyStore> property_store;
+ if (FAILED(property_store.QueryFrom(i_shell_link)) || !property_store.get())
+ return false;
+
+ if (has_app_id &&
+ !SetAppIdForPropertyStore(property_store, properties.app_id.c_str())) {
+ return false;
+ }
+ if (has_dual_mode &&
+ !SetBooleanValueForPropertyStore(property_store,
+ PKEY_AppUserModel_IsDualMode,
+ properties.dual_mode)) {
+ return false;
+ }
+ }
+
+ HRESULT result = i_persist_file->Save(shortcut_path.value().c_str(), TRUE);
+
+ // Release the interfaces in case the SHChangeNotify call below depends on
+ // the operations above being fully completed.
+ i_persist_file.Release();
+ i_shell_link.Release();
+
+ // If we successfully created/updated the icon, notify the shell that we have
+ // done so.
+ const bool succeeded = SUCCEEDED(result);
+ if (succeeded) {
+ if (shortcut_existed) {
+ // TODO(gab): SHCNE_UPDATEITEM might be sufficient here; further testing
+ // required.
+ SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
+ } else {
+ SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, shortcut_path.value().c_str(),
+ NULL);
+ }
+ }
+
+ return succeeded;
+}
+
+bool ResolveShortcut(const FilePath& shortcut_path,
+ FilePath* target_path,
+ string16* args) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ HRESULT result;
+ ScopedComPtr<IShellLink> i_shell_link;
+
+ // Get pointer to the IShellLink interface.
+ result = i_shell_link.CreateInstance(CLSID_ShellLink, NULL,
+ CLSCTX_INPROC_SERVER);
+ if (FAILED(result))
+ return false;
+
+ ScopedComPtr<IPersistFile> persist;
+ // Query IShellLink for the IPersistFile interface.
+ result = persist.QueryFrom(i_shell_link);
+ if (FAILED(result))
+ return false;
+
+ // Load the shell link.
+ result = persist->Load(shortcut_path.value().c_str(), STGM_READ);
+ if (FAILED(result))
+ return false;
+
+ WCHAR temp[MAX_PATH];
+ if (target_path) {
+ // Try to find the target of a shortcut.
+ result = i_shell_link->Resolve(0, SLR_NO_UI);
+ if (FAILED(result))
+ return false;
+
+ result = i_shell_link->GetPath(temp, MAX_PATH, NULL, SLGP_UNCPRIORITY);
+ if (FAILED(result))
+ return false;
+
+ *target_path = FilePath(temp);
+ }
+
+ if (args) {
+ result = i_shell_link->GetArguments(temp, MAX_PATH);
+ if (FAILED(result))
+ return false;
+
+ *args = string16(temp);
+ }
+ return true;
+}
+
+bool TaskbarPinShortcutLink(const wchar_t* shortcut) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ // "Pin to taskbar" is only supported after Win7.
+ if (GetVersion() < VERSION_WIN7)
+ return false;
+
+ int result = reinterpret_cast<int>(ShellExecute(NULL, L"taskbarpin", shortcut,
+ NULL, NULL, 0));
+ return result > 32;
+}
+
+bool TaskbarUnpinShortcutLink(const wchar_t* shortcut) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ // "Unpin from taskbar" is only supported after Win7.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return false;
+
+ int result = reinterpret_cast<int>(ShellExecute(NULL, L"taskbarunpin",
+ shortcut, NULL, NULL, 0));
+ return result > 32;
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/shortcut.h b/src/base/win/shortcut.h
new file mode 100644
index 0000000..c1e7d5c
--- /dev/null
+++ b/src/base/win/shortcut.h
@@ -0,0 +1,141 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_SHORTCUT_H_
+#define BASE_WIN_SHORTCUT_H_
+
+#include <windows.h>
+
+#include "base/logging.h"
+#include "base/file_path.h"
+#include "base/string16.h"
+
+namespace base {
+namespace win {
+
+enum ShortcutOperation {
+ // Create a new shortcut (overwriting if necessary).
+ SHORTCUT_CREATE_ALWAYS = 0,
+ // Overwrite an existing shortcut (fails if the shortcut doesn't exist).
+ SHORTCUT_REPLACE_EXISTING,
+ // Update specified properties only on an existing shortcut.
+ SHORTCUT_UPDATE_EXISTING,
+};
+
+// Properties for shortcuts. Properties set will be applied to the shortcut on
+// creation/update, others will be ignored.
+// Callers are encouraged to use the setters provided which take care of
+// setting |options| as desired.
+struct ShortcutProperties {
+ enum IndividualProperties {
+ PROPERTIES_TARGET = 1 << 0,
+ PROPERTIES_WORKING_DIR = 1 << 1,
+ PROPERTIES_ARGUMENTS = 1 << 2,
+ PROPERTIES_DESCRIPTION = 1 << 3,
+ PROPERTIES_ICON = 1 << 4,
+ PROPERTIES_APP_ID = 1 << 5,
+ PROPERTIES_DUAL_MODE = 1 << 6,
+ };
+
+ ShortcutProperties() : icon_index(-1), dual_mode(false), options(0U) {}
+
+ void set_target(const FilePath& target_in) {
+ target = target_in;
+ options |= PROPERTIES_TARGET;
+ }
+
+ void set_working_dir(const FilePath& working_dir_in) {
+ working_dir = working_dir_in;
+ options |= PROPERTIES_WORKING_DIR;
+ }
+
+ void set_arguments(const string16& arguments_in) {
+ // Size restriction as per MSDN at http://goo.gl/TJ7q5.
+ DCHECK(arguments_in.size() < MAX_PATH);
+ arguments = arguments_in;
+ options |= PROPERTIES_ARGUMENTS;
+ }
+
+ void set_description(const string16& description_in) {
+ // Size restriction as per MSDN at http://goo.gl/OdNQq.
+ DCHECK(description_in.size() < MAX_PATH);
+ description = description_in;
+ options |= PROPERTIES_DESCRIPTION;
+ }
+
+ void set_icon(const FilePath& icon_in, int icon_index_in) {
+ icon = icon_in;
+ icon_index = icon_index_in;
+ options |= PROPERTIES_ICON;
+ }
+
+ void set_app_id(const string16& app_id_in) {
+ app_id = app_id_in;
+ options |= PROPERTIES_APP_ID;
+ }
+
+ void set_dual_mode(bool dual_mode_in) {
+ dual_mode = dual_mode_in;
+ options |= PROPERTIES_DUAL_MODE;
+ }
+
+ // The target to launch from this shortcut. This is mandatory when creating
+ // a shortcut.
+ FilePath target;
+ // The name of the working directory when launching the shortcut.
+ FilePath working_dir;
+ // The arguments to be applied to |target| when launching from this shortcut.
+ // The length of this string must be less than MAX_PATH.
+ string16 arguments;
+ // The localized description of the shortcut.
+ // The length of this string must be less than MAX_PATH.
+ string16 description;
+ // The path to the icon (can be a dll or exe, in which case |icon_index| is
+ // the resource id).
+ FilePath icon;
+ int icon_index;
+ // The app model id for the shortcut (Win7+).
+ string16 app_id;
+ // Whether this is a dual mode shortcut (Win8+).
+ bool dual_mode;
+ // Bitfield made of IndividualProperties. Properties set in |options| will be
+ // set on the shortcut, others will be ignored.
+ uint32 options;
+};
+
+// This method creates (or updates) a shortcut link at |shortcut_path| using the
+// information given through |properties|.
+// Ensure you have initialized COM before calling into this function.
+// |operation|: a choice from the ShortcutOperation enum.
+// If |operation| is SHORTCUT_REPLACE_EXISTING or SHORTCUT_UPDATE_EXISTING and
+// |shortcut_path| does not exist, this method is a no-op and returns false.
+BASE_EXPORT bool CreateOrUpdateShortcutLink(
+ const FilePath& shortcut_path,
+ const ShortcutProperties& properties,
+ ShortcutOperation operation);
+
+// Resolve Windows shortcut (.LNK file)
+// This methods tries to resolve a shortcut .LNK file. The path of the shortcut
+// to resolve is in |shortcut_path|. If |target_path| is not NULL, the target
+// will be resolved and placed in |target_path|. If |args| is not NULL, the
+// arguments will be retrieved and placed in |args|. The function returns true
+// if all requested fields are found successfully.
+// Callers can safely use the same variable for both |shortcut_path| and
+// |target_path|.
+BASE_EXPORT bool ResolveShortcut(const FilePath& shortcut_path,
+ FilePath* target_path,
+ string16* args);
+
+// Pins a shortcut to the Windows 7 taskbar. The shortcut file must already
+// exist and be a shortcut that points to an executable.
+BASE_EXPORT bool TaskbarPinShortcutLink(const wchar_t* shortcut);
+
+// Unpins a shortcut from the Windows 7 taskbar. The shortcut must exist and
+// already be pinned to the taskbar.
+BASE_EXPORT bool TaskbarUnpinShortcutLink(const wchar_t* shortcut);
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_SHORTCUT_H_
diff --git a/src/base/win/shortcut_unittest.cc b/src/base/win/shortcut_unittest.cc
new file mode 100644
index 0000000..26b22be
--- /dev/null
+++ b/src/base/win/shortcut_unittest.cc
@@ -0,0 +1,250 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/shortcut.h"
+
+#include <string>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/test/test_file_util.h"
+#include "base/test/test_shortcut_win.h"
+#include "base/win/scoped_com_initializer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+static const char kFileContents[] = "This is a target.";
+static const char kFileContents2[] = "This is another target.";
+
+class ShortcutTest : public testing::Test {
+ protected:
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ ASSERT_TRUE(temp_dir_2_.CreateUniqueTempDir());
+
+ link_file_ = temp_dir_.path().Append(L"My Link.lnk");
+
+ // Shortcut 1's properties
+ {
+ const FilePath target_file(temp_dir_.path().Append(L"Target 1.txt"));
+ file_util::WriteFile(target_file, kFileContents,
+ arraysize(kFileContents));
+
+ link_properties_.set_target(target_file);
+ link_properties_.set_working_dir(temp_dir_.path());
+ link_properties_.set_arguments(L"--magic --awesome");
+ link_properties_.set_description(L"Chrome is awesome.");
+ link_properties_.set_icon(link_properties_.target, 4);
+ link_properties_.set_app_id(L"Chrome");
+ link_properties_.set_dual_mode(false);
+ }
+
+ // Shortcut 2's properties (all different from properties of shortcut 1).
+ {
+ const FilePath target_file_2(temp_dir_.path().Append(L"Target 2.txt"));
+ file_util::WriteFile(target_file_2, kFileContents2,
+ arraysize(kFileContents2));
+
+ FilePath icon_path_2;
+ file_util::CreateTemporaryFileInDir(temp_dir_.path(), &icon_path_2);
+
+ link_properties_2_.set_target(target_file_2);
+ link_properties_2_.set_working_dir(temp_dir_2_.path());
+ link_properties_2_.set_arguments(L"--super --crazy");
+ link_properties_2_.set_description(L"The best in the west.");
+ link_properties_2_.set_icon(icon_path_2, 0);
+ link_properties_2_.set_app_id(L"Chrome.UserLevelCrazySuffix");
+ link_properties_2_.set_dual_mode(true);
+ }
+ }
+
+ base::win::ScopedCOMInitializer com_initializer_;
+ base::ScopedTempDir temp_dir_;
+ base::ScopedTempDir temp_dir_2_;
+
+ // The link file to be created/updated in the shortcut tests below.
+ FilePath link_file_;
+
+ // Properties for the created shortcut.
+ base::win::ShortcutProperties link_properties_;
+
+ // Properties for the updated shortcut.
+ base::win::ShortcutProperties link_properties_2_;
+};
+
+} // namespace
+
+TEST_F(ShortcutTest, CreateAndResolveShortcut) {
+ base::win::ShortcutProperties only_target_properties;
+ only_target_properties.set_target(link_properties_.target);
+
+ ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, only_target_properties, base::win::SHORTCUT_CREATE_ALWAYS));
+
+ FilePath resolved_name;
+ EXPECT_TRUE(base::win::ResolveShortcut(link_file_, &resolved_name, NULL));
+
+ char read_contents[arraysize(kFileContents)];
+ file_util::ReadFile(resolved_name, read_contents, arraysize(read_contents));
+ EXPECT_STREQ(kFileContents, read_contents);
+}
+
+TEST_F(ShortcutTest, ResolveShortcutWithArgs) {
+ ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, link_properties_, base::win::SHORTCUT_CREATE_ALWAYS));
+
+ FilePath resolved_name;
+ string16 args;
+ EXPECT_TRUE(base::win::ResolveShortcut(link_file_, &resolved_name, &args));
+
+ char read_contents[arraysize(kFileContents)];
+ file_util::ReadFile(resolved_name, read_contents, arraysize(read_contents));
+ EXPECT_STREQ(kFileContents, read_contents);
+ EXPECT_EQ(link_properties_.arguments, args);
+}
+
+TEST_F(ShortcutTest, CreateShortcutWithOnlySomeProperties) {
+ base::win::ShortcutProperties target_and_args_properties;
+ target_and_args_properties.set_target(link_properties_.target);
+ target_and_args_properties.set_arguments(link_properties_.arguments);
+
+ ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, target_and_args_properties,
+ base::win::SHORTCUT_CREATE_ALWAYS));
+
+ base::win::ValidateShortcut(link_file_, target_and_args_properties);
+}
+
+TEST_F(ShortcutTest, CreateShortcutVerifyProperties) {
+ ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, link_properties_, base::win::SHORTCUT_CREATE_ALWAYS));
+
+ base::win::ValidateShortcut(link_file_, link_properties_);
+}
+
+TEST_F(ShortcutTest, UpdateShortcutVerifyProperties) {
+ ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, link_properties_, base::win::SHORTCUT_CREATE_ALWAYS));
+
+ ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, link_properties_2_, base::win::SHORTCUT_UPDATE_EXISTING));
+
+ base::win::ValidateShortcut(link_file_, link_properties_2_);
+}
+
+TEST_F(ShortcutTest, UpdateShortcutUpdateOnlyTargetAndResolve) {
+ ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, link_properties_, base::win::SHORTCUT_CREATE_ALWAYS));
+
+ base::win::ShortcutProperties update_only_target_properties;
+ update_only_target_properties.set_target(link_properties_2_.target);
+
+ ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, update_only_target_properties,
+ base::win::SHORTCUT_UPDATE_EXISTING));
+
+ base::win::ShortcutProperties expected_properties = link_properties_;
+ expected_properties.set_target(link_properties_2_.target);
+ base::win::ValidateShortcut(link_file_, expected_properties);
+
+ FilePath resolved_name;
+ EXPECT_TRUE(base::win::ResolveShortcut(link_file_, &resolved_name, NULL));
+
+ char read_contents[arraysize(kFileContents2)];
+ file_util::ReadFile(resolved_name, read_contents, arraysize(read_contents));
+ EXPECT_STREQ(kFileContents2, read_contents);
+}
+
+TEST_F(ShortcutTest, UpdateShortcutMakeDualMode) {
+ ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, link_properties_, base::win::SHORTCUT_CREATE_ALWAYS));
+
+ base::win::ShortcutProperties make_dual_mode_properties;
+ make_dual_mode_properties.set_dual_mode(true);
+
+ ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, make_dual_mode_properties,
+ base::win::SHORTCUT_UPDATE_EXISTING));
+
+ base::win::ShortcutProperties expected_properties = link_properties_;
+ expected_properties.set_dual_mode(true);
+ base::win::ValidateShortcut(link_file_, expected_properties);
+}
+
+TEST_F(ShortcutTest, UpdateShortcutRemoveDualMode) {
+ ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, link_properties_2_, base::win::SHORTCUT_CREATE_ALWAYS));
+
+ base::win::ShortcutProperties remove_dual_mode_properties;
+ remove_dual_mode_properties.set_dual_mode(false);
+
+ ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, remove_dual_mode_properties,
+ base::win::SHORTCUT_UPDATE_EXISTING));
+
+ base::win::ShortcutProperties expected_properties = link_properties_2_;
+ expected_properties.set_dual_mode(false);
+ base::win::ValidateShortcut(link_file_, expected_properties);
+}
+
+TEST_F(ShortcutTest, UpdateShortcutClearArguments) {
+ ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, link_properties_, base::win::SHORTCUT_CREATE_ALWAYS));
+
+ base::win::ShortcutProperties clear_arguments_properties;
+ clear_arguments_properties.set_arguments(string16());
+
+ ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, clear_arguments_properties,
+ base::win::SHORTCUT_UPDATE_EXISTING));
+
+ base::win::ShortcutProperties expected_properties = link_properties_;
+ expected_properties.set_arguments(string16());
+ base::win::ValidateShortcut(link_file_, expected_properties);
+}
+
+TEST_F(ShortcutTest, FailUpdateShortcutThatDoesNotExist) {
+ ASSERT_FALSE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, link_properties_, base::win::SHORTCUT_UPDATE_EXISTING));
+ ASSERT_FALSE(file_util::PathExists(link_file_));
+}
+
+TEST_F(ShortcutTest, TruncateShortcutAllProperties) {
+ ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, link_properties_, base::win::SHORTCUT_CREATE_ALWAYS));
+
+ ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, link_properties_2_, base::win::SHORTCUT_REPLACE_EXISTING));
+
+ base::win::ValidateShortcut(link_file_, link_properties_2_);
+}
+
+TEST_F(ShortcutTest, TruncateShortcutSomeProperties) {
+ ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, link_properties_, base::win::SHORTCUT_CREATE_ALWAYS));
+
+ base::win::ShortcutProperties new_properties;
+ new_properties.set_target(link_properties_2_.target);
+ new_properties.set_description(link_properties_2_.description);
+ ASSERT_TRUE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, new_properties, base::win::SHORTCUT_REPLACE_EXISTING));
+
+ // Expect only properties in |new_properties| to be set, all other properties
+ // should have been overwritten.
+ base::win::ShortcutProperties expected_properties = new_properties;
+ expected_properties.set_working_dir(FilePath());
+ expected_properties.set_arguments(string16());
+ expected_properties.set_icon(FilePath(), 0);
+ expected_properties.set_app_id(string16());
+ expected_properties.set_dual_mode(false);
+ base::win::ValidateShortcut(link_file_, expected_properties);
+}
+
+TEST_F(ShortcutTest, FailTruncateShortcutThatDoesNotExist) {
+ ASSERT_FALSE(base::win::CreateOrUpdateShortcutLink(
+ link_file_, link_properties_, base::win::SHORTCUT_REPLACE_EXISTING));
+ ASSERT_FALSE(file_util::PathExists(link_file_));
+}
diff --git a/src/base/win/startup_information.cc b/src/base/win/startup_information.cc
new file mode 100644
index 0000000..aff52eb
--- /dev/null
+++ b/src/base/win/startup_information.cc
@@ -0,0 +1,109 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/startup_information.h"
+
+#include "base/logging.h"
+#include "base/win/windows_version.h"
+
+namespace {
+
+typedef BOOL (WINAPI *InitializeProcThreadAttributeListFunction)(
+ LPPROC_THREAD_ATTRIBUTE_LIST attribute_list,
+ DWORD attribute_count,
+ DWORD flags,
+ PSIZE_T size);
+static InitializeProcThreadAttributeListFunction
+ initialize_proc_thread_attribute_list;
+
+typedef BOOL (WINAPI *UpdateProcThreadAttributeFunction)(
+ LPPROC_THREAD_ATTRIBUTE_LIST attribute_list,
+ DWORD flags,
+ DWORD_PTR attribute,
+ PVOID value,
+ SIZE_T size,
+ PVOID previous_value,
+ PSIZE_T return_size);
+static UpdateProcThreadAttributeFunction update_proc_thread_attribute_list;
+
+typedef VOID (WINAPI *DeleteProcThreadAttributeListFunction)(
+ LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList);
+static DeleteProcThreadAttributeListFunction delete_proc_thread_attribute_list;
+
+} // namespace
+
+namespace base {
+namespace win {
+
+StartupInformation::StartupInformation() {
+ memset(&startup_info_, 0, sizeof(startup_info_));
+
+ // Pre Windows Vista doesn't support STARTUPINFOEX.
+ if (base::win::GetVersion() < base::win::VERSION_VISTA) {
+ startup_info_.StartupInfo.cb = sizeof(STARTUPINFO);
+ return;
+ }
+
+ startup_info_.StartupInfo.cb = sizeof(startup_info_);
+
+ // Load the attribute API functions.
+ if (!initialize_proc_thread_attribute_list ||
+ !update_proc_thread_attribute_list ||
+ !delete_proc_thread_attribute_list) {
+ HMODULE module = ::GetModuleHandleW(L"kernel32.dll");
+ initialize_proc_thread_attribute_list =
+ reinterpret_cast<InitializeProcThreadAttributeListFunction>(
+ ::GetProcAddress(module, "InitializeProcThreadAttributeList"));
+ update_proc_thread_attribute_list =
+ reinterpret_cast<UpdateProcThreadAttributeFunction>(
+ ::GetProcAddress(module, "UpdateProcThreadAttribute"));
+ delete_proc_thread_attribute_list =
+ reinterpret_cast<DeleteProcThreadAttributeListFunction>(
+ ::GetProcAddress(module, "DeleteProcThreadAttributeList"));
+ }
+}
+
+StartupInformation::~StartupInformation() {
+ if (startup_info_.lpAttributeList) {
+ delete_proc_thread_attribute_list(startup_info_.lpAttributeList);
+ delete [] reinterpret_cast<BYTE*>(startup_info_.lpAttributeList);
+ }
+}
+
+bool StartupInformation::InitializeProcThreadAttributeList(
+ DWORD attribute_count) {
+ if (startup_info_.StartupInfo.cb != sizeof(startup_info_) ||
+ startup_info_.lpAttributeList)
+ return false;
+
+ SIZE_T size = 0;
+ initialize_proc_thread_attribute_list(NULL, attribute_count, 0, &size);
+ if (size == 0)
+ return false;
+
+ startup_info_.lpAttributeList =
+ reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(new BYTE[size]);
+ if (!initialize_proc_thread_attribute_list(startup_info_.lpAttributeList,
+ attribute_count, 0, &size)) {
+ delete [] reinterpret_cast<BYTE*>(startup_info_.lpAttributeList);
+ startup_info_.lpAttributeList = NULL;
+ return false;
+ }
+
+ return true;
+}
+
+bool StartupInformation::UpdateProcThreadAttribute(
+ DWORD_PTR attribute,
+ void* value,
+ size_t size) {
+ if (!startup_info_.lpAttributeList)
+ return false;
+ return !!update_proc_thread_attribute_list(startup_info_.lpAttributeList, 0,
+ attribute, value, size, NULL, NULL);
+}
+
+} // namespace win
+} // namespace base
+
diff --git a/src/base/win/startup_information.h b/src/base/win/startup_information.h
new file mode 100644
index 0000000..7cef81f
--- /dev/null
+++ b/src/base/win/startup_information.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_STARTUP_INFORMATION_H_
+#define BASE_WIN_STARTUP_INFORMATION_H_
+
+#include <windows.h>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+namespace win {
+
+// Manages the lifetime of additional attributes in STARTUPINFOEX.
+class BASE_EXPORT StartupInformation {
+ public:
+ StartupInformation();
+
+ ~StartupInformation();
+
+ // Initialize the attribute list for the specified number of entries.
+ bool InitializeProcThreadAttributeList(DWORD attribute_count);
+
+ // Sets one entry in the initialized attribute list.
+ bool UpdateProcThreadAttribute(DWORD_PTR attribute,
+ void* value,
+ size_t size);
+
+ LPSTARTUPINFOW startup_info() { return &startup_info_.StartupInfo; }
+ const LPSTARTUPINFOW startup_info() const {
+ return const_cast<const LPSTARTUPINFOW>(&startup_info_.StartupInfo);
+ }
+
+ bool has_extended_startup_info() const {
+ return !!startup_info_.lpAttributeList;
+ }
+
+ private:
+ STARTUPINFOEXW startup_info_;
+ DISALLOW_COPY_AND_ASSIGN(StartupInformation);
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_SCOPED_STARTUP_INFO_EX_H_
+
diff --git a/src/base/win/startup_information_unittest.cc b/src/base/win/startup_information_unittest.cc
new file mode 100644
index 0000000..1903564
--- /dev/null
+++ b/src/base/win/startup_information_unittest.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2012 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 <string>
+
+#include "base/command_line.h"
+#include "base/test/multiprocess_test.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/startup_information.h"
+#include "base/win/windows_version.h"
+#include "testing/multiprocess_func_list.h"
+
+const wchar_t kSectionName[] = L"EventTestSection";
+const size_t kSectionSize = 4096;
+
+MULTIPROCESS_TEST_MAIN(FireInheritedEvents) {
+ HANDLE section = ::OpenFileMappingW(PAGE_READWRITE, false, kSectionName);
+ HANDLE* events = reinterpret_cast<HANDLE*>(::MapViewOfFile(section,
+ PAGE_READWRITE, 0, 0, kSectionSize));
+ // This event should not be valid because it wasn't explicitly inherited.
+ if (::SetEvent(events[1]))
+ return -1;
+ // This event should be valid because it was explicitly inherited.
+ if (!::SetEvent(events[0]))
+ return -1;
+
+ return 0;
+}
+
+class StartupInformationTest : public base::MultiProcessTest {};
+
+// Verify that only the explicitly specified event is inherited.
+TEST_F(StartupInformationTest, InheritStdOut) {
+ if (base::win::GetVersion() < base::win::VERSION_VISTA)
+ return;
+
+ base::win::ScopedProcessInformation process_info;
+ base::win::StartupInformation startup_info;
+
+ HANDLE section = ::CreateFileMappingW(INVALID_HANDLE_VALUE, NULL,
+ PAGE_READWRITE, 0, kSectionSize,
+ kSectionName);
+ ASSERT_TRUE(section);
+
+ HANDLE* events = reinterpret_cast<HANDLE*>(::MapViewOfFile(section,
+ FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, kSectionSize));
+
+ // Make two inheritable events.
+ SECURITY_ATTRIBUTES security_attributes = { sizeof(security_attributes),
+ NULL, true };
+ events[0] = ::CreateEvent(&security_attributes, false, false, NULL);
+ ASSERT_TRUE(events[0]);
+ events[1] = ::CreateEvent(&security_attributes, false, false, NULL);
+ ASSERT_TRUE(events[1]);
+
+ ASSERT_TRUE(startup_info.InitializeProcThreadAttributeList(1));
+ ASSERT_TRUE(startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &events[0],
+ sizeof(events[0])));
+
+ std::wstring cmd_line =
+ this->MakeCmdLine("FireInheritedEvents", false).GetCommandLineString();
+
+ ASSERT_TRUE(::CreateProcess(NULL, const_cast<wchar_t*>(cmd_line.c_str()),
+ NULL, NULL, true, EXTENDED_STARTUPINFO_PRESENT,
+ NULL, NULL, startup_info.startup_info(),
+ process_info.Receive())) << ::GetLastError();
+ // Only the first event should be signalled
+ EXPECT_EQ(WAIT_OBJECT_0, ::WaitForMultipleObjects(2, events, false,
+ 4000));
+}
+
diff --git a/src/base/win/text_services_message_filter.cc b/src/base/win/text_services_message_filter.cc
new file mode 100644
index 0000000..7ce233d
--- /dev/null
+++ b/src/base/win/text_services_message_filter.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/text_services_message_filter.h"
+
+namespace base {
+namespace win {
+
+TextServicesMessageFilter::TextServicesMessageFilter()
+ : client_id_(TF_CLIENTID_NULL),
+ is_initialized_(false) {
+}
+
+TextServicesMessageFilter::~TextServicesMessageFilter() {
+ if (is_initialized_)
+ thread_mgr_->Deactivate();
+}
+
+bool TextServicesMessageFilter::Init() {
+ if (FAILED(thread_mgr_.CreateInstance(CLSID_TF_ThreadMgr)))
+ return false;
+
+ if (FAILED(message_pump_.QueryFrom(thread_mgr_)))
+ return false;
+
+ if (FAILED(keystroke_mgr_.QueryFrom(thread_mgr_)))
+ return false;
+
+ if (FAILED(thread_mgr_->Activate(&client_id_)))
+ return false;
+
+ is_initialized_ = true;
+ return is_initialized_;
+}
+
+// Wraps for ITfMessagePump::PeekMessage with win32 PeekMessage signature.
+// Obtains messages from application message queue.
+BOOL TextServicesMessageFilter::DoPeekMessage(MSG* msg,
+ HWND window_handle,
+ UINT msg_filter_min,
+ UINT msg_filter_max,
+ UINT remove_msg) {
+ BOOL result = FALSE;
+ if (FAILED(message_pump_->PeekMessage(msg, window_handle,
+ msg_filter_min, msg_filter_max,
+ remove_msg, &result))) {
+ result = FALSE;
+ }
+
+ return result;
+}
+
+// Sends message to Text Service Manager.
+// The message will be used to input composition text.
+// Returns true if |message| was consumed by text service manager.
+bool TextServicesMessageFilter::ProcessMessage(const MSG& msg) {
+ if (msg.message == WM_KEYDOWN) {
+ BOOL eaten = FALSE;
+ HRESULT hr = keystroke_mgr_->TestKeyDown(msg.wParam, msg.lParam, &eaten);
+ if (FAILED(hr) && !eaten)
+ return false;
+ eaten = FALSE;
+ hr = keystroke_mgr_->KeyDown(msg.wParam, msg.lParam, &eaten);
+ return (SUCCEEDED(hr) && !!eaten);
+ }
+
+ if (msg.message == WM_KEYUP) {
+ BOOL eaten = FALSE;
+ HRESULT hr = keystroke_mgr_->TestKeyUp(msg.wParam, msg.lParam, &eaten);
+ if (FAILED(hr) && !eaten)
+ return false;
+ eaten = FALSE;
+ hr = keystroke_mgr_->KeyUp(msg.wParam, msg.lParam, &eaten);
+ return (SUCCEEDED(hr) && !!eaten);
+ }
+
+ return false;
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/text_services_message_filter.h b/src/base/win/text_services_message_filter.h
new file mode 100644
index 0000000..facd613
--- /dev/null
+++ b/src/base/win/text_services_message_filter.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_TEXT_SERVICES_MESSAGE_FILTER_H_
+#define BASE_WIN_TEXT_SERVICES_MESSAGE_FILTER_H_
+
+#include <msctf.h>
+#include <Windows.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_pump_win.h"
+#include "base/win/metro.h"
+#include "base/win/scoped_comptr.h"
+
+namespace base {
+namespace win {
+
+// TextServicesMessageFilter extends MessageFilter with methods that are using
+// Text Services Framework COM component.
+class BASE_EXPORT TextServicesMessageFilter
+ : public base::MessagePumpForUI::MessageFilter {
+ public:
+ TextServicesMessageFilter();
+ virtual ~TextServicesMessageFilter();
+ virtual BOOL DoPeekMessage(MSG* msg,
+ HWND window_handle,
+ UINT msg_filter_min,
+ UINT msg_filter_max,
+ UINT remove_msg) OVERRIDE;
+ virtual bool ProcessMessage(const MSG& msg) OVERRIDE;
+
+ bool Init();
+
+ private:
+ TfClientId client_id_;
+ bool is_initialized_;
+ base::win::ScopedComPtr<ITfThreadMgr> thread_mgr_;
+ base::win::ScopedComPtr<ITfMessagePump> message_pump_;
+ base::win::ScopedComPtr<ITfKeystrokeMgr> keystroke_mgr_;
+
+ DISALLOW_COPY_AND_ASSIGN(TextServicesMessageFilter);
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_TEXT_SERVICES_MESSAGE_FILTER_H_
diff --git a/src/base/win/win_util.cc b/src/base/win/win_util.cc
new file mode 100644
index 0000000..b885d6e
--- /dev/null
+++ b/src/base/win/win_util.cc
@@ -0,0 +1,249 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/win_util.h"
+
+#include <aclapi.h>
+#include <shobjidl.h> // Must be before propkey.
+#include <initguid.h>
+#include <propkey.h>
+#include <propvarutil.h>
+#include <sddl.h>
+#include <shlobj.h>
+#include <signal.h>
+#include <stdlib.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/win/registry.h"
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+
+namespace {
+
+// Sets the value of |property_key| to |property_value| in |property_store|.
+// Clears the PropVariant contained in |property_value|.
+bool SetPropVariantValueForPropertyStore(
+ IPropertyStore* property_store,
+ const PROPERTYKEY& property_key,
+ PROPVARIANT* property_value) {
+ DCHECK(property_store);
+
+ HRESULT result = property_store->SetValue(property_key, *property_value);
+ if (result == S_OK)
+ result = property_store->Commit();
+
+ PropVariantClear(property_value);
+ return SUCCEEDED(result);
+}
+
+void __cdecl ForceCrashOnSigAbort(int) {
+ *((int*)0) = 0x1337;
+}
+
+} // namespace
+
+namespace base {
+namespace win {
+
+static bool g_crash_on_process_detach = false;
+
+#define NONCLIENTMETRICS_SIZE_PRE_VISTA \
+ SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(NONCLIENTMETRICS, lfMessageFont)
+
+void GetNonClientMetrics(NONCLIENTMETRICS* metrics) {
+ DCHECK(metrics);
+
+ static const UINT SIZEOF_NONCLIENTMETRICS =
+ (base::win::GetVersion() >= base::win::VERSION_VISTA) ?
+ sizeof(NONCLIENTMETRICS) : NONCLIENTMETRICS_SIZE_PRE_VISTA;
+ metrics->cbSize = SIZEOF_NONCLIENTMETRICS;
+ const bool success = !!SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
+ SIZEOF_NONCLIENTMETRICS, metrics,
+ 0);
+ DCHECK(success);
+}
+
+bool GetUserSidString(std::wstring* user_sid) {
+ // Get the current token.
+ HANDLE token = NULL;
+ if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token))
+ return false;
+ base::win::ScopedHandle token_scoped(token);
+
+ DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
+ scoped_array<BYTE> user_bytes(new BYTE[size]);
+ TOKEN_USER* user = reinterpret_cast<TOKEN_USER*>(user_bytes.get());
+
+ if (!::GetTokenInformation(token, TokenUser, user, size, &size))
+ return false;
+
+ if (!user->User.Sid)
+ return false;
+
+ // Convert the data to a string.
+ wchar_t* sid_string;
+ if (!::ConvertSidToStringSid(user->User.Sid, &sid_string))
+ return false;
+
+ *user_sid = sid_string;
+
+ ::LocalFree(sid_string);
+
+ return true;
+}
+
+bool IsShiftPressed() {
+ return (::GetKeyState(VK_SHIFT) & 0x8000) == 0x8000;
+}
+
+bool IsCtrlPressed() {
+ return (::GetKeyState(VK_CONTROL) & 0x8000) == 0x8000;
+}
+
+bool IsAltPressed() {
+ return (::GetKeyState(VK_MENU) & 0x8000) == 0x8000;
+}
+
+bool UserAccountControlIsEnabled() {
+ // This can be slow if Windows ends up going to disk. Should watch this key
+ // for changes and only read it once, preferably on the file thread.
+ // http://code.google.com/p/chromium/issues/detail?id=61644
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ base::win::RegKey key(HKEY_LOCAL_MACHINE,
+ L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
+ KEY_READ);
+ DWORD uac_enabled;
+ if (key.ReadValueDW(L"EnableLUA", &uac_enabled) != ERROR_SUCCESS)
+ return true;
+ // Users can set the EnableLUA value to something arbitrary, like 2, which
+ // Vista will treat as UAC enabled, so we make sure it is not set to 0.
+ return (uac_enabled != 0);
+}
+
+bool SetBooleanValueForPropertyStore(IPropertyStore* property_store,
+ const PROPERTYKEY& property_key,
+ bool property_bool_value) {
+ PROPVARIANT property_value;
+ if (FAILED(InitPropVariantFromBoolean(property_bool_value, &property_value)))
+ return false;
+
+ return SetPropVariantValueForPropertyStore(property_store,
+ property_key,
+ &property_value);
+}
+
+bool SetStringValueForPropertyStore(IPropertyStore* property_store,
+ const PROPERTYKEY& property_key,
+ const wchar_t* property_string_value) {
+ PROPVARIANT property_value;
+ if (FAILED(InitPropVariantFromString(property_string_value, &property_value)))
+ return false;
+
+ return SetPropVariantValueForPropertyStore(property_store,
+ property_key,
+ &property_value);
+}
+
+bool SetAppIdForPropertyStore(IPropertyStore* property_store,
+ const wchar_t* app_id) {
+ // App id should be less than 64 chars and contain no space. And recommended
+ // format is CompanyName.ProductName[.SubProduct.ProductNumber].
+ // See http://msdn.microsoft.com/en-us/library/dd378459%28VS.85%29.aspx
+ DCHECK(lstrlen(app_id) < 64 && wcschr(app_id, L' ') == NULL);
+
+ return SetStringValueForPropertyStore(property_store,
+ PKEY_AppUserModel_ID,
+ app_id);
+}
+
+static const char16 kAutoRunKeyPath[] =
+ L"Software\\Microsoft\\Windows\\CurrentVersion\\Run";
+
+bool AddCommandToAutoRun(HKEY root_key, const string16& name,
+ const string16& command) {
+ base::win::RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE);
+ return (autorun_key.WriteValue(name.c_str(), command.c_str()) ==
+ ERROR_SUCCESS);
+}
+
+bool RemoveCommandFromAutoRun(HKEY root_key, const string16& name) {
+ base::win::RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE);
+ return (autorun_key.DeleteValue(name.c_str()) == ERROR_SUCCESS);
+}
+
+bool ReadCommandFromAutoRun(HKEY root_key,
+ const string16& name,
+ string16* command) {
+ base::win::RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_QUERY_VALUE);
+ return (autorun_key.ReadValue(name.c_str(), command) == ERROR_SUCCESS);
+}
+
+void SetShouldCrashOnProcessDetach(bool crash) {
+ g_crash_on_process_detach = crash;
+}
+
+bool ShouldCrashOnProcessDetach() {
+ return g_crash_on_process_detach;
+}
+
+void SetAbortBehaviorForCrashReporting() {
+ // Prevent CRT's abort code from prompting a dialog or trying to "report" it.
+ // Disabling the _CALL_REPORTFAULT behavior is important since otherwise it
+ // has the sideffect of clearing our exception filter, which means we
+ // don't get any crash.
+ _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
+
+ // Set a SIGABRT handler for good measure. We will crash even if the default
+ // is left in place, however this allows us to crash earlier. And it also
+ // lets us crash in response to code which might directly call raise(SIGABRT)
+ signal(SIGABRT, ForceCrashOnSigAbort);
+}
+
+bool IsMachineATablet() {
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return false;
+ const int kMultiTouch = NID_INTEGRATED_TOUCH | NID_MULTI_INPUT | NID_READY;
+ const int kMaxTabletScreenWidth = 1366;
+ const int kMaxTabletScreenHeight = 768;
+ int sm = GetSystemMetrics(SM_DIGITIZER);
+ if ((sm & kMultiTouch) == kMultiTouch) {
+ int cx = GetSystemMetrics(SM_CXSCREEN);
+ int cy = GetSystemMetrics(SM_CYSCREEN);
+ // Handle landscape and portrait modes.
+ return cx > cy ?
+ (cx <= kMaxTabletScreenWidth && cy <= kMaxTabletScreenHeight) :
+ (cy <= kMaxTabletScreenWidth && cx <= kMaxTabletScreenHeight);
+ }
+ return false;
+}
+
+} // namespace win
+} // namespace base
+
+#ifdef _MSC_VER
+//
+// If the ASSERT below fails, please install Visual Studio 2005 Service Pack 1.
+//
+extern char VisualStudio2005ServicePack1Detection[10];
+COMPILE_ASSERT(sizeof(&VisualStudio2005ServicePack1Detection) == sizeof(void*),
+ VS2005SP1Detect);
+//
+// Chrome requires at least Service Pack 1 for Visual Studio 2005.
+//
+#endif // _MSC_VER
+
+#ifndef COPY_FILE_COPY_SYMLINK
+#error You must install the Windows 2008 or Vista Software Development Kit and \
+set it as your default include path to build this library. You can grab it by \
+searching for "download windows sdk 2008" in your favorite web search engine. \
+Also make sure you register the SDK with Visual Studio, by selecting \
+"Integrate Windows SDK with Visual Studio 2005" from the Windows SDK \
+menu (see Start - All Programs - Microsoft Windows SDK - \
+Visual Studio Registration).
+#endif
diff --git a/src/base/win/win_util.h b/src/base/win/win_util.h
new file mode 100644
index 0000000..d5bcd7a
--- /dev/null
+++ b/src/base/win/win_util.h
@@ -0,0 +1,127 @@
+// Copyright (c) 2012 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.
+
+// =============================================================================
+// PLEASE READ
+//
+// In general, you should not be adding stuff to this file.
+//
+// - If your thing is only used in one place, just put it in a reasonable
+// location in or near that one place. It's nice you want people to be able
+// to re-use your function, but realistically, if it hasn't been necessary
+// before after so many years of development, it's probably not going to be
+// used in other places in the future unless you know of them now.
+//
+// - If your thing is used by multiple callers and is UI-related, it should
+// probably be in app/win/ instead. Try to put it in the most specific file
+// possible (avoiding the *_util files when practical).
+//
+// =============================================================================
+
+#ifndef BASE_WIN_WIN_UTIL_H_
+#define BASE_WIN_WIN_UTIL_H_
+
+#include <windows.h>
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/string16.h"
+
+struct IPropertyStore;
+struct _tagpropertykey;
+typedef _tagpropertykey PROPERTYKEY;
+
+namespace base {
+namespace win {
+
+// A Windows message reflected from other windows. This message is sent
+// with the following arguments:
+// hWnd - Target window
+// uMsg - kReflectedMessage
+// wParam - Should be 0
+// lParam - Pointer to MSG struct containing the original message.
+const int kReflectedMessage = WM_APP + 3;
+
+BASE_EXPORT void GetNonClientMetrics(NONCLIENTMETRICS* metrics);
+
+// Returns the string representing the current user sid.
+BASE_EXPORT bool GetUserSidString(std::wstring* user_sid);
+
+// Returns true if the shift key is currently pressed.
+BASE_EXPORT bool IsShiftPressed();
+
+// Returns true if the ctrl key is currently pressed.
+BASE_EXPORT bool IsCtrlPressed();
+
+// Returns true if the alt key is currently pressed.
+BASE_EXPORT bool IsAltPressed();
+
+// Returns false if user account control (UAC) has been disabled with the
+// EnableLUA registry flag. Returns true if user account control is enabled.
+// NOTE: The EnableLUA registry flag, which is ignored on Windows XP
+// machines, might still exist and be set to 0 (UAC disabled), in which case
+// this function will return false. You should therefore check this flag only
+// if the OS is Vista or later.
+BASE_EXPORT bool UserAccountControlIsEnabled();
+
+// Sets the boolean value for a given key in given IPropertyStore.
+BASE_EXPORT bool SetBooleanValueForPropertyStore(
+ IPropertyStore* property_store,
+ const PROPERTYKEY& property_key,
+ bool property_bool_value);
+
+// Sets the string value for a given key in given IPropertyStore.
+BASE_EXPORT bool SetStringValueForPropertyStore(
+ IPropertyStore* property_store,
+ const PROPERTYKEY& property_key,
+ const wchar_t* property_string_value);
+
+// Sets the application id in given IPropertyStore. The function is intended
+// for tagging application/chromium shortcut, browser window and jump list for
+// Win7.
+BASE_EXPORT bool SetAppIdForPropertyStore(IPropertyStore* property_store,
+ const wchar_t* app_id);
+
+// Adds the specified |command| using the specified |name| to the AutoRun key.
+// |root_key| could be HKCU or HKLM or the root of any user hive.
+BASE_EXPORT bool AddCommandToAutoRun(HKEY root_key, const string16& name,
+ const string16& command);
+// Removes the command specified by |name| from the AutoRun key. |root_key|
+// could be HKCU or HKLM or the root of any user hive.
+BASE_EXPORT bool RemoveCommandFromAutoRun(HKEY root_key, const string16& name);
+
+// Reads the command specified by |name| from the AutoRun key. |root_key|
+// could be HKCU or HKLM or the root of any user hive. Used for unit-tests.
+BASE_EXPORT bool ReadCommandFromAutoRun(HKEY root_key,
+ const string16& name,
+ string16* command);
+
+// Sets whether to crash the process during exit. This is inspected by DLLMain
+// and used to intercept unexpected terminations of the process (via calls to
+// exit(), abort(), _exit(), ExitProcess()) and convert them into crashes.
+// Note that not all mechanisms for terminating the process are covered by
+// this. In particular, TerminateProcess() is not caught.
+BASE_EXPORT void SetShouldCrashOnProcessDetach(bool crash);
+BASE_EXPORT bool ShouldCrashOnProcessDetach();
+
+// Adjusts the abort behavior so that crash reports can be generated when the
+// process is aborted.
+BASE_EXPORT void SetAbortBehaviorForCrashReporting();
+
+// A tablet by this definition is something that has integrated multi-touch
+// ready to use and also has screen resolution not greater than 1366x768.
+BASE_EXPORT bool IsMachineATablet();
+
+// Get the size of a struct up to and including the specified member.
+// This is necessary to set compatible struct sizes for different versions
+// of certain Windows APIs (e.g. SystemParametersInfo).
+#define SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(struct_name, member) \
+ offsetof(struct_name, member) + \
+ (sizeof static_cast<struct_name*>(NULL)->member)
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_WIN_UTIL_H_
diff --git a/src/base/win/win_util_unittest.cc b/src/base/win/win_util_unittest.cc
new file mode 100644
index 0000000..b79ed56
--- /dev/null
+++ b/src/base/win/win_util_unittest.cc
@@ -0,0 +1,58 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "base/string_util.h"
+#include "base/win/win_util.h"
+#include "base/win/windows_version.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace win {
+
+namespace {
+
+// Saves the current thread's locale ID when initialized, and restores it when
+// the instance is going out of scope.
+class ThreadLocaleSaver {
+ public:
+ ThreadLocaleSaver() : original_locale_id_(GetThreadLocale()) {}
+ ~ThreadLocaleSaver() { SetThreadLocale(original_locale_id_); }
+
+ private:
+ LCID original_locale_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadLocaleSaver);
+};
+
+} // namespace
+
+// The test is somewhat silly, because the Vista bots some have UAC enabled
+// and some have it disabled. At least we check that it does not crash.
+TEST(BaseWinUtilTest, TestIsUACEnabled) {
+ if (GetVersion() >= base::win::VERSION_VISTA) {
+ UserAccountControlIsEnabled();
+ } else {
+ EXPECT_TRUE(UserAccountControlIsEnabled());
+ }
+}
+
+TEST(BaseWinUtilTest, TestGetUserSidString) {
+ std::wstring user_sid;
+ EXPECT_TRUE(GetUserSidString(&user_sid));
+ EXPECT_TRUE(!user_sid.empty());
+}
+
+TEST(BaseWinUtilTest, TestGetNonClientMetrics) {
+ NONCLIENTMETRICS metrics = {0};
+ GetNonClientMetrics(&metrics);
+ EXPECT_TRUE(metrics.cbSize > 0);
+ EXPECT_TRUE(metrics.iScrollWidth > 0);
+ EXPECT_TRUE(metrics.iScrollHeight > 0);
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/windows_version.cc b/src/base/win/windows_version.cc
new file mode 100644
index 0000000..c130e0e
--- /dev/null
+++ b/src/base/win/windows_version.cc
@@ -0,0 +1,111 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/windows_version.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "base/win/registry.h"
+
+namespace base {
+namespace win {
+
+// static
+OSInfo* OSInfo::GetInstance() {
+ // Note: we don't use the Singleton class because it depends on AtExitManager,
+ // and it's convenient for other modules to use this classs without it. This
+ // pattern is copied from gurl.cc.
+ static OSInfo* info;
+ if (!info) {
+ OSInfo* new_info = new OSInfo();
+ if (InterlockedCompareExchangePointer(
+ reinterpret_cast<PVOID*>(&info), new_info, NULL)) {
+ delete new_info;
+ }
+ }
+ return info;
+}
+
+OSInfo::OSInfo()
+ : version_(VERSION_PRE_XP),
+ architecture_(OTHER_ARCHITECTURE),
+ wow64_status_(GetWOW64StatusForProcess(GetCurrentProcess())) {
+ OSVERSIONINFOEX version_info = { sizeof version_info };
+ GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info));
+ version_number_.major = version_info.dwMajorVersion;
+ version_number_.minor = version_info.dwMinorVersion;
+ version_number_.build = version_info.dwBuildNumber;
+ if ((version_number_.major == 5) && (version_number_.minor > 0)) {
+ // Treat XP Pro x64, Home Server, and Server 2003 R2 as Server 2003.
+ version_ = (version_number_.minor == 1) ? VERSION_XP : VERSION_SERVER_2003;
+ } else if (version_number_.major == 6) {
+ switch (version_number_.minor) {
+ case 0:
+ // Treat Windows Server 2008 the same as Windows Vista.
+ version_ = VERSION_VISTA;
+ break;
+ case 1:
+ // Treat Windows Server 2008 R2 the same as Windows 7.
+ version_ = VERSION_WIN7;
+ break;
+ default:
+ DCHECK_EQ(version_number_.minor, 2);
+ // Treat Windows Server 2012 the same as Windows 8.
+ version_ = VERSION_WIN8;
+ break;
+ }
+ } else if (version_number_.major > 6) {
+ NOTREACHED();
+ version_ = VERSION_WIN_LAST;
+ }
+ service_pack_.major = version_info.wServicePackMajor;
+ service_pack_.minor = version_info.wServicePackMinor;
+
+ SYSTEM_INFO system_info = { 0 };
+ GetNativeSystemInfo(&system_info);
+ switch (system_info.wProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_INTEL: architecture_ = X86_ARCHITECTURE; break;
+ case PROCESSOR_ARCHITECTURE_AMD64: architecture_ = X64_ARCHITECTURE; break;
+ case PROCESSOR_ARCHITECTURE_IA64: architecture_ = IA64_ARCHITECTURE; break;
+ }
+ processors_ = system_info.dwNumberOfProcessors;
+ allocation_granularity_ = system_info.dwAllocationGranularity;
+}
+
+OSInfo::~OSInfo() {
+}
+
+std::string OSInfo::processor_model_name() {
+ if (processor_model_name_.empty()) {
+ const wchar_t kProcessorNameString[] =
+ L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
+ base::win::RegKey key(HKEY_LOCAL_MACHINE, kProcessorNameString, KEY_READ);
+ string16 value;
+ key.ReadValue(L"ProcessorNameString", &value);
+ processor_model_name_ = UTF16ToUTF8(value);
+ }
+ return processor_model_name_;
+}
+
+// static
+OSInfo::WOW64Status OSInfo::GetWOW64StatusForProcess(HANDLE process_handle) {
+ typedef BOOL (WINAPI* IsWow64ProcessFunc)(HANDLE, PBOOL);
+ IsWow64ProcessFunc is_wow64_process = reinterpret_cast<IsWow64ProcessFunc>(
+ GetProcAddress(GetModuleHandle(L"kernel32.dll"), "IsWow64Process"));
+ if (!is_wow64_process)
+ return WOW64_DISABLED;
+ BOOL is_wow64 = FALSE;
+ if (!(*is_wow64_process)(process_handle, &is_wow64))
+ return WOW64_UNKNOWN;
+ return is_wow64 ? WOW64_ENABLED : WOW64_DISABLED;
+}
+
+Version GetVersion() {
+ return OSInfo::GetInstance()->version();
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/windows_version.h b/src/base/win/windows_version.h
new file mode 100644
index 0000000..d466dad
--- /dev/null
+++ b/src/base/win/windows_version.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_WINDOWS_VERSION_H_
+#define BASE_WIN_WINDOWS_VERSION_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+typedef void* HANDLE;
+
+namespace base {
+namespace win {
+
+// The running version of Windows. This is declared outside OSInfo for
+// syntactic sugar reasons; see the declaration of GetVersion() below.
+// NOTE: Keep these in order so callers can do things like
+// "if (base::win::GetVersion() >= base::win::VERSION_VISTA) ...".
+enum Version {
+ VERSION_PRE_XP = 0, // Not supported.
+ VERSION_XP,
+ VERSION_SERVER_2003, // Also includes XP Pro x64 and Server 2003 R2.
+ VERSION_VISTA, // Also includes Windows Server 2008.
+ VERSION_WIN7, // Also includes Windows Server 2008 R2.
+ VERSION_WIN8, // Also includes Windows Server 2012.
+ VERSION_WIN_LAST, // Indicates error condition.
+};
+
+// A singleton that can be used to query various pieces of information about the
+// OS and process state. Note that this doesn't use the base Singleton class, so
+// it can be used without an AtExitManager.
+class BASE_EXPORT OSInfo {
+ public:
+ struct VersionNumber {
+ int major;
+ int minor;
+ int build;
+ };
+
+ struct ServicePack {
+ int major;
+ int minor;
+ };
+
+ // The processor architecture this copy of Windows natively uses. For
+ // example, given an x64-capable processor, we have three possibilities:
+ // 32-bit Chrome running on 32-bit Windows: X86_ARCHITECTURE
+ // 32-bit Chrome running on 64-bit Windows via WOW64: X64_ARCHITECTURE
+ // 64-bit Chrome running on 64-bit Windows: X64_ARCHITECTURE
+ enum WindowsArchitecture {
+ X86_ARCHITECTURE,
+ X64_ARCHITECTURE,
+ IA64_ARCHITECTURE,
+ OTHER_ARCHITECTURE,
+ };
+
+ // Whether a process is running under WOW64 (the wrapper that allows 32-bit
+ // processes to run on 64-bit versions of Windows). This will return
+ // WOW64_DISABLED for both "32-bit Chrome on 32-bit Windows" and "64-bit
+ // Chrome on 64-bit Windows". WOW64_UNKNOWN means "an error occurred", e.g.
+ // the process does not have sufficient access rights to determine this.
+ enum WOW64Status {
+ WOW64_DISABLED,
+ WOW64_ENABLED,
+ WOW64_UNKNOWN,
+ };
+
+ static OSInfo* GetInstance();
+
+ Version version() const { return version_; }
+ // The next two functions return arrays of values, [major, minor(, build)].
+ VersionNumber version_number() const { return version_number_; }
+ ServicePack service_pack() const { return service_pack_; }
+ WindowsArchitecture architecture() const { return architecture_; }
+ int processors() const { return processors_; }
+ size_t allocation_granularity() const { return allocation_granularity_; }
+ WOW64Status wow64_status() const { return wow64_status_; }
+ std::string processor_model_name();
+
+ // Like wow64_status(), but for the supplied handle instead of the current
+ // process. This doesn't touch member state, so you can bypass the singleton.
+ static WOW64Status GetWOW64StatusForProcess(HANDLE process_handle);
+
+ private:
+ OSInfo();
+ ~OSInfo();
+
+ Version version_;
+ VersionNumber version_number_;
+ ServicePack service_pack_;
+ WindowsArchitecture architecture_;
+ int processors_;
+ size_t allocation_granularity_;
+ WOW64Status wow64_status_;
+ std::string processor_model_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(OSInfo);
+};
+
+// Because this is by far the most commonly-requested value from the above
+// singleton, we add a global-scope accessor here as syntactic sugar.
+BASE_EXPORT Version GetVersion();
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_WINDOWS_VERSION_H_
diff --git a/src/base/win/wrapped_window_proc.cc b/src/base/win/wrapped_window_proc.cc
new file mode 100644
index 0000000..04e59c5
--- /dev/null
+++ b/src/base/win/wrapped_window_proc.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/wrapped_window_proc.h"
+
+#include "base/atomicops.h"
+#include "base/logging.h"
+#include "base/process_util.h"
+
+namespace {
+
+base::win::WinProcExceptionFilter s_exception_filter = NULL;
+
+} // namespace.
+
+namespace base {
+namespace win {
+
+WinProcExceptionFilter SetWinProcExceptionFilter(
+ WinProcExceptionFilter filter) {
+ subtle::AtomicWord rv = subtle::NoBarrier_AtomicExchange(
+ reinterpret_cast<subtle::AtomicWord*>(&s_exception_filter),
+ reinterpret_cast<subtle::AtomicWord>(filter));
+ return reinterpret_cast<WinProcExceptionFilter>(rv);
+}
+
+int CallExceptionFilter(EXCEPTION_POINTERS* info) {
+ return s_exception_filter ? s_exception_filter(info) :
+ EXCEPTION_CONTINUE_SEARCH;
+}
+
+BASE_EXPORT void InitializeWindowClass(
+ const char16* class_name,
+ WNDPROC window_proc,
+ UINT style,
+ int class_extra,
+ int window_extra,
+ HCURSOR cursor,
+ HBRUSH background,
+ const char16* menu_name,
+ HICON large_icon,
+ HICON small_icon,
+ WNDCLASSEX* class_out) {
+ class_out->cbSize = sizeof(WNDCLASSEX);
+ class_out->style = style;
+ class_out->lpfnWndProc = window_proc;
+ class_out->cbClsExtra = class_extra;
+ class_out->cbWndExtra = window_extra;
+ class_out->hInstance = base::GetModuleFromAddress(window_proc);
+ class_out->hIcon = large_icon;
+ class_out->hCursor = cursor;
+ class_out->hbrBackground = background;
+ class_out->lpszMenuName = menu_name;
+ class_out->lpszClassName = class_name;
+ class_out->hIconSm = small_icon;
+
+ // Check if |window_proc| is valid.
+ DCHECK(class_out->hInstance != NULL);
+}
+
+} // namespace win
+} // namespace base
diff --git a/src/base/win/wrapped_window_proc.h b/src/base/win/wrapped_window_proc.h
new file mode 100644
index 0000000..b5793f2
--- /dev/null
+++ b/src/base/win/wrapped_window_proc.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2012 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.
+
+// Provides a way to handle exceptions that happen while a WindowProc is
+// running. The behavior of exceptions generated inside a WindowProc is OS
+// dependent, but it is possible that the OS just ignores the exception and
+// continues execution, which leads to unpredictable behavior for Chrome.
+
+#ifndef BASE_WIN_WRAPPED_WINDOW_PROC_H_
+#define BASE_WIN_WRAPPED_WINDOW_PROC_H_
+
+#include <windows.h>
+
+#include "base/base_export.h"
+#include "base/string16.h"
+
+namespace base {
+namespace win {
+
+// An exception filter for a WindowProc. The return value determines how the
+// exception should be handled, following standard SEH rules. However, the
+// expected behavior for this function is to not return, instead of returning
+// EXCEPTION_EXECUTE_HANDLER or similar, given that in general we are not
+// prepared to handle exceptions.
+typedef int (__cdecl *WinProcExceptionFilter)(EXCEPTION_POINTERS* info);
+
+// Sets the filter to deal with exceptions inside a WindowProc. Returns the old
+// exception filter, if any.
+// This function should be called before any window is created.
+BASE_EXPORT WinProcExceptionFilter SetWinProcExceptionFilter(
+ WinProcExceptionFilter filter);
+
+// Calls the registered exception filter.
+BASE_EXPORT int CallExceptionFilter(EXCEPTION_POINTERS* info);
+
+// Initializes the WNDCLASSEX structure |*class_out| to be passed to
+// RegisterClassEx() making sure that it is associated with the module
+// containing the window procedure.
+BASE_EXPORT void InitializeWindowClass(
+ const char16* class_name,
+ WNDPROC window_proc,
+ UINT style,
+ int class_extra,
+ int window_extra,
+ HCURSOR cursor,
+ HBRUSH background,
+ const char16* menu_name,
+ HICON large_icon,
+ HICON small_icon,
+ WNDCLASSEX* class_out);
+
+// Wrapper that supplies a standard exception frame for the provided WindowProc.
+// The normal usage is something like this:
+//
+// LRESULT CALLBACK MyWinProc(HWND hwnd, UINT message,
+// WPARAM wparam, LPARAM lparam) {
+// // Do Something.
+// }
+//
+// ...
+//
+// WNDCLASSEX wc = {0};
+// wc.lpfnWndProc = WrappedWindowProc<MyWinProc>;
+// wc.lpszClassName = class_name;
+// ...
+// RegisterClassEx(&wc);
+//
+// CreateWindowW(class_name, window_name, ...
+//
+template <WNDPROC proc>
+LRESULT CALLBACK WrappedWindowProc(HWND hwnd, UINT message,
+ WPARAM wparam, LPARAM lparam) {
+ LRESULT rv = 0;
+ __try {
+ rv = proc(hwnd, message, wparam, lparam);
+ } __except(CallExceptionFilter(GetExceptionInformation())) {
+ }
+ return rv;
+}
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_WRAPPED_WINDOW_PROC_H_
diff --git a/src/base/win/wrapped_window_proc_unittest.cc b/src/base/win/wrapped_window_proc_unittest.cc
new file mode 100644
index 0000000..ccf3c85
--- /dev/null
+++ b/src/base/win/wrapped_window_proc_unittest.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/wrapped_window_proc.h"
+#include "base/message_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+DWORD kExceptionCode = 12345;
+WPARAM kCrashMsg = 98765;
+
+// A trivial WindowProc that generates an exception.
+LRESULT CALLBACK TestWindowProc(HWND hwnd, UINT message,
+ WPARAM wparam, LPARAM lparam) {
+ if (message == kCrashMsg)
+ RaiseException(kExceptionCode, 0, 0, NULL);
+ return DefWindowProc(hwnd, message, wparam, lparam);
+}
+
+// This class implements an exception filter that can be queried about a past
+// exception.
+class TestWrappedExceptionFiter {
+ public:
+ TestWrappedExceptionFiter() : called_(false) {
+ EXPECT_FALSE(s_filter_);
+ s_filter_ = this;
+ }
+
+ ~TestWrappedExceptionFiter() {
+ EXPECT_EQ(s_filter_, this);
+ s_filter_ = NULL;
+ }
+
+ bool called() {
+ return called_;
+ }
+
+ // The actual exception filter just records the exception.
+ static int Filter(EXCEPTION_POINTERS* info) {
+ EXPECT_FALSE(s_filter_->called_);
+ if (info->ExceptionRecord->ExceptionCode == kExceptionCode)
+ s_filter_->called_ = true;
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+
+ private:
+ bool called_;
+ static TestWrappedExceptionFiter* s_filter_;
+};
+TestWrappedExceptionFiter* TestWrappedExceptionFiter::s_filter_ = NULL;
+
+} // namespace.
+
+TEST(WrappedWindowProc, CatchesExceptions) {
+ HINSTANCE hinst = GetModuleHandle(NULL);
+ std::wstring class_name(L"TestClass");
+
+ WNDCLASS wc = {0};
+ wc.lpfnWndProc = base::win::WrappedWindowProc<TestWindowProc>;
+ wc.hInstance = hinst;
+ wc.lpszClassName = class_name.c_str();
+ RegisterClass(&wc);
+
+ HWND window = CreateWindow(class_name.c_str(), 0, 0, 0, 0, 0, 0, HWND_MESSAGE,
+ 0, hinst, 0);
+ ASSERT_TRUE(window);
+
+ // Before generating the exception we make sure that the filter will see it.
+ TestWrappedExceptionFiter wrapper;
+ base::win::WinProcExceptionFilter old_filter =
+ base::win::SetWinProcExceptionFilter(TestWrappedExceptionFiter::Filter);
+
+ SendMessage(window, kCrashMsg, 0, 0);
+ EXPECT_TRUE(wrapper.called());
+
+ base::win::SetWinProcExceptionFilter(old_filter);
+}